Integrating External Services

Overview

Many OKD applications use external resources, such as external databases, or an external SaaS endpoint. These external resources can be modeled as native OKD services, so that applications can work with them as they would any other internal service.

Egress traffic can be controlled by firewall rules or an Egress router. This permits having a static IP address for their application service.

Defining a Service for an External Database

One of the most common types of external services is an external database. To support an external database, an application needs:

  1. An endpoint to communicate with.

  2. A set of credentials and coordinates, including:

    • A user name

    • A passphrase

    • A database name

The solution for integrating with an external database includes:

  • A Service object to represent the SaaS provider as an OKD service.

  • One or more Endpoints for the service.

  • Environment variables in the appropriate pods containing the credentials.

The following steps outline a scenario for integrating with an external MySQL database:

Step 1: Define a Service

You can define a service either by providing an IP address and endpoints, or by providing a Fully qualified domain name (FQDN).

Using an IP address

  1. Create an OKD service to represent your external database. This is similar to creating an internal service; the difference is in the service’s Selector field.

    Internal OKD services use the Selector field to associate pods with services using labels. The EndpointsController system component synchronizes the endpoints for services that specify selectors with the pods that match the selector. The service proxy and OKD router load-balance requests to the service amongst the service’s endpoints.

    Services that represent an external resource do not require associated pods. Instead, leave the Selector field unset. This represents the external service, making the EndpointsController ignore the service and allows you to specify endpoints manually:

    1. kind: "Service"
    2. apiVersion: "v1"
    3. metadata:
    4. name: "external-mysql-service"
    5. spec:
    6. ports:
    7. -
    8. name: "mysql"
    9. protocol: "TCP"
    10. port: 3306
    11. targetPort: 3306 (1)
    12. nodePort: 0
    13. selector: {} (2)
    1Optional: The port on the backing pods to which the service forwards connections.
    2The selector field to leave blank.
  2. Next, create the required endpoints for the service. This gives the service proxy and router the location to send traffic directed to the service:

    1. kind: "Endpoints"
    2. apiVersion: "v1"
    3. metadata:
    4. name: "external-mysql-service" (1)
    5. subsets: (2)
    6. -
    7. addresses:
    8. -
    9. ip: "10.0.0.0" (3)
    10. ports:
    11. -
    12. port: 3306 (4)
    13. name: "mysql"
    1The name of the Service instance, as defined in the previous step.
    2Traffic to the service will be load-balanced between the supplied Endpoints if more than one is supplied.
    3Endpoints IPs cannot be loopback (127.0.0.0/8), link-local (169.254.0.0/16), or link-local multicast (224.0.0.0/24).
    4The port and name definition must match the port and name value in the service defined in the previous step.

Using an External Domain Name

Using external domain names make it easier to manage an external service linkage, because you do not have to worry about the external service’s IP addresses changing.

ExternalName services do not have selectors, or any defined ports or endpoints, therefore, you can use an ExternalName service to direct traffic to an external service.

  1. kind: "Service"
  2. apiVersion: "v1"
  3. metadata:
  4. name: "external-mysql-service"
  5. spec:
  6. type: ExternalName
  7. externalName: example.domain.name
  8. selector: {} (1)
1The selector field to leave blank.

Using an external domain name service tells the system that the DNS name in the externalName field (example.domain.name in the previous example) is the location of the resource that backs the service. When a DNS request is made against the Kubernetes DNS server, it returns the externalName in a CNAME record telling the client to look up the returned name to get the IP address.

Step 2: Consume a Service

Now that the service and endpoints are defined, give the appropriate pods access to the credentials to use the service by setting environment variables in the appropriate containers:

  1. kind: "DeploymentConfig"
  2. apiVersion: "v1"
  3. metadata:
  4. name: "my-app-deployment"
  5. spec: (1)
  6. strategy:
  7. type: "Rolling"
  8. rollingParams:
  9. updatePeriodSeconds: 1 (2)
  10. intervalSeconds: 1 (3)
  11. timeoutSeconds: 120
  12. replicas: 2
  13. selector:
  14. name: "frontend"
  15. template:
  16. metadata:
  17. labels:
  18. name: "frontend"
  19. spec:
  20. containers:
  21. -
  22. name: "helloworld"
  23. image: "origin-ruby-sample"
  24. ports:
  25. -
  26. containerPort: 3306
  27. protocol: "TCP"
  28. env:
  29. -
  30. name: "MYSQL_USER"
  31. value: "${MYSQL_USER}" (4)
  32. -
  33. name: "MYSQL_PASSWORD"
  34. value: "${MYSQL_PASSWORD}" (5)
  35. -
  36. name: "MYSQL_DATABASE"
  37. value: "${MYSQL_DATABASE}" (6)
1Other fields on the DeploymentConfig are omitted
2The time to wait between individual pod updates.
3The time to wait between polling the deployment status after update.
4The user name to use with the service.
5The passphrase to use with the service.
6The database name.

External Database Environment Variables

