|
1 | 1 | #postgres-on-kubernetes
|
2 | 2 | PostgreSQL on Kubernetes
|
| 3 | + |
| 4 | +##Prerequisites |
| 5 | + |
| 6 | +```bash |
| 7 | +# install minikube to create a single-node cluster |
| 8 | +brew install minikube |
| 9 | + |
| 10 | +# start cluster using VMs |
| 11 | +minikube start --vm=true |
| 12 | + |
| 13 | +# create a custom namespace and context |
| 14 | +kubectl create namespace postgres |
| 15 | +kubectl config set-context postgres --namespace postgres --cluster minikube --user minikube |
| 16 | +kubectl config use-context postgres |
| 17 | +``` |
| 18 | + |
| 19 | +##Persistent Volumes |
| 20 | + |
| 21 | +In order to persist the data stored in PostgreSQL it’s necessary to create[Persistent Volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) that have a pod-independent lifecycle. Within a Stateful Set a so called_Persistent Volume Claim_ with a specific[Storage Class](https://kubernetes.io/docs/concepts/storage/storage-classes/) can be configured. |
| 22 | + |
| 23 | +There are two ways to create Persistent Volumes. Either you manually create a volume per replica of PostgreSQL or you configure[dynamic provisioning](https://minikube.sigs.k8s.io/docs/handbook/persistent_volumes/#dynamic-provisioning-and-csi). For simplicity we choose the manual approach first. |
| 24 | + |
| 25 | +```bash |
| 26 | +# create 3 persistent volumes |
| 27 | +kubectl apply -f pv-0.yaml |
| 28 | +kubectl apply -f pv-1.yaml |
| 29 | +kubectl apply -f pv-2.yaml |
| 30 | + |
| 31 | +# list persistent volumes |
| 32 | +kubectl get pv |
| 33 | +NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE |
| 34 | +pv-postgresql-0 1Gi RWO Retain Available default 9s |
| 35 | +pv-postgresql-1 1Gi RWO Retain Available default 7s |
| 36 | +pv-postgresql-2 1Gi RWO Retain Available default 3s |
| 37 | +``` |
| 38 | + |
| 39 | +##Headless Service |
| 40 | + |
| 41 | +A[Headless Service](https://kubernetes.io/docs/concepts/services-networking/service/#headless-services) is specified with`clusterIP: None` and omits to use a L4 load balancer. By also defining a selector, the endpoints controller creates`Endpoint` records and also modifies the DNS configuration so that`A` records are returned that point directly to the pods. |
| 42 | + |
| 43 | +```bash |
| 44 | +# create headless service |
| 45 | +kubectl apply -f svc.yaml |
| 46 | + |
| 47 | +# describe headless service |
| 48 | +kubectl describe svc postgresql-svc |
| 49 | +Name: postgresql-svc |
| 50 | +Namespace: postgres |
| 51 | +Labels: sfs=postgresql-sfs |
| 52 | +Annotations:<none> |
| 53 | +Selector: sfs=postgresql-sfs |
| 54 | +Type: ClusterIP |
| 55 | +IP Family Policy: SingleStack |
| 56 | +IP Families: IPv4 |
| 57 | +IP: None |
| 58 | +IPs: None |
| 59 | +Port: postgresql-port 5432/TCP |
| 60 | +TargetPort: 5432/TCP |
| 61 | +Endpoints:<none> |
| 62 | +Session Affinity: None |
| 63 | +Events:<none> |
| 64 | +``` |
| 65 | + |
| 66 | +##Secrets |
| 67 | + |
| 68 | +PostgreSQL uses environment variables for configuration. The most important one for the official[PostgreSQL Docker image](https://hub.docker.com/_/postgres) is`POSTGRES_PASSWORD`. We utilize[Secrets](https://kubernetes.io/docs/concepts/configuration/secret/) to inject the respective value into the container later on. |
| 69 | + |
| 70 | +```bash |
| 71 | +# create secret from literal |
| 72 | +kubectl create secret generic postgresql-secrets --from-literal=POSTGRES_PASSWORD=tes6Aev8 |
| 73 | + |
| 74 | +# describe secret |
| 75 | +kubectl describe secrets postgresql-secrets |
| 76 | +Name: postgresql-secrets |
| 77 | +Namespace: postgres |
| 78 | +Labels:<none> |
| 79 | +Annotations:<none> |
| 80 | + |
| 81 | +Type: Opaque |
| 82 | + |
| 83 | +Data |
| 84 | +==== |
| 85 | +POSTGRES_PASSWORD: 8 bytes |
| 86 | +``` |
| 87 | + |
| 88 | +##Stateful Sets |
| 89 | + |
| 90 | +A[Stateful Set](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/) is similar to a_Replica Set_ in a sense that it also handles pods for the configured number of replicas. In contrast to a_Replica Set_, it maintains a sticky identity for each of them. This means they are created in a fixed, sequential order and deleted counterwise. Their network identity is stable as well, what enables us to reference them by the automatically assigned DNS host inside of the cluster. |
| 91 | + |
| 92 | +```bash |
| 93 | +# create stateful set with 3 replicas |
| 94 | +kubectl apply -f sfs.yaml |
| 95 | + |
| 96 | +# list stateful sets |
| 97 | +kubectl get statefulsets |
| 98 | +NAME READY AGE |
| 99 | +postgresql-sfs 3/3 16s |
| 100 | + |
| 101 | +# list pods |
| 102 | +kubectl get pods |
| 103 | +NAME READY STATUS RESTARTS AGE |
| 104 | +postgresql-sfs-0 1/1 Running 0 86s |
| 105 | +postgresql-sfs-1 1/1 Running 0 83s |
| 106 | +postgresql-sfs-2 1/1 Running 0 80s |
| 107 | + |
| 108 | +# inspect logs of a random pod |
| 109 | +kubectl logs postgresql-sfs-0 |
| 110 | + |
| 111 | +PostgreSQL Database directory appears to contain a database; Skipping initialization |
| 112 | + |
| 113 | +2021-08-04 08:19:50.832 UTC [1] LOG: starting PostgreSQL 13.3 (Debian 13.3-1.pgdg100+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 8.3.0-6) 8.3.0, 64-bit |
| 114 | +2021-08-04 08:19:50.832 UTC [1] LOG: listening on IPv4 address"0.0.0.0", port 5432 |
| 115 | +2021-08-04 08:19:50.832 UTC [1] LOG: listening on IPv6 address"::", port 5432 |
| 116 | +2021-08-04 08:19:50.835 UTC [1] LOG: listening on Unix socket"/var/run/postgresql/.s.PGSQL.5432" |
| 117 | +2021-08-04 08:19:50.838 UTC [26] LOG: database system was shut down at 2021-08-03 14:33:17 UTC |
| 118 | +2021-08-04 08:19:50.843 UTC [1] LOG: database system is ready to accept connections |
| 119 | + |
| 120 | +# describe the service to see that 3 endpoints were created automatically |
| 121 | +kubectl describe svc postgresql-svc |
| 122 | +Name: postgresql-svc |
| 123 | +Namespace: postgres |
| 124 | +Labels: sfs=postgresql-sfs |
| 125 | +Annotations:<none> |
| 126 | +Selector: sfs=postgresql-sfs |
| 127 | +Type: ClusterIP |
| 128 | +IP Family Policy: SingleStack |
| 129 | +IP Families: IPv4 |
| 130 | +IP: None |
| 131 | +IPs: None |
| 132 | +Port: postgresql-port 5432/TCP |
| 133 | +TargetPort: 5432/TCP |
| 134 | +Endpoints: 172.17.0.3:5432,172.17.0.4:5432,172.17.0.5:5432 |
| 135 | +Session Affinity: None |
| 136 | +Events:<none> |
| 137 | +``` |
| 138 | + |
| 139 | +##Connect to |
| 140 | + |
| 141 | +You are able to directly connect to PostgreSQL by starting`bash` within a particular pod. |
| 142 | + |
| 143 | +```bash |
| 144 | +kubectlexec -it postgresql-sfs-0 -- bash |
| 145 | +root@postgresql-sfs-0:/# PGPASSWORD=tes6Aev8 psql -U postgres |
| 146 | +psql (13.3 (Debian 13.3-1.pgdg100+1)) |
| 147 | +Type"help"for help. |
| 148 | + |
| 149 | +postgres=# exit |
| 150 | +root@postgresql-sfs-0:/# exit |
| 151 | +exit |
| 152 | +``` |
| 153 | + |
| 154 | +An alternative approach is running a temporary PostgreSQL container and using the included`psql` to connect to one of the database instances. Where the hostname is the automatically created DNS host of the service we deployed earlier. The format of that hostname is:`<service-name>.<namespace>.svc.cluster.local` and will be resolved to a random pod running a database server. |
| 155 | + |
| 156 | +```bash |
| 157 | +kubectl run -it --rm pg-psql --image=postgres:13.3 --restart=Never --env="PGPASSWORD=tes6Aev8" -- psql -h postgresql-svc.postgres.svc.cluster.local -U postgres |
| 158 | +If you don't see a command prompt, try pressing enter. |
| 159 | +postgres=# \q |
| 160 | +pod "pg-psql" deleted |
| 161 | +``` |
| 162 | +
|
| 163 | +To check that the DNS hostname works we deploy a busybox instance. |
| 164 | +
|
| 165 | +```bash |
| 166 | +kubectl run -it --rm busybox --image=busybox --restart=Never -- sh |
| 167 | +If you don't see acommand prompt, try pressing enter. |
| 168 | +/# ping postgresql-svc.postgres.svc.cluster.local |
| 169 | +PING postgresql-svc.postgres.svc.cluster.local (172.17.0.3): 56 data bytes |
| 170 | +64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.828 ms |
| 171 | +64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.080 ms |
| 172 | +64 bytes from 172.17.0.3: seq=2 ttl=64 time=0.080 ms |
| 173 | +^C |
| 174 | +--- postgresql-svc.postgres.svc.cluster.local ping statistics --- |
| 175 | +3 packets transmitted, 3 packets received, 0% packet loss |
| 176 | +round-trip min/avg/max = 0.080/0.329/0.828 ms |
| 177 | + |
| 178 | +/# nslookup postgresql-svc.postgres.svc.cluster.local |
| 179 | +Server:10.96.0.10 |
| 180 | +Address:10.96.0.10:53 |
| 181 | + |
| 182 | +Name:postgresql-svc.postgres.svc.cluster.local |
| 183 | +Address: 172.17.0.5 |
| 184 | +Name:postgresql-svc.postgres.svc.cluster.local |
| 185 | +Address: 172.17.0.4 |
| 186 | +Name:postgresql-svc.postgres.svc.cluster.local |
| 187 | +Address: 172.17.0.3 |
| 188 | + |
| 189 | +*** Can't find postgresql-svc.postgres.svc.cluster.local: No answer |
| 190 | +
|
| 191 | +/ # exit |
| 192 | +pod "busybox" deleted |
| 193 | +``` |