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 !