Polyaxon v3 is coming →

How to deploy Postgres on Kubernetes

A practical walkthrough for deploying Postgres on Kubernetes with ConfigMaps, persistent volumes, deployments, services, and basic production cautions.

July 9, 2024by Polyaxon
Jul 9, 2024

How to deploy Postgres on Kubernetes

A practical walkthrough for deploying Postgres on Kubernetes with ConfigMaps, persistent volumes, deployments, services, and basic production cautions.

Picture

Running Postgres on Kubernetes is not automatically a good idea. Databases are stateful, and Kubernetes is happiest when workloads are disposable. Still, there are valid cases: local development, internal tools, test environments, and teams that already operate storage and backups carefully.

This walkthrough shows the mechanics: ConfigMaps, persistent volumes, deployments, and services. Treat it as a practical baseline, not a production architecture by itself.

Why deploy Postgres on Kubernetes?

The combination of PostgreSQL and Kubernetes provides a scalable and highly available (HA) database solution that's well suited for modern application development and deployment practices. While creating a HA solution is out of the scope of this article, you'll learn how to set up a simple container with PostgreSQL, which offers a number of benefits.

Improved performance

Modern cloud-native applications are often built using microservices, which are small, self-contained services that can be individually deployed and scaled. PostgreSQL can be used as the database for each microservice, and Kubernetes can be used to manage the deployment and scaling of the application as a whole.

Easier disaster recovery

You don't want to lose your operational or user data in any environment, but user error or technical failure may result in it anyhow. PostgreSQL's Write-Ahead Logs (WAL) allows for easier disaster recovery by ensuring that all data is stored in the logs before the write operation to the database is performed, easing data recovery when required, and allowing even unwritten updates to be salvaged.

Better utilization of resources

Kubernetes is very efficient with scaling, and allows for use cases like scaling pods up during peak hours and down afterwards without service interruption. Scaling helps optimize resource utilization and save on cost, as you use only the resources necessary, not over provisioning to accommodate an infrequent or irregular surge in demand.

Deploying PostgreSQL on Kubernetes

To deploy PostgreSQL on Kubernetes, you need to have some tools set up.

Prerequisites

  • A working Kubernetes cluster. For this tutorial, a DigitalOcean cluster is used, but the steps of this tutorial will be the same for any cluster. To work locally, you can use something like kind or minikubeto set your cluster.
  • A basic understanding of psql.
  • kubectl installed and authenticated on your environment. You'll also need some working knowledge of the tool.

Deploying Postgres via ConfigMap with a PersistentVolume is one of the popular options for deployment, and it's the approach you'll be taking in this tutorial.

Create a ConfigMap

ConfigMaps help you separate data from code, and prevent secrets from exposing themselves in your application's source code. With ConfigMaps, you can more easily deploy and update applications.

Create a ConfigMap by pasting the following code into your terminal:

kubectl create configmap postgres-config --from-literal=POSTGRES_DB=my_database --from-literal=POSTGRES_USER=my_user --from-literal=POSTGRES_PASSWORD=my_password

or

cat <<EOF > postgres-config.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: postgres-config
  labels:
    app: postgres
data:
  POSTGRES_DB: postgresdb
  POSTGRES_USER: admin
  POSTGRES_PASSWORD: psltest

EOF

The fields POSTGRES_DB, POSTGRES_USER, and POSTGRES_PASSWORD are your secrets, and you can change the values according to your preference. You can edit these values using text editors like vim or nano.

The command below creates a new ConfigMap for our PostgreSQL deployment with a custom configuration. The configuration consists of the fields POSTGRES_DB, POSTGRES_USER, and POSTGRES_PASSWORD.


$ kubectl apply -f postgres-config.yaml

>> configmap/postgres-config created

Create and apply persistent storage volume and persistent volume claim

In order to ensure data persistence, you should use a persistent volume (PV) and persistent volume claims (PVC). A persistent volume (PV) is a durable volume that will remain even if the pod is deleted and stores data.

A persistent volume claim (PVC) is how users request and consume PV resources. Think of it as requesting the PV with parameters such as size of your storage disk, access modes, and storage class.

To deploy stateful applications such as a PostgreSQL database, for example, you'll need to create a PVC for the database data. You can create a pod that mounts the PVC and runs the MySQL database.

For this tutorial, you will move forward with a local volume, using /mnt/data as the path to volume:

cat <<EOF > postgres-pvc-pv.yaml

kind: PersistentVolume
apiVersion: v1
metadata:
  name: postgres-pv-volume  # Sets PV's name
  labels:
    type: local  # Sets PV's type to local
    app: postgres
spec:
  storageClassName: manual
  capacity:
    storage: 5Gi # Sets PV Volume
  accessModes:
    - ReadWriteMany
  hostPath:
    path: "/mnt/data"
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: postgres-pv-claim  # Sets name of PVC
  labels:
    app: postgres
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteMany  # Sets read and write access
  resources:
    requests:
      storage: 5Gi  # Sets volume size
EOF

Run the following command to create a new PVC and PV for your PostgreSQL deployment:

$ kubectl apply -f postgres-pvc-pv.yaml

>> persistentvolume/postgres-pv-volume created
>> persistentvolumeclaim/postgres-pv-claim created

Create and apply PostgreSQL deployment

Deployments are a way to manage rolling out and updating applications in a Kubernetes cluster. They provide a declarative way to define how an application should be deployed and updated, and can be used to roll back to previous versions if needed.

After creating PVCs, PVs, and ConfigMaps, you can create a stateful application by creating a stateful pod as follows:

cat <<EOF > postgres-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres  # Sets Deployment name
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:10.1 # Sets Image
          imagePullPolicy: "IfNotPresent"
          ports:
            - containerPort: 5432  # Exposes container port
          envFrom:
            - configMapRef:
                name: postgres-config
          volumeMounts:
            - mountPath: /var/lib/postgresql/data
              name: postgredb
      volumes:
        - name: postgredb
          persistentVolumeClaim:
            claimName: postgres-pv-claim

EOF

Run the following command to create a new deployment for your PostgreSQL deployment:

$ kubectl apply -f postgres-deployment.yaml

>> deployment.apps/postgres created

Create and apply PostgreSQL service

Kubernetes services help you expose ports in various ways, including through a NodePort. NodePorts expose a service on every node in a cluster, meaning that the service is accessible from outside the cluster. This can be useful for services that need to be accessible from outside the cluster. To keep things simple for this tutorial, you'll expose the database using NodePort with the help of the following manifest:

cat <<EOF > postgres-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: postgres # Sets service name
  labels:
    app: postgres # Labels and Selectors
spec:
  type: NodePort # Sets service type
  ports:
    - port: 5432 # Sets port to run the postgres application
  selector:
    app: postgres

EOF

Run the following command to create a new service for your PostgreSQL deployment:

$ kubectl apply -f postgres-service.yaml

>> service/postgres created

Connect to PostgreSQL

The Kubernetes command line client ships with a feature that lets you connect to a pod directly from your host command line. The kubectl exec command accepts a pod name, any commands that should be executed, and an interactive flag that lets you launch a shell. You'll use kubectl exec to connect to PostgreSQL pod:

$ kubectl exec -it [pod-name] --  psql -h localhost -U admin --password -p 5432 postgresdb

Use the password from the ConfigMap you created earlier, and the options -it.

  • -i: Stands for interactive.
  • -t: Attaches a tty (terminal) to the running command.

Best Practices Deploying PostgreSQL on Kubernetes

When deploying PostgreSQL on Kubernetes, there are some best practices that you should follow to ensure the security and stability of your application.

Run the container as unprivileged user

You should always run the database container as an unprivileged user. This helps secure your data and avoid unauthorized access to your database. The most essential things to ensure that you run the container as an unprivileged user are:

*Make sure your container image launches as a user other than root (e.g. ensure USER is not 0 or root).

  • Make sure your Pod Security Context is set to non-root by setting runAsNonRoot to true.

Encrypt your data

You should always encrypt your data to avoid data loss or theft. You can use various tools and make sure your data is encrypted in transit as well as in rest to prevent various misconfiguration and breaches. To learn more about how to encrypt your data, check out this CNCF webinar on the subject.

Create a separate namespace for your database

You should create a separate namespace for your database so that it's isolated from other applications and services. RBAC should be implemented to your namespace via ClusterRole and RoleBindings to prevent unauthorized access.

A new database namespace also helps monitor resources, and you can apply limits if you need to balance resources.

You can create a namespace as follows:

$ kubectl create namespace [namespace-name]

Conclusion

This walkthrough deployed a PostgreSQL database on Kubernetes. That proves the Kubernetes objects work together, but it does not settle production concerns such as backups, storage durability, upgrades, and failover.

Final thoughts

You can deploy Postgres on Kubernetes with a ConfigMap, persistent storage, a Deployment, and a Service. That proves the mechanics, not the production readiness. Backups, recovery, upgrades, storage class behavior, and access controls are the real test.

For Polyaxon deployments, treat metadata storage as critical infrastructure. A demo manifest is fine for learning; production deserves managed storage policy and a recovery plan you have actually tested.