Setup of spring boot application & initialization of PostgreSQL database on Kubernetes – PART 1

  • How to deploy a containerized spring boot application , with PostgreSQL as database on minikube.
  • This post will also share details on how to initialize the database with tables and data during the initialization process.
  • Will use a spring boot application order service with a REST endpoint to fetch customer details
    • GET /customers
  • It uses spring JPA to access PostgreSQL
  • Details of the service – https://github.com/rajeshsgr/order-svc-k8
  • This article will demonstrate deploying this service on Minikube , More details on minikube can be found at- https://belowthemalt.com/2022/03/17/minikube-kubectl-and-local-development-deployment-of-apps-in-kubernetes/
  • Key steps for this deployment process are
    • Building a docker image of order-service & upload to docker repository
    • Writing Kubernetes deployment file to initialize and run database
    • Writing Kubernetes deployment file for order-svc
    • Testing the service

Step 1: Building a docker image of order-service & uploading to docker repository

  • Build the application to generate jar file
    • mvn clean install -DskipTests
  • If you want to run this application is local , you will have to update the application.properties file with database details
  • Create a docker image for the service , refer to Dockerfile https://github.com/rajeshsgr/order-svc-k8/blob/main/Dockerfile
FROM openjdk:8-jdk-alpine
VOLUME /tmp
EXPOSE 8080
RUN mkdir -p /app/
RUN mkdir -p /app/logs/
ADD target/order-0.0.1-SNAPSHOT.jar /app/app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-Dspring.profiles.active=container", "-jar", "/app/app.jar"]
  • Dockerfile has mainly instructions to add openjdk 8 image, expose port 8080 and add the generated jar file from previous step to the image
  • To build an image with the name order-svc-k8 from the Dockerfile , run the below command
    • docker build -t raje/order-svc-k8 .
  • Once the above step is done you can check the image with docker images command
  • To push this image to docker repository, give the command docker push raje/order-svc-k8
  • At this stage, we have built our spring boot app, dockerized it and uploaded to the docker repository

Step 2: Writing Kubernetes deployment file to initialize and run database

  • For PostgreSQL, we will use the docker image . Link to repo – https://hub.docker.com/_/postgres
  • Other 2 important use cases of this demo are
    • Mount a path on host , so that the data can be retrieved even if the cluster goes down
    • Initialize database with table and scripts
  • In order to mount a persistent storage, we will have to deploy a resource type of Persistent Volume and Persistent Volume Claim
  • Since we need a persistent storage for PostgreSQL to store data, we will provision the storage using Persistent Volume yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: postgresql-claim0
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/Users/rajeshp/docker/postgres/docker-pg-vol/data"
  • Above yaml is for PersistentVolume object , which has details of host path , storage class , size etc. The name defined for this is – postgresql-claim0
  • Persistent volumes have a lifecycle that is independent of any individual pod that uses the storage.
  • In order for our application to consume this storage space, we will have to request it using a Persistent Volume Claim request.
  • yaml for Persistent Volume Claim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgresql-claim0
  labels:
    app: postgresql
    tier: database
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 100Mi
  • Next we want to build a configuration map object, which will have our database initialization scripts
  • Configuration maps are kubernetes objects which are used to store non-confidential data in key-value pairs
apiVersion: v1
kind: ConfigMap
metadata:
  name: postgresql-initdb-config
data:
  init.sql: |
    CREATE TABLE IF NOT EXISTS customers (
        customer_id bpchar NOT NULL,
        company_name character varying(40) NOT NULL,
        contact_name character varying(30),
        contact_title character varying(30),
        address character varying(60),
        city character varying(15),
        region character varying(15),
        postal_code character varying(10),
        country character varying(15),
        phone character varying(24),
        fax character varying(24)
    );

 INSERT INTO customers VALUES ('ALFKI', 'Alfreds Futterkiste', 'Maria Anders', 'Sales Representative', 'Obere Str. 57', 'Berlin', NULL, '12209', 'Germany', '030-0074321', '030-0076545');
  • Since our order service will need to access this database, we will need a Kubernetes service object
  • Service is responsible for enabling network access to a set of pods
apiVersion: v1
kind: Service
metadata:
  name: postgresql
  labels:
    app: postgresql
    tier: database
spec:
  ports:
    - port: 5432
      targetPort: 5432
  selector:
    app: postgresql
    tier: database
  • Services select Pods based on their labels. When a network request is made to the service, it selects all Pods in the cluster matching the service’s selector, chooses one of them, and forwards the network request to it.
  • Next, we need the deployment object.
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgresql
  labels:
    app: postgresql
    tier: database
spec:
  selector:
    matchLabels:
      app: postgresql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: postgresql
        tier: database
    spec:
      containers:
        - name: postgresql
          image: postgres:12
          imagePullPolicy: "IfNotPresent"
          env:
            - name: POSTGRES_DB
              value: northwind
            - name: POSTGRES_USER
              value: postgres
            - name: POSTGRES_PASSWORD
              value: changeme
          ports:
            - containerPort: 5432
              name: postgresql
          volumeMounts:
            - name: postgresql-claim0
              mountPath: /var/lib/postgresql/data

            - mountPath: /docker-entrypoint-initdb.d
              name: postgresql-initdb
      volumes:
        - name: postgresql-claim0
          persistentVolumeClaim:
            claimName: postgresql-claim0

        - name: postgresql-initdb
          configMap:
            name: postgresql-initdb-config
  • Key elements of the deployment yaml
    • Deploys postgres:12 container
    • Have environment variables , which has the database name, user and password
    • Mounts the persistent volume , defined using Persistent volume object
    • Passes the configmap value for docker to execute
  • Bring minikube up by issuing command: minikube start
  • Let us now deploy all the kubernetes resources by giving following commands
kubectl apply -f postgresql-claim0-persistentvolume.yaml
kubectl apply -f postgresql-claim0-persistentvolumeclaim.yaml
kubectl apply -f postgresql-initial-data-configmap.yamlkubectl kubectl apply -f postgresql-deployment.yaml
kubectl apply -f postgresql-service.yaml
  • You can check status by kubectl get all command
  • In case you want to check the logs of database pod, you can check by issuing this command kubectl logs pod/postgresql-7bf5994f6f-kv5gs . Please replace the pod name with your pod name
  • Let us now verify this deployment , by connecting to database. Commands for the operations are –kubectl exec -it pod/postgresql-7bf5994f6f-kv5gs bash
  • Replace pod name with your pod name in the above command
  • Connect to postgres – psql -U postgres
  • Connect to northwind database – \c northwind
  • Check the tables – \dt
  • Check if database is initialized by executing – select * from customers;

Next steps of deploying the spring boot application is available in the Part 2 .

Thank You !!

2 thoughts on “Setup of spring boot application & initialization of PostgreSQL database on Kubernetes – PART 1

Leave a Reply