Connecting PetClinic application to PostgreSQL database

postgresql spring boot

In this configuration, we leverage the Service Binding Operator to collect the binding data from the PostgreSQL database and to project them into the Spring PetClinic application.

The PostgreSQL database in this scenario is deployed using Kubernetes Deployment of the postgres container image.

This scenario involves the following procedures:

Creating a PostgreSQL database instance

For the application to use a PostgreSQL database service, you must create a PostgreSQL database instance.

To create a PostgreSQL database instance, you must create the following Kubernetes resources:

Deployment resource to run the actual database instance

apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-petclinic-postgresql
  labels:
    app: spring-petclinic-postgresql
  annotations:
    service.binding/type: "postgresql"
    service.binding/host: "path={.metadata.name}.{.metadata.namespace}"
    service.binding/port: "path={.spec.template.spec.containers[0].ports[0].containerPort}"
    service.binding: "path={.spec.template.spec.volumes[0].secret.secretName},objectType=Secret"
spec:
  replicas: 1
  selector:
    matchLabels:
      app: spring-petclinic-postgresql
  template:
    metadata:
      labels:
        app: spring-petclinic-postgresql
    spec:
      containers:
        - name: postgresql
          image: postgres:13
          imagePullPolicy: IfNotPresent
          env:
            - name: POSTGRES_DB_FILE
              value: /secrets/database
            - name: POSTGRES_PASSWORD_FILE
              value: /secrets/password
            - name: POSTGRES_USER_FILE
              value: /secrets/username
            - name: PGDATA
              value: /tmp/data
          volumeMounts:
            - name: spring-petclinic-postgresql
              mountPath: "/secrets"
              readOnly: true
          ports:
            - name: postgresql
              containerPort: 5432
      volumes:
        - name: spring-petclinic-postgresql
          secret:
            secretName: spring-petclinic-postgresql

Secret resource to store the database credentials

apiVersion: v1
kind: Secret
metadata:
  name: spring-petclinic-postgresql
stringData:
  database: spring-petclinic-postgresql-db
  username: spring-petclinic-postgresql-user
  password: spring-petclinic-postgresql-passwd

Service resource to provide a way to access to the database

apiVersion: v1
kind: Service
metadata:
  labels:
    app: spring-petclinic-postgresql
  name: spring-petclinic-postgresql
spec:
  ports:
    - port: 5432
      protocol: TCP
      targetPort: 5432
  selector:
    app: spring-petclinic-postgresql

Procedure

  1. Create the database resources by running the following command:

  2. After you have created the database instance, verify that the respective pod in the my-petclinic namespace is up and running (it will take less than a minute):

    kubectl get pods -n my-petclinic
    Example output:
    NAME                                          READY  STATUS   RESTARTS  AGE
    spring-petclinic-postgresql-6db5594876-4556g  1/1    Running  0         16m

    The previous output verifies that the setup of the database for the application is complete. You can deploy the sample application and connect it to the database service.

The Deployment resource of the database has a couple of annotations set in the .metadata.annotations section:

service.binding/type: "postgresql"
service.binding/host: "path={.metadata.name}.{.metadata.namespace}"
service.binding/port: "path={.spec.template.spec.containers[0].ports[0].containerPort}"
service.binding: "path={.spec.template.spec.volumes[0].secret.secretName},objectType=Secret"

You can view them by running the following command:

kubectl annotate --list deployment spring-petclinic-postgresql -n my-petclinic | grep '^service.binding'

These annotations are necessary to be set on the Deployment resource of the database to make it a bindable service. Doing so exposes the binding data such as the database connection information or credentials that can be detected by the Service Binding Operator. Binding data values such as the type of service, the host and port connection information, and a reference to the secret containing the database credentials are specified in these annotations.

Based on the type of the resource, there are many ways to expose binding data from the backing service.

Now, after the database is configured for the application, you can deploy the application and connect it to the database service.

Deploying the Spring PetClinic application

To deploy the Spring PetClinic application on our Kubernetes cluster, use a deployment configuration consisting of the following resources:

Deployment resource to run the actual application instance

apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-petclinic
  labels:
    app: spring-petclinic
spec:
  replicas: 1
  selector:
    matchLabels:
      app: spring-petclinic
  template:
    metadata:
      labels:
        app: spring-petclinic
    spec:
      containers:
        - name: app
          image: quay.io/service-binding/spring-petclinic:latest
          imagePullPolicy: Always
          env:
          - name: SPRING_PROFILES_ACTIVE
            value: postgres
          ports:
          - name: http
            containerPort: 8080

