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.

moteur

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 commande pgbackrest 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) :


DALIBO

DALIBO est le spécialiste français de PostgreSQL®. Nous proposons du support, de la formation et du conseil depuis 2005.