k8spractice PostgreSQL Service Deployment

This is part 2 of a 4-part series on deploying an application to Azure Kubernetes. Here’s a full list of this series:

Motivation

First, I want to point out that Azure provides a PostgreSQL service (see here for info) that you should prefer in a production environment. I haven’t personally used it so I can’t speak to its quality other than it’s probably better than doing what this post describes. My main motivation for deploying my own PostgreSQL instance is to have something to practice deploying a Kubernetes Persistent Volume. Before going any further the yaml file for the PostgreSQL deployment is available here.

Create the Azure Disk resource

The PostgreSQL service needs a persistent location to save the database data. For that, I’m using an Azure Managed Disk. I chose this because PostgreSQL creates symlinks for its data which this resource is able to do.

Start by following the instructions to “Create an Azure Disk”. After the disk resource was created, I went in and updated the network access to “Private endpoint (through disk access)” set Network access to “Private Endpoint” and added a Disk Access resource. See here for more information.

Configure persistent volume store

Below is the PersistentVolume resource definition:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: psqlvol
spec:
  storageClassName: psqldata
  azureDisk:
    kind: Managed
    diskURI: <AZ DISK INSTANCE ID>
    diskName: <AZ disk name>
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: 2Gi
  persistentVolumeReclaimPolicy: Retain

To get the value for <AZ DISK INSTANCE ID> use the following “az” comand

az disk show -g <Resource group name> -n <AZ DISK NAME> --query 'id'

Note that the value of <AZ disk name> must match the Disk resource’s name. AKS’ PersistentVolume implementation allows for an Azure Disk to be dynamically created. See here for more info on dynamically creating disks. Since this disk will be used by only a single Pod instance access mode is set to “ReadWriteOnce”. Reclaim policy is set to “Retain” so that data is not deleted if all claims are released.

Configure Persistent Volume Claim

Now that we have a Persistent Volume its time to claim space on it for use by the DB server. Below is the PVC configuration:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: psqlpvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: psqldata

This is rather straightforward. Request 1 GB of space for a Persistent Volume in the “psqldata” storage class. At this point we have a disk mount that PostgreSQL can store its data to; so, time to create a PostgreSQL deployment in our cluster.

Determining if the Persistent Volume and Persistent Volume Claim configuration are correct will have to wait until the PostgreSQL deployment is done.

Configure the PostgreSQL Deployment

The official PostgreSQL container requires that the POSTGRES_PASSWORD environment variable be set to the intended password for the DB superuser account. Create a secret for the POSTGRES_PASSWORD environment variable that will be used by the Deployment

kubectl create secret generic postgresqlsuperpass --from-literal='POSTGRES_PASSWORD=<PASSWORD HERE>'

Below is the Deployment configuration for the PostgreSQL service:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: psqlserver
spec:
  replicas: 1
  selector:
    matchLabels:
      role: dbserver
  template:
    metadata:
      labels:
        role: dbserver
    spec:
      containers:
      - name: psqlserver
        image: postgres:12-alpine
        imagePullPolicy: IfNotPresent
        envFrom:
        - secretRef:
            name: postgresqlsuperpass
        env:
        - name: PGDATA
          value: "/mnt/permstore/pgdata"
        ports:
        - containerPort: 5432
          protocol: TCP
          name: psqlport
        volumeMounts:
        - name: psqldata
          mountPath: /mnt/permstore
      volumes:
      - name: psqldata
        persistentVolumeClaim:
          claimName: psqlpvc
          readOnly: false

In the container definition the secretRef will make the stored POSTGRES_PASSWORD available to the container. The PGDATA environment variable sets the directory path to store the server’s data. The “volumeMounts” and “volumes” configures the container to use the “psqlpvc” Persistent Volume Claim defined above.

If the PV, PVC are usable by the PostgreSQL container deployed by the Deployment you’ll have one running postgresqlserver pod. Finally, create a Kubernetes Service to make the database server available to the cluster. Here’s the Service configuration:

apiVersion: v1
kind: Service
metadata:
  name: dbserver
spec:
  selector:
    role: dbserver
  ports:
  - port: 5432
    protocol: TCP

Create the database and roles that Backend service will use

At this point we have a running service on the cluster. The easiest way to connect to it would be to use “kubectl exec” and run psql on the running pod. First run “kubectl get pod” to get the name of the postgresql pod. Start “psql” on the pod by running the following:

kubectl exec <pod name> -t -i -- psql -U postgres -h localhost

This is the easiest way to connect to the PostgreSQL server instance, and since psql will use the Unix-domain socket the superuser password is not required. When the command above is ran you’ll have an interactive connection to psql console program. Run the SQL scripts in /app/backend/db to create the necessary role, ACL and data definition. After that create a login user for the Backend service that has a k8spractice_backend role.

In the next post, we will deploy the k8spractice app to the cluster.