PostgressHA in Kubernetes

Use 'helm' to install the configuration. 'helm'uses charts for this and I'll create a chart for the PostgressHA cluster.

Steps

Create the Chart

Info

Commands

helm list --all

list all charts

helm install -n <name> <chart name>

install the chart and give it a specific name

helm delete --purge <name>

delete the chart and also purge any storage

kubectl get pv

list the Persistent Volumes

kubectl get pvc 

list the Persistent Volume Claims

kubectl apply -f <yaml file>

apply the yaml file

kubectl delete pod <podname>

delete pod

vSphere

Links:

govc (https://github.com/vmware/govmomi/tree/master/govc) Use govc to configure the vSphere Cloud Provider
export GOVC_URL=<IP/URL>
export GOVC_USERNAME=<vCenter User>
export GOVC_PASSWORD=<vCenter Password>
export GOVC_INSECURE=1
Set some environment variables used by govc
govc vm.change -e="disk.enableUUID=1" -vm=<VMNAME>
Enable UUID for a VM
   
   

Files

postgresha/install.sh

export NAME=$1
if [ -z $NAME ]; then
  echo "usage: $0 <name>"
  exit
fi
helm install -n $NAME .

postgresha/delete.sh

export NAME=$1
if [ -z $NAME ]; then
  echo "usage: $0 <name>"
  exit
fi

helm delete $NAME --purge

kubectl delete pvc data-master-$NAME-master-0
kubectl delete pvc data-slave-$NAME-slave-0
kubectl delete pvc data-slave-$NAME-slave-1

postgresha/Chart.yaml

apiVersion: v1
description: postgresha cluster
name: postgresha
version: 0.1.0
maintainers:
- name: Milo van der Zee
  email: <email>

postgresha/requirements.yaml

<empty file>

postgresha/values.yaml

persistence:
  size: 20Gi
  iscsi:
  - name: master
    usage: master
    target: <target ip>:3260
    iqn: <iqn>
  - name: slave1
    usage: slave
    target: <target ip>:3260
    iqn: <iqn>
  - name: slave2
    usage: slave
    target: <target ip>:3260
    iqn: <iqn>
statefulsets:
  - name: postgresha-master
    replicas: 1
    usage: master
  - name: postgresha-slave
    replicas: 2
    usage: slave
postgres:
  image: postgres:9.6.2
  user: postgres
  password: <postgres password>
  repuser: repuser
  reppassword: <replication user password>
  pgdata: /var/lib/postgresql/data/pgdata

postgresha/templates/persistentvolume.yaml

{{- $values := .Values -}}

{{ range $k, $v := .Values.persistence.iscsi }}
apiVersion: v1
kind: PersistentVolume
metadata:
  labels:
    type: iSCSI
    usage: {{ $v.usage }}
  name: {{ $v.name }}
spec:
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: {{ $values.persistence.size }}
  iscsi:
    fsType: ext4
    iqn: {{ $v.iqn }}
    iscsiInterface: default
    lun: 0
    targetPortal: {{ $v.target }}
  persistentVolumeReclaimPolicy: Retain
---
{{ end }}

postgresha/templates/statefulset.yaml

{{- $values := .Values -}}

{{ range $k, $v := .Values.statefulsets }}
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  labels:
    app: {{ $v.name }}
  name: {{ $v.name }}
  namespace: default
spec:
  replicas: {{ $v.replicas }}
  selector:
    matchLabels:
      app: {{ $v.name }}
  serviceName: postgres
  template:
    metadata:
      labels:
        app: {{ $v.name }}
        function: postgresha
    spec:
      containers:
      - command:
        - bash
        - -c
        - su $PGUSER -c "bash /postgresha/start.sh"
        env:
        - name: PGDATA
          value: {{ $values.postgres.pgdata }}
        - name: PGUSER
          value: {{ $values.postgres.user }}
        - name: PGPASSWORD
          value: {{ $values.postgres.password }}
        - name: POD_IP
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: status.podIP
        image: {{ $values.postgres.image }}
        imagePullPolicy: IfNotPresent
        livenessProbe:
          exec:
            command:
            - sh
            - -c
            - exec pg_isready --host localhost
          failureThreshold: 6
          initialDelaySeconds: 60
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 5
        name: postgresql
        ports:
        - containerPort: 5432
          name: postgresql
          protocol: TCP
        readinessProbe:
          exec:
            command:
            - sh
            - -c
            - exec pg_isready --host localhost
          failureThreshold: 3
          initialDelaySeconds: 5
          periodSeconds: 5
          successThreshold: 1
          timeoutSeconds: 3
        volumeMounts:
        - mountPath: {{ $values.postgres.pgdata }}
          name: data-{{ $v.usage }}
          subPath: postgresql-db
        - mountPath: /postgresha
          name: postgresha
      volumes:
      - configMap:
          defaultMode: 420
          name: postgresha
        name: postgresha
  volumeClaimTemplates:
  - metadata:
      name: data-{{ $v.usage }}
    spec:
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: {{ $values.persistence.size }}
      selector:
        matchLabels:
          usage: {{ $v.usage }}
---
{{ end }}

postgresha/templates/service-postgresha.yaml

apiVersion: v1
kind: Service
metadata:
  name: postgresha-ro
  namespace: default
spec:
  ports:
  - name: postgresql
    port: 5432
    protocol: TCP
    targetPort: 5432
  selector:
    function: postgresha
  type: NodePort
---
{{ range $k, $v := .Values.statefulsets }}
apiVersion: v1
kind: Service
metadata:
  name: {{ $v.name }}
  namespace: default
spec:
  ports:
  - name: postgresql
    port: 5432
    protocol: TCP
    targetPort: 5432
  selector:
    app: {{ $v.name }}
  type: NodePort
---
{{ end }}

postgresha/templates/config-map.yaml

kind: ConfigMap
apiVersion: v1
metadata:
  name: postgresha
data:
  start.sh: |-
    export PATH=/usr/lib/postgresql/$PG_MAJOR/bin:$PATH
    postgres &
    while [ 1 ]; do
      echo "live..."
      sleep 600
    done

  setup-master.sh: |-
    if [ -z $PGDATA ]; then echo "PGDATA must be set!"; exit; fi
    killall postgres
    rm -rf $PGDATA/*
    su -m postgres -c "export PATH=/usr/lib/postgresql/$PG_MAJOR/bin:$PATH; initdb"
    cp /postgresha/postgresql-master.conf $PGDATA/postgresql.conf
    cp /postgresha/pg_hba-master.conf $PGDATA/pg_hba.conf
    export ARCHIVEDIR=$PGDATA/archivedir
    [ ! -d "$ARCHIVEDIR" ] && mkdir $ARCHIVEDIR
    chown -R postgres: $PGDATA
    su -m postgres -c "export PATH=/usr/lib/postgresql/$PG_MAJOR/bin:$PATH; pg_ctl -D /var/lib/postgresql/data/pgdata -l logfile -w start"
    # create the replication user
    echo "CREATE USER {{ .Values.postgres.repuser }} REPLICATION ENCRYPTED PASSWORD '{{ .Values.postgres.reppassword }}'" | psql
    echo "ALTER USER postgres WITH PASSWORD '{{ .Values.postgres.password }}'" | psql
    date >> $PGDATA/setup_date

  setup-slave.sh: |-
    if [ -z $PGDATA ]; then echo "PGDATA must be set!"; exit; fi
    killall postgres
    rm -rf $PGDATA/*
    # Copy data from the master to the slave
    su -m postgres -c "export PATH=/usr/lib/postgresql/$PG_MAJOR/bin:$PATH; pg_basebackup --host=postgresha-master -D $PGDATA -U {{ .Values.postgres.repuser }} -v -P -W --xlog-method=stream"
    cp /postgresha/postgresql-slave.conf $PGDATA/postgresql.conf
    cp /postgresha/recovery-slave.conf $PGDATA/recovery.conf
    chown -R postgres: $PGDATA
    su -m postgres -c "export PATH=/usr/lib/postgresql/$PG_MAJOR/bin:$PATH; pg_ctl -D /var/lib/postgresql/data/pgdata -l logfile -w start"
    date >> $PGDATA/setup_date

  postgresql-master.conf: |-
    listen_addresses = '*'
    port = 5432
    max_connections = 100
    shared_buffers = 128MB
    dynamic_shared_memory_type = posix
    log_timezone = 'UTC'
    datestyle = 'iso, mdy'
    lc_messages = 'en_US.utf8'
    lc_monetary = 'en_US.utf8'
    lc_time = 'en_US.utf8'
    default_text_search_config = 'pg_catalog.english'
    wal_level = hot_standby
    archive_mode = on
    archive_command = 'cp -i %p $PGDATA/archivedir/%f'
    max_wal_senders = 3
    wal_keep_segments = 8

  pg_hba-master.conf: |-
    # TYPE  DATABASE        USER                               ADDRESS                 METHOD
    local   all             all                                                        trust
    host    all             all                                127.0.0.1/32            trust
    host    all             all                                ::1/128                 trust
    host    replication     {{ .Values.postgres.repuser}}      0.0.0.0/0               md5
    host    all             all                                0.0.0.0/0               md5

  postgresql-slave.conf: |-
    listen_addresses = '*'
    port = 5432
    max_connections = 100
    shared_buffers = 128MB
    dynamic_shared_memory_type = posix
    log_timezone = 'UTC'
    datestyle = 'iso, mdy'
    lc_messages = 'en_US.utf8'
    lc_monetary = 'en_US.utf8'
    lc_time = 'en_US.utf8'
    default_text_search_config = 'pg_catalog.english'
    hot_standby = on

  recovery-slave.conf: |-
    standby_mode = on
    primary_conninfo = 'host=postgresha-master port=5432 user={{ .Values.postgres.repuser }} password={{ .Values.postgres.reppassword }}'