Aller au contenu

Postgres-Operator#

Sur notre infrastructure nous utilisons l'operator postgres de Zalando.

Version actuelle 1.6.1

Source GitHub

Fonctionnement#

alt text

Implémentation#

Vous trouverez un service postgres-operator dans le namespace postgres-operator dans chaque zone qui communique directement avec les clusters postgres de chaque service

Déploiement#

Le déploiement s'effectue avec la couche kustomize qui est piloté via un playbook ansible

Source

Logical Backup#

Cette fonction permet de créer des dump des base et de les uploder sur un bucket S3, permet de décentraliser les data et apporte une solution de PRA (plan de reprise d’activité)

La couche terraforme créé les bucket S3 sur FE avec l'implémentation des secret dans vault accessible grace au service account.

Un rétention est directement définie sur le bucket S3 car l'operator postgres ne permet pas de supprimer de la data sur le bucket.

Le nom du bucket : postgres-backup-{{ zone_name }}

Visualisation des dump sur le bucket S3 FE :

Fixme

Missing content

Restore#

Il n’existe pas de service de roolback avec l'operator mais des solution sont possibles:

  • Créer un clone d'un cluster via un dump disponible sur un bucket S3
  • Importer le dump après la création d'un cluster vierge

Documentation officielle

Retour d’expérience#

Incident Cause Résolution Impacte
L'operator ne peux pas généré le STS Modification du STS non pris en charge pas l'operator (label) l est possible de l'ajouté manuellement en ajoutant la modification dans l'objet postgresql et dans le sts, sinon vous devez détrure le sts (cluster postgres) et l'operator va regénéré le sts avec les nouveaux paramètres. Le cluster est fonctionnelle mais mais plus sync avec l'operator, risque de divergence des slaves
Cluster postgres qui n'est plus sync STS pas a jour, erreur de configuration du cluster, divergence des replicas Vérification des log du cluster, reinit les pod replica, recréation du sts, vérification du déploiement Le cluster n'est pas forcement opérationnelle, risque de divergence des slaves voir destruction
Cache hit ratio < 90% manque de mémoire (RAM) dans le cluster Augmentation de la RAM dans le cluster Performance du cluster

Commandes utiles#

Visualisation des schedule logical backup#

kubectl get cronjob -A | grep logical-

Création d'un JOB pour backup un cluster manuellement#

kubectl create job --from=cronjob/NOM_DU_CRON NOM_DU_JOB -n NAME_SPACE

## exemple pour clair
kubectl create job --from=cronjob/logical-backup-clair-postgresql-cluster logical-backup-clair-postgresql-cluster -n clair

Vérification de l’état d'un clusteur#

kubectl get postgresql -A             
NAMESPACE         NAME                       TEAM        VERSION   PODS   VOLUME   CPU-REQUEST   MEMORY-REQUEST   AGE   STATUS
clair             clair-postgresql-cluster   clair       11        3      50Gi     100m          400Mi            4d    Running
concourse-infra   concourse-postgresql       concourse   11        2      50Gi     100m          256Mi            54d   Running
gitea             gitea-postgresql-cluster   gitea       11        3      20Gi     100m          100Mi            17h   Running
keycloak          pocwatt-keycloak-cluster   pocwatt     11        3      2Gi      100m          200Mi            59d   Running
quay              quay-postgresql-cluster    quay        11        3      50Gi     100m          200Mi            23h   Running

## Vérification dans le cluster via patroni
kubectl exec -it -n quay quay-postgresql-cluster-1 -- bash -c "patronictl list"
+ Cluster: quay-postgresql-cluster (6869646851055235153) -----+----+-----------+
|           Member          |     Host     |  Role  |  State  | TL | Lag in MB |
+---------------------------+--------------+--------+---------+----+-----------+
| quay-postgresql-cluster-0 | 172.16.0.118 | Leader | running |  2 |           |
| quay-postgresql-cluster-1 |  172.16.1.4  |        | running |  2 |         0 |
| quay-postgresql-cluster-2 | 172.16.0.192 |        | running |  2 |         0 |
+---------------------------+--------------+--------+---------+----+-----------+

