Lyon, le 19 mai 2025
CloudNativePG ou comment embarquer un éléphant sur un porte-conteneurs !
Si vous envisagez d’utiliser CloudNativePG pour déployer certaines de vos instances PostgreSQL, vous aurez sûrement besoin de rapatrier certaines bases de données dans ces nouvelles instances. Différentes techniques existent. Regardons deux méthodes d’import que propose l’opérateur 📥.
spec.bootstrap.initdb.import
spec.bootstrap.initdb.import
est LA section YAML qui nous intéresse dans la
définition de notre Cluster
. La section spec.bootstrap.initdb
permet de
définir comment notre instance va être créée. La partie import
permet de définir
quelle(s) base(s) de donnée(s) doi(ven)t être importée(s). On parle bien ici
d’imports logiques de bases qui existeraient déjà dans une autre instance
PostgreSQL. Cette instance doit d’ailleurs être accessible par la nouvelle
instance qui sera créée par CloudNativePG.
Deux méthodes d’import existent :
- la méthode
microservice
📦; - et la méthode
monolith
🪨.
La première méthode ne vous permet d’importer les données que d’une seule base.
Ses données seront importées dans la base par défaut de l’instance (donc app
).
Si vous souhaitez conserver le même nom de base, n’oubliez pas de modifier le
paramètre spec.bootstrap.initdb.database
de la définition du Cluster
. Cette
méthode d’import est intéressante si vous souhaitez diviser une instance
multi-bases en plusieurs instances mono-base.
La seconde méthode vous permet d’importer autant de bases que vous le souhaitez
ainsi que des rôles PostgreSQL qui seraient existant dans l’instance source.
C’est très pratique si vous souhaitez faire une migration complète de votre
instance. Petite astuce, vous pouvez utiliser le caractère wildcard *
pour
signifier que vous voulez importer toutes les bases ou tous les rôles.
Quelque soit la méthode utilisée, l’utilitaire pg_dump
est utilisé pour
récupérer les données de la base et créer un dump
dans le volume associé à la
nouvelle instance. Attention donc à l’espace de stockage lors du déclenchement
de l’import 🖴.
Comme j’aime à le dire, des exemples valent souvent mieux que de longs discours.
Prenons donc le cas d’un service Recherche et Développement qui aurait à sa
disposition trois bases de données rd_a
, rd_b
et rd_c
dans une même
instance.
Import en mode microservice
Dans cet exemple, seule la base de rd_a
va être importée car
l’équipe considère que les autres ne seront pas utilisées. En mode
microservices, une seule base peut être importée dans l’instance.
Construisons notre fichier YAML pas à pas. Tout d’abord, le Cluster
se basera
sur une image en version 17.0 et il n’y aura pas de réplication mise en place.
Restons simple et ne touchons pas à la configuration de l’instance.
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: postgresql-rda
spec:
imageName: ghcr.io/cloudnative-pg/postgresql:17.0
instances: 1
storage:
size: 20Gi
Il nous faut ensuite ajouter la section spec.bootstrap.initdb.import
à cette
définition de notre nouveau Cluster
. On en profite au passage pour définir le
nom de la base où seront importées les données (pour cet exemple, il n’est plus
question d’utiliser de tiret bas présent dans l’ancien nom), le propriétaire de
celle-ci et activer les sommes de contrôle (une bonne pratique 🪄!) sur l’instance.
[...]
bootstrap:
initdb:
database: rda
owner: monrole
dataChecksums: true
import:
type: microservice
databases:
- rd_a
source:
externalCluster: old-rd-pg
La partie import
contient les informations de la base à importer, notamment :
- son nom, dans la liste
databases
; - et où la trouver avec le paramètre
source
.
Le paramètre source
contient le nom d’un externalCluster
qui fait référence
à la définition de l’instance PostgreSQL externe décrite dans ce même YAML, dont
voici le contenu :
[...]
externalClusters:
- name: old-rd-pg
connectionParameters:
host: 51.159.74.201
user: dalibo
dbname: postgres
port: "5432"
password:
name: local-pg-secret
key: password
On y retrouve très logiquement les informations de connexions (ip, port,
utilisateur, …) et également le mot de passe associé au compte dalibo
qui se
trouve être le propriétaire de la base rd_a
. Le mot de passe doit être
renseigné dans un Secret
. Cela permet à l’opérateur de le récupérer sans
devoir le renseigner en clair.
Voici la définition du Secret
:
apiVersion: v1
kind: Secret
data:
password: ZGFsaWJvCg==
metadata:
name: local-pg-secret
type: kubernetes.io/basic-auth
Le fichier YAML complet du Cluster
est donc le suivant :
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: postgresql-rda
spec:
imageName: ghcr.io/cloudnative-pg/postgresql:17.0
instances: 1
storage:
size: 20Gi
bootstrap:
initdb:
database: rda
owner: monrole
dataChecksums: true
import:
type: microservice
databases:
- rd_a
source:
externalCluster: old-rd-pg
externalClusters:
- name: old-rd-pg
connectionParameters:
host: 51.159.74.201
user: dalibo
dbname: postgres
port: "5432"
password:
name: local-pg-secret
key: password
Il ne reste plus qu’à appliquer ce fichier pour créer notre instance.
Automatiquement, CloudNativePG va créer le dump
dans un dossier du PGDATA
puis, lorsqu’il sera créé, débutera la restauration avec pg_restore
. Vérifiez
qu’il ait suffisamment d’espace de stockage.
Import en mode monolith
La deuxième solution possible est l’import dit monolith qui permet d’importer une ou plusieurs base de données ainsi que un ou plusieurs rôles dans une même instance.
⚠️ Faites attention au point suivant qui peut avoir son importance ! ⚠️
Veillez à utiliser un rôle qui puisse accéder à tous ces objets. Par exemple,
l’import des rôles se fait en récupérant les informations depuis la vue
pg_catalog.pg_authid
. Utilisez donc un rôle SUPERUSER
qui a le droit d’y
accéder… autrement :
ERROR: permission denied for table pg_authid (SQLSTATE 42501)
J’imagine déjà des cas où cela pourrait poser problème, notamment avec
les offres PostgreSQL managées qui ne donnent généralement pas accès à des rôles
SUPERUSER
😏. Des rôles prédéfinis comme pg_read_all_data
pourraient vous
aider dans ce cas là.
Ceci étant dit, regardons un exemple de YAML.
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: cluster-monolith
spec:
imageName: ghcr.io/cloudnative-pg/postgresql:17.0
instances: 1
storage:
size: 20Gi
bootstrap:
initdb:
owner: dalibo
import:
type: monolith
databases:
- rd_a
- rd_c
source:
externalCluster: local-pg
externalClusters:
- name: local-pg
connectionParameters:
host: 51.159.74.201
user: dalibo
dbname: postgres
port: "15834"
password:
name: local-pg-secret
key: password
Peu d’éléments changent par rapport à l’import de type microservice.
import.type
est passé à monolith
et une liste de bases est renseignée.
Ici, seules les bases rda
et rdc
vont être récupérées, nos collègues
estimant inutiles de récupérer rdb
.
Versions différentes
Je ne vous ai pas tout dit !
La version de l’instance source était la version 16.8. Les instances créées avec
CloudNativePG se basent quant à elles sur la version 17.0 de PostgreSQL. Il est
donc tout à fait possible d’importer une base d’une ancienne version dans une
instance en version plus récente aussi “facilement”. Cela ne devrait pas
surprendre les DBA PostgreSQL 😏, qui savent bien que c’est une possibilité
qu’offrent pg_dump
/pg_restore
. Ce n’est en rien une nouveauté apportée par CloudNativePG.
Conclusion
Nous venons de voir deux méthodes de CloudNativePG qui permettent d’importer des
bases de données existantes lors de la création d’une nouvelle instance dans
Kubernetes. Toute la configuration se fait dans la section
bootstrap.initdb.import
de l’objet Cluster
.
Dans les deux cas (monolith
ou microservice
, la copie du dump se fera dans
le Persistant Volume
associé au nouveau Cluster
, nécessitant donc de la
place de stockage. Attention donc aux dumps volumineux. Si vous avez déjà des
dumps présents, cette solution semble peu pertinente.
Cela étant dit, ces deux méthodes sont très simples d’utilisation et permettent de gérer un export / import de manière déclaratif pour initier une instance avec des données déjà existantes. Simple et efficace 🔥 !
Des questions, des commentaires ? Écrivez-nous !