openshift/patroni.dc.yaml
---
apiVersion: v1
kind: Template
metadata:
annotations:
description: Patroni Postgresql database cluster, with persistent storage.
iconClass: icon-postgresql
openshift.io/display-name: Patroni Postgresql (Persistent)
openshift.io/long-description:
This template deploys a patroni postgresql HA cluster
with persistent storage.
tags: postgresql
name: patroni-pgsql-persistent
labels:
app: "${NAME}-${INSTANCE}"
app.kubernetes.io/instance: "${INSTANCE}"
app.kubernetes.io/component: database
app.kubernetes.io/name: patroni
app.kubernetes.io/managed-by: template
app.kubernetes.io/part-of: "${NAME}-${INSTANCE}"
app.kubernetes.io/version: "12"
app.openshift.io/runtime: postgresql
cluster-name: "${INSTANCE}"
objects:
- apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: "allow-patroni-${INSTANCE}-to-patroni-${INSTANCE}-cluster"
spec:
# Allow Patroni pods to talk to its cluster
ingress:
- from:
- podSelector:
matchLabels:
cluster-name: "${INSTANCE}"
statefulset: "patroni-${INSTANCE}"
ports:
- port: 5432
protocol: TCP
- port: 8008
protocol: TCP
podSelector:
matchLabels:
cluster-name: "${INSTANCE}"
statefulset: "patroni-${INSTANCE}"
- apiVersion: v1
kind: Service
metadata:
name: "${NAME}-${INSTANCE}"
spec:
ports:
- port: 5432
protocol: TCP
targetPort: 5432
selector:
cluster-name: "${INSTANCE}"
role: master
app.kubernetes.io/name: patroni
sessionAffinity: None
type: ClusterIP
- apiVersion: v1
kind: Service
metadata:
name: "${NAME}-${INSTANCE}-readonly"
spec:
ports:
- port: 5432
protocol: TCP
targetPort: 5432
selector:
cluster-name: "${INSTANCE}"
role: replica
app.kubernetes.io/name: patroni
sessionAffinity: None
type: ClusterIP
- apiVersion: apps/v1
kind: StatefulSet
metadata:
name: "${NAME}-${INSTANCE}"
spec:
podManagementPolicy: OrderedReady
replicas: ${{REPLICAS}}
revisionHistoryLimit: 10
selector:
matchLabels:
statefulset: "${NAME}-${INSTANCE}"
serviceName: "${NAME}-${INSTANCE}"
template:
metadata:
labels:
cluster-name: "${INSTANCE}"
statefulset: "${NAME}-${INSTANCE}"
app.kubernetes.io/name: patroni
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: statefulset
operator: In
values:
- ${NAME}-${INSTANCE}
topologyKey: "kubernetes.io/hostname"
containers:
- name: postgresql
image: "${IMAGE_REGISTRY}/${IMAGE_STREAM_NAMESPACE}/${IMAGE_STREAM_TAG}"
imagePullPolicy: Always
env:
- name: APP_DATABASE
valueFrom:
secretKeyRef:
key: app-db-name
name: "${NAME}-${INSTANCE}-secret"
- name: APP_USER
valueFrom:
secretKeyRef:
key: app-db-username
name: "${NAME}-${INSTANCE}-secret"
- name: APP_PASSWORD
valueFrom:
secretKeyRef:
key: app-db-password
name: "${NAME}-${INSTANCE}-secret"
- name: POD_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
- name: PATRONI_KUBERNETES_LABELS
value: '{"cluster-name": "${INSTANCE}", "app.kubernetes.io/name": "patroni"}'
- name: PATRONI_KUBERNETES_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: PATRONI_SUPERUSER_USERNAME
valueFrom:
secretKeyRef:
key: superuser-username
name: "${NAME}-${INSTANCE}-secret"
- name: PATRONI_SUPERUSER_PASSWORD
valueFrom:
secretKeyRef:
key: superuser-password
name: "${NAME}-${INSTANCE}-secret"
- name: PATRONI_REPLICATION_USERNAME
valueFrom:
secretKeyRef:
key: replication-username
name: "${NAME}-${INSTANCE}-secret"
- name: PATRONI_REPLICATION_PASSWORD
valueFrom:
secretKeyRef:
key: replication-password
name: "${NAME}-${INSTANCE}-secret"
- name: PATRONI_LOG_LEVEL
value: WARNING
- name: PATRONI_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: PATRONI_POSTGRESQL_DATA_DIR
value: "/home/postgres/pgdata/pgroot/data"
- name: PATRONI_POSTGRESQL_PGPASS
value: "/tmp/pgpass"
- name: PATRONI_POSTGRESQL_LISTEN
value: 0.0.0.0:5432
- name: PATRONI_RESTAPI_LISTEN
value: 0.0.0.0:8008
- name: PATRONI_SCOPE
value: "${INSTANCE}"
- name: POSTGRESQL_MAX_CONNECTIONS
value: "500"
- name: POSTGRESQL_MAX_PREPARED_TRANSACTIONS
value: "500"
lifecycle:
preStop:
exec:
command:
- /usr/bin/env
- bash
- -c
- |
# switch leader pod if the current pod is the leader
if curl --fail http://localhost:8008/read-write; then
patronictl switchover --force
fi
livenessProbe:
exec:
command:
- /usr/bin/env
- bash
- -c
- |
set -Eeuo pipefail && curl -s localhost:8008/liveness | jq -e ". | select(.state == \"running\")"
initialDelaySeconds: 10
timeoutSeconds: 5
failureThreshold: 3
ports:
- containerPort: 8008
protocol: TCP
- containerPort: 5432
protocol: TCP
readinessProbe:
exec:
command:
- /usr/bin/env
- bash
- -c
- |
set -Eeuo pipefail && df "${PATRONI_POSTGRESQL_DATA_DIR:-/home/postgres/pgdata}" --output=pcent | tail -n 1 | awk '{if ($1+0 > 90) exit 1; else exit 0;}' && pg_isready -q && curl -s localhost:8008/readiness | jq -e ". | select(.state == \"running\")"
initialDelaySeconds: 10
timeoutSeconds: 5
failureThreshold: 3
resources:
requests:
cpu: ${CPU_REQUEST}
memory: ${MEMORY_REQUEST}
limits:
cpu: ${CPU_LIMIT}
memory: ${MEMORY_LIMIT}
terminationMessagePath: "/dev/termination-log"
terminationMessagePolicy: File
volumeMounts:
- mountPath: "/home/postgres/pgdata"
name: postgresql
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccountName: "${NAME}-${INSTANCE}"
volumes:
- name: postgresql
persistentVolumeClaim:
claimName: postgresql
updateStrategy:
type: RollingUpdate
volumeClaimTemplates:
- metadata:
annotations:
volume.beta.kubernetes.io/storage-class: "${STORAGE_CLASS}"
labels:
app: "${NAME}-${INSTANCE}"
name: postgresql
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: "${PVC_SIZE}"
storageClassName: "${STORAGE_CLASS}"
- apiVersion: v1
kind: ServiceAccount
metadata:
name: "${NAME}-${INSTANCE}"
- apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: "${NAME}-${INSTANCE}"
rules:
- apiGroups:
- ""
resources:
- services
verbs:
- create
- get
- list
- patch
- update
- watch
- delete
- apiGroups:
- ""
resources:
- configmaps
verbs:
- create
- get
- list
- patch
- update
- watch
- delete
- apiGroups:
- ""
resources:
- endpoints
verbs:
- get
- patch
- update
- create
- list
- watch
- delete
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- list
- patch
- update
- watch
- apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: "${NAME}-${INSTANCE}"
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: "${NAME}-${INSTANCE}"
subjects:
- kind: ServiceAccount
name: "${NAME}-${INSTANCE}"
parameters:
- name: NAME
description: The name of the application for labelling all artifacts.
displayName: Application Name
required: true
value: patroni
- name: INSTANCE
description: The name of this instance of the application
displayName: Application Instance Name
required: true
- name: NAMESPACE
description: Target namespace reference (i.e. 'k8vopl-dev')
displayName: Target Namespace
required: true
- name: REPLICAS
description: The number of StatefulSet replicas to use.
displayName: Replicas
required: true
value: "3"
- name: IMAGE_REGISTRY
description: The base OpenShift docker registry
displayName: Docker Image Registry
required: true
value: artifacts.developer.gov.bc.ca
- name: IMAGE_STREAM_NAMESPACE
description:
The OpenShift Namespace where the patroni and postgresql ImageStream
resides.
displayName: ImageStream Namespace
required: true
value: bcgov-docker-local
- name: IMAGE_STREAM_TAG
description: Patroni ImageTag
displayName: ImageStream Tag
required: true
value: patroni-postgres:2.0.1-12.4-latest
- name: CPU_REQUEST
description: Starting amount of CPU the container can use.
displayName: CPU Request
required: true
value: "50m"
- name: CPU_LIMIT
description: Maximum amount of CPU the container can use.
displayName: CPU Limit
required: true
value: "1"
- name: MEMORY_REQUEST
description: Starting amount of memory the container can use.
displayName: Memory Request
required: true
value: 256Mi
- name: MEMORY_LIMIT
description: Maximum amount of memory the container can use.
displayName: Memory Limit
required: true
value: 512Mi
- name: PVC_SIZE
description: The size of the persistent volume to create.
displayName: Persistent Volume Size
required: true
value: 2Gi
- name: STORAGE_CLASS
description: The type of the persistent volume to create.
displayName: Persistent Volume Class
required: true
value: netapp-block-standard