Vallée de Munster, 03 avril 2025
Les développeurs de CloudNativePG travaillent sur divers projets et fonctionnalités que nous suivons attentivement (merci le Logiciel Libre et la gouvernance ouverte 😊).
L’une des fonctionnalités qui nous intéresse, et est à l’origine de nombreux chantiers, est la possibilité d’étendre un cluster CloudNativePG avec des plugins.
Nous vous proposons aujourd’hui une présentation de cette fonctionnalité. Nous illustrerons notre article avec un plugin expérimental que nous avons développé. Ce plugin permet d’archiver les WALs et sauvegarder une instance vers un bucket S3 avec pgBackRest.
Pourquoi des Plugins
Initialement les développeurs de CloudNativePG ont choisi d’intégrer certains composants directement dans l’opérateur. Vous pouvez décider d’utiliser ou de ne pas utiliser ces composants, par contre vous n’avez pas la possibilité de les changer.
Par exemple l’opérateur permet de configurer et d’utiliser Barman Cloud pour les sauvegardes au fil de l’eau (PITR). Si cette solution ne répond pas à vos besoins, vous deviez jusqu’à présent, soit :
- Bricoler une solution alternative ;
- Vivre avec Barman Cloud ;
- Choisir une autre solution pour opérer PostgreSQL dans Kubernetes.
Cette vision a été la source de plusieurs demandes sur le gestionnaire de demandes du projet (Exemple: 6272, 3077 ou encore 3432). Les développeurs de CloudNativePG ont pris en compte ces remarques. Depuis la version 1.25.0 sortie en décembre 2024, il est maintenant possible d’utiliser l’interface cnpg-i (expérimental) pour étendre et enrichir les fonctionnalités des Clusters CloudNativePG.
Expérimentations avec pgBackRest
Dans le cadre de notre R&D, nous avons testé cette interface. Voici une présentation du fonctionnement de notre plugin expérimental pour utiliser pgBackRest avec CloudNativePG.
Notre solution est composée d’un :
- Contrôleur (
pgbackrest-controller
) dédié à notre plugin qui va communiquer avec l’opérateur CloudNativePG et est responsable de configurer pgBackRest au sein de nos clusters ; - Sidecar container par Cluster CloudNativePG configuré avec notre
plugin. Ce conteneur (
pgbackrest-plugin
) doit s’occuper de l’archivage et la sauvegarde. Ce conteneur se charge de lancer la commandepgbackrest
lorsque c’est nécessaire (lors de l’archivage ou une sauvegarde).
Contrôleur pgbackrest-controller
Le contrôleur pgbackrest-controller
est responsable de faire muter les
ressources demandées par un⋅e utilisateurice. Lorsque l’opérateur est
sollicité pour initier ou modifier un Cluster, notre contrôleur va
le modifier pour injecter et configurer un sidecar container. Ce
conteneur va ensuite être responsable de l’archivage (des WALs) et
des sauvegardes. Cette mutation est réalisée avant que la requête (pour
initier le Pod) ne soit envoyée à l’API de Kubernetes.
Pour permettre cela, notre plugin doit déclarer auprès de l’opérateur CloudNativePG qu’il est capable de mettre à jour (“patcher”) des objets. Pour cela, nous utilisons les capabilites définies par l’interface CNPG-I pour gérer cycle de vie (lifecycle) des instances.
Ce contrôleur s’exécute sous la forme d’un Pod au sein du
namespace dédié à l’opérateur (par exemple : cnpg-system
) et
communique avec le contrôleur de l’opérateur. Ce composant est unique
(un seul déploiement) et est partagé entre tous les Clusters
CloudNativePG.
Le contrôleur se charge aussi de configurer pgBackRest. Lorsqu’il ajoute
un sidecar container, le contrôleur injecte les variables
d’environnement qui seront utilisées par pgBackRest (PGBACKREST_...
).
$ kubectl get pod --namespace cnpg-system
NAME READY STATUS RESTARTS AGE
cnpg-controller-manager-76cd6b9b68-wcf6t 1/1 Running 2 (55m ago) 46h
pgbackrest-controller-78dbb67f9-85r2v 1/1 Running 2 (55m ago) 45h
Ici, c’est pgbackrest-controller-78dbb67f9-85r2v
, qui porte cette
responsabilité.
Sidecar container pgbackrest-plugin
Comme évoqué plus haut, notre plugin, via le contrôleur
pgbackrest-controller
, ajoute à nos clusters un sidecar
container pgbackrest-plugin
dans le Pod de l’instance
PostgreSQL. Ce sidecar container nouvellement ajouté va ensuite
archiver et sauvegarder notre instance via pgBackrest.
Lorsque qu’un WAL doit être archivé, l’instance PostgreSQL, via
l’archive_command
contacte notre plugin (dans le sidecar
container du cluster) pour l’archivage. Lors de la réception d’une
telle requête, la commande pgbackrest archive-push
va être exécutée
(dans le sidecar container) pour archiver le WAL vers un
bucket s3.
L’appel à notre système d’archivage est possible car CloudNativePG
utilise et configure la commande d’archivage (archive_command
)
suivante :
controller/manager wal-archive --log-destination /controller/log/postgres.json %p
.
Le code derrière cette commande utilise un système de hook qui
permet d’appeler les plugins (dont le nôtre) qui exposent les
capabilities d’archivage et de sauvegarde (Exemple :
WALCapability_RPC_TYPE_ARCHIVE_WAL
et
BackupCapability_RPC_TYPE_BACKUP
).
Pour permettre l’archivage depuis notre sidecar container, ce
dernier doit avoir accès aux WALs. Cela est possible car il est
configuré (par le contrôleur) pour monter le volume pour le pg_data
.
Le volume contenant les WALs est donc partagé entre les conteneurs
pgbackrest-plugin
et postgres
.
Il en est de même lorsqu’un·e utilisateurice demande la sauvegarde d’un Cluster. pgBackRest (avec la commande pgbackrest backup) est lancé depuis le sidecar container dédié.
Configuration et utilisation
Concrètement, pour utiliser un plugin avec CloudNativePG, il faut
ajouter une entrée sous l’entrée plugins
(prévue par les CRD) au
manifest de notre Cluster :
---
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: cluster-demo
spec:
instances: 1
plugins:
- name: pgbackrest.dalibo.com
parameters:
s3-bucket: demo-pgbackrest
s3-endpoint: s3.fr-par.scw.cloud
s3-region: fr-par
s3-repo-path: /demo-pgbackrest/cluster-demo
stanza: pgbackrest
storage:
size: 1Gi
Dans notre cas, nous avons opté pour une approche simple. Les paramètres
pour configurer pgBackRest sont sous la clé parameters
du
plugin.
Si on applique ce manifest (avec kubectl apply
), on observe que le
Pod résultant est composé de 2 conteneurs:
$ kubectl get pods --namespace default cluster-demo-1
NAME READY STATUS RESTARTS AGE
cluster-demo-1 2/2 Running 2 (70m ago) 15h
En regardant plus attentivement, on découvre le conteneur
pgbackrest-plugin
en plus de l’habituel conteneur postgres
.
pgbackrest-plugin
est défini avec des variables d’environnement
contenant la configuration de pgBackRest. Ce sidecar container a été
injecté et configuré par le contrôleur pgbackrest-controller
lors de
l’initialisation du cluster CloudNativePG.
$ kubectl describe pods --namespace default cluster-demo-1
...
Containers:
...
pgbackrest-plugin:
Container ID: containerd://3733b1d757a30ebb2161d2b14cee0b2fceed8f281706afb02f7b994f1d5863bd
Image: pgbackrest-sidecar
Image ID: docker.io/library/import-2025-03-06@sha256:3b29d3384b12b47e776187b968dcf6608397ec6e3fa1c5e937ba1e089d38e86b
Port: <none>
Host Port: <none>
Command:
/app/bin/cnpg-i-pgbackrest
Args:
instance
...
Environment:
NAMESPACE: default
CLUSTER_NAME: cluster-demo
PGBACKREST_delta: y
PGBACKREST_pg1-path: /var/lib/postgresql/data/pgdata
PGBACKREST_process-max: 2
...
PGBACKREST_repo1-type: s3
PGBACKREST_stanza: pgbackrest
PGBACKREST_repo1-s3-key: <set to the key 'key' in secret 'pgbackrest-s3-secret'>
Nous pouvons aussi observer et constater l’archivage des WALs en consultant les traces :
$ kubectl logs -f -c pgbackrest-plugin cluster-demo-1 --namespace default
{"level":"info","ts":"2025-03-07T11:31:15.206973833Z","msg":"Starting pgbackrest instance plugin"}
{"level":"info","ts":"2025-03-07T11:31:15.207750359Z","logger":"controller-runtime.metrics","msg":"Starting metrics server"}
{"level":"info","ts":"2025-03-07T11:31:15.207921755Z","msg":"Starting plugin listener","protocol":"unix","socketName":"/plugins/pgbackrest.dalibo.com"}
{"level":"info","ts":"2025-03-07T11:31:15.208280361Z","msg":"TCP server not active, skipping TLSCerts generation"}
{"level":"info","ts":"2025-03-07T11:31:15.208314216Z","logger":"controller-runtime.metrics","msg":"Serving metrics server","bindAddress":":8080","secure":false}
{"level":"info","ts":"2025-03-07T11:31:15.208354576Z","msg":"Starting plugin","name":"pgbackrest.dalibo.com","displayName":"pgBackRest demo / experimental plugin","version":"0.0.1"}
{"level":"info","ts":"2025-03-07T11:36:17.956473106Z","msg":"pgBackRest archive-push successful","WAL":"/var/lib/postgresql/data/pgdata/pg_wal/000000010000000000000027"}
...
L’archivage et les sauvegardes sont maintenant assurés par ce conteneur, pgBackRest est sollicité lorsque :
- PostgreSQL exécute son
archive_command
- Un·e utilisateurice demande la sauvegarde d’une instance
Les CRD (Custom Resource Definition) de CloudNativePG pour les sauvegardes permettent, de base, l’utilisation de plugins. Nous pouvons déclarer une sauvegarde avec le manifest suivant :
---
apiVersion: postgresql.cnpg.io/v1
kind: Backup
metadata:
name: backup-example
spec:
method: plugin
pluginConfiguration:
name: pgbackrest.dalibo.com
cluster:
name: cluster-demo
Il en est de même pour des sauvegardes planifiées :
---
apiVersion: postgresql.cnpg.io/v1
kind: ScheduledBackup
metadata:
name: backup-example
spec:
method: plugin
pluginConfiguration:
name: pgbackrest.dalibo.com
schedule: "0 15 * * * *"
cluster:
name: cluster-demo
Conclusion
Cette nouvelle fonctionnalité bien que complexe, nous semble prometteuse. Elle va progressivement prendre de l’ampleur. L’équipe qui développe CloudNativePG a annoncé que l’intégration de composants via des plugins deviendra la norme. Par exemple, Barman Cloud, dans les prochaines versions de l’opérateur sera intégré sous forme de plugin.
Notre preuve de concept nous a permis de :
- Tester l’interface CNPG-I ;
- Approfondir nos connaissances des mécanismes de sauvegarde avec CloudNativePG ;
- Vérifier que pgBackRest est en mesure d’archiver et de sauvegarder une instance dans un environnement conteneurisé ;
- Mettre en lumière cette nouvelle fonctionnalité expérimentale.
De notre côté, nous avons prévu de poursuivre nos expérimentations avec cette interface ainsi que pgBackRest. Nous sommes convaincus que cette diversité ouvre la voie vers une adoption plus large de PostgreSQL dans Kubernetes.
Si le sujet vous intéresse, nous vous invitons donc à suivre l’activité des projets (et pourquoi pas contribuer) :
- PostgreSQL (427) ,
- sauvegarde (11) ,
- Kubernetes (1) ,
- planetpgfr (28) ,
- CloudNativePG (7) ,
- plugin (3) ,
- pgBackRest (5)