ServiceBindingRequest

Connecting applications to the services that support them—for example, establishing the exchange of credentials between a Java application and a database that it requires—is referred to as binding. The configuration and maintenance of this binding together of applications and backing services can be a tedious and inefficient process. Manually editing YAML files to define binding information is error-prone and can introduce difficult-to-debug failures.

Note: Service Binding Operator has changed significantly since this article was published and the information is now out of date. Please read How to use service binding with RabbitMQ for the latest information about this technology.

Introduction to service binding

The goal of the Service Binding Operator is to solve this binding problem. By making it easier for application developers to bind applications with needed backing services, the Service Binding Operator also assists operator providers in promoting and expanding the adoption of their operators. This article introduces the Service Binding Operator and describes how it functions. In the next article, we'll demonstrate its use through a real-world example.

The case for managed binding

The Service Binding Operator enables applications to use external services by automatically collecting and sharing binding information (credentials, connection details, volume mounts, secrets, etc.) with the application. In effect, the Service Binding Operator defines a contract between a "bindable" backing service (for example, a database operator) and an application requiring that backing service.

Note that in addition to the initial sharing of binding information, the binding is also "managed" by the Service Binding Operator. This statement means that, if credentials or URLs undergo modification by the backing service operator, those changes are automatically reflected in the application.

There are two parts to this contract. The first part concerns making the backing service bindable and the second part concerns binding the application and the service together. Both parts are supported by a new custom resource, the ServiceBindingRequest.

The ServiceBindingRequest custom resource

The Service Binding Operator enables application developers to more easily bind applications together with operator-managed backing services (such as a database) without having to perform manual configuration of secrets, ConfigMaps, etc. The Service Binding Operator accomplishes this task by automatically collecting binding information and sharing it with an application and an operator-managed backing service. This binding is performed through a new custom resource called a ServiceBindingRequest.

apiVersion: apps.openshift.io/v1alpha1
kind: ServiceBindingRequest
metadata:
  name: binding-request
  namespace: service-binding-demo
spec:
  applicationSelector:
    resourceRef: nodejs-rest-http-crud
    group: apps
    version: v1
    resource: deployments
  backingServiceSelector:
    group: postgresql.baiju.dev
    version: v1alpha1
    kind: Database
    resourceRef: db-demo
ServiceBindingRequest

Figure 1: The selectors in a ServiceBindingRequest.">

A ServiceBindingRequest includes the following two selectors. The first is the applicationSelector, which identifies the application to be bound with the backing service. The ResourceRef defined here marks an application for binding. The second is the backingServiceSelector, which identifies the backing service with which applications will be bound, as shown in Figure 1:

Additional data in the ServiceBindingRequest can contain a combination of sensitive information such as usernames and passwords, plus non-sensitive information such as port numbers. In order to configure an existing operator to be bindable, an operator provider has to add a ServiceBindingRequest descriptor to the operator’s manifest. The statusDescriptors in that manifest will contain the information needed by the Service Binding Operator to bind the application together with the backing service operator.

Note: Sample backing service operators that are already bindable are available here.

Figure 2 illustrates the relationship between the ServiceBindingRequest, its selectors, the application being bound, and the backing service. Note that for the applicationSelector, the relevant attributes are the application’s group, version, resource, and resourceRef, and that for the backingServiceSelector, the relevant attributes are the version, kind, and resourceRef:

The relationship between the ServiceBindingRequest and related components.
Figure 2: The relationship between the ServiceBindingRequest and related components.

Making an operator-managed backing service bindable

To make a service bindable, the operator provider needs to express the information needed by applications to bind with the services provided by the operator. In other words, the operator provider must express the information that is interesting to applications.

The binding information is provided as annotations in the Custom Resource Definition (CRD) of the operator that manages the backing service. The Service Binding Operator extracts the annotations to bind the application together with the backing service.

For example, Figure 3 shows a bind-able operator's annotations in its CRD for a PostgreSQL database backing operator. Note the highlighted text and that status.dbConfigMap is a ConfigMap where the username and password are interesting for binding:

A bindable operator's CRD annotations.
Figure 3: A bindable operator's CRD annotations.

An alternate method for making a service bindable enables operators that manage backing services, but which do not have any metadata in their CSV to use the Service Binding Operator to bind together the service and applications. The Service Binding Operator binds all sub-resources defined in the backing service CR by populating the binding secret with information from the routes, services, ConfigMaps, and secrets owned by the backing service CR.

Note: This is how resource and sub-resource relationships are set in Kubernetes.

The binding itself is initiated by the introduction of the API option in the backing service CR (as shown in Figure 4):

Binding initiation.
Figure 4: Binding initiation.

When this API option is set to true, the Service Binding Operator automatically detects the routes, services, ConfigMaps, and secrets owned by the backing service CR.

Binding an application together with a backing service

Manually binding an application together with a backing service without the Service Binding Operator is a time-consuming and error-prone process. The steps needed to perform the binding include:

  1. Locating the binding information in the backing service’s resources.
  2. Creating and referencing any necessary secrets.
  3. Manually editing the application’s DeploymentConfig, Deployment, Replicaset, KnativeService, or anything else that uses a standard PodSpec to reference the binding request.

In contrast, by using the Service Binding Operator, the only action that an application developer must make during the import of the application is to make clear the intent that the binding must be performed. This task is accomplished by creating the ServiceBindingRequest. The Service Binding Operator takes that intent and performs the binding on behalf of the application developer.

In summary, there are two steps that an application developer must perform. First, they must signal the intent to bind the application to the backing service by adding labels to the application. Second, they must create a new ServiceBindingRequest that references the backing service.

When the ServiceBindingRequest is created, the Service Binding Operator's controller collects the binding information into an intermediary secret, which it shares with the application through environment variables.

Note that an optional approach that can be used to provide binding information is through custom environment variables. We'll provide more on this topic, along with a real-world example, in the next article.

Resources

Last updated: November 5, 2021