Lyon, le 24 avril 2025
CloudNativePG ou comment embarquer un éléphant sur un porte-conteneurs !
Dans le premier article, nous avons eu un aperçu de ce que l’opérateur
CloudNativePG permet de faire. Le déploiement des instances se déroulait sans
encombre … mais que se passe-t-il vraiment lorsque la commande kubectl apply
-f ~/cluster.yaml est utilisée ? C’est ce que nous allons voir avec ce nouvel
article consacré à l’opérateur CloudNativePG.

La séquence de démarrage 🚀
L’objectif aujourd’hui est de comprendre les mécanismes qui entrent en jeu lors de la création d’un cluster PostgreSQL avec CloudNativePG. Je pars du principe qu’un cluster Kubernetes est en cours de fonctionnement et que l’opérateur est installé. Reprenons la définition du cluster du premier article de notre série :
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: postgresql
spec:
imageName: ghcr.io/cloudnative-pg/postgresql:17.0
instances: 1
storage:
size: 20Gi
Décortiquons ce qui se passe lorsque kubectl apply -f ~/cluster.yaml est utilisé.
La commande kubectl get pod --watch est très utile pour comprendre cela.
k get pod --watch
NAME READY STATUS RESTARTS AGE
postgresql-1-initdb-n4c5n 0/1 PodInitializing 0 1s
postgresql-1-initdb-n4c5n 0/1 Completed 0 2s
postgresql-1 0/1 Pending 0 0s
postgresql-1 0/1 Init:0/1 0 0s
postgresql-1 0/1 PodInitializing 0 1s
postgresql-1 0/1 Running 0 10s
postgresql-1 1/1 Running 0 10s
Pod initdb
Un premier Pod est créé, ici postgresql-1-initdb-n4c5n. Le mot initdb doit
normalement vous dire quelque chose (pssst, si jamais vous avez oublié, il
s’agit d’un utilitaire qui
permet de préparer l’instance en créant notamment les répertoires de
l’arborescence du PGDATA, les tables du catalogue PostgreSQL et des bases de
données comme template1). Lorsque celui-ci a terminé de s’exécuter, un autre
Pod est lancé, qui lui correspondra à notre instance PostgreSQL.
Revenons sur le premier Pod. Il contient en réalité deux conteneurs, nommés
bootstrap-controller 📦 et initdb 📦.
Le conteneur bootstrap-controller est ce qu’on appelle un init-container.
C’est un type de conteneur spécial qui est démarré avant le ou les conteneurs
applicatifs (ici initdb).