Service resource to provide a way to access the application UI

apiVersion: v1
kind: Service
metadata:
  labels:
    app: spring-petclinic
  name: spring-petclinic
spec:
  type: NodePort
  ports:
    - port: 80
      protocol: TCP
      targetPort: 8080
  selector:
    app: spring-petclinic

Procedure

  1. Create the application resources by running the following command:

  2. At this stage, the application is not yet connected to the database service. Hence the pod fails to start. To verify run the following command:

    kubectl get pods -n my-petclinic
    Example output:
    NAME                                 READY  STATUS            RESTARTS    AGE
    spring-petclinic-5d47b7dbcd-7zd8v    0/1    CrashLoopBackOff  1 (7s ago)  28s

You can now use the Service Binding Operator to connect the application to the database service.

Connecting the application to the database service with Service Binding Operator

In the absence of the Service Binding Operator, as an administrator of the application, you must perform the following steps manually to extract all the configuration details, create a Secret resource, and expose it to the application through a volume mount in Kubernetes:

  1. Identify the required values for connecting the application to the database.

  2. Locate the resources where the values are present.

  3. Take the values from different resources and create a Secret resource.

  4. Mount the Secret resource into the application.

  5. Depending on the application requirement expose the values as environment variables or files.

To leverage the Service Binding Operator as a way to easily and safely connect the sample application to the database service, you must create a ServiceBinding custom resource (CR) that triggers the Service Binding Operator to project the binding data into the application:

ServiceBinding resource to project the binding data

apiVersion: binding.operators.coreos.com/v1alpha1
kind: ServiceBinding
metadata:
  name:
    spring-petclinic-postgresql
spec:
  services:
    - group: apps
      version: v1
      kind: Deployment
      name: spring-petclinic-postgresql
  application:
    name: spring-petclinic
    group: apps
    version: v1
    resource: deployments

The .spec field of the ServiceBinding CR has two sections:

  • The first section is a list of service resources (.spec.services). The services resources point to the database service resources. For more information on how the values are exposed from the service resources, see the Exposing binding data section.

  • The second section is the application (.spec.application). The application points to a Deployment or any resource that is compliant with PodSpec.

Procedure

  1. Create the ServiceBinding CR by running the following command in shell:

  2. Verify that the request for service binding is successful by running the following command:

    kubectl get servicebindings -n my-petclinic
    Example output:
    NAME                          READY   REASON              AGE
    spring-petclinic-postgresql   True    ApplicationsBound   28s

    By creating this ServiceBinding resource, we now have the binding data values from the database that is to be projected into the application container as files, by default. Alternatively, you can also choose to project the binding data values as environment variables if you prefer. If you check under the /bindings/spring-petclinic-postgresql directory, you can see all the values from the Secret resource projected there.

    In the case of the previous example, you can find username and password as the projected values. The values pointed out through the annotation are also projected, such as the database, host, and port values. For connectivity, the type value is projected as the binding data.

    The application looks for the SERVICE_BINDING_ROOT environment variable to find the location of the /bindings directory. The Spring Boot application used here uses the Spring Cloud Bindings library and it looks for the SERVICE_BINDING_ROOT environment variable to get the projected binding data. For more information on how an application uses these values, see the Projecting binding data section.

  3. Verify that the binding is successful by setting up the port forwarding from the application port to access the sample application from your local environment:

    kubectl port-forward --address 0.0.0.0 svc/spring-petclinic 8080:80 -n my-petclinic
    Example output:
    Forwarding from 0.0.0.0:8080 -> 8080
  4. Access http://localhost:8080.

    You can now remotely access web UI of the application and see that the application is now connected to the database service.

For more information on creating requests for service binding, see the Creating service binding section.

Conclusion

In this scenario, we set up a PostgreSQL database and connected it to the Spring PetClinic application using the Service Binding Operator to collect the binding data and expose them to the application.

Next Steps

By using service bindings, developers are able to more easily leverage the services available to them on a Kubernetes cluster. This method provides consistency across different services and is repeatable for the developers. Service binding provides a unified way to create a binding connection between the application and service and eliminates the need for the usual manual and error-prone configuration.

For more information, see the following sections: