Chapter 1. Service binding
Chapter 1. Service binding The following chapter provides information about service binding and workload projection that were added to Red Hat build of Quarkus in version 2.7.5 and are in the state of Technology Preview in version 3.8. Generally, OpenShift applications and services also referred to as deployable workloads, need to be connected to other services for retrieving additional information, such as service URLs or credentials. The Service Binding Operator facilitates retrieval of the necessary information, which is then made available to applications and service-binding tools like the quarkus-kubernetes-service-binding extension through environment variables without directly influencing or determining the use of the extension tool itself. Quarkus supports the Service binding specification for Kubernetes to bind services to applications. Specifically, Quarkus implements the workload projection part of the specification, enabling applications to bind to services like databases or brokers, requiring only minimal configuration. To enable service binding for the available extensions, include the quarkus-kubernetes-service-binding extension to the application dependencies. You can use the following extensions for service binding and for workload projection: quarkus-jdbc-mariadb quarkus-jdbc-mssql quarkus-jdbc-mysql quarkus-jdbc-postgresql quarkus-mongo-client - Technology Preview quarkus-kafka-client quarkus-smallrye-reactive-messaging-kafka quarkus-reactive-mssql-client - Technology Preview quarkus-reactive-mysql-client quarkus-reactive-pg-client 1.1. Workload projection Workload projection is a process of obtaining the configuration for services from the Kubernetes cluster. This configuration takes the form of directory structures that follow certain conventions and are attached to an application or a service as a mounted volume. The kubernetes-service-binding extension uses this directory structure to create configuration sources, which allows you to configure additional modules, such as databases or message brokers. You can use workload projection during application development to connect your application to a development database or other locally run services without changing the application code or configuration. For an example of a workload projection where the directory structure is included in the test resources and passed to an integration test, see the Kubernetes Service Binding datasource GitHub repository. Note The k8s-sb directory is the root of all service bindings. In this example, only one database called fruit-db is intended to be bound. This binding database has the type file, which specifies postgresql as the database type, while the other files in the directory provide the necessary information to establish the connection. When your Red Hat build of Quarkus project obtains information from SERVICE_BINDING_ROOT environment variables that are set by OpenShift Container Platform, you can locate generated configuration files that are present in the file system and use them to map the configuration-file values to properties of certain extensions. 1.2. Introduction to Service Binding Operator The Service Binding Operator is an Operator that implements the Service Binding Specification for Kubernetes and is meant to simplify the binding of services to an application. Containerized applications that support workload projection obtain service binding information in the form of volume mounts. The Service Binding Operator reads binding service information and mounts it to the application containers that need it. The correlation between application and bound services is expressed through the ServiceBinding resources, which declares the intent of what services are meant to be bound to what application. The Service Binding Operator watches for ServiceBinding resources, which inform the Operator what applications are meant to be bound with what services. When a listed application is deployed, the Service Binding Operator collects all the binding information that must be passed to the application and then upgrades the application container by attaching a volume mount with the binding information. The Service Binding Operator completes the following actions: Observes ServiceBinding resources for workloads bound to a particular service. Applies the binding information to the workload using volume mounts. The following chapter describes the automatic and semi-automatic service binding approaches and their use cases. The kubernetes-service-binding extension generates a ServiceBinding resource with either approach. With the semi-automatic approach, users must manually provide a configuration for target services. With the automatic approach, no additional configuration is needed for a limited set of services generating the ServiceBinding resource. Additional resources Workload projection 1.3. Semi-automatic service binding A service binding process starts with a user specification of required services that will be bound to a certain application. This expression is summarized in the ServiceBinding resource generated by the kubernetes-service-binding extension. The use of the kubernetes-service-binding extensions helps users to generate ServiceBinding resources with minimal configuration, therefore simplifying the process overall. The Service Binding Operator responsible for the binding process then reads the information from the ServiceBinding resource and mounts the required files to a container accordingly. An example of the ServiceBinding resource: apiVersion: binding.operators.coreos.com/v1beta1 kind: ServiceBinding metadata: name: binding-request namespace: service-binding-demo spec: application: name: java-app group: apps version: v1 resource: deployments services: - group: postgres-operator.crunchydata.com version: v1beta1 kind: Database name: db-demo id: postgresDB Note The quarkus-kubernetes-service-binding extension provides a more compact way of expressing the same information. For example: quarkus.kubernetes-service-binding.services.db-demo.api-version=postgres-operator.crunchydata.com/v1beta1 quarkus.kubernetes-service-binding.services.db-demo.kind=Database After adding the earlier configuration properties inside your application.properties , the quarkus-kubernetes , in combination with the quarkus-kubernetes-service-binding extension, automatically generates the ServiceBinding resource. The earlier mentioned db-demo property-configuration identifier now has a double role and also completes the following actions: Correlates and groups api-version and kind properties together. Defines the name property for the custom resource, which you can edit later if needed. For example: quarkus.kubernetes-service-binding.services.db-demo.api-version=postgres-operator.crunchydata.com/v1beta1 quarkus.kubernetes-service-binding.services.db-demo.kind=Database quarkus.kubernetes-service-binding.services.db-demo.name=my-db Additional resources How to use Quarkus with the Service Binding Operator List of bindable Operators 1.4. Generating a ServiceBinding custom resource by using the semi-automatic method You can generate a ServiceBinding resource semi-automatically. The following procedure shows the OpenShift Container Platform deployment process, including the installation of operators for configuring and deploying an application. In this procedure, you install the Service Binding Operator and the PostgreSQL Operator from Crunchy Data . Important PostgreSQL Operator is a third-party component. For PostgreSQL Operator support policies and
apiVersion: binding.operators.coreos.com/v1beta1 kind: ServiceBinding metadata: name: binding-request namespace: service-binding-demo spec: application: name: java-app group: apps version: v1 resource: deployments services: - group: postgres-operator.crunchydata.com version: v1beta1 kind: Database name: db-demo id: postgresDB quarkus.kubernetes-service-binding.services.db-demo.api-version=postgres-operator.crunchydata.com/v1beta1 quarkus.kubernetes-service-binding.services.db-demo.kind=Database quarkus.kubernetes-service-binding.services.db-demo.api-version=postgres-operator.crunchydata.com/v1beta1 quarkus.kubernetes-service-binding.services.db-demo.kind=Database quarkus.kubernetes-service-binding.services.db-demo.name=my-db get csv -w get csv -w new-project demo apiVersion: postgres-operator.crunchydata.com/v1beta1 kind: PostgresCluster metadata: name: hippo spec: openshift: true image: registry.developers.crunchydata.com/crunchydata/crunchy-postgres:ubi8-14.2-1 postgresVersion: 14 instances: - name: instance1 dataVolumeClaimSpec: accessModes: - "ReadWriteOnce" resources: requests: storage: 1Gi backups: pgbackrest: image: registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.38-0 repos: - name: repo1 volume: volumeClaimSpec: accessModes: - "ReadWriteOnce" resources: requests: storage: 1Gi apply -f ~/pg-cluster.yml get pods -n demo mvn com.redhat.quarkus.platform:quarkus-maven-plugin:3.8.6.SP3-redhat-00002:create -DplatformGroupId=com.redhat.quarkus.platform -DplatformVersion=3.8.6.SP3-redhat-00002 -DprojectGroupId=org.acme -DprojectArtifactId=todo-example -DclassName="org.acme.TodoResource" -Dpath="/todo" ./mvnw quarkus:add-extension -Dextensions="resteasy-reactive-jackson,jdbc-postgresql,hibernate-orm-panache,openshift,kubernetes-service-binding" package org.acme; import jakarta.persistence.Column; import jakarta.persistence.Entity; import io.quarkus.hibernate.orm.panache.PanacheEntity; @Entity public class Todo extends PanacheEntity { @Column(length = 40, unique = true) public String title; public boolean completed; public Todo() { } public Todo(String title, Boolean completed) { this.title = title; } } package org.acme; import jakarta.transaction.Transactional; import jakarta.ws.rs.*; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response.Status; import java.util.List; @Path("/todo") public class TodoResource { @GET @Path("/") public List<Todo> getAll() { return Todo.listAll(); } @GET @Path("/{id}") public Todo get(@PathParam("id") Long id) { Todo entity = Todo.findById(id); if (entity == null) { throw new WebApplicationException("Todo with id of " + id + " does not exist.", Status.NOT_FOUND); } return entity; } @POST @Path("/") @Transactional public Response create(Todo item) { item.persist(); return Response.status(Status.CREATED).entity(item).build(); } @GET @Path("/{id}/complete") @Transactional public Response complete(@PathParam("id") Long id) { Todo entity = Todo.findById(id); entity.id = id; entity.completed = true; return Response.ok(entity).build(); } @DELETE @Transactional @Path("/{id}") public Response delete(@PathParam("id") Long id) { Todo entity = Todo.findById(id); if (entity == null) { throw new WebApplicationException("Todo with id of " + id + " does not exist.", Status.NOT_FOUND); } entity.delete(); return Response.noContent().build(); } } quarkus.kubernetes-service-binding.services.my-db.api-version=postgres-operator.crunchydata.com/v1beta1 quarkus.kubernetes-service-binding.services.my-db.kind=PostgresCluster quarkus.kubernetes-service-binding.services.my-db.name=hippo quarkus.datasource.db-kind=postgresql quarkus.hibernate-orm.database.generation=drop-and-create quarkus.hibernate-orm.sql-load-script=import.sql INSERT INTO todo(id, title, completed) VALUES (nextval('hibernate_sequence'), 'Finish the blog post', false); mvn clean install -Dquarkus.kubernetes.deploy=true -DskipTests get pods -n demo -w port-forward service/todo-example 8080:80 http://localhost:8080/todo quarkus.datasource.db-kind=postgresql services: - apiVersion: postgres-operator.crunchydata.com/v1beta1 kind: PostgresCluster name: postgresql quarkus.datasource.fruits-db.db-kind=postgresql services: - apiVersion: postgres-operator.crunchydata.com/v1beta1 kind: PostgresCluster name: fruits-db quarkus.datasource.fruits-db.db-kind=mysql services: - apiVersion: pxc.percona.com/v1-9-0 kind: PerconaXtraDBCluster name: fruits-db quarkus.datasource.db-kind=postgresql quarkus.kubernetes-service-binding.services.postgresql.api-version=postgres-operator.crunchydata.com/v1beta2 quarkus.datasource.fruits-db.db-kind=postgresql quarkus.kubernetes-service-binding.services.fruits-db.api-version=postgres-operator.crunchydata.com/v1beta1 quarkus.kubernetes-service-binding.services.fruits-db.kind=PostgresCluster quarkus.kubernetes-service-binding.services.fruits-db.name=fruits-db