Création d'un dump sql d'une base#

## Vérification du pod master
kubectl get -A pod -l application=spilo,spilo-role=master

## Exécution du dump sur le master (exemple keycloak)
kubectl exec -it -n keycloak pocwatt-keycloak-cluster-2 -- bash -c "pg_dump -Fc -cC -U keycloak -d keycloak" > dump-keycloak.sql

Récupération d'un dump sql depuis le bucket S3#

Les dumps sont rangés sur le bucket propre à chaque zone, selon la règle de nommage suivante :
s3://postgres-backup-${ZONE_NAME}/snapshots/YYYY-MM-AA/postgres-dump-${ZONE_NAME}-${DB_USER}-${DB_NAME}-YYYYMMAA-HHmm.sql

Les identifiants de connexion sont rangés dans le vault propre à la zone :

export VAULT_ADDR=https://vault.ocb-test01.caascad.com                                           
vault login -method oidc

export AWS_ACCESS_KEY_ID=$(vault kv get -field=access_key secret/bucket/postgres-backup)
export AWS_SECRET_ACCESS_KEY=$(vault kv get -field=secret_key secret/bucket/postgres-backup)

env | aws s3 ls s3://postgres-backup-ocb-test01/snapshots/2021-06-13/ \
  --endpoint-url=https://oss.eu-west-0.prod-cloud-ocb.orange-business.com \
  --region=eu-west-0

env | aws s3 cp s3://postgres-backup-ocb-test01/snapshots/2021-06-13/postgres-dump-ocb-test01-keycloak-keycloak-20210613-0009.sql.gz ./dump.sql.gz \
  --endpoint-url=https://oss.eu-west-0.prod-cloud-ocb.orange-business.com \
  --region=eu-west-0

Suppression d'un cluster et PVC#

## Exemple avec quay
kubectl delete postgresql -n quay quay-postgresql-cluster
L'operator détecte la suppression de l'objet postgresql il exécute la suppression de l'ensemble des objets de celui ci : svc, ep, secret, sts, pdb

cela peut prendre du temps, si le clusteur n'est plus sync vous pouvez le faire a la main

Importation d'un dump .sql (backup manuel ou importation S3)#

Warning

les fichiers dump.sql contiennent la plupart du temps des commandes qui nécessitent qu'aucune connexion ne soit active sur la base, typiquement DROP DATABASE.

Afin de couper les connexions, exécutez les commandes suivantes :

## récupération du pod Leader de tous les clusters
kubectl get -A pod -l application=spilo,spilo-role=master

NAMESPACE         NAME                         READY   STATUS    RESTARTS   AGE
keycloak          pocwatt-keycloak-cluster-1   2/2     Running   0          6d22h


## connexion à la base via le pod Leader
kubectl exec -it -n keycloak pocwatt-keycloak-cluster-1 -- bash -c "psql -U keycloak -d keycloak"

## commande SQL pour lister les connexions
SELECT pg_stat_activity.pid
 FROM pg_stat_activity
 WHERE pg_stat_activity.datname = 'keycloak' -- nom de la database a fournir ici
 AND pid <> pg_backend_pid();

## commande SQL pour supprimer les connexions
SELECT pg_terminate_backend(pg_stat_activity.pid)
 FROM pg_stat_activity
 WHERE pg_stat_activity.datname = 'keycloak'
 AND pid <> pg_backend_pid();

Pour importer le fichier dump.sql, passer par le Leader également :

## exécution du script via le pod Leader (apres que les connexions aient été coupées)
cat database.sql | kubectl exec -it -n keycloak pocwatt-keycloak-cluster-1 -- bash -c "pg_restore -Fc -v ON_ERROR_STOP=1 -U keycloak -d keycloak"

L'option ON_ERROR_STOP=1 est préconisée pour l'import.

Changement de master#

## Vérification du cluster via patroni
kubectl exec -it -n quay quay-postgresql-cluster-1 -- bash -c "patronictl list"

