Esercizio: Persistenza Postgres

Persistenza di applicativo postgres

Perspost

La datadir di postgres è mappata ad un Persistent Volume.

Passi di Esecuzione

Per preparare un servizio postgres occorre:

  • acquisire l’immagine nel registry locale
  • PV e PVC per postgres
  • secrets per postgres
  • deployment e servizio per postgres

I secrets necessari si scoprono consultando sul web il sito per ‘postgres docker’

  • nel nostro caso POSTGRES_PASSWORD

Immagine in Registry Locale

Dalla macchina host:

docker pull postgres:12.2
docker tag postgres:12.2 localhost:5000/postgres:12.2
docker push localhost:5000/postgres:12.2

PV e PVC per postgres

Nel container kub, prepariamo la directory per gli scripts:

mkdir -p ~/scripts/pg

Il manifest è ~/scripts/pg/pg-volume.yml

vim ~/scripts/pg/pg-volume.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: postgres-pv-volume
spec:
  storageClassName: standard
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: /data/pg/
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: postgres-pv-claim
spec:
  volumeName: postgres-pv-volume
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi

Applicare la configurazione:

kubectl apply -f ~/scripts/pg/pg-volume.yml

Secrets per postgres

La password di postgres sarà ‘secret’. Quindi:

echo -n secret | base64
c2VjcmV0

Il manifest è ~/scripts/pg/pg-secret.yml:

vim ~/scripts/pg/pg-secret.yml
apiVersion: v1
kind: Secret
metadata:
  name: pg-secret
type: Opaque
data:
  root-password: c2VjcmV0

Applicare il file di configurazione:

kubectl apply -f ~/scripts/pg/pg-secret.yml

Deployment e Servizio

Anch'essi si possono combinare in un’unica configurazione. E’ raro che vi sia un deployment senza servizio.

Il manifest è ~/scripts/pg/pg-deployment.yml:

vim ~/scripts/pg/pg-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pg-deployment
  labels:
    app: postgres
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      volumes:
        - name: postgresdb
          persistentVolumeClaim:
            claimName: postgres-pv-claim
      containers:
      - name: postgres
        image: localhost:5000/postgres:12.2
        volumeMounts:
        - name: postgresdb
          mountPath: /var/lib/postgresql/data
        ports:
        - containerPort: 5432
        env:
        - name: POSTGRES_PASSWORD
          valueFrom:
            secretKeyRef:
              name: pg-secret
              key: root-password
---
apiVersion: v1
kind: Service
metadata:
  name: pg-service
spec:
  selector:
    app: postgres
  type: LoadBalancer
  ports:
    - protocol: TCP
      port: 5432
      targetPort: 5432

Applicare la configurazione:

kubectl apply -f ~/scripts/pg/pg-deployment.yml

Attendere la generazione dei pods con:

kubectl get pods

Test dell'Esercizio

Creare un oggetto in postgres

Connettersi al pod che ha postgres:

kubectl get pods
NAME                             READY   STATUS    RESTARTS   AGE
pg-deployment-794d8fcb76-c2hnn   1/1     Running   0          72s

Collegarsi con una shell al pod:

kubectl exec -ti pg-deployment-794d8fcb76-c2hnn -- bash

Naturalmente il nome del pod sarà diverso.

Verificare di essere sul pod:

hostname
pg-deployment-794d8fcb76-c2hnn

Connettersi al database:

psql -U postgres
psql (12.2 (Debian 12.2-2.pgdg100+1))
Type "help" for help.

postgres=#

La connessione può fallire. Postgres la prima volta che viene lanciato crea la struttura della sua datadir, e questo può impiegare qualche minuto.

Semplicemente attendere e riprovare.

Postgres ha nella sua datadir il file di configurazione di accessi pg_hba.conf. Contiene di default un settaggio che permette l'accesso come utente amministrativo postgres dalla macchina locale, senza che venga chiaesta la password.

Listare i database correnti: \l

Creare un database: create database test;

Listare di nuovo i database correnti: \l

Uscire dalla connessione a database: \q

Rimuovere tutto e ricreare tutto da capo, poi verificare se il database test esiste ancora.

Prossimi passi:

  • uscire dal pod: exit
  • rimuovere il cluster: ~/teardown.sh

Sulla macchina host verificare la persistenza dei dati:

sudo ls /data/pg

Ripartenza e Test di Connettività

cd
./setup.sh

Procedure Shell per l'Applicativo

Per automatizzare il setup e teardown dell’applicativo postgres creiamo due procedure shell, nella directory di postgres.

~/scripts/pg/pg-setup.sh

vim ~/scripts/pg/pg-setup.sh
#! /bin/sh
echo "Starting application: postgres"
kubectl apply -f ~/scripts/pg/pg-volume.yml
kubectl apply -f ~/scripts/pg/pg-secret.yml
kubectl apply -f ~/scripts/pg/pg-deployment.yml
echo
echo "Wait up to 120s for Postgres deployment to be ready ..."
kubectl wait deployment -n default pg-deployment --for condition=Available=True --timeout=120s
echo
echo "      POSTGRES NOW READY"
echo " ===> Pods running:"
kubectl get pods

La rendiamo eseguibile:

chmod +x ~/scripts/pg/pg-setup.sh

~/scripts/pg/pg-teardown.sh

vim ~/scripts/pg/pg-teardown.sh
#! /bin/sh
echo "About to delete application 'postgres'"
echo "Press Control-C within 10 seconds to interrupt"
sleep 10
kubectl delete -f ~/scripts/pg/pg-deployment.yml
kubectl delete -f ~/scripts/pg/pg-secret.yml
kubectl delete -f ~/scripts/pg/pg-volume.yml
echo
echo "Application deleted: postgres"

