# Découverte de CloudNativePG

> Découvrir l’opérateur CloudNativePG, ses ressources, son administration et les premiers travaux pratiques.

---

Index LLMS : [llms.txt](/fr/llms.txt)

---

## 2/ Découverte de CloudNativePG {#cnpg1-ch2}

Ce chapitre prolonge l’introduction du [chapitre 1](/fr/cnpg1/01-introduction-postgresql-cloudnativepg/#cnpg1-ch1) et prépare les configurations détaillées au [chapitre 3](/fr/cnpg1/03-configuration-gestion-ressources/#cnpg1-ch3).

Le texte ci-dessous reprend intégralement le PDF complet, révision 26.10, pages 59-131. Les travaux pratiques et les solutions sont conservés dans leur ordre original.

**Figure 2-59-f01.** Figure 2\-59\-f01 \- Photo de Walter Gehr\, sous licence Creative Commons\.

![Photo de Walter Gehr\, sous licence Creative Commons\.](/images/cnpg1/manual/p059-f01.png)

Photo de Walter Gehr, Creative Commons licence.

### 2.1 OBJECTIFS {#cnpg1-2-1}

- Prise en main de l’opérateur CloudNativePG
- Déploiement d’instances PostgreSQL via l’opérateur
- Tests et découvertes de fonctionnalités

Différents sujets seront abordés pour présenter l’opérateur du mieux possible, que ce soit sur le projet CloudNativePG (historique, développement, CNCF...), sur ses fonctionnalités, ses mécanismes internes ou encore les images utilisées. L’objectif de ce module est la découverte de l’opérateur CloudNativePG mais également de sa prise en main comme l’opérateur facilite le déploiement de clusters PostgreSQL dans Kubernetes. Le TP permet d’installer l’opérateur CloudNativePG, de déployer un cluster PostgreSQL et d’effectuer différentes opérations comme la configuration d’une instance, la mise en place de sauvegardes ou de la restauration. Des exercices optionnels sont également présents.

### 2.2 AVANT-PROPOS KUBERNETES {#cnpg1-2-2}

**Figure 2-61-f01.** Figure 2\-61\-f01 \- Logo Kubernetes\.

![Logo Kubernetes\.](/images/cnpg1/manual/p061-f01.png)

Quelques explications concernant Kubernetes

- Kubernetes, K8s
- Orchestrateur de conteneurs
- Initialement prévu pour des applications dites Stateless
- De plus en plus d’applications de type base de données

Kubernetes est une plateforme qui permet d’orchestrer le déploiement de nombreuses applications sous la forme de conteneurs. Cette plateforme, initialement imaginée pour des applications dites Stateless (sans état), est devenue petit à petit une pierre angulaire de l’infrastructure informatique de nombreuses sociétés. Particulièrement appréciée des développeurs, cette solution se voit être de plus en plus utilisée pour héberger des applications de type base de données, qu’elles soient relationnelles ou non.

**Figure 2-62-f01.** Figure 2\-62\-f01 \- Schéma de principe d’un cluster Kubernetes\, adapté du tutoriel Kubernetes\.

![Schéma de principe d’un cluster Kubernetes\.](/images/cnpg1/manual/p062-f01.png)

Ce schéma montre très simplement le principe d’un cluster Kubernetes, composé de trois nœuds de travail, ou Workers, et d’un plan de contrôle, ou Control Plane qui est en charge de gérer les ressources et la distribution des conteneurs sur l’ensemble des nœuds du cluster.

Sur chacun des nœuds se trouve un environnement d’exécution de conteneur (ou Container Runtime, comme Docker Engine, containerd ou encore CRI-O) qui permet au Kubelet (composant Kubernetes) de créer les Pods et les conteneurs qui les composent.

Lorsqu’une application conteneurisée doit être déployée, un nœud est choisi en suivant certaines règles et un Pod est créé. Il peut contenir un ou plusieurs conteneurs qui partageront alors un certain nombre de ressources (mémoire, processeur, Huge Pages, pile réseau, etc.).

Tout l’objet de ce module est de voir comment il est possible de déployer des instances PostgreSQL conteneurisées grâce au concept d’opérateur Kubernetes.

Sources du PDF (page 62) :

- [https://kubernetes.io/docs/setup/production-environment/container-runtimes/#docker](<https://kubernetes.io/docs/setup/production-environment/container-runtimes/#docker>)
- [https://kubernetes.io/docs/setup/production-environment/container-runtimes/#containerd](<https://kubernetes.io/docs/setup/production-environment/container-runtimes/#containerd>)
- [https://kubernetes.io/docs/setup/production-environment/container-runtimes/#cri-o](<https://kubernetes.io/docs/setup/production-environment/container-runtimes/#cri-o>)

- Quelques objets basiques de Kubernetes
    - Pod: un ou plusieurs conteneurs applicatifs
    - Service: permet d’accéder durablement à un ou plusieurs Pods
    - Deployment, Secret, ConfigMap, etc.
- Domaines spécifiques
- Certains génériques

Kubernetes permet la création de certains objets (Resources), au sein d’un cluster. Il y de nombreux objets ayant chacun un rôle bien précis dans différents domaines (réseau, stockage, déploiement de conteneur, configuration, tâche planifiée, etc.). C’est à partir de ces objets de base que l’on peut définir les applications à déployer en écrivant des manifests au format YAML. Un Pod par exemple est le plus petit objet dans Kubernetes. Il représente la plus petite unité déployable au sein d’un cluster. Il embarque un ou plusieurs conteneurs applicatifs, une identité réseau pour les rendre joignables, des options supplémentaires et des ressources (RAM, CPU, etc.) qui, le cas échéant, seront partagées entre les différents conteneurs. Les Services quant à eux permettent d’accéder aux Pods quel que soit l’emplacement où ils se trouvent, c’est-à-dire que, si un Pod est redéployé sur un autre nœud, il saura acheminer les paquets réseau au bon endroit. Ces objets, bien que nombreux, peuvent être assez génériques, notamment les Pods qui se “contentent” de déployer des conteneurs. Ils n’ont aucune connaissance sur le type d’application qu’ils contiennent. C’est exactement à ce moment que rentrent en jeu les opérateurs avec la définition de ressources spécifiques et leur gestion fine des applications déployées.

#### 2.2.1 Installation de l’opérateur {#cnpg1-2-2-1}

- Deux éléments:
    - L’opérateur (dans un ou plusieurs Pods)
    - Les Custom Resource Definitions (extension de l’API Kubernetes)
- Installation:
    - Manifest YAML
    - Helm Chart (version packagée)
    - OLM (Operator Lifecycle Manager)
- Un opérateur par cluster Kubernetes

#### 2.2.2 Principe de fonctionnement {#cnpg1-2-2-2}

**Figure 2-64-f01.** Figure 2\-64\-f01 \- Fonctionnement général de l’opérateur CloudNativePG et de ses ressources\.

![Fonctionnement général de l’opérateur CloudNativePG et de ses ressources\.](/images/cnpg1/manual/p064-f01.png)

Un opérateur Kubernetes est, de manière simplifiée, composé de deux éléments:

- Un contrôleur;
- Des Custom Resource Definitions.

Le premier élément n’est ni plus ni moins que le code de l’opérateur, son intelligence. Il embarque un ensemble de fonctions pour gérer convenablement PostgreSQL. L’opérateur CloudNativePG est écrit
en Go et se base sur le framework de développement de CLI Cobra. Nous ne rentrerons pas dans le détail du développement de l’opérateur, mais sachez que pour chaque Custom Resource définie, il existe dans le code un controller. Cet élément est en charge de la gestion de chacune des ressources qui peuvent être gérées par l’opérateur ( Backup, Cluster, etc.).

Les Custom Resource Definitions contiennent la définition des nouvelles ressources Kubernetes qu’apporte l’opérateur. À partir du moment où les définitions sont déclarées dans le cluster Kubernetes, elles pourront être créées par un utilisateur. Ce sera alors l’opérateur qui saura les gérer (création, modification...). L’installation de l’opérateur peut se faire de plusieurs manières différentes:

- soit en appliquant directement les fichiers YAML;
- soit en utilisant le Helm Chart fourni par le projet;
- soit via OLM (Operator Lifecycle Manager).

Les fichiers YAML se trouvent sur le githubusercontent.com du projet CloudNativePG. Pour la version 1.30.0 de l’opérateur, il est possible de les trouver ici: https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.30/releases/cnpg-1.30.0.yaml. Pour installer l’opérateur sur un cluster Kubernetes auquel vous avez accès, rien de plus simple. Il suffit d’appeler kubectl apply --server-side -f suivi de l’URL.

**Exemple 2-65-e01.** Exemple ou sortie de commande extraite du PDF\, page 65\.

```bash
kubectl apply --server-side \
-f https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/main/releases/cnpg-1.30.0.yaml
```

Différentes ressources Kubernetes seront créées ( Namespace, ServiceAccount, ConfigMap, Deployment, etc.). Toutes sont importantes évidemment, mais le Deployment nommé cnpg-controller-manager est le plus central puisqu’il correspond concrètement à l’intelligence de l’opérateur CloudNativePG.

**Exemple 2-65-e02.** Exemple ou sortie de commande extraite du PDF\, page 65\.

```text
namespace/cnpg-system serverside-applied
customresourcedefinition.apiextensions.k8s.io/backups.postgresql.cnpg.io serverside-applied
customresourcedefinition.apiextensions.k8s.io/clusterimagecatalogs.postgresql.cnpg.io serverside-applied
customresourcedefinition.apiextensions.k8s.io/clusters.postgresql.cnpg.io serverside-applied
customresourcedefinition.apiextensions.k8s.io/imagecatalogs.postgresql.cnpg.io serverside-applied
customresourcedefinition.apiextensions.k8s.io/poolers.postgresql.cnpg.io serverside-applied
customresourcedefinition.apiextensions.k8s.io/scheduledbackups.postgresql.cnpg.io serverside-applied
serviceaccount/cnpg-manager serverside-applied
clusterrole.rbac.authorization.k8s.io/cnpg-manager serverside-applied
clusterrolebinding.rbac.authorization.k8s.io/cnpg-manager-rolebinding serverside-applied
configmap/cnpg-default-monitoring serverside-applied
service/cnpg-webhook-service serverside-applied
deployment.apps/cnpg-controller-manager serverside-applied
```

Sources du PDF (page 65) :

- [https://cobra.dev/](<https://cobra.dev/>)

**Exemple 2-66-e01.** Exemple ou sortie de commande extraite du PDF\, page 66\.

```text
mutatingwebhookconfiguration.admissionregistration.k8s.io/cnpg-mutating-webhook-configuration serverside-applied
validatingwebhookconfiguration.admissionregistration.k8s.io/cnpg-validating-webhook-configuration serverside-applied
```

L’autre possibilité est d’utiliser le Helm Chart proposé par le projet CloudNativePG (ou vos propres Helm Charts si vous êtes suffisamment à l’aise). Il est également disponible publiquement sur GitHub (voir https://github.com/cloudnative-pg/charts/tree/main/charts/cloudnative-pg). Il vous faudra avant tout ajouter le dépôt cnpg pour retrouver les Charts:

**Exemple 2-66-e02.** Exemple ou sortie de commande extraite du PDF\, page 66\.

```bash
helm repo add cnpg https://cloudnative-pg.github.io/charts
```

Puis installer avec la commande helm upgrade --install:

**Exemple 2-66-e03.** Exemple ou sortie de commande extraite du PDF\, page 66\.

```bash
helm upgrade --install cnpg \
  --namespace cnpg-system \
  --create-namespace \
  cnpg/cloudnative-pg
```

La dernière option, via OLM, nécessite d’installer le manager OLM dans votre cluster Kubernetes, puis CloudNativePG via ce manager. Le choix de la méthode d’installation vous revient entièrement. Elle doit être adaptée à votre méthode de déploiement (à la main? avec une chaîne CI/CD?). Quelle que soit la manière dont est installé l’opérateur, vous ne pourrez installer qu’un seul opérateur CloudNativePG par cluster Kubernetes.

#### 2.2.3 Travaux pratiques {#cnpg1-2-2-3}

Se trouvent dans le handout HTML

- Prise en main du cluster Kubernetes
- Installation de l’opérateur CloudNativePG

#### 2.2.4 Nouvelles ressources {#cnpg1-2-2-4}

- Un opérateur installé

**Exemple 2-66-e04.** Exemple ou sortie de commande extraite du PDF\, page 66\.

```bash
kubectl get pod -n cnpg-system
NAME                                                 READY     STATUS
cnpg-controller-manager-65bfdb64c9-nztfj             1/1       Running
```

- Quels types de Custom Resources peuvent être créés?

L’opérateur est installé dans le cluster Kubernetes. Regardons maintenant quelques ressources qu’il nous est possible de créer.

#### 2.2.5 Cluster {#cnpg1-2-2-5}

- Définition minimale d’un Cluster PostgreSQL

**Exemple 2-67-e01.** Exemple ou sortie de commande extraite du PDF\, page 67\.

```yaml
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: postgresql
spec:
  instances: 1
  storage:
    size: 2Gi
  walStorage: # Bonne pratique
    size: 2Gi
```

- Il ne manquerait pas quelque chose?

Quelques lignes de YAML suffisent pour décrire un cluster PostgreSQL. Voici une explication des différents champs présents:

- apiVersion: la version de l’API de Kubernetes utilisée (ici celle apportée par l’opérateur CloudNativePG);
- kind: le type d’objet créé; ici un cluster PostgreSQL (donc une ou plusieurs instances);
- metadata: des informations pour identifier l’objet, notamment son nom ( name);
- spec: les spécifications de l’objet en question;
    - instances: le nombre d’instances voulues (1 primaire, plus d’éventuels secondaires);
 - storage: les informations sur le stockage souhaité pour PGDATA, par exemple la taille ( size) des Persistent Volumes;
    - walStorage: les informations sur le stockage souhaité pour les journaux de transactions.

Vous noterez qu’il n’est pas obligatoire d’indiquer quelle image sera utilisée. Par défaut, l’image utilisée pour ce(s) Pod sera celle correspondant à la dernière version majeure de PostgreSQL. Une bonne pratique est de toujours indiquer l’image à utiliser ainsi que son tag. Une autre bonne pratique dans PostgreSQL, souvent recommandée mais peu appliquée, est d’avoir au moins deux espaces de stockage bien distincts:

- un pour les données;
- un pour les journaux de transactions (WAL).

CloudNativePG respecte cette bonne pratique avec le paramètre spec.walStorage. Lorsque le cluster est créé, deux Persistent Volumes distincts sont créés automatiquement, un pour PGDATA et un pour les journaux de transactions (WAL). Si ce paramètre n’est pas utilisé, les journaux se trouveront dans le même volume que PGDATA, ce que nous ne recommandons pas. De nombreux paramètres supplémentaires existent pour configurer nos instances PostgreSQL. Des subtilités existent aussi dans le déploiement du Pod, notamment sur le fait qu’un init-container

est déployé avant le conteneur PostgreSQL. Tout ceci sera détaillé plus tard dans ce module. En cas de panne de l’opérateur CloudNativePG, les objets Cluster, et autres ressources, restent disponibles et fonctionnelles dans Kubernetes: les instances ne sont pas arrêtées! Cependant, en cas de modifications de configuration ou d’incident, l’opérateur n’étant plus présent, aucune action automatique ne se fera.

#### 2.2.6 Éléments initiaux {#cnpg1-2-2-6}

- Lors de la création du Cluster
- D’autres ressources sont créées
    - Kubernetes
    - PostgreSQL
    - Certaines sont liées ( Secret et ROLE)

Lorsque vous demandez à CloudNativePG de créer un Cluster, c’est-à-dire une ou plusieurs instance(s) fonctionnant ensemble, plusieurs ressources sont automatiquement créées. Certaines sont propres à Kubernetes, d’autres sont propres à PostgreSQL avec, par exemple, la création de rôles ou d’une base de données. L’opérateur ayant connaissance de ces deux mondes, il va pouvoir « lier » ces ressources entre elles. Nous le verrons avec la gestion du mot de passe du ROLE par défaut qui est stocké dans une ressource Secret.

- PostgreSQL
    - Base de données: app
    - Rôles: app, streaming_replica
    - Règles: pg_hba, pg_ident
- Kubernetes
    - Secrets: postgresql-app (contient le mot de passe du rôle app)
        - kubectl describe secrets postgresql-app
    - Services: postgresql-r, postgresql-ro, postgresql-rw
    - Pod(s): où est déployé PostgreSQL

L’instance déployée est partiellement configurée pour fonctionner dès sa création. Plusieurs paramètres PostgreSQL sont déjà configurés, le fichier pg_hba.conf est en partie renseigné, des certificats sont générés, etc. Deux nouveaux rôles existent: app et streaming_replica. Le premier est un rôle basique avec le droit de connexion. Le deuxième est un rôle utilisé par les instances secondaires lors de la mise en

place de la réplication physique. Le rôle postgres existe lui aussi mais n’est pas créé par CloudNativePG.

Une base de données nommée app est d’office créée avec la configuration suivante:

- Name: app
- Owner: app
- Encoding: UTF8
- Locale Provider: libc
- Collate: C
- Ctype: C

La présence de cette base de données permet à vos développeurs de pouvoir directement utiliser l’instance sans devoir créer eux-mêmes ladite base.

Des discussions sont en cours (été 2026) au sein du projet CloudNativePG pour laisser le choix de la création ou non de cette base.

Du côté de Kubernetes aussi, des ressources sont automatiquement créées. Certaines nous concernent directement en tant qu’administrateur PostgreSQL. Il y a notamment un Secret qui est créé et qui contient le mot de passe du rôle PostgreSQL app. D’autres objets comme des Services sont utilisés pour la connectivité de l’instance, ou des instances si des réplications existent.

Pour chaque cluster PostgreSQL déployé, trois services dédiés sont créés. Par exemple, pour le Cluster nommé postgresql:

- un service qui permet d’accéder au primaire: postgresql-rw qui est en lecture/écriture;
- un service qui permet d’accéder uniquement aux secondaires: postgresql-ro qui sont en lecture seule;
- un service qui permet d’accéder à toutes les instances: postgresql-r.

Nous verrons lorsque le sujet de la haute disponibilité sera abordé à quoi ces Services peuvent servir.

#### 2.2.7 Modification des éléments initiaux {#cnpg1-2-2-7}

- Modification possible de certains éléments
    - Uniquement lors du premier démarrage
- Partie spec.bootstrap.initdb du fichier YAML du Cluster
- La commande initdb est utilisée

**Exemple 2-70-e01.** Exemple ou sortie de commande extraite du PDF\, page 70\.

```yaml
spec: # Cluster
```

[...]

**Exemple 2-70-e02.** Exemple ou sortie de commande extraite du PDF\, page 70\.

```yaml
bootstrap:
  initdb:
    database: mabase
    owner: monrole
instances: 1
storage:
  size: 2Gi
walStorage: # Bonne pratique
  size: 2Gi
```

Les éléments déployés par défaut par CloudNativePG peuvent être modifiés s’ils ne vous conviennent pas. Cependant, vous devez le faire à l’initialisation de l’instance. Cela ne sera plus possible après coup.

La section bootstrap correspond à la manière dont est initiée l’instance. Par défaut c’est la méthode initdb qui est utilisée, mais nous verrons que d’autres méthodes peuvent être utilisées, notamment pour créer une instance à partir d’une sauvegarde.

Les changements que vous voulez apporter doivent être inscrits dans la section bootstrap.initdb. Dans l’exemple suivant, le nom de la base automatiquement créée et le nom du rôle sont modifiés par mabase et monrole.

**Exemple 2-70-e03.** Exemple ou sortie de commande extraite du PDF\, page 70\.

```yaml
bootstrap:
  initdb:
    database: mabase
    owner: monrole
```

De nombreuses autres options peuvent être passées à initdb, comme les plus notables:

- dataChecksums: pour activer les sommes de contrôle sur l’instance ( false par défaut);
- encoding: pour choisir l’encodage des caractères ( UTF8 par défaut);
- walSegmentSize: si vous voulez changer la taille par défaut (16 Mo) des journaux de transactions.

#### 2.2.8 Database {#cnpg1-2-2-8}

- Définition minimale d’une Database

**Exemple 2-71-e01.** Exemple ou sortie de commande extraite du PDF\, page 71\.

```yaml
apiVersion: postgresql.cnpg.io/v1
kind: Database
metadata:
  name: mabase
spec:
  name: mabase
  owner: monrole
  ensure: present
  isTemplate: false
  cluster:
    name: postgresql
```

Vous pouvez créer des ressources Database depuis la version 1.25.0 de l’opérateur. L’exemple ci-dessus permet de créer la base mabase dans l’instance PostgreSQL référencée par le paramètre spec.cluster.name. Le rôle monrole sera le propriétaire de cette base. Il doit exister dans l’instance pour que la création de cette ressource se fasse correctement. Autrement, un message d’erreur apparaîtra dans la description de la ressource. Par exemple:

**Exemple 2-71-e02.** Exemple ou sortie de commande extraite du PDF\, page 71\.

```bash
kubectl get databases.postgresql.cnpg.io -o=custom-columns=MESSAGE:..message
```

**Exemple 2-71-e03.** Exemple ou sortie de commande extraite du PDF\, page 71\.

```text
MESSAGE
while creating database "mabase": ERROR: role "monrole" does not exist (SQLSTATE 42704)
```

Il existe là aussi de nombreux paramètres pour configurer une ressource Database. En ce qui concerne l’exemple ci-dessus:

- apiVersion: la version de l’API de Kubernetes utilisée (ici celle apportée par l’opérateur CloudNativePG);
- kind: le type d’objet créé, ici une base de données;
- metadata: des informations pour identifier l’objet, notamment son nom ( name);
- spec: les spécifications de l’objet en question;
    - name: le nom de la base de données;
 - owner: le nom du rôle PostgreSQL qui sera le propriétaire de la base de données. Il doit exister dans l’instance;
    - cluster: le nom du cluster PostgreSQL dans lequel doit être créée cette base de données;
 - ensure: indique si la base doit être présente ( present) ou absente ( absent) de l’instance. Attention, si une base de données existe et qu’une CRD Database est appliquée avec

**Exemple 2-71-e04.** Exemple ou sortie de commande extraite du PDF\, page 71\.

```text
ensure: absent , elle sera supprimée par l’opérateur.
```

Vous pouvez trouver de manière détaillée les autres paramètres sur cette page. Voici d’autres paramètres de la section spec qui nous semblent intéressants:

Sources du PDF (page 71) :

- [https://cloudnative-pg.io/docs/1.30/cloudnative-pg.v1#database](<https://cloudnative-pg.io/docs/1.30/cloudnative-pg.v1#database>)

**Exemple 2-72-e01.** Exemple ou sortie de commande extraite du PDF\, page 72\.

```text
     -   encoding : permet d’indiquer l’encodage de la base (UTF8, LATIN1...);
     -   template : nom du modèle de base à utiliser pour la base créée;
     -   isTemplate : permet d’indiquer si la base créée est un modèle ou pas;
     -   allowConnections : permet d’indiquer s’il sera possible de se connecter à cette base;
     -   connectionLimit : nombre de connexions simultanées autorisées à cette base;
     -   tablespace : TABLESPACE où sera créée la base de données.
```

Ces paramètres ne sont ni plus ni moins que les options de la commande CREATE DATABASE de PostgreSQL. De nouvelles possibilités sont offertes avec, notamment, la version 1.28 de l’opérateur. La CRD Database a été modifiée pour prendre en compte la création de Foreign Data Wrappers de manière déclarative avec les sections spec.fdws et spec.servers. Si la création de la ressource ne peut se faire, par exemple si le rôle n’existe pas dans l’instance, une erreur sera retournée. Elle pourra être visible dans la description de la ressource Database. Si le rôle est créé après la création de la Database, la boucle de réconciliation fera en sorte de créer la base de données dès que possible. La suppression d’un objet Database ne supprime pas la base de données dans l’instance ciblée. Le renommage d’une base de données n’est pas possible.

#### 2.2.9 Schema {#cnpg1-2-2-9}

- Pas de Custom Resource Definition
- Déclaré dans la ressource Database
- spec.schemas

**Exemple 2-72-e02.** Exemple ou sortie de commande extraite du PDF\, page 72\.

```yaml
apiVersion: postgresql.cnpg.io/v1
kind: Database
```

[...]

**Exemple 2-72-e03.** Exemple ou sortie de commande extraite du PDF\, page 72\.

```yaml
spec:
  schemas:
  - name: monschema
    owner: moi
    ensure: present
```

La création d’un schéma dans une base de données se fait avec l’instruction CREATE SCHEMA, mais vous pouvez aussi le faire en l’indiquant dans le fichier YAML de l’objet Database.

**Exemple 2-72-e04.** Exemple ou sortie de commande extraite du PDF\, page 72\.

```yaml
apiVersion: postgresql.cnpg.io/v1
kind: Database
```

[...]

**Exemple 2-73-e01.** Exemple ou sortie de commande extraite du PDF\, page 73\.

```yaml
spec:
  schemas:
  - name: monschema
    owner: moi
    ensure: present
```

ensure peut prendre la valeur present ou absent. Dans le deuxième cas de figure, CloudNativePG va faire en sorte que le schéma ne soit pas présent et le supprimera s’il existe! Attention au nom que vous renseignez.

#### 2.2.10 DatabaseRole {#cnpg1-2-2-10}

- Nouvelle CRD (v1.30)
- Attention au nom
    - Bien associé à un Cluster PostgreSQL
- Nécessite un Secret pour le mot de passe
- Ne pas oublier de définir la databaseRoleReclaimPolicy

**Exemple 2-73-e02.** Exemple ou sortie de commande extraite du PDF\, page 73\.

```yaml
apiVersion: postgresql.cnpg.io/v1
kind: DatabaseRole
metadata:
  name: dalibo
spec:
  cluster:
    name: postgresql
  name: dalibo
  comment: "Utilisateur Support"
  login: true
  superuser: true
  createdb: true
  databaseRoleReclaimPolicy: delete
  passwordSecret:
    name: postgresql-dalibo # doit exister
```

Les rôles PostgreSQL peuvent être définis de deux manières différentes. La première, directement dans l’objet Cluster, dans une section spec.managed.roles. C’était la seule méthode possible avant la version 1.30 de l’opérateur. Depuis, la nouvelle CRD DatabaseRole est disponible et permet de gérer les rôles directement comme un objet Kubernetes. Seule cette deuxième méthode va être détaillée dans ce support comme elle devient la méthode privilégiée.

- name: est évidemment le nom du rôle à créer.
    - comment: simple champ commentaire ajouté au rôle si renseigné;

**Exemple 2-74-e01.** Exemple ou sortie de commande extraite du PDF\, page 74\.

```text
-   login : indique si le rôle peut se connecter ( true , valeur par défaut), false sinon ;
-   superuser : indique si le rôle est un super-utilisateur, false (valeur par défaut);
-   createdb : indique si le rôle peut créer des bases de données au sein de l’instance;
-   passwordSecret.name      : indique le nom du Secret Kubernetes où se trouve le mot de
```

passe du rôle. Si ce champ n’est pas renseigné, le rôle n’aura pas de mot de passe attribué. Vous pouvez trouver de manière détaillée les autres paramètres sur cette page. Voici d’autres paramètres de la section spec qui nous semblent intéressants:

- validUntil: la date à partir de laquelle le rôle ne sera plus valide:
    - Exemple validUntil: "2027-06-17T15:00:00Z";
- inRoles: la liste des rôles auxquels le rôle créé doit appartenir;
- replication: indique si le rôle doit avoir le rôle REPLICATION, par défaut à false.

Si vous souhaitez attribuer un mot de passe au rôle, il vous faudra créer en amont un Secret qui sera référencé dans la ressource DatabaseRole. Le champ username doit correspondre au nom du rôle du DatabaseRole. Par exemple:

**Exemple 2-74-e02.** Exemple ou sortie de commande extraite du PDF\, page 74\.

```yaml
apiVersion: v1
data:
  username: ZGFsaWJvCg==
  password: aEV2WVJ4VFIySDVzcjVsQg==
kind: Secret
metadata:
  name: postgresql-dalibo
  labels:
    cnpg.io/reload: "true"
type: kubernetes.io/basic-auth
```

Les champs username et password doivent contenir les valeurs encodées en BASE64. Cela peut se faire en ligne de commande avec des outils comme printf et base64. Déléguer la création des rôles à l’opérateur est une bonne chose et apporte plus de flexibilité. Les créer est une chose, les supprimer en est une autre. Il est important de définir la notion de ReclaimPolicy de la ressource via le champ databaseRoleReclaimPolicy. Elle indique ce qui doit être fait si la ressource Cluster à laquelle est lié le rôle venait à être supprimée. Par défaut à retain, la ressource restera dans le cluster Kubernetes en attente, soit d’une suppression manuelle, soit de la remise en service du Cluster. Exemple:

**Exemple 2-74-e03.** Exemple ou sortie de commande extraite du PDF\, page 74\.

```bash
kubectl get databaseroles.postgresql.cnpg.io
NAME     AGE   CLUSTER      PG NAME   APPLIED                       MESSAGE
dalibo   35m   postgresql   dalibo    false                         cluster resource has been deleted, skipping reconciliation
```

La réconciliation du rôle est arrêtée puisque le Cluster n’existe plus. À la recréation du Cluster, le rôle sera automatiquement réconcilié et donc créé dans l’instance.

Sources du PDF (page 74) :

- [https://cloudnative-pg.io/docs/1.30/cloudnative-pg.v1/#databaserolespec](<https://cloudnative-pg.io/docs/1.30/cloudnative-pg.v1/#databaserolespec>)

**Exemple 2-75-e01.** Exemple ou sortie de commande extraite du PDF\, page 75\.

```bash
kubectl get databaseroles.postgresql.cnpg.io
NAME     AGE   CLUSTER      PG NAME   APPLIED             MESSAGE
dalibo   46m   postgresql   dalibo    true
```

Enfin, il existe déjà une ressource native de Kubernetes nommée ClusterRole qui correspond à un utilisateur au sein du cluster. C’est pourquoi les développeurs de CloudNativePG ont utilisé DatabaseRole comme nom de CRD. Cela étant dit, la ressource créée avec un DatabaseRole est bien un ROLE au sein d’une instance PostgreSQL, donc d’un Cluster. Dans PostgreSQL, les rôles sont associés à une instance et non à une base de données spécifique.

#### 2.2.11 Extensions au sein d’une Database {#cnpg1-2-2-11}

- Indiquées dans le YAML
- Présentes dans l’image utilisée
- spec.extensions de l’objet Database

**Exemple 2-75-e02.** Exemple ou sortie de commande extraite du PDF\, page 75\.

```yaml
apiVersion: postgresql.cnpg.io/v1
kind: Database
```

[...]

**Exemple 2-75-e03.** Exemple ou sortie de commande extraite du PDF\, page 75\.

```yaml
spec:
  extensions:
  - name: vector
    ensure: present
```

Suivant le même principe que pour les schémas, l’ajout d’une extension peut se faire avec l’instruction

**Exemple 2-75-e04.** Exemple ou sortie de commande extraite du PDF\, page 75\.

```sql
CREATE EXTENSION . Vous pouvez le faire à la main lorsque vous êtes connectés à la base, ou alors le
```

demander à CloudNativePG lors de la création de l’objet Database. Pour cela, indiquer les extensions que vous souhaitez en les renseignant dans la partie spec.extensions. Par exemple:

**Exemple 2-75-e05.** Exemple ou sortie de commande extraite du PDF\, page 75\.

```yaml
apiVersion: postgresql.cnpg.io/v1
kind: Database
```

[...]

**Exemple 2-75-e06.** Exemple ou sortie de commande extraite du PDF\, page 75\.

```yaml
spec:
  extensions:
  - name: vector
    ensure: present
```

D’autres paramètres peuvent être utilisés pour indiquer la version de l’extension à installer ou le schéma dans lequel elle doit l’être.

Les fichiers de l’extension doivent évidemment être présents dans l’image du conteneur. Certaines sont embarquées dans l’image fournie par CloudNativePG, d’autres non. Voyons comment intégrer de nouvelles extensions avec le mécanisme d’ajout dynamique (v1.27).

#### 2.2.12 Ajout dynamique d’extensions {#cnpg1-2-2-12}

- Version 1.27+ de l’opérateur
- Version 18+ de PostgreSQL
    - Nouveau paramètre GUC extension_control_path
- Images externes à gérer
- Peuvent être ajoutées après le déploiement

**Exemple 2-76-e01.** Exemple ou sortie de commande extraite du PDF\, page 76\.

```yaml
spec:
  postgresql:
    extensions:
      - name: ext
        image:
          reference: maregistry/monimage:tag # image dédiée
```

Jusqu’à présent, une extension devait nécessairement se trouver dans l’image utilisée pour déployer PostgreSQL. CloudNativePG en embarquait par défaut et il nous était possible d’en ajouter en créant des images personnalisées.

À partir de la version 1.27 de l’opérateur et de la version 18 de PostgreSQL, il est possible de tirer parti du chargement dynamique d’une extension. Ce mécanisme repose sur le concept d’ ImageVolume de Kubernetes disponible en version 1.33.

Le principe est de décorréler l’image utilisée pour déployer PostgreSQL et celles utilisées pour ajouter une ou des extensions.

Les images utilisées pour ajouter une extension doivent respecter certaines contraintes pour pouvoir être utilisées par CloudNativePG (voir les indications disponibles sur cette page).

Une attention particulière est à apporter sur les versions des extensions utilisées ainsi que sur les distributions utilisées pour ces images: elles doivent être compatibles au niveau système et architecture CPU.

Sources du PDF (page 76) :

- [https://cloudnative-pg.io/docs/1.30/imagevolume_extensions/#image-specifications](<https://cloudnative-pg.io/docs/1.30/imagevolume_extensions/#image-specifications>)

#### 2.2.13 Tablespace {#cnpg1-2-2-13}

- Pas de Custom Resource Definition
- Déclaré dans la ressource Cluster
- spec.tablespaces

**Exemple 2-77-e01.** Exemple ou sortie de commande extraite du PDF\, page 77\.

```yaml
                  spec: # Cluster
```

[...]

**Exemple 2-77-e02.** Exemple ou sortie de commande extraite du PDF\, page 77\.

```yaml
tablespaces:
  - name: data
    storage:
      size: 1Gi
    owner:
      name: dalibo
  - name: fast
    storage:
      size: 2Gi
      storageClass: fast
    owner:
      name: dalibo
```

À l’image des rôles, les tablespaces sont également déclarés dans la ressource Cluster. Il est possible de renseigner une liste de tablespaces et de configurer chacun d’eux de manière spécifique.

- tablespaces: est la liste des tablespaces qui doivent être créés;
 - name: est évidemment le nom du tablespace. N’oubliez pas le - en début de ligne qui indique qu’il s’agit d’un nouvel élément de la liste;
    - storage: configuration du stockage du tablespace;
    - size: la taille du volume où sera créé le tablespace;
    - storageClass: indique quelle classe de stockage doit être utilisée pour ce volume-là;
    - owner: indique le nom du propriétaire de ce tablespace;
    - temporary: permet d’indiquer si le tablespace doit être créé avec la clause TEMPORARY.

Si le propriétaire du tablespace n’est pas renseigné, l’utilisateur app le sera par défaut. Par défaut aussi, le tablespace est créé avec le paramètre temporary à false. L’option storageClass est particulièrement intéressante pour configurer la classe de stockage sousjacente au tablespace et donc, in fine, au volume. Cela vous permet d’utiliser des classes de stockage plus ou moins rapides selon vos besoins. Lorsque le Cluster est créé, ou modifié, l’opérateur se charge de créer les objets Persistent Volumes / Persistent Volume Claims nécessaires et les associe au Pod de l’instance. Par exemple, lors d’un premier déploiement sans tablespace supplémentaire nous sommes dans la situation suivante avec un seul PVC et un seul PV.

**Exemple 2-77-e03.** Exemple ou sortie de commande extraite du PDF\, page 77\.

```bash
kubectl get pvc
```

**Exemple 2-78-e01.** Exemple ou sortie de commande extraite du PDF\, page 78\.

```text
NAME              STATUS   VOLUME                            CAPACITY ACCESS MODES     STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
postgresql-1       Bound    pvc-b0eb7368-0795-48ea-89be-88475dc2a486 2Gi     RWO            standard   <unset>            3m8s
```

**Exemple 2-78-e02.** Exemple ou sortie de commande extraite du PDF\, page 78\.

```bash
kubectl get pv
NAME                          CAPACITY ACCESS MODES   RECLAIM POLICY    STATUS CLAIM                   STORAGECLASS VOLUMEATTRIBUTESCLASS   REASON   A
pvc-67dda23b-5b3c-4e15-950d-681db723d62f   1Gi          RWO                 Delete          Bound       default/postgresql-1-tbs-data   standard       <unset>                          3m10s
```

Après l’ajout des deux tablespaces, la situation est la suivante, avec trois PVC et trois PV. Le premier couple PV / PVC correspond au tablespace par défaut.

**Exemple 2-78-e03.** Exemple ou sortie de commande extraite du PDF\, page 78\.

```bash
kubectl get pvc
NAME               STATUS VOLUME                              CAPACITY ACCESS MODES    STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
postgresql-1        Bound   pvc-b0eb7368-0795-48ea-89be-88475dc2a486 2Gi      RWO          standard    <unset>           6m41s
postgresql-1-tbs-data Bound    pvc-67dda23b-5b3c-4e15-950d-681db723d62f 1Gi     RWO          standard    <unset>           4m4s
postgresql-1-tbs-fast Bound    pvc-934a3ab9-123e-481c-af44-d3b741961859 2Gi     RWO          standard    <unset>           4m4s
```

**Exemple 2-78-e04.** Exemple ou sortie de commande extraite du PDF\, page 78\.

```bash
kubectl get pv
NAME                          CAPACITY ACCESS MODES   RECLAIM POLICY    STATUS CLAIM                   STORAGECLASS VOLUMEATTRIBUTESCLASS   REASON   A
pvc-67dda23b-5b3c-4e15-950d-681db723d62f   1Gi          RWO                 Delete          Bound       default/postgresql-1-tbs-data   standard       <unset>                          3m59s
pvc-934a3ab9-123e-481c-af44-d3b741961859   2Gi          RWO                 Delete          Bound       default/postgresql-1-tbs-fast   standard       <unset>                          3m59s
pvc-b0eb7368-0795-48ea-89be-88475dc2a486   2Gi          RWO                 Delete          Bound       default/postgresql-1   standard       <unset>                           6m46s
```

Lors de l’ajout d’un ou de plusieurs tablespaces dans un Cluster en fonctionnement, le Pod PostgreSQL est redémarré, générant ainsi une interruption de service.

### 2.3 CONFIGURATION DE L’INSTANCE {#cnpg1-2-3}

- Déclaratif, tout se fait en YAML
- Accès direct aux fichiers interdit!
    - postgresql.conf
    - pg_hba.conf
    - pg_ident.conf

Comme vous le savez, installer une instance PostgreSQL ne suffit pas. Il faut en plus la configurer. Généralement, la configuration se fait dans le fichier postgresql.conf et nécessite soit un rechargement, soit un redémarrage de l’instance selon le paramètre modifié.

Avec l’utilisation de CloudNativePG, il n’est plus possible de modifier directement le fichier de configuration. Tout se fait dans la définition YAML de l’instance. Voyons quels sont les changements auxquels s’attendre.

#### 2.3.1 postgresql.conf {#cnpg1-2-3-1}

- Tous les paramètres ne sont pas modifiables
- ALTER SYSTEM désactivé
    - allow_alter_system à false (v17+)
- Des vérifications sont mises en place
- Redémarrage ou rechargement automatique
    - Un garde-fou existe ( primaryUpdateStrategy)

Voici par exemple comment passer le paramètre shared_buffers à 1GB, et activer la compression des journaux de transactions.

[...]

**Exemple 2-79-e01.** Exemple ou sortie de commande extraite du PDF\, page 79\.

```yaml
spec:
  postgresql:
    parameters:
      shared_buffers: "1GB"
      wal_compression: "on"
```

[...]

La configuration d’une instance se fait dans la section.spec.postgresql.parameters de la définition YAML du Cluster. Les noms des paramètres sont les mêmes que ceux de PostgreSQL. CloudNativePG ne les renomme pas d’une manière ou d’une autre.

Un bon nombre de paramètres PostgreSQL ne sont pas modifiables dans la section.spec.postgresql.parameters de l’instance. La liste est disponible dans la documentation. Cette interdiction peut paraitre surprenante, d’autant plus qu’avec un logiciel open source et libre comme PostgreSQL, nous nous attendons plutôt à être libres de nos mouvements. Le parti pris par les développeurs est que tous ces paramètres sont liés à des fonctionnalités dont l’opérateur a la charge (sauvegarde, réplication, archivage des journaux, gestion des traces, etc.). Certains d’entre eux, comme allow_alter_system (disponible à partir de la version 17 de PostgreSQL), sont modifiables par l’utilisation d’éléments présents dans d’autres sections de configuration, comme enableAlterSystem, qui se trouve dans la section.spec.postgresql.

Des vérifications sont faites sur certains. Pour reprendre l’exemple de shared_buffers, si vous renseignez une valeur plus grande que resources.requests.memory (allouée au Pod), vous lèverez une alerte, et votre modification ne sera pas appliquée. Exemple d’un message d’erreur remonté:

**Exemple 2-80-e01.** Exemple ou sortie de commande extraite du PDF\, page 80\.

```text
The Cluster "postgresql" is invalid: spec.resources.requests.memory:
Invalid value: "512Mi": Memory request is lower than PostgreSQL `shared_buffers` value
```

Vous n’êtes pas sans savoir que la modification de certains paramètres nécessite soit un rechargement de la configuration, soit un redémarrage de l’instance. Par défaut l’opération de rechargement ou de redémarrage est déclenchée automatiquement lorsque le nouveau paramètre est appliqué. Concrètement, si vous modifiez max_connections, vos instances seront automatiquement redémarrées. Ce dernier point sera détaillé lorsque l’on traitera de la stratégie de mise à jour que vous voulez mettre en place avec le paramètre primaryUpdateStrategy.

#### 2.3.2 pg_hba.conf et pg_ident.conf {#cnpg1-2-3-2}

- Préconfigurés
    - FIXED RULES, DEFAULT RULES
- Dans la définition du Cluster
    - USER-DEFINED RULES
- Reconfiguration dynamique du pg_hba.conf

[...]

**Exemple 2-80-e02.** Exemple ou sortie de commande extraite du PDF\, page 80\.

```yaml
postgresql:
  pg_hba:
    - host app 10.244.0.0/16 scram-sha-256
```

[...]

Sources du PDF (page 80) :

- [https://cloudnative-pg.io/docs/1.30/postgresql_conf#fixed-parameters](<https://cloudnative-pg.io/docs/1.30/postgresql_conf#fixed-parameters>)

La configuration de ces fichiers est évidemment possible. Il faut utiliser la syntaxe comme dans l’exemple pour ajouter une ligne de configuration. Il existe trois sections dans le fichier

**Exemple 2-81-e01.** Exemple ou sortie de commande extraite du PDF\, page 81\.

```text
 pg_hba.conf :
```

- FIXED RULES: qui sont des règles fixées par l’opérateur. On retrouve une règle concernant la réplication par exemple;
- USER-DEFINED RULES: qui correspond aux règles qui seront créées par les administrateurs;
- DEFAULT RULES: qui autorise par défaut toutes les connexions par mot de passe à toutes les bases de données.

Il existe deux sections dans le fichier pg_ident.conf:

- FIXED RULES: qui sont des règles fixées par l’opérateur. On retrouve une ligne concernant l’association de l’utilisateur système postgres au rôle postgres;
- USER-DEFINED RULES: qui correspond aux règles qui seront créées par les administrateurs.

L’opérateur s’intègre très bien dans l’environnement Kubernetes et s’adapte à une gestion plus dynamique des instances et des accès à celles-ci. Il est désormais possible (v1.29+) d’utiliser des références aux Pods ayant un label spécifique directement dans la configuration du pg_hba.

**Exemple 2-81-e02.** Exemple ou sortie de commande extraite du PDF\, page 81\.

```yaml
postgresql:
  pg_hba:
    - host postgres admin ${podselector:app-pgadmin} scram-sha-256
```

Le champ ${podselector:app-pgadmin} fait référence à une configuration du Cluster dans laquelle on indique quels Pods il est nécessaire de suivre.

**Exemple 2-81-e03.** Exemple ou sortie de commande extraite du PDF\, page 81\.

```yaml
podSelectorRefs:
- name: app-pgadmin
  selector:
    matchLabels:
      app: pgadmin # cherche les pods avec le label app=pgadmin
```

Désormais, dès lors qu’un Pod avec le label app=pgadmin est créé dans le cluster Kubernetes, CloudNativePG ajustera le fichier pg_hba.conf et rechargera l’instance PostgreSQL pour que la nouvelle adresse IP soit autorisée à se connecter. Voir notre article de blog sur ce sujet https://blog.dalibo.com/2026/05/28/cnpg-12.html.

#### 2.3.3 Travaux pratiques {#cnpg1-2-3-3}

- Déploiement d’instances PostgreSQL
- Éléments initiaux

### 2.4 ADMINISTRATION DE L’INSTANCE {#cnpg1-2-4}

- Plugin kubectl
- Connexion
- Traces
- Réplication
- Archivage
- Sauvegarde
- Restauration
- Montée de version de PostgreSQL
- Montée de version de CloudNativePG
- Hibernation et fencing

#### 2.4.1 Plugin kubectl {#cnpg1-2-4-1}

- kubectl cnpg --help
- Ligne de commande écrite en Go
- Interaction avec l’opérateur, un Cluster, une instance spécifique
- Commandes:
    - status
    - psql
    - promote
    - backup
    - logs
    - reload
    - restart
    -...

Un plugin CloudNativePG ( cnpg) existe pour l’utilitaire kubectl. Il permet d’obtenir, très simplement, un ensemble d’informations sur un cluster PostgreSQL ou de se connecter à une instance, déclencher une sauvegarde, promouvoir un secondaire en primaire, etc. Il est écrit en Go et se base, comme le code de l’opérateur, sur le framework Cobra. Il existe plusieurs méthodes d’installation: par script, par paquets.rpm ou.deb ou encore via krew ou homebrew. À vous de choisir ce qui convient le mieux à vos utilisateurs et administrateurs, qu’ils soient sur Linux, macOS ou Windows.

Sources du PDF (page 82) :

- [https://github.com/spf13/cobra](<https://github.com/spf13/cobra>)

La liste des fonctionnalités offertes est assez longue. Elles couvrent des thèmes très variés allant de la simple connexion psql à une instance, à la création de publication pour des réplications logiques ou encore le déclenchement de tests de charge avec fio ou pgbench. Voici quelques-unes des commandes qui paraissent essentielles à connaitre dans un premier temps:

- kubectl cnpg status [cluster]: génère un résumé sur l’état du cluster PostgreSQL (nombre d’instances, sauvegardes, secondaires, réplications...);
- kubectl cnpg psql [cluster]: permet de se connecter avec psql à l’instance primaire;
- kubectl cnpg logs [cluster]: affiche les traces PostgreSQL. La commande pretty permet d’afficher les traces de manière plus lisible. L’outil jq peut également s’avérer utile;
- kubectl cnpg reload [cluster]: déclenche une boucle de réconciliation pour prendre en compte les modifications apportées au cluster PostgreSQL;
- kubectl cnpg restart [cluster] [node]: redémarre soit le cluster en entier, soit une seule instance si [node] est renseigné;
- kubectl cnpg promote [cluster] [node]: fait de l’instance indiquée la nouvelle primaire;
- kubectl cnpg backup [cluster]: déclenche une sauvegarde physique de l’instance mentionnée. La configuration de la sauvegarde doit être faite dans la définition du cluster.

#### 2.4.2 Connexion {#cnpg1-2-4-2}

- Plus d’accès au serveur
    - Comme avec du PGaaS
- Question d’accessibilité
    - en interne
    - depuis l’extérieur
- Masquage des adresses IP dans les traces PostgreSQL
    -%h du paramètre log_line_prefix

En embarquant PostgreSQL dans un conteneur et dans Kubernetes, il ne vous sera plus possible d’accéder au serveur sur lequel est installé PostgreSQL. Cela vous demandera davantage de configuration et de connaissances (notamment sur les différentes couches qui existent dans Kubernetes). Vous n’aurez plus accès au serveur sous-jacent, comme ce serait le cas avec une solution de PGaaS. Si vous gérez vous-même votre cluster Kubernetes, il vous sera évidemment possible d’accéder aux nœuds Workers. Il faut différencier deux types d’accès à une instance PostgreSQL:

1. les accès initiés depuis un client déployé dans le cluster Kubernetes (interne);
2. et ceux initiés depuis un client en dehors du cluster Kubernetes (externe).

Dans le premier cas, l’application pourra accéder à l’instance PostgreSQL si les Network Policies l’autorisent. Dans le second cas, vos administrateurs Kubernetes devront mettre en place un Load Balancer en frontal du cluster pour autoriser les accès externes. Dès lors que votre instance est accessible

depuis l’extérieur (interface et port exposés), vous pourrez vous y connecter avec psql par exemple. Dans le cas contraire, vous ne pourrez pas vous y connecter directement.

L’outil kubectl permet à des utilisateurs de se connecter à une instance spécifique. La commande

**Exemple 2-84-e01.** Exemple ou sortie de commande extraite du PDF\, page 84\.

```bash
kubectl exec permet d’exécuter une commande dans un conteneur. Par « chance », l’image utilisée
```

pour déployer PostgreSQL contient psql. Dès lors que vous avez accès au cluster Kubernetes et que vous avez les bons droits pour le faire, kubectl exec -it POD CONTAINER -- psql vous permet de vous connecter à l’instance avec le rôle postgres.

**Exemple 2-84-e02.** Exemple ou sortie de commande extraite du PDF\, page 84\.

```bash
kubectl exec -it postgresql-1 -c postgres -- psql
psql (17.2 (Debian 17.2-1.pgdg110+1))
Type "help" for help.
```

**Exemple 2-84-e03.** Exemple ou sortie de commande extraite du PDF\, page 84\.

```sql
postgres=#
```

Le plugin cnpg permet la même chose avec sa commande psql:

**Exemple 2-84-e04.** Exemple ou sortie de commande extraite du PDF\, page 84\.

```bash
kubectl cnpg psql postgresql
psql (17.2 (Debian 17.2-1.pgdg110+1))
Type "help" for help.
```

**Exemple 2-84-e05.** Exemple ou sortie de commande extraite du PDF\, page 84\.

```sql
postgres=#
```

Au sein du cluster Kubernetes, vous pouvez utiliser le DNS attribué au Pod ou au Service pour vous connecter à une instance.

#### 2.4.3 Traces {#cnpg1-2-4-3}

- Format JSON
- Un seul flux pour différents loggers, pas que PostgreSQL
- Sortie standard du Pod
- Certains paramètres log_* non modifiables
- Outil de centralisation de traces obligatoire (Loki, Fluentd, etc.)
- Exploitables par pgBadger

#### 2.4.4 Traces {#cnpg1-2-4-4}

**Exemple 2-85-e01.** Exemple ou sortie de commande extraite du PDF\, page 85\.

```text
{
     "level": "info",
     "ts": "2026-03-20T13:10:03.114096395Z",
     "logger": "postgres",
     "msg": "record",
     "logging_pod": "postgresql-prod-1",
     "record": {
       "log_time": "2026-03-20 13:10:03.113 UTC",
       "process_id": "36",
```

[...]

**Exemple 2-85-e02.** Exemple ou sortie de commande extraite du PDF\, page 85\.

```text
    }
}
```

- Parfois emballées dans du JSON

Dès lors que vous déploierez PostgreSQL avec CloudNativePG, les traces que vous connaissez ne seront ni accessibles dans le fichier postgresql.log ni du même format. Concernant les traces de l’opérateur, vous pouvez gérer le niveau de celles-ci avec l’argument --log-level du Deployment de l’opérateur. Les valeurs error, warning, info, debug et trace sont disponibles. La valeur par défaut est info.

Concernant les Pods PostgreSQL, les traces contiennent les traces de l’instance, mais également les traces d’autres éléments comme celles de l’ instance manager ou encore de la solution de sauvegarde. Toutes les traces sont renvoyées sur la sortie standard du Pod au format JSON et sont mélangées. La clé logger de la trace JSON indique qui est responsable de cette ligne. Par exemple, la trace suivante a été générée par l’instance. On peut le voir avec le paramètre logger qui est à postgres.

**Exemple 2-85-e03.** Exemple ou sortie de commande extraite du PDF\, page 85\.

```text
{
    "level": "info",
    "ts": 1619781249.7188137,
    "logger": "postgres",
    "msg": "record",
    "record": {
      "log_time": "2021-04-30 11:14:09.718 UTC",
      "user_name": "",
```

[...]

**Exemple 2-85-e04.** Exemple ou sortie de commande extraite du PDF\, page 85\.

```text
    }
}
```

Concernant les paramètres de traces PostgreSQL, vous ne pouvez pas modifier les paramètres PostgreSQL suivants, CloudNativePG l’interdit.

**Exemple 2-85-e05.** Exemple ou sortie de commande extraite du PDF\, page 85\.

```text
log_destination
log_directory
```

**Exemple 2-86-e01.** Exemple ou sortie de commande extraite du PDF\, page 86\.

```text
log_file_mode
log_filename
log_rotation_age
log_rotation_size
log_truncate_on_rotation
logging_collector
```

Il est possible de suivre les traces d’un Pod en ligne de commande avec la commande kubectl logs -f `<pod>` ou kubectl cnpg logs cluster `<cluster>` si vous avez installé le plugin cnpg pour kubectl. Les traces ne sont pas conservées dans le Pod. Il est donc essentiel d’avoir une solution de centralisation des traces pour que vos administrateurs puissent y avoir accès. Des outils comme Loki ou Fluentd peuvent remplir ce rôle.

L’outil pgBadger supporte le format des traces générées par CloudNativePG.

#### 2.4.5 Réplication {#cnpg1-2-4-5}

- Mise en place facilitée
    - instances: N
    - 1 primaire et N-1 secondaires
- Streaming Replication
- Slot de réplication créé par défaut
- Asynchrone par défaut

Le déploiement d’instances secondaires est très grandement facilité par CloudNativePG. En modifiant uniquement le nombre d’instances dans l’objet Cluster, une ou plusieurs nouvelles instances seront créées et configurées pour suivre le primaire grâce à la réplication physique (Streaming Replication). Un nouveau Pod sera donc créé, reprenant le nom du Cluster suivi d’un chiffre incrémenté de 1 pour chaque nouvelle instance. Avec la sortie de la commande kubectl get pod suivante, nous pouvons comprendre qu’au sein du cluster Kubernetes, deux Pods PostgreSQL existent et appartiennent au même Cluster (au sens de CloudNativePG) nommé postgresql.

**Exemple 2-86-e02.** Exemple ou sortie de commande extraite du PDF\, page 86\.

```bash
kubectl get pod
NAME                          READY     STATUS      RESTARTS      AGE
postgresql-1                  1/1       Running     0             14h
postgresql-2                  1/1       Running     0             7h
```

Le chiffre qui suit le nom du cluster, ici 1 et 2, n’indique PAS le rôle de l’instance (primaire ou secondaire). Se baser sur ce chiffre pour connaitre le rôle d’une instance est une erreur.

Sources du PDF (page 86) :

- [https://grafana.com/oss/loki/](<https://grafana.com/oss/loki/>)
- [https://docs.fluentd.org/0.12](<https://docs.fluentd.org/0.12>)
- [https://github.com/darold/pgbadger](<https://github.com/darold/pgbadger>)

Il existe plusieurs méthodes pour retrouver l’instance primaire d’un cluster. Par exemple:

**Exemple 2-87-e01.** Exemple ou sortie de commande extraite du PDF\, page 87\.

```bash
kubectl get cluster postgresql
NAME         AGE   INSTANCES   READY            STATUS                           PRIMARY
postgresql   14h   2           2                Cluster in healthy state         postgresql-1
```

L’opérateur et la configuration de base font en sorte que les instances secondaires soient réparties, si cela est possible, sur les différents workers qui composent le cluster Kubernetes. L’idée est de ne pas déployer au même endroit toutes les instances, sans quoi, en cas de panne, l’intégralité du Cluster PostgreSQL serait perdu. Un slot de réplication sera automatiquement créé pour chaque nouveau secondaire. Le principal avantage est de ne plus avoir de décrochage du secondaire en conservant les journaux de transactions nécessaires. La conséquence directe est le risque d’accumulation de ces mêmes journaux qui pourraient saturer l’espace disque du primaire. Le nom du slot de réplication est automatiquement généré avec cnpg et le nom de l’instance. Voici un extrait de la vue pg_replication_slots qui montre cela.

**Exemple 2-87-e02.** Exemple ou sortie de commande extraite du PDF\, page 87\.

```text
-[ RECORD 1 ]-------+-------------------
slot_name           | _cnpg_postgresql_2
plugin              |
slot_type           | physical
```

[...]

Vous trouverez davantage d’informations sur le concept de slot de réplication dans notre module W2B. Par défaut, la réplication mise en place est asynchrone. Il est possible de mettre en place de la réplication synchrone. Cette fonctionnalité sera traitée dans un second module de formation.

#### 2.4.6 Travaux pratiques {#cnpg1-2-4-6}

- Déploiement d’une instance secondaire
- Tests de bascules

#### 2.4.7 Archivage et Sauvegarde - Introduction {#cnpg1-2-4-7}

- Nécessite un plugin
- Un seul proposé Barman Cloud Plugin
- D’autres arriveront (pgBackRest...)

Historiquement, la méthode pour sauvegarder une instance PostgreSQL consistait à utiliser les outils de sauvegarde présents dans l’image et définir toute sa configuration dans l’objet Cluster. Cette

Sources du PDF (page 87) :

- [https://dali.bo/w2b_html](<https://dali.bo/w2b_html>)

méthode est désormais abandonnée et un message d’alerte sera remonté si vous l’utilisez. La dépréciation complète de cette méthode est prévue en version 1.30 de l’opérateur. Warning: Native support for Barman Cloud backups and recovery is deprecated and will be completely removed in CloudNativePG 1.30.0. Found usage in: spec.backup.barmanObjectStore. Please migrate existing clusters to the new Barman Cloud Plugin to ensure a smooth transition.

Il est désormais nécessaire d’utiliser un plugin pour gérer l’archivage et la sauvegarde des instances PostgreSQL. Il existe une autre méthode de sauvegarde que nous verrons plus tard basée elle sur une fonctionnalité de Kubernetes. Le projet CloudNativePG maintient le plugin Barman Cloud Plugin. C’est celui que nous utiliserons pour la présentation et les travaux pratiques. Il peut tout à fait être utilisé en production. Aussi, chaque plugin aura ses propres prérequis. Référez-vous à sa documentation d’installation pour les connaitre. L’intérêt de cette méthode de plugin est de laisser la possibilité d’utiliser d’autres outils de sauvegarde, sans devoir les intégrer aux images de conteneur. Pour les plus connaisseurs, c’est un conteneur sidecar qui sera créé à côté du conteneur PostgreSQL. On peut notamment penser à pgBackRest. Cet article sur notre blog présente d’ailleurs cette possibilité. En ce qui concerne Barman Cloud Plugin, il doit être installé dans le même Namespace que l’opérateur et nécessite que cert-manager soit installé dans le cluster Kubernetes. Il apporte une nouvelle ressource: ObjectStore.

#### 2.4.8 Plugin de sauvegarde {#cnpg1-2-4-8}

- Indiqué dans le Cluster

**Exemple 2-88-e01.** Exemple ou sortie de commande extraite du PDF\, page 88\.

```yaml
                     plugins:
                     - name: barman-cloud.cloudnative-pg.io
                       isWALArchiver: true
                       parameters:
                         barmanObjectName: objectstore-demo
```

L’utilisation de tel ou tel plugin doit être renseignée dans la ressource Cluster. S’il s’agit d’un plugin permettant de faire des sauvegardes, et que celui-ci est en capacité d’archiver les journaux de transactions, le paramètre isWALArchiver doit être renseigné. Parmi la liste des plugins, un seul peut avoir cette capacité d’archivage. Selon le plugin utilisé, que ce soit pour de la sauvegarde, ou pour tout autre besoin, il est possible de renseigner de la configuration supplémentaire avec la section parameters.

Sources du PDF (page 88) :

- [https://cloudnative-pg.io/plugin-barman-cloud/](<https://cloudnative-pg.io/plugin-barman-cloud/>)
- [https://blog.dalibo.com/2025/04/03/cnpg-6.html](<https://blog.dalibo.com/2025/04/03/cnpg-6.html>)

#### 2.4.9 Hibernation et fencing {#cnpg1-2-4-9}

- Hibernation
    - Arrêter le Pod en conservant les volumes
    - Déclarative ou via le plugin cnpg
- Fencing
    - Arrêter uniquement le service postmaster

Le principe d’hibernation vous permet d’arrêter un Cluster PostgreSQL tout en conservant les volumes de données. Les Pods PostgreSQL seront arrêtés un à un en commençant par le primaire. Les PVC et PV de chaque instance, qu’elle soit primaire ou secondaire, existeront toujours.

La première méthode pour passer un Cluster en hibernation est de lui ajouter l’annotation cnpg.io/hibernation=on, avec, par exemple, la commande suivante:

**Exemple 2-89-e01.** Exemple ou sortie de commande extraite du PDF\, page 89\.

```bash
kubectl annotate cluster <cluster-name> --overwrite cnpg.io/hibernation=on
```

Voici un exemple de situation lorsqu’une instance est en hibernation.

**Exemple 2-89-e02.** Exemple ou sortie de commande extraite du PDF\, page 89\.

```text
$ kubectl get pod
No resources found in default namespace.
```

**Exemple 2-89-e03.** Exemple ou sortie de commande extraite du PDF\, page 89\.

```text
$ kubectl get pvc
NAME          STATUS   VOLUME                                 CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
postgresql-1 Bound      pvc-bb811ce2-c4db-4f3d-8286-1187bcd91174 2Gi         RWO          standard      <unset>              7m30s
postgresql-2 Bound      pvc-b8b35ea1-073c-4dd9-8ff1-3fc9d6f60418 2Gi         RWO           standard      <unset>              2m2s
```

Il n’y a plus de Pod mais les PVC sont bien encore présents. Si vous souhaitez redéployer les Pods (primaire et secondaire) du Cluster, utilisez la même commande avec l’option off cette fois-ci.

Il est également possible d’hiberner un Cluster avec le plugin cnpg de kubectl. Une différence notable existe entre ces deux méthodes. Avec cette deuxième méthode, seul le PVC de l’instance primaire est conservé.

**Exemple 2-89-e04.** Exemple ou sortie de commande extraite du PDF\, page 89\.

```text
$ kubectl cnpg hibernate on postgresql
hibernation process starting...
waiting for the cluster to be fenced
cluster is now fenced, storing primary pg_controldata output
primary pg_controldata output fetched
annotating the PVC with the cluster manifest
PVC annotation complete
destroying the primary instance while preserving the pvc
Instance postgresql-1 of cluster postgresql has been destroyed and the PVC was kept
primary instance destroy completed
deleting the cluster resource
cluster resource deletion complete
Hibernation completed
```

**Exemple 2-89-e05.** Exemple ou sortie de commande extraite du PDF\, page 89\.

```text
$ kubectl get pvc
NAME          STATUS   VOLUME                                CAPACITY    ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
postgresql-1   Bound    pvc-bb811ce2-c4db-4f3d-8286-1187bcd91174  2Gi        RWO           standard      <unset>               10m
```

Lorsque le Cluster sortira d’hibernation (avec kubectl cnpg hibernate off postgresql) les PVC nécessaires aux secondaires seront recréés. Selon la volumétrie des instances, cette copie pourra représenter beaucoup de volume et de temps. Le fencing quant à lui permet d’arrêter le service postmaster de PostgreSQL sans pour autant arrêter le Pod. Cela revient à faire un arrêt propre de l’instance PostgreSQL au sein du Pod. Il est possible de passer en fencing une instance spécifique (qu’elle soit primaire ou secondaire), une liste d’instances, ou toutes les instances d’un Cluster. Là encore, ce mécanisme est géré par une annotation, en l’occurrence cnpg.io/fencedInstances. Par exemple, avec:

- cnpg.io/fencedInstances: '["postgresql-1"]', seule cette instance sera en fencing;
- cnpg.io/fencedInstances: '["postgresql-1","postgresql-3"]', ces deux instances seront passées en fencing;
- cnpg.io/fencedInstances: '["*"]', toutes les instances du Cluster annoté passeront en fencing.

Vous pouvez soit utiliser la commande kubectl annotate..., soit le plugin cnpg avec par exemple

**Exemple 2-90-e01.** Exemple ou sortie de commande extraite du PDF\, page 90\.

```text
 kubectl cnpg fencing on postgresql 1
```

Lorsqu’une instance est passée en fencing, le Pod ne sera plus marqué READY, comme le montre cet exemple.

**Exemple 2-90-e02.** Exemple ou sortie de commande extraite du PDF\, page 90\.

```text
$ kubectl cnpg fencing on postgresql 1
postgresql-1 fenced
```

**Exemple 2-90-e03.** Exemple ou sortie de commande extraite du PDF\, page 90\.

```text
$ kubectl get pod
NAME           READY        STATUS      RESTARTS     AGE
postgresql-1   0/1          Running     0            19m
postgresql-2   1/1          Running     0            19m
```

Le Pod est toujours accessible, permettant par exemple de procéder à du débogage.

### 2.5 CONCLUSION {#cnpg1-2-5}

- Un opérateur complet, open-source, communautaire
- Déploiement et configuration facilités
- Approche déclarative
- Nouveaux mécanismes et configuration à connaitre
- Connaissance de PostgreSQL nécessaire

À travers ce module, vous a été présenté l’opérateur CloudNativePG, son principe de fonctionnement, son installation et une grande partie de ce qu’il permet de faire. Cela représente une bonne découverte de l’opérateur, tant sur ses fonctionnalités que sur certains aspects critiques de son utilisation. L’opérateur CloudNativePG est celui qui connait la plus forte adoption ces dernières années. Il est complet (niveau 5 sur le site https://operatorhub.io/), open-source, avec un souhait de gouvernance partagée. Il fait d’ailleurs partie du projet d’incubation de la Cloud Native Computing Foundation, garant de certains principes open-source. Le déploiement d’instances PostgreSQL est facilité par la nature déclarative de la gestion par CloudNativePG. En se reposant sur les fonctionnalités de Kubernetes, l’opérateur permet la mise en place de mécanismes complexes comme la haute disponibilité ou la bascule automatique. Des mécanismes propres à l’opérateur sont à connaitre, comme sa mise à jour et les conséquences sur les instances, les différents paramètres et spécifications utilisables dans les fichiers YAML ou encore ce qui est automatiquement créé par l’opérateur (rôle, base, Secret, etc.). L’opérateur se repose sur un certain nombre de concepts liés à PostgreSQL. Il s’agit donc de bien les connaître (réplication par flux, sauvegarde PITR, etc.) pour comprendre comment l’opérateur les utilise ou les configure. L’intégration de PostgreSQL dans Kubernetes est grandement facilitée par CloudNativePG. En contrepartie, cela demande à un administrateur PostgreSQL de vraies connaissances sur Kubernetes (qu’estce qu’un Pod? un Service? un Secret?) et de revoir sa manière de travailler avec son SGBD favori.

### 2.6 QUESTIONS {#cnpg1-2-6}

N’hésitez pas, c’est le moment!

### 2.7 TRAVAUX PRATIQUES {#cnpg1-2-7}

La version en ligne des solutions de ces TP est disponible sur https://dali.bo/k1_solutions.

### 2.8 PRISE EN MAIN DU CLUSTER KUBERNETES {#cnpg1-2-8}

But: Prendre en main le cluster Kubernetes.

Se connecter à la machine qui vous est dédiée.

Trouver la version de l’utilitaire kubectl.

Lister les nœuds du cluster Kubernetes.

Cet utilitaire sait avec quel cluster Kubernetes interagir grâce au fichier ~/.kube/config qui se trouve dans le répertoire de votre utilisateur.

### 2.9 INSTALLATION DE L’OPÉRATEUR CLOUDNATIVEPG {#cnpg1-2-9}

But: Installer l’opérateur dans le cluster Kubernetes ainsi que des modules complémentaires.

Il existe plusieurs méthodes pour installer l’opérateur: soit en appliquant directement les fichiers YAML soit en utilisant le Helm Chart fourni par le projet. Pour cet atelier, nous utiliserons la première méthode, plus simple et rapide.

Installer la version 1.30.0 de l’opérateur avec la commande kubectl apply -f:

**Exemple 2-95-e01.** Exemple ou sortie de commande extraite du PDF\, page 95\.

```bash
kubectl apply --server-side -f \
   https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.30/releases/cnpg-1.30.0.yaml
```

Lister les Pods présents dans le namespace cnpg-system.

Retrouver la liste des nouvelles ressources Kubernetes disponibles grâce à l’opérateur CloudNativePG.

### 2.10 DÉPLOIEMENT D’INSTANCES POSTGRESQL {#cnpg1-2-10}

But: Déployer un cluster PostgreSQL mono-instance, s’y connecter et suivre les traces de l’opérateur et de l’instance.

Voici un exemple de fichier YAML très simple qui permet de déployer une instance PostgreSQL en version 17.5 avec 5 Go de volume associés au PGDATA et 5 autres aux journaux de transactions.

**Exemple 2-96-e01.** Exemple ou sortie de commande extraite du PDF\, page 96\.

```yaml
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: postgresql-demo
spec:
  imageName: ghcr.io/cloudnative-pg/postgresql:17.5-standard-bookworm
  instances: 1
  storage:
    size: 5Gi
  walStorage:
    size: 5Gi
  affinity:
    enablePodAntiAffinity: true
    topologyKey: kubernetes.io/hostname
    podAntiAffinityType: preferred
  resources:
    requests:
      memory: "256Mi"
      cpu: "0.5"
    limits:
      memory: "512Mi"
      cpu: "1"
```

Quelques informations supplémentaires sur le contenu de ce fichier:

- `apiVersion` : La version de l’API de Kubernetes utilisée ;
- `kind` : Le type d’objet créé ;
- `metadata` : Des informations pour identifier l’objet ;
- `spec` : La définition de l’objet en question (« l’état désiré ») ;
- `imageName` : Le nom de l’image utilisée ;
- `instances` : Le nombre d’instances voulues (sera toujours 1 primaire + le reste en secondaire(s)) ;
- `storage` : Les informations sur le stockage souhaité pour le PGDATA ;
- `walStorage` : Les informations sur le stockage souhaité pour les journaux de transactions ;
- `affinity` : Indique où et comment seront déployées les instances de ce Cluster ;
- `resources` : L’indication de requests et limits sur la RAM et le CPU.

Créer le fichier postgresql-demo.yaml dans le home directory de dalibo et copier le contenu YAML ci-dessus.

Dans un autre terminal sur la VM, suivre les traces du controller avec la commande

`kubectl logs -f -n cnpg-system <POD>` et l’utilitaire `jq`. Pour retrouver le nom du Pod du contrôleur, vous pouvez utiliser `kubectl get pod -A`.

Retourner dans l’ancienne session SSH et créer l’instance PostgreSQL à partir du fichier ~/postgresql-demo.yaml avec kubectl. En parallèle regarder ce qu’il se passe dans les traces du controller.

Se connecter à l’instance et vérifier la version de celle-ci. Vous pouvez utiliser kubectl [...] comme ceci:

**Exemple 2-97-e02.** Exemple ou sortie de commande extraite du PDF\, page 97\.

```bash
kubectl exec -it postgresql-demo-1 -c postgres -- psql
```

ou, via le plugin:

**Exemple 2-97-e03.** Exemple ou sortie de commande extraite du PDF\, page 97\.

```bash
kubectl cnpg psql postgresql-demo
```

Suivre les traces de l’instance avec la commande kubectl logs -f postgresql-demo-1.

### 2.11 ÉLÉMENTS INITIAUX {#cnpg1-2-11}

But: Découvrir les éléments automatiquement créés par l’opérateur.

Avec quelques lignes de YAML et peu de commandes, une instance PostgreSQL est déployée et accessible. De nombreuses choses sont créées automatiquement pour nous. Voyons de quoi il s’agit. Bases de données Retrouver la liste des bases de données dans l’instance déployée. La méta-commande \l de

**Exemple 2-98-e01.** Exemple ou sortie de commande extraite du PDF\, page 98\.

```text
psql vous permet de récupérer la liste des bases.
```

Rôles et Secret

Retrouver la liste des rôles dans l’instance déployée. La méta-commande psql \du peut vous aider.

Se déconnecter de l’instance.

Se connecter à la base de données app avec le rôle app. Une erreur devrait vous être retournée.

Se connecter à la base de données app avec le rôle app et en passant par la stack TCP/IP.

Récupérer la liste des Secrets du cluster avec kubectl get secrets.

**Exemple 2-98-e02.** Exemple ou sortie de commande extraite du PDF\, page 98\.

```text
Récupérer le mot de passe présent dans le Secret postgresql-demo-app . N’oubliez pas qu’il
```

est encodé en Base64, il faut décoder le contenu obtenu.

Se connecter à l’instance en utilisant le mot de passe.

Services Un Service est une couche d’abstraction qui permet d’accéder à un ensemble de Pods spécifiques. L’association Service - Pods se fait via des labels. Un label est une étiquette, un tag, apposée à une ressource. Retrouver la liste des Services dans le cluster Kubernetes.

Retrouver les labels définis sur le Pod de votre instance.

Retrouver la description du Service postgresql-demo-ro et retrouver la partie Selector qui indique à quel(s) Pod (s) sera associé ce Service.

### 2.12 CRÉER UN RÔLE {#cnpg1-2-12}

Il existe plusieurs méthodes pour créer un rôle dans une instance. L’ordre SQL CREATE ROLE peut évidemment être utilisé, mais pour cet exemple, nous allons passer par la méthode déclarative et demander à l’opérateur de faire en sorte que le rôle soit présent dans l’instance.

Créer un fichier roles.yaml.

Créer un rôle dba ayant les droits SUPERUSER dans l’instance. Cela peut se faire grâce à la ressource DatabaseRole.

Appliquer la modification avec kubectl apply -f ~/roles.yaml.

Vérifier que le rôle a bien été créé.

Encoder le nom du rôle ( dba) en base64.

Encoder le mot de passe ( ilovemydba) en base64.

Créer un fichier ~/secret.yaml avec le contenu suivant puis créer le Secret.

**Exemple 2-100-e02.** Exemple ou sortie de commande extraite du PDF\, page 100\.

```yaml
apiVersion: v1
data:
  username: ZGJh
  password: aWxvdmVteWRiYQ==
kind: Secret
metadata:
  name: secret-password-dba
  labels:
    cnpg.io/reload: "true"
type: kubernetes.io/basic-auth
```

Ajouter ce mot de passe à la définition du rôle dba dans le fichier ~/roles.yaml, dans le champ

**Exemple 2-100-e03.** Exemple ou sortie de commande extraite du PDF\, page 100\.

```text
passwordSecret .
```

Appliquer les modifications.

Se connecter avec ce nouveau rôle à la base postgres.

### 2.13 CRÉER UNE BASE DE DONNÉES {#cnpg1-2-13}

Créer une base de données db1 dans l’instance postgresql-demo. Le propriétaire de cette base doit être le rôle app. Pour cela, créer un fichier db1.yaml contenant la définition d’une ressource Database.

Créer la nouvelle ressource avec kubectl apply -f ~/db1.yaml.

Vérifier la présence de cette base de données dans l’instance.

### 2.14 DÉPLOIEMENT D’UNE INSTANCE SECONDAIRE {#cnpg1-2-14}

But: Déployer une instance secondaire dans le cluster postgresql-demo.

Notre instance actuellement déployée ne possède pas de secondaire. L’ajout de secondaire se fait facilement en modifiant le paramètre instances dans la section spec de notre fichier YAML.

Déployer un secondaire à votre instance en modifiant le paramètre instances à 2.

### 2.15 CONFIGURATION PAR DÉFAUT {#cnpg1-2-15}

Regardons la configuration qui est mise en place par défaut.

Se connecter avec psql au secondaire nouvellement créé.

Récupérer le contenu du paramètre primary_conninfo.

Se connecter avec psql au primaire.

Récupérer le contenu de la vue pg_stat_replication.

Récupérer le contenu de la vue pg_replication_slots.

Sur le primaire, créer une table dans la base app.

Vérifier qu’elle se trouve également sur le secondaire.

### 2.16 EMPLACEMENT DES INSTANCES {#cnpg1-2-16}

L’opérateur CloudNativePG veille à déployer les instances PostgreSQL sur des nœuds différents afin de garantir la disponibilité. Cela permet de réduire les risques liés à un incident en s’assurant que toutes les instances ne sont pas affectées simultanément. Cette configuration permet également la répartition de la charge entre plusieurs nœuds pour des opérations de lecture.

Trouver le nom du nœud où est déployée chaque instance.

### 2.17 EXERCICES OPTIONNELS {#cnpg1-2-17}

But: Découvrir des fonctionnalités plus complexes.

#### 2.17.1 Mettre en place une réplication synchrone {#cnpg1-2-17-1}

Il existe plusieurs méthodes pour mettre en place une réplication synchrone. Le but ici n’est pas de les évoquer ni de les comparer, mais simplement de voir le principe de la configuration. Pour l’exemple, nous mettrons en place la méthode par Quorum.

Modifier la configuration de l’instance en rajoutant le bloc suivant à votre fichier ~/postgresql-restored-demo.ya

**Exemple 2-105-e01.** Exemple ou sortie de commande extraite du PDF\, page 105\.

```yaml
postgresql:
  synchronous:
    method: any
    number: 1
```

**Exemple 2-105-e02.** Exemple ou sortie de commande extraite du PDF\, page 105\.

```bash
kubectl apply -f postgresql.yaml
```

**Exemple 2-105-e03.** Exemple ou sortie de commande extraite du PDF\, page 105\.

```text
cluster.postgresql.cnpg.io/postgresql-demo configured
```

Vérifier que la réplication est synchrone en regardant le champ sync_state de la vue

**Exemple 2-105-e04.** Exemple ou sortie de commande extraite du PDF\, page 105\.

```bash
pg_stat_replication de l’instance primaire.
```

**Exemple 2-105-e05.** Exemple ou sortie de commande extraite du PDF\, page 105\.

```sql
postgres=# select * from pg_stat_replication\gx
-[ RECORD 1 ]----+------------------------------
pid              | 1210
usesysid         | 16386
usename          | streaming_replica
application_name | postgresql-restored-demo-1
client_addr      | 10.244.3.29
client_hostname |
client_port      | 51518
backend_start    | 2025-12-15 18:29:04.335147+00
backend_xmin     |
state            | streaming
sent_lsn         | 0/D000000
write_lsn        | 0/D000000
flush_lsn        | 0/D000000
replay_lsn       | 0/D000000
write_lag        |
flush_lag        |
replay_lag       |
sync_priority    | 1
sync_state       | quorum
reply_time       | 2025-12-15 18:52:30.849357+00
```

sync_state est bien à quorum.

Plus d’informations sur la documentation.

#### 2.17.2 Déploiement d’une application pgAdmin4 {#cnpg1-2-17-2}

Pour voir comment une application peut se connecter à une instance, nous allons déployer pgAdmin dans le cluster Kubernetes. Ouvrir une nouvelle session SSH. Passer en tant qu’utilisateur dalibo.

Créer le fichier ~/pgadmin.yaml avec le contenu suivant et le déployer dans le cluster Kubernetes.

**Exemple 2-106-e01.** Exemple ou sortie de commande extraite du PDF\, page 106\.

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pgadmin
spec:
  replicas: 1
  selector:
    matchLabels:
      app: pgadmin
  template:
    metadata:
      labels:
        app: pgadmin
    spec:
      containers:
        - name: pgadmin
          image: dpage/pgadmin4
          ports:
            - containerPort: 80
          env:
            - name: PGADMIN_DEFAULT_EMAIL
              value: admin@example.com
            - name: PGADMIN_DEFAULT_PASSWORD
              value: admin
```

Récupérer le nom du Pod pgAdmin déployé, et lancer la commande suivante:

**Exemple 2-106-e02.** Exemple ou sortie de commande extraite du PDF\, page 106\.

```bash
kubectl port-forward --address 0.0.0.0 pgadmin-*****-***** 8888:80
```

**Exemple 2-106-e03.** Exemple ou sortie de commande extraite du PDF\, page 106\.

```text
Accéder à l’interface de pgAdmin via votre navigateur http://adresseippublique:8888 et se
```

connecter à l’interface (admin@example.com / admin).

Sources du PDF (page 106) :

- [https://cloudnative-pg.io/docs/1.30/replication#synchronous-replication](<https://cloudnative-pg.io/docs/1.30/replication#synchronous-replication>)

Créer une nouvelle connexion avec les informations suivantes: Créer une nouvelle connexion avec cette fois-ci postgresql-demo-ro comme paramètre Host name/address et créer une

**Exemple 2-107-e01.** Exemple ou sortie de commande extraite du PDF\, page 107\.

```text
  table CREATE TABLE ma_table (i int); .
```

### 2.18 TRAVAUX PRATIQUES (SOLUTIONS) {#cnpg1-2-18}

### 2.19 PRISE EN MAIN DU CLUSTER KUBERNETES {#cnpg1-2-19}

But: Prendre en main le cluster Kubernetes.

Se connecter à la machine qui vous est dédiée.

**Exemple 2-109-e01.** Exemple ou sortie de commande extraite du PDF\, page 109\.

```text
ssh root@A.B.C.D
```

Trouver la version de l’utilitaire kubectl.

**Exemple 2-109-e02.** Exemple ou sortie de commande extraite du PDF\, page 109\.

```bash
kubectl version
```

**Exemple 2-109-e03.** Exemple ou sortie de commande extraite du PDF\, page 109\.

```bash
kubectl version
Client Version: v1.34.3
Kustomize Version: v5.7.1
Server Version: v1.34.0
```

L’utilitaire kubectl vous permet d’interagir avec le cluster Kubernetes déployé.

Lister les nœuds du cluster Kubernetes.

**Exemple 2-109-e04.** Exemple ou sortie de commande extraite du PDF\, page 109\.

```bash
kubectl get nodes
```

**Exemple 2-109-e05.** Exemple ou sortie de commande extraite du PDF\, page 109\.

```text
NAME                     STATUS     ROLES             AGE     VERSION
kind-control-plane       Ready      control-plane     135m    v1.34.0
kind-worker              Ready      <none>            134m    v1.34.0
kind-worker2             Ready      <none>            134m    v1.34.0
```

Cet utilitaire sait avec quel cluster Kubernetes interagir grâce au fichier ~/.kube/config qui se trouve dans le répertoire de votre utilisateur.

### 2.20 INSTALLATION DE L’OPÉRATEUR CLOUDNATIVEPG {#cnpg1-2-20}

But: Installer l’opérateur dans le cluster Kubernetes ainsi que des modules complémentaires.

Il existe plusieurs méthodes pour installer l’opérateur: soit en appliquant directement les fichiers YAML soit en utilisant le Helm Chart fourni par le projet. Pour cet atelier, nous utiliserons la première méthode, plus simple et rapide.

Installer la version 1.30.0 de l’opérateur avec la commande kubectl apply -f:

**Exemple 2-110-e01.** Exemple ou sortie de commande extraite du PDF\, page 110\.

```bash
kubectl apply --server-side -f \
   https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.30/releases/cnpg-1.30.0.yaml
```

**Exemple 2-110-e02.** Exemple ou sortie de commande extraite du PDF\, page 110\.

```text
namespace/cnpg-system serverside-applied
customresourcedefinition.apiextensions.k8s.io/backups.postgresql.cnpg.io serverside-applied
customresourcedefinition.apiextensions.k8s.io/clusterimagecatalogs.postgresql.cnpg.io serverside-applied
customresourcedefinition.apiextensions.k8s.io/clusters.postgresql.cnpg.io serverside-applied
customresourcedefinition.apiextensions.k8s.io/imagecatalogs.postgresql.cnpg.io serverside-applied
customresourcedefinition.apiextensions.k8s.io/poolers.postgresql.cnpg.io serverside-applied
customresourcedefinition.apiextensions.k8s.io/scheduledbackups.postgresql.cnpg.io serverside-applied
serviceaccount/cnpg-manager serverside-applied
clusterrole.rbac.authorization.k8s.io/cnpg-manager serverside-applied
clusterrolebinding.rbac.authorization.k8s.io/cnpg-manager-rolebinding serverside-applied
configmap/cnpg-default-monitoring serverside-applied
service/cnpg-webhook-service serverside-applied
deployment.apps/cnpg-controller-manager serverside-applied
mutatingwebhookconfiguration.admissionregistration.k8s.io/cnpg-mutating-webhook-configuration serverside-applied
validatingwebhookconfiguration.admissionregistration.k8s.io/cnpg-validating-webhook-configuration serverside-applied
```

Les fichiers seront récupérés depuis internet et appliqués sur votre cluster Kubernetes. Pour rappel, l’outil kubectl sait avec quel cluster Kubernetes interagir grâce au fichier kubeconfig. Ces fichiers-là contiennent la définition de différentes ressources:

- Un Namespace;
- Une CustomResourceDefinition pour les différentes ressources que l’opérateur va gérer ( Backup, Cluster, etc.);
- Mais aussi un ServiceAccount, un ClusterRoleBinding et surtout un déploiement du controller CloudNativePG.

Par défaut, le Controller, cerveau de l’opérateur, sera déployé dans le Namespace cnpg-system, créé lors de l’installation de l’opérateur. Ce controller n’est ni plus ni moins qu’une application. On peut voir le controller avec la commande kubectl get pods et en indiquant le bon Namespace:

Lister les Pods présents dans le namespace cnpg-system.

**Exemple 2-111-e01.** Exemple ou sortie de commande extraite du PDF\, page 111\.

```bash
kubectl get pods -n cnpg-system
```

**Exemple 2-111-e02.** Exemple ou sortie de commande extraite du PDF\, page 111\.

```text
NAME                                                READY     STATUS      RESTARTS     AGE
cnpg-controller-manager-7fc549dc69-xq7gq            1/1       Running     0            11s
```

Retrouver la liste des nouvelles ressources Kubernetes disponibles grâce à l’opérateur CloudNativePG.

**Exemple 2-111-e03.** Exemple ou sortie de commande extraite du PDF\, page 111\.

```bash
kubectl api-resources --api-group postgresql.cnpg.io
```

**Exemple 2-111-e04.** Exemple ou sortie de commande extraite du PDF\, page 111\.

```text
backups                                                  postgresql.cnpg.io/v1                    true Backup
clusterimagecatalogs                                        postgresql.cnpg.io/v1 false        ClusterImageCatalog
clusters                                                 postgresql.cnpg.io/v1                    true Cluster
imagecatalogs                                            postgresql.cnpg.io/v1                    true ImageCatalog
poolers                                                  postgresql.cnpg.io/v1                    true Pooler
scheduledbackups                                            postgresql.cnpg.io/v1 true         ScheduledBackup
```

### 2.21 DÉPLOIEMENT D’INSTANCES POSTGRESQL {#cnpg1-2-21}

But: Déployer un cluster PostgreSQL mono-instance, s’y connecter et suivre les traces de l’opérateur et de l’instance.

Voici un exemple de fichier YAML très simple qui permet de déployer une instance PostgreSQL en version 17.5 avec 5 Go de volume associés au PGDATA et 5 autres aux journaux de transactions.

**Exemple 2-112-e01.** Exemple ou sortie de commande extraite du PDF\, page 112\.

```yaml
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: postgresql-demo
spec:
  imageName: ghcr.io/cloudnative-pg/postgresql:17.5-standard-bookworm
  instances: 1
  storage:
    size: 5Gi
  walStorage:
    size: 5Gi
  affinity:
    enablePodAntiAffinity: true
    topologyKey: kubernetes.io/hostname
    podAntiAffinityType: preferred
  resources:
    requests:
      memory: "256Mi"
      cpu: "0.5"
    limits:
      memory: "512Mi"
      cpu: "1"
```

Quelques informations supplémentaires sur le contenu de ce fichier:

- `apiVersion` : La version de l’API de Kubernetes utilisée ;
- `kind` : Le type d’objet créé ;
- `metadata` : Des informations pour identifier l’objet ;
- `spec` : La définition de l’objet en question (« l’état désiré ») ;
- `imageName` : Le nom de l’image utilisée ;
- `instances` : Le nombre d’instances voulues (sera toujours 1 primaire + le reste en secondaire(s)) ;
- `storage` : Les informations sur le stockage souhaité pour le PGDATA ;
- `walStorage` : Les informations sur le stockage souhaité pour les journaux de transactions ;
- `affinity` : Indique où et comment seront déployées les instances de ce Cluster ;
- `resources` : L’indication de requests et limits sur la RAM et le CPU.

Créer le fichier postgresql-demo.yaml dans le home directory de dalibo et copier le contenu YAML ci-dessus:

**Exemple 2-113-e01.** Exemple ou sortie de commande extraite du PDF\, page 113\.

```yaml
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: postgresql-demo
spec:
  imageName: ghcr.io/cloudnative-pg/postgresql:17.5-standard-bookworm
  instances: 1
  storage:
    size: 5Gi
  walStorage:
    size: 5Gi
  affinity:
    enablePodAntiAffinity: true
    topologyKey: kubernetes.io/hostname
    podAntiAffinityType: preferred
  resources:
    requests:
      memory: "256Mi"
      cpu: "0.5"
    limits:
      memory: "512Mi"
      cpu: "1"
```

Dans un autre terminal sur la VM, suivre les traces du controller avec la commande

`kubectl logs -f -n cnpg-system <POD>` et l’utilitaire `jq`. Pour retrouver le nom du Pod du contrôleur, vous pouvez utiliser `kubectl get pod -A`.

**Exemple 2-113-e03.** Exemple ou sortie de commande extraite du PDF\, page 113\.

```bash
kubectl logs -f -n cnpg-system cnpg-controller-manager-7fc549dc69-8v8xw | jq
```

Retourner dans l’ancienne session SSH et créer l’instance PostgreSQL à partir du fichier ~/postgresql-demo.yaml avec kubectl. En parallèle regarder ce qu’il se passe dans les traces du controller:

**Exemple 2-113-e04.** Exemple ou sortie de commande extraite du PDF\, page 113\.

```bash
kubectl apply -f ~/postgresql-demo.yaml
cluster.postgresql.cnpg.io/postgresql-demo created
```

Une instance PostgreSQL est désormais en train d’être déployée par l’opérateur. Vous avez décrit ce que vous souhaitiez avoir, l’opérateur fait le reste. Plusieurs choses se passent lorsque vous appliquez ce fichier avec kubectl. Tout d’abord l’opérateur va déployer un premier Pod appelé `<clusterName>`-1-initdb-`<random>`. initdb devrait vous faire penser à la commande à exécuter lorsque vous devez créer une instance manuellement par exemple.

**Exemple 2-113-e05.** Exemple ou sortie de commande extraite du PDF\, page 113\.

```bash
kubectl get pods --watch
```

**Exemple 2-113-e06.** Exemple ou sortie de commande extraite du PDF\, page 113\.

```text
NAME                             READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED NODE   READINESS GATES
postgresql-demo-1-initdb-5pndc   0/1     Pending   0          2s    <none>   <none>   <none>           <none>
```

Ce Pod se repose sur une image qui doit être téléchargée. C’est pour cela que vous devez avoir autorisé l’accès vers internet (ou votre dépôt local d’images) à votre cluster. Lorsque celle-ci est récupérée, le Pod est « amorcé » et les conteneurs d’initialisation sont déployés, comme on peut le voir avec cette seconde remontée. Ici il existe un conteneur d’initialisation mais aucun n’est terminé.

**Exemple 2-114-e01.** Exemple ou sortie de commande extraite du PDF\, page 114\.

```text
NAME                             READY      STATUS      RESTARTS    AGE    IP         NODE          NOMINATED NODE   READINESS GATES
postgresql-demo-1-initdb-5pndc   0/1        Init:0/1    0           13s    <none>     k8s-demo      <none>           <none>
```

Au fur et à mesure, le Pod passe par d’autres états...

**Exemple 2-114-e02.** Exemple ou sortie de commande extraite du PDF\, page 114\.

```text
NAME                          READY  STATUS           RESTARTS       AGE    IP               NODE        NOMINATED NODE   READINESS GATES
postgresql-demo-1-initdb-5pndc   0/1     PodInitializing   0                24s     10.244.228.68     k8s-demo   <none>           <none>
```

... jusqu’à l’état Running. À cette étape-ci, le Pod va notamment initialiser l’instance avec la création de l’arborescence du PGDATA.

**Exemple 2-114-e03.** Exemple ou sortie de commande extraite du PDF\, page 114\.

```text
NAME                             READY     STATUS      RESTARTS    AGE    IP               NODE          NOMINATED NODE   READINESS GATES
postgresql-demo-1-initdb-5pndc   1/1       Running     0           35s    10.244.228.68    k8s-demo       <none>           <none>
```

Enfin, lorsque cette étape est terminée, l’opérateur CloudNativePG déploie un autre Pod qui cette fois-ci ne porte plus le mot initdb. Un numéro est ajouté à la fin du nom. Une adresse IP est attribuée à ce Pod (IP privée RFC 1918):

**Exemple 2-114-e04.** Exemple ou sortie de commande extraite du PDF\, page 114\.

```bash
kubectl get pods -o wide
```

**Exemple 2-114-e05.** Exemple ou sortie de commande extraite du PDF\, page 114\.

```text
NAME                 READY   STATUS      RESTARTS    AGE   IP                NODE         NOMINATED NODE     READINESS GATES
postgresql-demo-1    1/1     Running     0           10m   10.244.228.69     k8s-demo     <none>             <none>
```

Votre Pod est prêt et donc votre instance aussi!

Se connecter à l’instance et vérifier la version de celle-ci. Vous pouvez utiliser kubectl exec [...] comme ceci:

**Exemple 2-114-e06.** Exemple ou sortie de commande extraite du PDF\, page 114\.

```bash
kubectl exec -it postgresql-demo-1 -c postgres -- psql
```

ou, via le plugin:

**Exemple 2-114-e07.** Exemple ou sortie de commande extraite du PDF\, page 114\.

```bash
kubectl cnpg psql postgresql-demo
```

**Exemple 2-114-e08.** Exemple ou sortie de commande extraite du PDF\, page 114\.

```text
psql (17.5 (Debian 17.5-1.pgdg120+1))
Type "help" for help.
```

**Exemple 2-114-e09.** Exemple ou sortie de commande extraite du PDF\, page 114\.

```sql
postgres=# select version()\gx
-[ RECORD 1 ]----------------------------------------------------------------------------------------------------------------
version | PostgreSQL 17.5 (Debian 17.5-1.pgdg120+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit
```

La commande kubectl exec -it permet d’exécuter un programme au sein du Pod. L’outil psql étant présent dans l’image, cela est possible. Essayez avec vim, qui lui n’est pas présent dans l’image, un message d’erreur apparaîtra. Si besoin, pour quitter psql, vous pouvez utiliser control+d, \q ou exit.

**Exemple 2-114-e10.** Exemple ou sortie de commande extraite du PDF\, page 114\.

```sql
postgres=# \q
```

Sources du PDF (page 114) :

- [RFC 1918](https://datatracker.ietf.org/doc/html/rfc1918)

Suivre les traces de l’instance avec la commande kubectl logs -f postgresql-demo-1.

**Exemple 2-115-e01.** Exemple ou sortie de commande extraite du PDF\, page 115\.

```bash
kubectl logs -f postgresql-demo-1
```

**Exemple 2-115-e02.** Exemple ou sortie de commande extraite du PDF\, page 115\.

```jsonl
{"level":"info","ts":"2025-12-11T14:23:56.028300944Z","logger":"postgres","msg":"record","logging_pod":"postgresql-demo-1","record":{"log_time":"2025-12-11 14:23:56.027 UTC","process_id":"30","session_id":"693ad3fb.1e","session_line_num":"5","session_start_time":"2025-12-11 14:23:55 UTC","transaction_id":"0","error_severity":"LOG","sql_state_code":"00000","message":"listening on Unix socket \"/controller/run/.s.PGSQL.5432\"","backend_type":"postmaster","query_id":"0"}}
{"level":"info","ts":"2025-12-11T14:23:56.075466089Z","logger":"postgres","msg":"record","logging_pod":"postgresql-demo-1","record":{"log_time":"2025-12-11 14:23:56.075 UTC","process_id":"36","session_id":"693ad3fc.24","session_line_num":"1","session_start_time":"2025-12-11 14:23:56 UTC","transaction_id":"0","error_severity":"LOG","sql_state_code":"00000","message":"database system was shut down at 2025-12-11 14:23:48 UTC","backend_type":"startup","query_id":"0"}}
{"level":"info","ts":"2025-12-11T14:23:56.108020409Z","logger":"postgres","msg":"record","logging_pod":"postgresql-demo-1","record":{"log_time":"2025-12-11 14:23:56.107 UTC","process_id":"30","session_id":"693ad3fb.1e","session_line_num":"6","session_start_time":"2025-12-11 14:23:55 UTC","transaction_id":"0","error_severity":"LOG","sql_state_code":"00000","message":"database system is ready to accept connections","backend_type":"postmaster","query_id":"0"}}
```

Les logs de l’instance sont récupérés au format JSON, et sont, en l’état, peu exploitables. Pour les lire plus facilement, vous pouvez utiliser l’outil jq.

**Exemple 2-115-e03.** Exemple ou sortie de commande extraite du PDF\, page 115\.

```bash
kubectl logs -f postgresql-demo-1 | jq
```

[...]

**Exemple 2-115-e04.** Exemple ou sortie de commande extraite du PDF\, page 115\.

```json
{
  "level": "info",
  "ts": "2025-09-16T11:58:11.525416928Z",
  "logger": "postgres",
  "msg": "record",
  "logging_pod": "postgresql-demo-1",
  "record": {
    "log_time": "2025-09-16 11:58:11.525 UTC",
    "process_id": "22",
    "session_id": "68c950d3.16",
    "session_line_num": "6",
    "session_start_time": "2025-09-16 11:58:11 UTC",
    "transaction_id": "0",
    "error_severity": "LOG",
    "sql_state_code": "00000",
    "message": "database system is ready to accept connections",
    "backend_type": "postmaster",
    "query_id": "0"
  }
}
```

### 2.22 ÉLÉMENTS INITIAUX {#cnpg1-2-22}

But: Découvrir les éléments automatiquement créés par l’opérateur.

Avec quelques lignes de YAML et peu de commandes, une instance PostgreSQL est déployée et accessible. De nombreuses choses sont créées automatiquement pour nous. Voyons de quoi il s’agit. Bases de données Retrouver la liste des bases de données dans l’instance déployée. La méta-commande \l de psql vous permet de récupérer la liste des bases.

**Exemple 2-116-e02.** Exemple ou sortie de commande extraite du PDF\, page 116\.

```bash
kubectl exec -it postgresql-demo-1 -- psql -c '\l'
```

ou

**Exemple 2-116-e03.** Exemple ou sortie de commande extraite du PDF\, page 116\.

```bash
kubectl cnpg psql postgresql-demo -- -c '\l'
```

**Exemple 2-116-e04.** Exemple ou sortie de commande extraite du PDF\, page 116\.

```text
Defaulted container "postgres" out of: postgres, bootstrap-controller (init)
                                                List of databases
   Name    | Owner    | Encoding | Locale Provider | Collate | Ctype | Locale | ICU Rules |   Access privileges
-----------+----------+----------+-----------------+---------+-------+--------+-----------+-----------------------
 app       | app      | UTF8     | libc            | C       | C     |        |           |
 postgres | postgres | UTF8      | libc            | C       | C     |        |           |
 template0 | postgres | UTF8     | libc            | C       | C     |        |           | =c/postgres           +
           |          |          |                 |         |       |        |           | postgres=CTc/postgres
 template1 | postgres | UTF8     | libc            | C       | C     |        |           | =c/postgres           +
           |          |          |                 |         |       |        |           | postgres=CTc/postgres
(4 rows)
```

Par défaut, une base de données app est créée dans l’instance PostgreSQL. Rôles et Secret

Retrouver la liste des rôles dans l’instance déployée. La méta-commande psql \du peut vous aider.

La méta-commande psql \du vous permet de récupérer la liste des rôles.

**Exemple 2-116-e05.** Exemple ou sortie de commande extraite du PDF\, page 116\.

```bash
kubectl exec -it postgresql-demo-1 -- psql -c "\du"
```

ou

**Exemple 2-116-e06.** Exemple ou sortie de commande extraite du PDF\, page 116\.

```bash
kubectl cnpg psql postgresql-demo -- -c '\du'
```

**Exemple 2-116-e07.** Exemple ou sortie de commande extraite du PDF\, page 116\.

```text
                                 List of roles
     Role name     |                         Attributes
-------------------+------------------------------------------------------------
 app               |
 postgres          | Superuser, Create role, Create DB, Replication, Bypass RLS
 streaming_replica | Replication
```

Par défaut deux rôles sont créés: app et streaming_replica. Dans la liste des bases de données, on peut d’ailleurs voir que le rôle app est propriétaire de la base app.

Se déconnecter de l’instance.

**Exemple 2-117-e01.** Exemple ou sortie de commande extraite du PDF\, page 117\.

```sql
postgres=# \q
```

Se connecter à la base de données app avec le rôle app. Une erreur devrait vous être retournée.

**Exemple 2-117-e02.** Exemple ou sortie de commande extraite du PDF\, page 117\.

```bash
kubectl exec -it postgresql-demo-1 -- psql -U app
```

ou

**Exemple 2-117-e03.** Exemple ou sortie de commande extraite du PDF\, page 117\.

```bash
kubectl cnpg psql postgresql-demo -- -U app
```

**Exemple 2-117-e04.** Exemple ou sortie de commande extraite du PDF\, page 117\.

```text
psql: error: connection to server on socket "/controller/run/.s.PGSQL.5432" failed: FATAL: Peer authentication failed for user "app"
command terminated with exit code 2
```

Se connecter à la base de données app avec le rôle app et en passant par la stack TCP/IP.

L’authentification du rôle app avec la méthode peer ne peut pas se faire. Mais alors comment se connecter avec app? En sachant que listen_addresses est positionné à * par défaut, une solution pour tester la connexion est de passer par la stack TCP/IP classique en utilisant l’option -h de

**Exemple 2-117-e05.** Exemple ou sortie de commande extraite du PDF\, page 117\.

```text
psql
```

**Exemple 2-117-e06.** Exemple ou sortie de commande extraite du PDF\, page 117\.

```bash
kubectl exec -it postgresql-demo-1 -- psql -U app -h 127.0.0.1
```

**Exemple 2-117-e07.** Exemple ou sortie de commande extraite du PDF\, page 117\.

```text
Defaulted container "postgres" out of: postgres, bootstrap-controller (init)
Password for user app:
```

Il faut comprendre que le 127.0.0.1 fait référence à l’adresse localhost du Pod. On demande à

**Exemple 2-117-e08.** Exemple ou sortie de commande extraite du PDF\, page 117\.

```text
psql , via kubectl , de se connecter sur l’interface localhost du Pod ... mais il nous faut le mot
```

de passe de app... où le trouver? CloudNativePG crée automatiquement un Secret qui contient des informations de connexion, notamment le mot de passe de app.

**Exemple 2-117-e09.** Exemple ou sortie de commande extraite du PDF\, page 117\.

```text
Récupérer la liste des Secrets du cluster avec kubectl get secrets .
```

**Exemple 2-117-e10.** Exemple ou sortie de commande extraite du PDF\, page 117\.

```bash
kubectl get secrets
```

**Exemple 2-117-e11.** Exemple ou sortie de commande extraite du PDF\, page 117\.

```text
NAME                                 TYPE                             DATA    AGE
postgresql-demo-app                  kubernetes.io/basic-auth         11      24m
postgresql-demo-ca                   Opaque                           2       24m
postgresql-demo-replication          kubernetes.io/tls                2       24m
postgresql-demo-server               kubernetes.io/tls                2       24m
```

**Exemple 2-117-e12.** Exemple ou sortie de commande extraite du PDF\, page 117\.

```text
Récupérer le mot de passe présent dans le Secret postgresql-demo-app . N’oubliez pas qu’il
```

est encodé en BASE64, il faut décoder le contenu obtenu.

**Exemple 2-117-e13.** Exemple ou sortie de commande extraite du PDF\, page 117\.

```bash
kubectl get secret postgresql-demo-app -o json | jq '.data.password'
```

**Exemple 2-118-e01.** Exemple ou sortie de commande extraite du PDF\, page 118\.

```text
"VFdyejRQbmY1RWMwVjFjUHlqYkdFZnI5RG52WE5YaXN0NUhIaFZkOENwSkpKOEthVkVLUkNxUGwweTRzaGlVbw=="
```

Ou bien sans jq, avec une commande kubectl un peu plus poussée:

**Exemple 2-118-e02.** Exemple ou sortie de commande extraite du PDF\, page 118\.

```bash
kubectl get secret postgresql-demo-app --no-headers -o custom-columns=Passwd:.data.password
VFdyejRQbmY1RWMwVjFjUHlqYkdFZnI5RG52WE5YaXN0NUhIaFZkOENwSkpKOEthVkVLUkNxUGwweTRzaGlVbw==
```

Le résultat est encodé en BASE64. Il faut donc le décoder avec la commande suivante:

**Exemple 2-118-e03.** Exemple ou sortie de commande extraite du PDF\, page 118\.

```bash
kubectl get secret postgresql-demo-app --no-headers -o custom-columns=Passwd:.data.password | base64 -d
TWrz4Pnf5Ec0V1cPyjbGEfr9DnvXNXist5HHhVd8CpJJJ8KaVEKRCqPl0y4shiUo
```

Se connecter à l’instance en utilisant le mot de passe.

**Exemple 2-118-e04.** Exemple ou sortie de commande extraite du PDF\, page 118\.

```bash
kubectl exec -it postgresql-demo-1 -- psql -U app -h 127.0.0.1
```

**Exemple 2-118-e05.** Exemple ou sortie de commande extraite du PDF\, page 118\.

```text
Defaulted container "postgres" out of: postgres, bootstrap-controller (init)
Password for user app:
psql (17.5 (Debian 17.5-1.pgdg120+1))
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off, ALPN: postgresql)
Type "help" for help.
```

**Exemple 2-118-e06.** Exemple ou sortie de commande extraite du PDF\, page 118\.

```text
app=>
```

L’astuce d’utiliser kubectl et psql avec l’option -h permet à des administrateurs de se connecter, mais cela n’est pas envisageable pour des applications. Les applications doivent passer par les objets Services.

Services Un Service est une couche d’abstraction qui permet d’accéder à un ensemble de Pods spécifiques. L’association Service - Pods se fait via des labels. Un label est une étiquette, un tag, apposée à une ressource. Retrouver la liste des Services dans le cluster Kubernetes.

Comme toutes les autres ressources Kubernetes, vous pouvez récupérer les objets Services avec get.

**Exemple 2-118-e07.** Exemple ou sortie de commande extraite du PDF\, page 118\.

```bash
kubectl get svc
```

**Exemple 2-118-e08.** Exemple ou sortie de commande extraite du PDF\, page 118\.

```text
NAME                     TYPE          CLUSTER-IP           EXTERNAL-IP      PORT(S)      AGE
kubernetes               ClusterIP     10.96.0.1            <none>           443/TCP      6h24m
postgresql-demo-r        ClusterIP     10.105.219.134       <none>           5432/TCP     6h11m
postgresql-demo-ro       ClusterIP     10.105.155.153       <none>           5432/TCP     6h11m
postgresql-demo-rw       ClusterIP     10.105.191.44        <none>           5432/TCP     6h11m
```

À chaque cluster PostgreSQL déployé, trois services sont créés:

- Un service qui permet d’accéder au primaire: postgresql-demo-rw qui est en lecture/écriture;

- Un service qui permet d’accéder uniquement au(x) secondaire(s): postgresql-demo-ro qui sont en lecture seule;
- Un service qui permet d’accéder à toutes les instances: postgresql-demo-r.

Retrouver les labels définis sur le Pod de votre instance:

**Exemple 2-119-e01.** Exemple ou sortie de commande extraite du PDF\, page 119\.

```bash
kubectl get pod postgresql-demo-1 --show-labels
```

**Exemple 2-119-e02.** Exemple ou sortie de commande extraite du PDF\, page 119\.

```text
NAME                READY   STATUS    RESTARTS   AGE   LABELS
postgresql-demo-1   1/1     Running   0          26h cnpg.io/cluster=postgresql-demo,cnpg.io/instanceName=postgresql-demo-1,cnpg.io/instanceRole=primary,cnpg.io/podRole=instance,role=primary
```

Retrouver la description du Service postgresql-demo-ro et retrouver la partie Selector qui indique à quel(s) Pod (s) sera associé ce Service.

**Exemple 2-119-e03.** Exemple ou sortie de commande extraite du PDF\, page 119\.

```bash
kubectl describe service postgresql-demo-ro
```

**Exemple 2-119-e04.** Exemple ou sortie de commande extraite du PDF\, page 119\.

```text
Name:                     postgresql-demo-ro
Namespace:                default
Labels:                   cnpg.io/cluster=postgresql-demo
Annotations:              cnpg.io/operatorVersion: 1.28.1
Selector:               cnpg.io/cluster=postgresql-demo,cnpg.io/instanceRole=replica
Type:                     ClusterIP
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.96.123.203
IPs:                      10.96.123.203
Port:                     postgres 5432/TCP
TargetPort:               5432/TCP
Endpoints:
Session Affinity:         None
Internal Traffic Policy: Cluster
Events:                   <none>
```

Lorsqu’une bascule a lieu, les labels des Pods sont mis à jour et l’association Service - Pod est automatiquement adaptée. De ce fait, si vos applications utilisent bien le nom du Service dans les informations de connexion, elles seront automatiquement redirigées vers la nouvelle instance primaire par exemple.

#### 2.22.1 Créer un rôle {#cnpg1-2-22-1}

Il existe plusieurs méthodes pour créer un rôle dans une instance. L’ordre SQL CREATE ROLE... peut évidemment être utilisé, mais pour cet exemple, nous allons passer par la méthode déclarative et demander à l’opérateur de faire en sorte que le rôle soit présent dans l’instance.

Créer un fichier ~/roles.yaml.

**Exemple 2-119-e05.** Exemple ou sortie de commande extraite du PDF\, page 119\.

```text
touch ~/roles.yaml
```

Créer un rôle dba ayant les droits SUPERUSER dans l’instance. Cela peut se faire grâce à la ressource DatabaseRole.

Par exemple, le fichier ~/roles.yaml ressemblerait à:

**Exemple 2-120-e02.** Exemple ou sortie de commande extraite du PDF\, page 120\.

```yaml
apiVersion: postgresql.cnpg.io/v1
kind: DatabaseRole
metadata:
  name: dba
spec:
  cluster:
    name: postgresql-demo
  name: dba
  comment: "Utilisateur Support"
  login: true
  superuser: true
  databaseRoleReclaimPolicy: delete
```

Appliquer la modification avec kubectl apply -f ~/roles.yaml.

**Exemple 2-120-e03.** Exemple ou sortie de commande extraite du PDF\, page 120\.

```bash
kubectl apply -f ~/roles.yaml
databaserole.postgresql.cnpg.io/dba created
```

Vérifier que le rôle a bien été créé.

**Exemple 2-120-e04.** Exemple ou sortie de commande extraite du PDF\, page 120\.

```bash
kubectl exec -it postgresql-demo-1 -- psql -c '\du'
```

**Exemple 2-120-e05.** Exemple ou sortie de commande extraite du PDF\, page 120\.

```text
Defaulted container "postgres" out of: postgres, bootstrap-controller (init)
                                   List of roles
       Role name       |                         Attributes
```

**Exemple 2-120-e06.** Exemple ou sortie de commande extraite du PDF\, page 120\.

```text
-----------------------+----------------------------------------
--------------------
 app                   |
 cnpg_metrics_exporter |
 dba                   | Superuser
 postgres              | Superuser, Create role, Create DB, Repl
ication, Bypass RLS
 streaming_replica     | Replication
```

Le rôle est bien créé, mais il n’a actuellement pas de mot de passe configuré. Si vous souhaitez en ajouter un, vous pouvez le faire de plusieurs manières:

- en exécutant la requête ALTER ROLE... PASSWORD...;
- en utilisant \password `<user>` (la préférer à ALTER ROLE...);
- en demandant à CloudNativePG de le faire. Cela nécessite la création d’un objet Secret.

C’est cette dernière méthode que nous allons suivre. Pour cela, le mot de passe n’est jamais passé en clair dans le fichier YAML. Il est en fait nécessaire de créer un objet Secret qui contiendra ce mot de passe encodé en BASE64 ainsi que le nom du rôle. C’est ce Secret qui sera utilisé dans le fichier YAML.

Encoder le nom du rôle ( dba) en BASE64.

**Exemple 2-121-e01.** Exemple ou sortie de commande extraite du PDF\, page 121\.

```text
printf "dba" | base64
ZGJh
```

Encoder le mot de passe ( ilovemydba) en BASE64.

Vous pouvez ajouter un espace avant printf pour que la commande n’apparaisse pas dans l’historique de l’utilisateur dalibo.

**Exemple 2-121-e02.** Exemple ou sortie de commande extraite du PDF\, page 121\.

```text
printf "ilovemydba" | base64
aWxvdmVteWRiYQ==
```

Créer un fichier ~/secret.yaml avec le contenu suivant puis créer le Secret.

**Exemple 2-121-e03.** Exemple ou sortie de commande extraite du PDF\, page 121\.

```yaml
apiVersion: v1
data:
  username: ZGJh
  password: aWxvdmVteWRiYQ==
kind: Secret
metadata:
  name: secret-password-dba
  labels:
    cnpg.io/reload: "true"
type: kubernetes.io/basic-auth
```

**Exemple 2-121-e04.** Exemple ou sortie de commande extraite du PDF\, page 121\.

```bash
kubectl apply -f ~/secret.yaml
```

**Exemple 2-121-e05.** Exemple ou sortie de commande extraite du PDF\, page 121\.

```text
secret/secret-password-dba created
```

Ajouter ce mot de passe à la définition du rôle dba dans le fichier ~/roles.yaml, dans le champ

**Exemple 2-121-e06.** Exemple ou sortie de commande extraite du PDF\, page 121\.

```text
passwordSecret .
```

**Exemple 2-121-e07.** Exemple ou sortie de commande extraite du PDF\, page 121\.

```yaml
apiVersion: postgresql.cnpg.io/v1
kind: DatabaseRole
metadata:
  name: dba
spec:
  cluster:
    name: postgresql-demo
  name: dba
  comment: "Utilisateur Support"
  login: true
  superuser: true
  databaseRoleReclaimPolicy: delete
  passwordSecret:
    name: secret-password-dba
```

Appliquer les modifications.

**Exemple 2-121-e08.** Exemple ou sortie de commande extraite du PDF\, page 121\.

```bash
kubectl apply -f ~/roles.yaml
```

**Exemple 2-121-e09.** Exemple ou sortie de commande extraite du PDF\, page 121\.

```text
databaserole.postgresql.cnpg.io/dba configured
```

Se connecter avec ce nouveau rôle à la base postgres.

Le rôle dba peut désormais se connecter avec son super mot de passe. Par exemple:

**Exemple 2-122-e01.** Exemple ou sortie de commande extraite du PDF\, page 122\.

```bash
kubectl exec -it postgresql-demo-1 -c postgres -- psql -d postgres -U dba -h 127.0.0.1
```

**Exemple 2-122-e02.** Exemple ou sortie de commande extraite du PDF\, page 122\.

```text
Defaulted container "postgres" out of: postgres, bootstrap-controller (init)
Password for user dba:
psql (17.0 (Debian 17.0-1.pgdg110+1))
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off, ALPN: postgresql)
Type "help" for help.
```

**Exemple 2-122-e03.** Exemple ou sortie de commande extraite du PDF\, page 122\.

```sql
postgres=#
```

#### 2.22.2 Créer une base de données {#cnpg1-2-22-2}

Créer une base de données db1 dans l’instance postgresql-demo. Le propriétaire de cette base doit être le rôle app. Pour cela, créer un fichier db1.yaml contenant la définition d’une ressource Database.

Voici la déclaration d’une telle base de données.

**Exemple 2-122-e04.** Exemple ou sortie de commande extraite du PDF\, page 122\.

```yaml
apiVersion: postgresql.cnpg.io/v1
kind: Database
metadata:
  name: db1
spec:
  name: db1
  owner: app
  cluster:
    name: postgresql-demo
```

Créer la nouvelle ressource avec kubectl apply -f ~/db1.yaml.

**Exemple 2-122-e05.** Exemple ou sortie de commande extraite du PDF\, page 122\.

```bash
kubectl apply -f db1.yaml
database.postgresql.cnpg.io/db1 created
```

Vérifier la présence de cette base de données dans l’instance.

Plusieurs possibilités. En voici une:

**Exemple 2-122-e06.** Exemple ou sortie de commande extraite du PDF\, page 122\.

```bash
kubectl cnpg psql postgresql-demo -- -c "\l"
```

**Exemple 2-122-e07.** Exemple ou sortie de commande extraite du PDF\, page 122\.

```text
                                                List of databases
   Name    | Owner    | Encoding | Locale Provider | Collate | Ctype | Locale | ICU Rules |   Access privileges
-----------+----------+----------+-----------------+---------+-------+--------+-----------+-----------------------
 app       | app      | UTF8     | libc            | C       | C     |        |           |
 db1       | app      | UTF8     | libc            | C       | C     |        |           |
 postgres | postgres | UTF8      | libc            | C       | C     |        |           |
 template0 | postgres | UTF8     | libc            | C       | C     |        |           | =c/postgres           +
           |          |          |                 |         |       |        |           | postgres=CTc/postgres
 template1 | postgres | UTF8     | libc            | C       | C     |        |           | =c/postgres           +
           |          |          |                 |         |       |        |           | postgres=CTc/postgres
(5 rows)
```

### 2.23 DÉPLOIEMENT D’UNE INSTANCE SECONDAIRE {#cnpg1-2-23}

But: Déployer une instance secondaire dans le cluster postgresql-demo.

Notre instance actuellement déployée ne possède pas de secondaire. L’ajout de secondaire se fait facilement en modifiant le paramètre instances dans la section spec de notre fichier YAML.

Déployer un secondaire à votre instance en modifiant le paramètre instances à 2.

[...]

**Exemple 2-123-e01.** Exemple ou sortie de commande extraite du PDF\, page 123\.

```yaml
spec:
  imageName: ghcr.io/cloudnative-pg/postgresql:17.5-standard-bookworm
  instances: 2
```

[...]

**Exemple 2-123-e02.** Exemple ou sortie de commande extraite du PDF\, page 123\.

```bash
kubectl apply -f ~/postgresql-demo.yaml
```

Un second Pod va être déployé.

**Exemple 2-123-e03.** Exemple ou sortie de commande extraite du PDF\, page 123\.

```bash
kubectl get pod | grep demo
postgresql-demo-1                     1/1       Running       0             73m
postgresql-demo-2-join-h2vvw          0/1       Init:0/1      0             38s
```

**Exemple 2-123-e04.** Exemple ou sortie de commande extraite du PDF\, page 123\.

```bash
kubectl get pod | grep demo
```

**Exemple 2-123-e05.** Exemple ou sortie de commande extraite du PDF\, page 123\.

```text
postgresql-demo-1                     1/1       Running                0             73m
postgresql-demo-2-join-h2vvw          0/1       PodInitializing        0             58s
```

**Exemple 2-123-e06.** Exemple ou sortie de commande extraite du PDF\, page 123\.

```bash
kubectl get pod | grep demo
```

**Exemple 2-123-e07.** Exemple ou sortie de commande extraite du PDF\, page 123\.

```text
postgresql-demo-1                     1/1       Running        0             73m
postgresql-demo-2                     1/1       Running        0             31s
```

Et voilà, un secondaire a été créé! L’opérateur CloudNativePG s’assure de tout configurer: ajout du paramètre primary_conninfo, création du fichier standby.signal, mise à jour de pg_hba.conf, slot de réplication, etc. Le secondaire se connecte alors au primaire en utilisant la réplication physique native de PostgreSQL (Streaming Replication).

#### 2.23.1 Configuration par défaut {#cnpg1-2-23-1}

Regardons la configuration qui est mise en place par défaut.

Se connecter avec psql au secondaire nouvellement créé.

**Exemple 2-123-e08.** Exemple ou sortie de commande extraite du PDF\, page 123\.

```bash
kubectl exec -it postgresql-demo-2 -c postgres -- psql
```

Récupérer le contenu du paramètre primary_conninfo.

**Exemple 2-124-e01.** Exemple ou sortie de commande extraite du PDF\, page 124\.

```sql
postgres=# \x
Expanded display is on.
```

**Exemple 2-124-e02.** Exemple ou sortie de commande extraite du PDF\, page 124\.

```sql
postgres=# SHOW primary_conninfo ;
-[ RECORD 1 ]----+---------------------
primary_conninfo | host=postgresql-demo-rw user=streaming_replica [...]
```

La sortie a été mise en forme pour plus de lisibilité.

**Exemple 2-124-e03.** Exemple ou sortie de commande extraite du PDF\, page 124\.

```text
host=postgresql-demo-rw
user=streaming_replica
port=5432
sslkey=/controller/certificates/streaming_replica.key
sslcert=/controller/certificates/streaming_replica.crt
sslrootcert=/controller/certificates/server-ca.crt
application_name=postgresql-demo-2
sslmode=verify-ca
```

Le secondaire utilise le Service postgresql-demo-rw pour accéder à l’instance primaire. Vous comprendrez qu’une résolution DNS interne au cluster Kubernetes doit se faire pour retrouver l’adresse IP associée. La réplication utilise l’utilisateur dédié streaming_replica créé par CloudNativePG lors du déploiement de la première instance. L’authentification se fait par certificat. Le paramètre application_name permet d’indiquer un nom d’application dans les informations liées à la connexion. Se connecter avec psql au primaire.

**Exemple 2-124-e04.** Exemple ou sortie de commande extraite du PDF\, page 124\.

```bash
kubectl exec -it postgresql-demo-1 -c postgres -- psql
```

ou, via le plugin:

**Exemple 2-124-e05.** Exemple ou sortie de commande extraite du PDF\, page 124\.

```bash
kubectl cnpg psql postgresql-demo
```

Récupérer le contenu de la vue pg_stat_replication.

**Exemple 2-124-e06.** Exemple ou sortie de commande extraite du PDF\, page 124\.

```sql
postgres=# SELECT * FROM pg_stat_replication\gx
-[ RECORD 1 ]----+------------------------------
pid              | 16026
usesysid         | 16386
usename          | streaming_replica
application_name | postgresql-demo-2
client_addr      | 10.244.1.11
client_hostname |
client_port      | 54458
backend_start    | 2025-12-12 07:13:15.829844+00
backend_xmin     |
state            | streaming
sent_lsn         | 0/B000130
write_lsn        | 0/B000130
flush_lsn        | 0/B000130
replay_lsn       | 0/B000130
```

**Exemple 2-125-e01.** Exemple ou sortie de commande extraite du PDF\, page 125\.

```text
write_lag             | 00:00:00.000806
flush_lag             | 00:00:00.013548
replay_lag            | 00:00:00.014192
sync_priority         | 0
sync_state            | async
reply_time            | 2025-12-12 07:17:57.892685+00
```

Par défaut, c’est une réplication asynchrone qui est créée.

Récupérer le contenu de la vue pg_replication_slots.

**Exemple 2-125-e02.** Exemple ou sortie de commande extraite du PDF\, page 125\.

```sql
postgres=# SELECT * FROM pg_replication_slots \gx
-[ RECORD 1 ]-------+------------------------
slot_name           | _cnpg_postgresql_demo_2
plugin              |
slot_type           | physical
datoid              |
database            |
temporary           | f
active              | t
active_pid          | 16026
xmin                |
catalog_xmin        |
restart_lsn         | 0/C000060
confirmed_flush_lsn |
wal_status          | reserved
safe_wal_size       |
two_phase           | f
inactive_since      |
conflicting         |
invalidation_reason |
failover            | f
synced              | f
```

Par défaut, CloudNativePG crée automatiquement un slot de réplication pour sécuriser la réplication. Son nom permet de savoir facilement à quoi il correspond. Le slot de réplication garantit au secondaire que son primaire ne recyclera pas les journaux de transactions dont il aura encore besoin. Le secondaire peut donc prendre un retard conséquent sans risque de décrochage. Attention à l’accumulation des journaux de transactions qu’il peut y avoir sur le primaire en cas de retard ou de problème (coupure réseau, crash secondaire, etc.).

Sur le primaire, créer une table dans la base app.

Sur le primaire:

**Exemple 2-125-e03.** Exemple ou sortie de commande extraite du PDF\, page 125\.

```sql
postgres=# \c app
app=# CREATE TABLE ma_table (i int);
CREATE TABLE
```

Vérifier qu’elle se trouve également sur le secondaire.

Sur le secondaire:

**Exemple 2-126-e01.** Exemple ou sortie de commande extraite du PDF\, page 126\.

```sql
postgres=# \c app
You are now connected to database "app" as user "postgres".
app=# \dt
          List of relations
 Schema |   Name   | Type | Owner
--------+----------+-------+----------
 public | ma_table | table | postgres
(1 row)
```

La réplication fonctionne!

#### 2.23.2 Emplacement des instances {#cnpg1-2-23-2}

L’opérateur CloudNativePG veille à déployer les instances PostgreSQL sur des nœuds différents afin de garantir la disponibilité. Cela permet de réduire les risques liés à un incident en s’assurant que toutes les instances ne sont pas affectées simultanément. Cette configuration permet également la répartition de la charge entre plusieurs nœuds pour des opérations de lecture.

Trouver le nom du nœud où est déployée chaque instance.

**Exemple 2-126-e02.** Exemple ou sortie de commande extraite du PDF\, page 126\.

```bash
kubectl get pod -o wide
```

**Exemple 2-126-e03.** Exemple ou sortie de commande extraite du PDF\, page 126\.

```text
NAME                READY   STATUS    RESTARTS   AGE     IP            NODE           NOMINATED NODE   READINESS GATES
postgresql-demo-1   1/1     Running   0          16h     10.244.2.11   kind-worker2   <none>           <none>
postgresql-demo-2   1/1     Running   0          8m34s   10.244.1.11   kind-worker    <none>           <none>
```

Cette répartition est possible grâce notamment à la configuration de l’affinité et de l’anti-affinité entre Pod ajoutée au YAML du Cluster.

**Exemple 2-126-e04.** Exemple ou sortie de commande extraite du PDF\, page 126\.

```yaml
affinity:
  enablePodAntiAffinity: true
  topologyKey: kubernetes.io/hostname
  podAntiAffinityType: preferred
```

### 2.24 EXERCICES OPTIONNELS {#cnpg1-2-24}

But: Découvrir des fonctionnalités plus complexes.

#### 2.24.1 Mettre en place une réplication synchrone {#cnpg1-2-24-1}

Il existe plusieurs méthodes pour mettre en place une réplication synchrone. Le but ici n’est pas de les évoquer ni de les comparer, mais simplement de voir le principe de la configuration. Pour l’exemple, nous mettrons en place la méthode par Quorum.

Modifier la configuration de l’instance en rajoutant le bloc suivant à votre fichier ~/postgresql-restored-demo.ya

**Exemple 2-127-e01.** Exemple ou sortie de commande extraite du PDF\, page 127\.

```yaml
  postgresql:
    synchronous:
      method: any
      number: 1
```

**Exemple 2-127-e02.** Exemple ou sortie de commande extraite du PDF\, page 127\.

```bash
kubectl apply -f postgresql.yaml
```

**Exemple 2-127-e03.** Exemple ou sortie de commande extraite du PDF\, page 127\.

```text
cluster.postgresql.cnpg.io/postgresql-demo configured
```

Vérifier que la réplication est synchrone en regardant le champ sync_state de la vue

**Exemple 2-127-e04.** Exemple ou sortie de commande extraite du PDF\, page 127\.

```bash
pg_stat_replication de l’instance primaire.
```

**Exemple 2-127-e05.** Exemple ou sortie de commande extraite du PDF\, page 127\.

```sql
postgres=# select * from pg_stat_replication\gx
-[ RECORD 1 ]----+------------------------------
pid              | 1210
usesysid         | 16386
usename          | streaming_replica
application_name | postgresql-restored-demo-1
client_addr      | 10.244.3.29
client_hostname |
client_port      | 51518
backend_start    | 2025-12-15 18:29:04.335147+00
backend_xmin     |
state            | streaming
sent_lsn         | 0/D000000
write_lsn        | 0/D000000
flush_lsn        | 0/D000000
replay_lsn       | 0/D000000
write_lag        |
flush_lag        |
replay_lag       |
sync_priority    | 1
sync_state       | quorum
reply_time       | 2025-12-15 18:52:30.849357+00
```

sync_state est bien à quorum.

Plus d’informations sur la documentation.

#### 2.24.2 Déploiement d’une application pgAdmin4 {#cnpg1-2-24-2}

Pour voir comment une application peut se connecter à une instance, nous allons déployer pgAdmin dans le cluster Kubernetes. Ouvrir une nouvelle session SSH. Passer en tant qu’utilisateur dalibo.

Créer le fichier ~/pgadmin.yaml avec le contenu suivant et le déployer dans le cluster Kubernetes.

**Exemple 2-128-e01.** Exemple ou sortie de commande extraite du PDF\, page 128\.

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pgadmin
spec:
  replicas: 1
  selector:
    matchLabels:
      app: pgadmin
  template:
    metadata:
      labels:
        app: pgadmin
    spec:
      containers:
        - name: pgadmin
          image: dpage/pgadmin4
          ports:
            - containerPort: 80
          env:
            - name: PGADMIN_DEFAULT_EMAIL
              value: admin@example.com
            - name: PGADMIN_DEFAULT_PASSWORD
              value: admin
```

**Exemple 2-128-e02.** Exemple ou sortie de commande extraite du PDF\, page 128\.

```bash
kubectl apply -f ~/pgadmin.yaml
```

**Exemple 2-128-e03.** Exemple ou sortie de commande extraite du PDF\, page 128\.

```text
deployment.apps/pgadmin created
```

Récupérer le nom du Pod pgAdmin déployé, et lancer la commande suivante:

**Exemple 2-128-e04.** Exemple ou sortie de commande extraite du PDF\, page 128\.

```bash
kubectl port-forward --address 0.0.0.0 pgadmin-*****-***** 8888:80
```

Cette commande permet de forwarder le trafic entrant sur le port TCP 8888 de la machine vers le port 80 du Pod pgAdmin, rendant ainsi accessible l’application. Cette méthode reste valide pour des démonstrations, n’allez pas mettre ça en production:).

Sources du PDF (page 128) :

- [https://cloudnative-pg.io/docs/1.30/replication#synchronous-replication](<https://cloudnative-pg.io/docs/1.30/replication#synchronous-replication>)

**Exemple 2-129-e01.** Exemple ou sortie de commande extraite du PDF\, page 129\.

```text
  Accéder à l’interface de pgAdmin via votre navigateur http://adresseippublique:8888 et se
```

connecter à l’interface (admin@example.com / admin).

L’adresse IP publique de la machine peut être retrouvée avec la commande suivante:

**Exemple 2-129-e02.** Exemple ou sortie de commande extraite du PDF\, page 129\.

```text
ip -f inet addr show ens2
2: ens2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    altname enp0s2
    inet 51.158.67.253/32 metric 100 scope global dynamic ens2
       valid_lft 842sec preferred_lft 842sec
```

**Figure 2-129-f01.** Figure 2\-129\-f01 \- Écran de connexion de pgAdmin4 déployé dans Kubernetes\.

![Écran de connexion de pgAdmin4 déployé dans Kubernetes\.](/images/cnpg1/manual/p129-f01.png)

Créer une nouvelle connexion avec les informations suivantes:

**Figure 2-129-f02.** Figure 2\-129\-f02 \- Menu de connexion de pgAdmin4\.

![Menu de connexion de pgAdmin4\.](/images/cnpg1/manual/p129-f02.png)

- Name: postgresql-demo (Onglet General);
- Host name/address: postgresql-demo-rw (Onglet Connection);
- Port: 5432;
- Username: app;
- Password: celui récupéré dans le Secret;
- Cliquer sur Save.

Créer une nouvelle connexion avec cette fois-ci postgresql-demo-ro comme paramètre

**Exemple 2-130-e01.** Exemple ou sortie de commande extraite du PDF\, page 130\.

```text
Host name/address et créer une table CREATE TABLE ma_table (i int); .
```

Cette commande ne pourra pas être exécutée comme le Service renvoie sur une instance secondaire qui est nécessairement en lecture seule. Le message d’erreur sera:

**Exemple 2-130-e02.** Exemple ou sortie de commande extraite du PDF\, page 130\.

```sql
CREATE TABLE ma_table (i int);
```

**Exemple 2-130-e03.** Exemple ou sortie de commande extraite du PDF\, page 130\.

```text
ERROR:     cannot execute CREATE TABLE in a read-only transaction
```

**Exemple 2-130-e04.** Exemple ou sortie de commande extraite du PDF\, page 130\.

```text
SQL state: 25006
```

Vous avez maintenant l’application pgAdmin déployée dans Kubernetes qui a accès à l’instance postgresql-demo. L’exemple ci-dessus montre comment une application peut accéder à une base de données en utilisant la ressource Service prévue à cet effet. Application et base se trouvent toutes deux dans Kubernetes. Accéder à l’instance PostgreSQL depuis une application externe (déployée ailleurs) est plus complexe, demande le déploiement d’autres ressources... mais ne sera pas traité ici.

### 2.25 QUIZ {#cnpg1-2-25}

Sources du PDF (page 131) :

- [https://dali.bo/k1_quiz](<https://dali.bo/k1_quiz>)