## changement de master
kubectl exec -it -n quay quay-postgresql-cluster-1 -- bash -c "patronictl switchover"

Reinit d'un pod master suite à une erreur de synchro entre les membres (beaucoup de fichiers WAL)#

Warning

Tout d'abord, essayer de réinitialiser le pod slave : rubrique suivante

Si cela ne résoud pas le soucis. Se connecter au pod, et lancer les commandes suivantes

Note

Prenons l'exemple d'un problème d'espace disque avec le master postgres Gitea dans le namespace gitea

kubectl exec -it -n gitea gitea-postgresql-cluster-0 -c postgres -- bash

  ____        _ _
/ ___| _ __ (_) | ___
\___ \| '_ \| | |/ _ \
  ___) | |_) | | | (_) |
|____/| .__/|_|_|\___/
      |_|

This container is managed by runit, when stopping/starting services use sv

Examples:

sv stop cron
sv restart patroni

Current status: (sv status /etc/service/*)

down: /etc/service/patroni: 2828s, normally up
down: /etc/service/pgqd: 2829s, normally up

Détection leader.

root@gitea-postgresql-cluster-0:/home/postgres# patronictl list
+ Cluster: clair-postgresql-cluster (7025907046366744645) -----+----+-----------+
|           Member           |     Host     |  Role  |  State  | TL | Lag in MB |
+----------------------------+--------------+--------+---------+----+-----------+
| clair-postgresql-cluster-0 |  172.16.1.8  |        | stopped |    |   unknown |
| clair-postgresql-cluster-1 | 172.16.0.247 |        | stopped |    |   unknown |
| clair-postgresql-cluster-2 | 172.16.1.18  | Leader | running |  7 |           |
+----------------------------+--------------+--------+---------+----+-----------+

Revalidation du leader pour éviter les cas de split-brain et de maintenance en cours.

kubectl exec -it -n gitea gitea-postgresql-cluster-2 -c postgres -- bash -c "patronictl list"

+ Cluster: clair-postgresql-cluster (7025907046366744645) -----+----+-----------+
|           Member           |     Host     |  Role  |  State  | TL | Lag in MB |
+----------------------------+--------------+--------+---------+----+-----------+
| clair-postgresql-cluster-0 |  172.16.1.8  |        | stopped |    |   unknown |
| clair-postgresql-cluster-1 | 172.16.0.247 |        | stopped |    |   unknown |
| clair-postgresql-cluster-2 | 172.16.1.18  | Leader | running |  7 |           |
+----------------------------+--------------+--------+---------+----+-----------+

Choisir un point d'historique à partir du quel vous purgez.

Prendre un fichier de 16M (16777216) suivi directement d'un fichier .history.

Ce même fichier de 16M sera donc votre fichier devant être fourni à la ligne de commande d'archivage.

kubectl exec -it -n gitea gitea-postgresql-cluster-2 -c postgres -- bash -c "ls -al /home/postgres/pgdata/pgroot/data/pg_wal/ | more"

total 245796
drwx------.  3 postgres postgres     4096 Nov 25 09:14 .
drwx------. 19 postgres postgres     4096 Nov 25 14:30 ..
-rw-------.  1 postgres postgres       42 Nov 17 08:52 00000002.history
-rw-------.  1 postgres postgres       85 Nov 17 08:57 00000003.history
-rw-------.  1 postgres postgres      128 Nov 17 09:16 00000004.history
-rw-------.  1 postgres postgres      171 Nov 17 09:18 00000005.history
-rw-------.  1 postgres postgres      214 Nov 17 09:20 00000006.history
-rw-------.  1 postgres postgres 16777216 Nov 25 08:28 000000060000000200000077
-rw-------.  1 postgres postgres      257 Nov 25 08:29 00000007.history
-rw-------.  1 postgres postgres 16777216 Nov 25 08:59 000000070000000200000077
-rw-------.  1 postgres postgres 16777216 Nov 25 09:04 000000070000000200000078
-rw-------.  1 postgres postgres 16777216 Nov 25 09:04 000000070000000200000079
-rw-------.  1 postgres postgres 16777216 Nov 25 09:05 00000007000000020000007A
-rw-------.  1 postgres postgres 16777216 Nov 25 09:05 00000007000000020000007B
-rw-------.  1 postgres postgres 16777216 Nov 25 09:07 00000007000000020000007C
-rw-------.  1 postgres postgres 16777216 Nov 25 09:10 00000007000000020000007D
-rw-------.  1 postgres postgres 16777216 Nov 25 09:10 00000007000000020000007E
-rw-------.  1 postgres postgres 16777216 Nov 25 09:40 00000007000000020000007F
-rw-------.  1 postgres postgres 16777216 Nov 25 02:59 000000070000000200000080
-rw-------.  1 postgres postgres 16777216 Nov 25 02:56 000000070000000200000081
-rw-------.  1 postgres postgres 16777216 Nov 25 02:54 000000070000000200000082
-rw-------.  1 postgres postgres 16777216 Nov 25 02:59 000000070000000200000083
-rw-------.  1 postgres postgres 16777216 Nov 25 03:29 000000070000000200000084
drwx------.  2 postgres postgres     4096 Nov 25 09:40 archive_status

Stopper la base de données et jouer la commande d'archivage

kubectl exec -it -n gitea gitea-postgresql-cluster-2 -c postgres -- bash -c "sv stop /etc/service/pgqd"
ok: down: /etc/service/pgqd: 695083s, normally up

kubectl exec -it -n gitea gitea-postgresql-cluster-2 -c postgres -- bash -c "pg_archivecleanup /home/postgres/pgdata/pgroot/data/pg_wal 000000060000000200000077"

Forcer la recréation du pod, pour qu'il prenne en compte la purge et se déverouille. L'option agit comme un kill -9

kubectl delete -n gitea gitea-postgresql-cluster-2 --force

Votre master est de nouveau opérationnel, vous pouvez le voir grâce à votre exporter ou votre application qui devrait réussir sa connexion.

Warning

Lorsque le master est de nouveau opérationnel, voir si le(s) slave(s) doivent être également réinitialisés. C'est le cas s'ils sont aussi en erreur (start failed / stopped).

En cas d'échec, seule la restoration depuis une sauvegarde pourra convenir :

  • tous les noeuds sont en erreurs

  • le master ne redémarre (postgres UP, leader patroni et base accessible via l'exporter) pas malgré l'archivage des fichiers WAL

  • le master ne redémarre pas malgré la suppression du pod

Reinit d'un pod slave suite à une erreur de synchro avec le master (beaucoup de fichiers WAL)#

Warning

Vérifier l'état de santé du master. En cas d'anomalie essayer de réinitialiser le pod master : rubrique précédente

Tout d'abord, essayer de réinitialiser le pod slave, se connecter au pod, et lancer la commande suivante :

kubectl exec -it -n concourse-infra concourse-postgresql-0 -- bash -c "patronictl reinit concourse-postgresql concourse-postgresql-1"

Si la réinitialisation ne fonctionne pas correctement, détruire le pod AVANT (avec un kubectl delete), attendre qu'il se lance, puis faire la commande de réinitialisation patroni.

Procédure de restauration manuelle d'une BDD#

De manière optionnelle et afin d'éviter des erreurs applicatives il est possible de supprimer l'application qui consomme la BDD

kubectl delete deployment {{Application}}

Copie du backup sur un des pods du cluster postgresql :

kubectl cp backup.sql ${PSQL_POD_NAME}:/tmp

Connexion au pod :

kubectl exec -it  ${PSQL_POD_NAME} -- bash
su - postgres

psql

Isolation de la db pour restauration :

REVOKE CONNECT ON DATABASE <db_name> FROM public;

SELECT pg_terminate_backend(pg_stat_activity.pid)
FROM pg_stat_activity
WHERE pg_stat_activity.datname = '<db_name>';

exit;
Restauration:
psql -f /tmp/backup.sql

Déverrouillage de la base:

psql

GRANT CONNECT ON DATABASE keycloak FROM public;
Redéploiement de l'application si elle a été supprimée en première étape