Skip to content

Instantly share code, notes, and snippets.

@baijum
Last active September 15, 2022 18:03
Show Gist options
  • Save baijum/96158408eaf544f692acfad42f2b49df to your computer and use it in GitHub Desktop.
Save baijum/96158408eaf544f692acfad42f2b49df to your computer and use it in GitHub Desktop.
Spring PetClinic with Amazon Relational Database Service using AWS Controllers for Kubernetes

Spring PetClinic with Amazon Relational Database Service using AWS Controllers for Kubernetes

Spring PetClinic is a sample Spring Boot web application. This article shows how to connect the application to an Amazon Relational Database Service using AWS Controllers for Kubernetes and an operator that implements the Service Binding Specification for Kubernetes.

Prerequisites

Spring PetClinic Container Image

I created a repository for the container image in the quay.io. Then I ran these commands to create the container image and push to the repository. This container image is going to be used to deploy the application.

$ git clone https://github.com/spring-projects/spring-petclinic.git
$ spring-petclinic
$ sudo apt-get install openjdk-11-jdk -y
$ export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64
$ ./mvnw spring-boot:build-image
$ docker images
REPOSITORY                                         TAG                   IMAGE ID       CREATED         SIZE
spring-petclinic                                   2.7.0-SNAPSHOT        73e160e426ea   42 years ago    266MB
$ docker tag spring-petclinic:2.7.0-SNAPSHOT quay.io/bmuthuka/spring-petclinic:2.7.0-SNAPSHOT
$ docker login quay.io # enter credentials when propmted
$ docker push quay.io/bmuthuka/spring-petclinic:2.7.0-SNAPSHOT

The container image is built with Spring Cloud Bindings. This makes the application to read database connection configuration values from the file system. The Service Binding website has a good explanation about how the application consume bindings. I have written a blog post about how to enable Spring Cloud Bindings for a Spring Boot application.

Secret Resource for Direct Secret Reference

You need a Secret resource with type, provider and password data entries. This Secret resource is used in the DBInstance resource to specify the password field. Later the same Secret resource can be used as the target in the FieldExport resources. Finally ServiceBinding resource can use the Secret resource for Direct Secret Reference.

apiVersion: v1
kind: Secret
metadata:
  name: rds-postgres-field-exports
  namespace: ack-system
stringData:
  type: postgresql
  provider: aws
  password: "secret123"

The Spring Cloud Bindings expects the value for type as postgresql. Note: The provider field is not used by the Spring PetClinic application.

Provision a PostgreSQL RDS Cluster using DBInstance Resource

The DBInstance custom resource can be used to define the RDS database. Since I need a PostgreSQL database, I specified the engine as postgres in the .spec.engine field.

apiVersion: rds.services.k8s.aws/v1alpha1
kind: DBInstance
metadata:
  name: rds-postgres-dbinstance
  namespace: ack-system
spec:
  allocatedStorage: 20
  dbInstanceClass: db.t3.micro
  dbInstanceIdentifier: rds-postgres-demo
  engine: postgres
  engineVersion: "14"
  masterUsername: dbuser
  masterUserPassword:
    namespace: ack-system
    name: rds-postgres-field-exports
    key: password

Export the Fields Required for Database Connection

To export the fields, you can create multiple FieldExport custom resources with the values required for database connectivity. Here is the resource that I defined:

---
apiVersion: services.k8s.aws/v1alpha1
kind: FieldExport
metadata:
  name: rds-postgres-field-exports-host
  namespace: ack-system
spec:
  to:
    name: rds-postgres-field-exports
    kind: secret
    key: host
  from:
    path: ".status.endpoint.address"
    resource:
      group: rds.services.k8s.aws
      kind: DBInstance
      name: rds-postgres-dbinstance
---
apiVersion: services.k8s.aws/v1alpha1
kind: FieldExport
metadata:
  name: rds-postgres-field-exports-database
  namespace: ack-system
spec:
  to:
    name: rds-postgres-field-exports
    kind: secret
    key: database
  from:
    path: ".spec.engine"
    resource:
      group: rds.services.k8s.aws
      kind: DBInstance
      name: rds-postgres-dbinstance
---
apiVersion: services.k8s.aws/v1alpha1
kind: FieldExport
metadata:
  name: rds-postgres-field-exports-port
  namespace: ack-system