En regardant les détails de ce conteneur avec kubectl describe postgresql-1-initdb-n4c5n,
on peut relever plusieurs éléments intéressants :
- Il se base sur la même image que l’opérateur
ghcr.io/cloudnative-pg/cloudnative-pg:1.25.1; - et il exécute la commande
/manager bootstrap /controller/manager --log-level=info.
[...]
Init Containers:
bootstrap-controller:
Container ID: docker://2f6f2d7fa4e64f739113805827235a629ffeea78d740265b44dcb0474e4a7784
Image: ghcr.io/cloudnative-pg/cloudnative-pg:1.25.1
[...]
Command:
/manager
bootstrap
/controller/manager
--log-level=info
State: Terminated
Reason: Completed
Exit Code: 0
Started: Tue, 22 Apr 2025 17:31:42 +0200
Finished: Tue, 22 Apr 2025 17:31:43 +0200
[...]
La commande a pour but de mettre à disposition l’opérateur dans le second
conteneur. Lorsqu’il a terminé de s’exécuter, le conteneur passe à l’état
Terminated et la séquence de démarrage continue par le lancement du conteneur
applicatif, ici initdb.
Suivant le même principe, ce conteneur est créé à partir d’une image, ici
ghcr.io/cloudnative-pg/postgresql:17.0.
Containers:
initdb:
Container ID: docker://af0bf7b2d64e7cc88a1bd0034a5f46a3644a41523c0d18eaa8719cf963a5acad
Image: ghcr.io/cloudnative-pg/postgresql:17.0
Là aussi, la commande à exécuter est spécifiée. En l’occurrence il s’agit de
/controller/manager instance init ... qui va déclencher l’initialisation de
l’instance PostgreSQL. Cette commande utilitaire manager qui est ni plus ni
moins qu’une partie de l’opérateur CloudNativePG. Cet utilitaire est écrit en Go et on peut
retrouver son code sur le projet Github.
Les arguments passés à cette commande sont ceux par défaut. Ils peuvent être
surchargés dans le manifest du Cluster. Aussi, à la lecture de ceux-ci, on
comprend qu’une base de données app et un rôle qui porte le même nom seront
automatiquement créés.
Command:
/controller/manager
instance
init
--initdb-flags
--encoding=UTF8 --lc-collate=C --lc-ctype=C
--app-db-name
app
--app-user
app
--log-level=info
On peut également voir certaines variables d’environnement positionnées dans la définition du conteneur et qui seront utilisées par cette commande.
Environment:
PGDATA: /var/lib/postgresql/data/pgdata
POD_NAME: postgresql-1-initdb
NAMESPACE: default
CLUSTER_NAME: postgresql
PSQL_HISTORY: /controller/tmp/.psql_history
PGPORT: 5432
PGHOST: /controller/run
TMPDIR: /controller/tmp
Lorsque cette commande aura fini de s’exécuter, le Persistant Volume sera prêt
à être utilisé par le second Pod.
Pod PostgreSQL
Quand les premiers conteneurs au sein du Pod initdb auront terminé leurs
tâches, le controller CloudNativePG va déployer un second et dernier Pod qui
contiendra notre instance PostgreSQL.

Le même principe d’init-container est utilisé avec le conteneur
boostrap-controller qui permet d’utiliser l’opérateur CloudNativePG dans le
Pod PostgreSQL. Ce Pod réutilise le Persistant Volume créé précédemment.
Cette fois-ci, le second conteneur est nommé postgres. Il se base lui aussi
sur l’image ghcr.io/cloudnative-pg/postgresql:17.0 et exécute la commande
suivante :
Command:
/controller/manager
instance
run
--status-port-tls
--log-level=info
L’instance PostgreSQL est démarrée par l’intermédiaire de l’outil
/controller/manager présent dans le conteneur. Notre éléphant 🐘 est embarqué
sur notre porte-conteneurs ⛴️!
Images utilisées
Les init-containers ou les conteneurs applicatifs (ici initdb et postgres)
se basent sur des images spécifiques. Ces images doivent se trouver sur le nœud
Kubernetes qui va héberger l’instance PostgreSQL. Si elles ne se trouvent pas
localement sur la machine, elles devront être téléchargées. Par défaut, elles
le seront depuis la registry Github associée au projet CloudNativePG. C’est ce
que l’on peut voir dans le nom des images avec la partie ghcr.io/cloudnative-pg.
Il faut donc s’assurer que votre cluster Kubernetes ait accès à internet pour pouvoir les récupérer… si ce n’est pas le cas, vous devrez mettre en place une registry interne comme Harbor, Zot, ou Distribution… mais cela ne nous regarde pas. On laissera ce choix être fait par l’équipe DevOps ou Système 😇.
Conclusion
Nous venons de découvrir ce qu’il se passe lorsqu’un Cluster PostgreSQL est
créé. Le concept Kubernetes d’Init Container est utilisé par l’opérateur
CloudNativePG lors de la création des différents Pods.
La séquence de démarrage se fait en deux étapes : d’abord la préparation de
l’instance et du volume par un Pod qui utilise initdb puis le déploiement du
Pod PostgreSQL sur ce même volume.
Des questions, des commentaires ? Écrivez-nous !