La rendiamo eseguibile:

chmod +x ~/scripts/pg/pg-teardown.sh

Continua il Test dell'Applicativo

Setup dell’applicativo:

~/scripts/pg/pg-setup.sh
....
 ===> Pods running:
NAME                             READY   STATUS    RESTARTS   AGE
pg-deployment-794d8fcb76-sqwtf   1/1     Running   0          17s

Verifica del servizio:

kubectl get svc

Collegamento al pod:

kubectl exec -ti pg-deployment-794d8fcb76-sqwtf -- bash

Collegamento a postgres:

psql -U postgres

Verifica dei database esistenti:

\l

Uscita da postgres:

\q

Uscita dal pod:

exit

Test di Connettività

Creare un container alpine sulla rete kind:

docker run -ti --rm --name alp --privileged --net kind alpine sh

Siamo nel container alpine. Installare il client postgres:

apk add postgresql-client

Connettersi:

psql -U postgres -h 172.18.255.200

Da il seguente errore:

psql: error: connection to server at "172.18.255.200", port 5432 failed: FATAL:  no pg_hba.conf entry for host "10.244.2.1", user "postgres", database "postgres", SSL off

Uscire con exit.

Il file di configurazione di accessi di Postgres, pg_hba.conf, di default può non contenere linee di configurazione per l'accesso da host remoti.

Queste linee hanno uno dei seguenti formati:

host        all             all             0.0.0.0/0               md5
host all all all md5

Tenendo conto che la datadir di Postgres è mappata sulla macchina ospite alla directory /data/pg, possiamo modificare il file di configurazione col comando:

sudo vim /data/pg/pg_hba.conf

aggiungendo la linea di configurazione:

host    all             all             0.0.0.0/0               md5

Occorre ora far ripartire il server postgres con:

kubectl delete -f ~/scripts/pg/pg-deployment.yml
kubectl apply -f ~/scripts/pg/pg-deployment.yml

Non potevamo modificare prima il file /data/pg/pg_hba.conf perchè la directory /data/pg ancora non esisteva, o non era ancora stata popolata come datadir.

Rilanciamo quindi il contenitore docker, installiamo il client postgres e riproviamo la connessione:

docker run -ti --rm --name alp --privileged --net kind alpine sh
apk add postgresql-client
psql -U postgres -h 172.18.255.200
Password for user postgres: secret
psql (14.5, server 12.2 (Debian 12.2-2.pgdg100+1))
Type "help" for help.

postgres=#

Test OK.

Uscire da postgres e dal container alpine.

Teardown dell’applicativo:

~/scripts/pg/pg-teardown.sh

Riscrittura di pg-setup.sh

Il problema è che non vogliamo editare un file a mano, ma vogliamo che tutte le attività vengano compiute in modo batch.

Riscriviamo quindi la procedura shell ~/scripts/pg/pg-setup.sh.

vim ~/scripts/pg/pg-setup.sh
#! /bin/sh
echo "Starting application: postgres"
kubectl apply -f ~/scripts/pg/pg-volume.yml
kubectl apply -f ~/scripts/pg/pg-secret.yml
kubectl apply -f ~/scripts/pg/pg-deployment.yml
echo
echo "Wait up to 120s for Postgres deployment to be ready ..."
kubectl wait deployment -n default pg-deployment --for condition=Available=True --timeout=120s
echo
echo "      POSTGRES NOW READY"
echo " ===> Pods running:"
kubectl get pods
# pod running the deployment
pod=$(kubectl get pod | grep pg-deployment | cut -d\  -f1)
echo "Waiting for datadir to be ready"
cycles=30
while true
do
  cycles=`expr $cycles - 1`
  if kubectl logs $pod | grep "ready to accept connections"
  then
    echo "Datadir ready"
    break
  else
    if [ $cycles -le 0 ]
    then
      echo "Losing patience. Patch pg_hba.conf yourself"
      exit 1
    fi
    echo "Not ready yet, waiting 10 seconds"
    sleep 10
  fi
done
if sudo grep "host" /data/pg/pg_hba.conf | grep 0.0.0.0 \
        || sudo grep "host all all all md5" /data/pg/pg_hba.conf
then
  echo pg_hba.conf is already patched
else
  sudo sed -i -e '$ahost        all             all             0.0.0.0/0               md5' /data/pg/pg_hba.conf
  echo pg_hba.conf has been patched
  echo Stopping the postgres service:
  kubectl delete -f ~/scripts/pg/pg-deployment.yml
  echo Restarting the postgres service:
  kubectl apply -f ~/scripts/pg/pg-deployment.yml
  echo "Wait up to 120s for Postgres deployment to be ready ..."
  kubectl wait deployment -n default pg-deployment --for condition=Available=True --timeout=120s
  echo
  echo "      POSTGRES NOW REALLY READY"
fi

Connessione Esterna

Da un semplice container sulla stessa rete del cluster:

docker run -ti --rm --net kind --name client \
  postgres:12.2 psql -h 172.18.255.200 -U postgres

Da un container che implementa un'utility grafica:

docker run -d --rm --name omnidb --net kind -v "$HOME/pg/omnidb":/data -p 127.0.0.1:8000:8000 -p 127.0.0.1:25482:25482 t4skforce/omnidb
  • collegarsi con un browser a localhost:8000
  • credenziali di login: admin/admin
  • creare una nuova scheda di connessione
  • usare la connessione
  • uscire da omnidb

Altra utility grafica:

docker run -d --net kind --name pgadmin -p 8080:80 -e 'PGADMIN_DEFAULT_EMAIL=mich@stormforce.ac' -e 'PGADMIN_DEFAULT_PASSWORD=supersecret' dpage/pgadmin4