spec:
  to:
    name: rds-postgres-field-exports
    kind: secret
    key: port
  from:
    path: ".status.endpoint.port"
    resource:
      group: rds.services.k8s.aws
      kind: DBInstance
      name: rds-postgres-dbinstance
---
apiVersion: services.k8s.aws/v1alpha1
kind: FieldExport
metadata:
  name: rds-postgres-field-exports-user
  namespace: ack-system
spec:
  to:
    name: rds-postgres-field-exports
    kind: secret
    key: username
  from:
    path: ".spec.masterUsername"
    resource:
      group: rds.services.k8s.aws
      kind: DBInstance
      name: rds-postgres-dbinstance

The .spec.to refers to the Secret resource created in previous section. The .spec.to.key refers to the target key name required for the PostgreSQL database connection. The .spec.from points to the DBInstance resource.

Application Deployment

In the Deployment, there is a special label psql.provider: aws added. This label is later used in the ServiceBinding resource to refer the workload. An environment variable SPRING_PROFILES_ACTIVE with value as postgres is required to activate the profile. This will make the application to look for PostgreSQL specific database configuration values. The container image should point to the newly updated location of the Spring PetClinic container image.

You can also create a Service pointing to the Deployment. Later it can be used to access the application.

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-petclinic
  namespace: ack-system
  labels:
    app: spring-petclinic
    psql.provider: aws
spec:
  replicas: 1
  selector:
    matchLabels:
      app: spring-petclinic
  template:
    metadata:
      labels:
        app: spring-petclinic
    spec:
      containers:
        - name: app
          image: quay.io/bmuthuka/spring-petclinic:2.7.0-SNAPSHOT
          imagePullPolicy: Always
          env:
          - name: SPRING_PROFILES_ACTIVE
            value: postgres
          ports:
          - name: http
            containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: spring-petclinic
  name: spring-petclinic
  namespace: ack-system
spec:
  type: NodePort
  ports:
    - port: 80
      protocol: TCP
      targetPort: 8080
  selector:
    app: spring-petclinic

Service Binding

Service Binding Specification for Kubernetes standardizes exposing backing service secrets to application workloads. A backing service is any process that the application consumes over the network as part of its regular operation.

In the ServiceBiding resource, the Secret resource represents the RDS PostgreSQL service. And the workload is identified through a label selector (psql.provider: aws).

apiVersion: servicebinding.io/v1beta1
kind: ServiceBinding
metadata:
  name: servicebinding-rds-demo
  namespace: ack-system
spec:
  service:
    apiVersion: v1
    kind: Secret
    name: rds-postgres-field-exports
  workload:
    selector:
      matchLabels:
        psql.provider: aws
    apiVersion: apps/v1
    kind: Deployment

You can read more about Service Biding in the application operator documentation.

Verify the Application

To verify the application, you can forward the port of the application service:

kubectl port-forward --address 0.0.0.0 svc/spring-petclinic 8080:80 -n ack-system

Now you can access Spring PetClinic application from a web browser at http://localhost:8080

Conclusion

TBD

@vijtrip2
Copy link

typo: Section "Spring PetClinic Container Image"

The contailer image -> The container image

@baijum
Copy link
Author

baijum commented Sep 15, 2022

typo: Section "Spring PetClinic Container Image"

The contailer image -> The container image

Thanks! Fixed.

@RedbackThomson
Copy link

Hey Baiju. I wanted to ask a bit more about the context of this piece - Is this aimed at people who are familiar with service bindings, or more for overall beginners? Are there previous articles that people could reference for using some of these tools in simpler demonstrations?

A few small things I noticed:

  • The service created manually to store the password is called rds-postgres-field-exports, but it is not generated through a field export. Might be good to just give that a clearer name like rds-postgres-configuration?
  • Would you mind hyperlinking the RDS DBInstance spec section to the API reference so that people can find more information about the spec fields?
  • The DBInstance you have created is not necessarily in the same VPC as the EKS cluster, how is it accessible to the deployment? There may need to be a section about setting up the VPC with proper security groups.
  • Does the application run its own auto-migrations on the database? Is there a need for a schema to be applied before connecting to the application?

I also wonder, have you tested failure scenarios for these operators? For instance, if the DBInstance cannot be started, I believe the FieldExport outputs will be empty - how is that handled by the ServiceBinding?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment