Skip to content

Instantly share code, notes, and snippets.

@FarooqKhan
Last active December 13, 2023 19:40
Show Gist options
  • Save FarooqKhan/7a81b1aa8167f1a927b82731ec435872 to your computer and use it in GitHub Desktop.
Save FarooqKhan/7a81b1aa8167f1a927b82731ec435872 to your computer and use it in GitHub Desktop.
Kubernetes Example: Deploying PostgreSQL with PersistentVolumes over NFS

Deploy PostgreSQL on Kubernetes

Create a NFS share on some Ubuntu system HowTo - Create NFS Share on Ubuntu

Create a YAML configuration as required by kubernetes for deploying PostgreSQL Full YAML Config file

Create PersistentVolume

Create a PersistentVolume that will be used by PersistentVolumeClaim that will then be used by PostgreSQL to store its data. The path in below config will be the path you added to /etc/exports on your NFS server. The server in below config will be the server address on which NFS server is hosted. Ensure the storageClassName is manual

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pgset-pv
  labels:
    app: pgset
spec:
  storageClassName: manual
  capacity:
    storage: 150M
  accessModes:
    - ReadWriteMany
  nfs:
    path: /kubernetes-volumes
    server: 172.16.17.10
  persistentVolumeReclaimPolicy: Retain

PersistentVolumeClaim

Create a PersistentVolumeClaim as below. Ensure the accessModes has exact same text as in the PersistentVolume, and the storageClassName is manual

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pgset-pvc
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 100M

Rest of Kubernetes config

apiVersion: v1
kind: ServiceAccount
metadata:
  name: pgset-sa
---
apiVersion: v1
kind: Service
metadata:
  name: pgset
  labels:
    app: pgset
spec:
  #Comment1: For troubleshooting if you need to connect to the PostgreSQL Cluster from outside the Kubernetes Cluster
  #Comment the next line `clusterIP: None`
  #Comment the next line and Uncomment the subsequent line and the to be able to
  #Uncomment the subsequent line `#type: NodePort` also uncomment the line further below `#nodePort: 30100`
  clusterIP: None
  #type: NodePort
  ports:
    - port: 5432
      name: web
  clusterIP: None
  selector:
    app: pgset
---
apiVersion: v1
kind: Service
metadata:
  name: pgset-primary
  labels:
    name: pgset-primary
spec:
  ports:
    - protocol: TCP
      port: 5432
      targetPort: 5432
      nodePort: 0
  selector:
    name: pgset-primary
  type: ClusterIP
  #type: NodePort
  sessionAffinity: None
---
apiVersion: v1
kind: Service
metadata:
  name: pgset-replica
  labels:
    name: pgset-replica
spec:
  ports:
    - protocol: TCP
      port: 5432
      targetPort: 5432
      nodePort: 0
  selector:
    name: pgset-replica
  type: ClusterIP
  sessionAffinity: None
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: pgset
spec:
  selector:
    matchLabels:
      app: pgset # has to match .spec.template.metadata.labels
  serviceName: pgset
  replicas: 2
  template:
    metadata:
      labels:
        app: pgset
        name: pgset-replica
    spec:
      serviceAccount: pgset-sa
      securityContext:
        fsGroup: 26
      containers:
        - name: pgset
          image: crunchydata/crunchy-postgres:centos7-10.2-1.8.0
          ports:
            - containerPort: 5432
              name: postgres
          env:
            - name: PG_PRIMARY_USER
              value: primaryuser
            - name: PGHOST
              value: /tmp
            - name: PG_MODE
              value: set
            - name: PG_PRIMARY_HOST
              value: pgset-primary
            - name: PG_PRIMARY_PORT
              value: "5432"
            - name: PG_PRIMARY_PASSWORD
              value: password
            - name: PG_USER
              value: testuser
            - name: PG_PASSWORD
              value: password
            - name: PG_DATABASE
              value: userdb
            - name: PG_ROOT_PASSWORD
              value: password
          volumeMounts:
            - name: pgdata
              mountPath: /pgdata
              readOnly: false
      volumes:
        - name: pgdata
          persistentVolumeClaim:
            claimName: pgset-pvc

Run the YAML config

Assuming you saved the entire YAML as postgres-deployment.yaml execute it as follows

kubectl create -f postgres-deployment.yaml

Troubleshooting

If you hit a problem, here are a few steps to follow in order to troubleshoot and figure out what could be the cause.

  1. Find the Pod name for your PostgreSQL Primary or Replica instance. kubectl get po

  2. Check the logs for each of the pods to see whats the problem might be kubectl logs <pod name>

  3. If there are any log messages that indicate some problem with the storage of postgresql files, maybe the cause is the PV and PVC. Check the status of both the PersistentVolume and PersistentVolumeClaim both should show as bound, if not maybe the nfs share is not allowing a write confirm that you have proper access on nfs server

  4. Maybe all is good but some database query is failing, you want to peep into the Database using some UI tool like pgAdmin or SQLWorkbenchJ. To be able to temporarily connect with your database follow the #Comment1: in the section that creates the Service for Postgres above just after the ServiceAccount account is created

Create NFS Share

Obtain a Ubuntu system (may be a VM) which has a reachable IP Address, for maybe: 172.16.17.10

Install NFS Server

sudo apt install nfs-kernel-server

Configure NFS Server

Create a folder on your Ubuntu server example:

/kubernetes-volumes

Edit file /etc/exports (create the file if it does not exists)

Configure the directories to be exported by adding them to the /etc/exports file. For example:

/kubernetes-volumes *(rw,sync,no_root_squash)

You can replace * with hostname to restrict access to specific hostnames or ipaddress

Start NFS Server

sudo systemctl start nfs-kernel-server.service

Test your Exports are fine:

sudo exportfs -u

You should see a output similar to following:

/kubernetes-volumes
          <world>

Test from client machine

showmount -e <nfs server ip>

You should see a output similar to following:

Export list for 139.59.20.67:
/kubernetes-volumes <word>
apiVersion: v1
kind: PersistentVolume
metadata:
name: pgset-pv
labels:
app: pgset
spec:
storageClassName: manual
capacity:
storage: 150M
accessModes:
- ReadWriteMany
nfs:
path: /kubernetes-volumes
server: 172.16.17.10
persistentVolumeReclaimPolicy: Retain
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pgset-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 100M
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: pgset-sa
---
apiVersion: v1
kind: Service
metadata:
name: pgset
labels:
app: pgset
spec:
ports:
- port: 5432
name: web
clusterIP: None
selector:
app: pgset
---
apiVersion: v1
kind: Service
metadata:
name: pgset-primary
labels:
name: pgset-primary
spec:
ports:
- protocol: TCP
port: 5432
targetPort: 5432
nodePort: 0
selector:
name: pgset-primary
type: ClusterIP
#type: NodePort
sessionAffinity: None
---
apiVersion: v1
kind: Service
metadata:
name: pgset-replica
labels:
name: pgset-replica
spec:
ports:
- protocol: TCP
port: 5432
targetPort: 5432
nodePort: 0
selector:
name: pgset-replica
type: ClusterIP
sessionAffinity: None
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: pgset
spec:
selector:
matchLabels:
app: pgset # has to match .spec.template.metadata.labels
serviceName: pgset
replicas: 2
template:
metadata:
labels:
app: pgset
name: pgset-replica
spec:
serviceAccount: pgset-sa
securityContext:
fsGroup: 26
containers:
- name: pgset
image: crunchydata/crunchy-postgres:centos7-10.2-1.8.0
ports:
- containerPort: 5432
name: postgres
env:
- name: PG_PRIMARY_USER
value: primaryuser
- name: PGHOST
value: /tmp
- name: PG_MODE
value: set
- name: PG_PRIMARY_HOST
value: pgset-primary
- name: PG_PRIMARY_PORT
value: "5432"
- name: PG_PRIMARY_PASSWORD
value: password
- name: PG_USER
value: testuser
- name: PG_PASSWORD
value: password
- name: PG_DATABASE
value: userdb
- name: PG_ROOT_PASSWORD
value: password
volumeMounts:
- name: pgdata
mountPath: /pgdata
readOnly: false
volumes:
- name: pgdata
persistentVolumeClaim:
claimName: pgset-pvc
@prasadsrg
Copy link

Good Details for Master-Slave PG, Please provide me details for connecting the pg database from outside, I am using bare matel ( nginx ingress ).

@FarooqKhan
Copy link
Author

Good Details for Master-Slave PG, Please provide me details for connecting the pg database from outside, I am using bare matel ( nginx ingress ).

Search for the "#Comment1:" in the above gist, I have the details you need. (its a temporary option only) you should not be exposing this permanently. If you need to expose this permantely I think more work might be required.

@erivandosena
Copy link

erivandosena commented Dec 1, 2022

Please provide me details for connecting the PostgreSQL database from outside by LoadBalancer IP using bare matel ?

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