Using an external service in your application is similar to using an internal service. Your application will be assigned environment variables for the service and the additional environment variables with the credentials described in the previous step. For example, a MySQL container receives the following environment variables:

  • **EXTERNAL_MYSQL_SERVICE_SERVICE_HOST=<ip_address>**

  • **EXTERNAL_MYSQL_SERVICE_SERVICE_PORT=<port_number>**

  • **MYSQL_USERNAME=<mysql_username>**

  • **MYSQL_PASSWORD=<mysql_password>**

  • **MYSQL_DATABASE_NAME=<mysql_database>**

The application is responsible for reading the coordinates and credentials for the service from the environment and establishing a connection with the database via the service.

External SaaS Provider

A common type of external service is an external SaaS endpoint. To support an external SaaS provider, an application needs:

  1. An endpoint to communicate with

  2. A set of credentials, such as:

    1. An API key

    2. A user name

    3. A passphrase

The following steps outline a scenario for integrating with an external SaaS provider:

Using an IP address and Endpoints

  1. Create an OKD service to represent the external service. This is similar to creating an internal service; however the difference is in the service’s Selector field.

    Internal OKD services use the Selector field to associate pods with services using labels. A system component called EndpointsController synchronizes the endpoints for services that specify selectors with the pods that match the selector. The service proxy and OKD router load-balance requests to the service amongst the service’s endpoints.

    Services that represents an external resource do not require that pods be associated with it. Instead, leave the Selector field unset. This makes the EndpointsController ignore the service and allows you to specify endpoints manually:

    1. kind: "Service"
    2. apiVersion: "v1"
    3. metadata:
    4. name: "example-external-service"
    5. spec:
    6. ports:
    7. -
    8. name: "mysql"
    9. protocol: "TCP"
    10. port: 3306
    11. targetPort: 3306 (1)
    12. nodePort: 0
    13. selector: {} (2)
    1Optional: The port on the backing pods to which the service forwards connections.
    2The selector field to leave blank.
  2. Next, create endpoints for the service containing the information about where to send traffic directed to the service proxy and the router:

    1. kind: "Endpoints"
    2. apiVersion: "v1"
    3. metadata:
    4. name: "example-external-service" (1)
    5. subsets: (2)
    6. - addresses:
    7. - ip: "10.10.1.1"
    8. ports:
    9. - name: "mysql"
    10. port: 3306
    1The name of the Service instance.
    2Traffic to the service is load-balanced between the subsets supplied here.
  3. Now that the service and endpoints are defined, give pods the credentials to use the service by setting environment variables in the appropriate containers:

    1. kind: "DeploymentConfig"
    2. apiVersion: "v1"
    3. metadata:
    4. name: "my-app-deployment"
    5. spec: (1)
    6. strategy:
    7. type: "Rolling"
    8. rollingParams:
    9. timeoutSeconds: 120
    10. replicas: 1
    11. selector:
    12. name: "frontend"
    13. template:
    14. metadata:
    15. labels:
    16. name: "frontend"
    17. spec:
    18. containers:
    19. -
    20. name: "helloworld"
    21. image: "openshift/openshift/origin-ruby-sample"
    22. ports:
    23. -
    24. containerPort: 3306
    25. protocol: "TCP"
    26. env:
    27. -
    28. name: "SAAS_API_KEY" (2)
    29. value: "<SaaS service API key>"
    30. -
    31. name: "SAAS_USERNAME" (3)
    32. value: "<SaaS service user>"
    33. -
    34. name: "SAAS_PASSPHRASE" (4)
    35. value: "<SaaS service passphrase>"
    1Other fields on the DeploymentConfig are omitted.
    2SAAS_API_KEY: The API key to use with the service.
    3SAAS_USERNAME: The user name to use with the service.
    4SAAS_PASSPHRASE: The passphrase to use with the service.

    These variables get added to the containers as environment variables. Using environment variables allows service-to-service communication and it may or may not require additional parameters such as API keys, user name and password authentication, or certificates.

External SaaS Provider Environment Variables

Similarly, when using an internal service, your application is assigned environment variables for the service and the additional environment variables with the credentials described in the previous steps. In the previous example, the container receives the following environment variables:

  • **EXAMPLE_EXTERNAL_SERVICE_SERVICE_HOST=<ip_address>**

  • **EXAMPLE_EXTERNAL_SERVICE_SERVICE_PORT=<port_number>**

  • **SAAS_API_KEY=<saas_api_key>**

  • **SAAS_USERNAME=<saas_username>**

  • **SAAS_PASSPHRASE=<saas_passphrase>**

The application reads the coordinates and credentials for the service from the environment and establishes a connection with the service.

Using an External Domain Name

ExternalName services do not have selectors, or any defined ports or endpoints. You can use an ExternalName service to assign traffic to an external service outside the cluster.

  1. kind: "Service"
  2. apiVersion: "v1"
  3. metadata:
  4. name: "external-mysql-service"
  5. spec:
  6. type: ExternalName
  7. externalName: example.domain.name
  8. selector: {} (1)
1The selector field to leave blank.

Using an ExternalName service maps the service to the value of the externalName field (example.domain.name in the previous example), by automatically injecting a CNAME record, mapping the service name directly to an outside DNS address, and bypassing the need for endpoint records.