But : Découvrir ce qui est automatiquement créé.
Avec quelques lignes de yaml et 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 meta-commande \l
de psql vous permet de récupérer la
liste des bases.
kubectl exec -it postgresql-demo-1 -- psql -c "\l"
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 meta-commande \du
de psql vous permet de récupérer la
liste des rôles.
kubectl cnpg psql postgresql-demo -- -c '\du'
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 connecter à la base de données app
avec le rôle
app
.
kubectl exec -it postgresql-demo-1 -- psql -U app
Defaulted container "postgres" out of: postgres, bootstrap-controller ( init )
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
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
postionné à *
par défaut, une solution pour tester la
connexion est de passer par la pile TCP/IP classique en utilisant
l’option -h
de psql
.
kubectl exec -it postgresql-demo-1 -- psql -U app -h 127.0.0.1
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 à 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
.
Récupérer la liste des Secrets
du cluster.
NAME TYPE DATA AGE
postgresql-demo-app kubernetes.io/basic-auth 9 39m
postgresql-demo-ca Opaque 2 39m
postgresql-demo-replication kubernetes.io/tls 2 39m
postgresql-demo-server kubernetes.io/tls 2 39m
Récupérer le mot de passe présent dans le Secret
postgresql-demo-app.
kubectl get secret postgresql-demo-app -o json | jq '.data.password'
"VFdyejRQbmY1RWMwVjFjUHlqYkdFZnI5RG52WE5YaXN0NUhIaFZkOENwSkpKOEthVkVLUkNxUGwweTRzaGlVbw=="
Ou bien sans jq
, avec une commande kubectl
un peu plus poussée :
$ 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 l’une
des commandes suivantes :
$ echo "VFdyejRQbmY1RWMwVjFjUHlqYkdFZnI5RG52WE5YaXN0NUhIaFZkOENwSkpKOEthVkVLUkNxUGwweTRzaGlVbw==" | base64 -d
TWrz4Pnf5Ec0V1cPyjbGEfr9DnvXNXist5HHhVd8CpJJJ8KaVEKRCqPl0y4shiUo
$ kubectl get secret postgresql-demo-app --no-headers -o custom-columns=Passwd:.data.password | base64 -d
TWrz4Pnf5Ec0V1cPyjbGEfr9DnvXNXist5HHhVd8CpJJJ8KaVEKRCqPl0y4shiUo
Se connecter à l’instance avec l’utilisateur app
.
kubectl exec -it postgresql-demo-1 -- psql -U app -h 127.0.0.1
Defaulted container "postgres" out of: postgres, bootstrap-controller ( init )
Password for user app:
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.
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
.
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 :
kubectl get pod postgresql-demo-1 --show-labels
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 avec quel(s) Pod
(s) sera
associé ce Service
.
kubectl describe service postgresql-demo-ro
Name: postgresql-demo-ro
Namespace: default
Labels: cnpg.io/cluster=postgresql-demo
Annotations: cnpg.io/operatorVersion: 1.24.0
Selector: cnpg.io/cluster=postgresql-demo,cnpg.io/instanceRole=replica
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.105.155.153
IPs: 10.105.155.153
Port: postgres 5432/TCP
TargetPort: 5432/TCP
Endpoints: 10.244.112.199:5432
Session Affinity: None
Internal Traffic Policy: Cluster
Events: < none>
Lorsqu’une bascule a lieu, les labels des Pods
sont mis à jour et l’assocation 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.
pg_hba
Le fichier pg_hba.conf
est un passage obligatoire pour
la bonne configuration de votre instance. Le modifier directement n’est
plus possible. Voyons tout de même ce qu’il contient par défaut et
comment configurer l’accès à l’instance via l’opérateur.
Retrouver le contenu du fichier pg_hba.conf
de
l’instance.
Voici quelques méthodes possibles :
Avec cat
depuis le Pod
:
kubectl exec -it postgresql-demo-1 -- cat /var/lib/postgresql/data/pgdata/pg_hba.conf
;
Avec psql
et la table pg_hba_file_rules
:
kubectl exec -it postgresql-demo-1 \ -- psql -c 'select type, database, user_name, address, netmask, auth_method, options from pg_hba_file_rules ORDER BY rule_number'
Avec la documentation
Il existe trois sections dans ce fichier :
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;
DEFAULT RULES
: qui autorise par défaut toutes les
connexions par mots de passe à toutes les bases de données.
But : Créer, configurer et manipuler notre
instance.
Modifications de
paramètres de configuration
Installer une instance PostgreSQL ne suffit pas. Il faut en plus la
configurer. Habituellement, 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é. Nous allons voir
comment le faire sur notre instance postgresql-demo-1
.
Dupliquer le fichier ~/postgresql-demo.yaml
pour
conserver une copie de la définition initiale de l’instance.
cp ~/postgresql-demo.yaml ~/postgresql-demo.bckp
Modifier le fichier ~/postgresql-demo.yaml
et ajouter la
section postgresql.parameters
comme dans l’exemple. Nous
allons tout d’abord modifier les paramètres shared_buffers
et max_connection
.
apiVersion : postgresql.cnpg.io/v1
kind : Cluster
metadata :
name : postgresql-demo
spec :
imageName : ghcr.io/cloudnative-pg/postgresql:17.0
instances : 1
storage :
size : 5Gi
postgresql :
parameters :
shared_buffers : 256MB
max_connections : '10'
resources :
requests :
memory : "256Mi"
cpu : "0.5"
limits :
memory : "512Mi"
cpu : "1"
Suivre les traces du Pod
et de l’instance avec
kubectl logs -f postgresql-demo-1 | jq
:
Utiliser kubectl apply -f ~/postgresql-demo.yaml
appliquer les modifications.
Dans les traces du Pod
, certains messages indiquent très
clairement ce qu’il va se passer. Les champs msg
et
message
sont les plus intéressants. Par exemple, celui-ci
qui indique qu’un rechargement de la configuration est nécessaire.
{
"level" : "info" ,
"ts" : "2024-11-19T10:16:20.260220994Z" ,
"msg" : "Requesting configuration reload" ,
"logger" : "instance-manager" ,
"logging_pod" : "postgresql-demo-1" ,
"controller" : "instance-cluster" ,
"controllerGroup" : "postgresql.cnpg.io" ,
"controllerKind" : "Cluster" ,
"Cluster" : {
"name" : "postgresql-demo" ,
"namespace" : "default"
},
"namespace" : "default" ,
"name" : "postgresql-demo" ,
"reconcileID" : "ee16f0eb-c352-41e9-a955-0587219b4d7b" ,
"pgdata" : "/var/lib/postgresql/data/pgdata"
}
Ou encore celui-ci, quelques lignes plus loin, qui indique que le
paramètre modifié implique qu’un redémarrage de l’instance est
nécessaire.
{
"level" : "info" ,
"ts" : "2024-11-19T10:16:20.269390325Z" ,
"logger" : "postgres" ,
"msg" : "record" ,
"logging_pod" : "postgresql-demo-1" ,
"record" : {
"log_time" : "2024-11-19 10:16:20.263 UTC" ,
"process_id" : "21" ,
"session_id" : "673c655c.15" ,
"session_line_num" : "9" ,
"session_start_time" : "2024-11-19 10:15:56 UTC" ,
"transaction_id" : "0" ,
"error_severity" : "LOG" ,
"sql_state_code" : "55P02" ,
"message" : "parameter \" shared_buffers \" cannot be changed without restarting the server" ,
"backend_type" : "postmaster" ,
"query_id" : "0"
}
}
À la toute fin, votre instance est de nouveau accessible comme
l’indique la ligne JSON :
{
"level" : "info" ,
"ts" : "2024-11-19T10:16:20.811302094Z" ,
"logger" : "postgres" ,
"msg" : "record" ,
"logging_pod" : "postgresql-demo-1" ,
"record" : {
"log_time" : "2024-11-19 10:16:20.811 UTC" ,
"process_id" : "204" ,
"session_id" : "673c6574.cc" ,
"session_line_num" : "6" ,
"session_start_time" : "2024-11-19 10:16:20 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"
}
}
Il faut donc comprendre que dès qu’une modification est apportée à la
configuration, par défaut, l’opérateur CloudNativePG va faire en sorte
de la prendre immédiatement en compte. Un rechargement de la
configuration sera effectué si le paramètre ne nécessite pas le
rédémarrage de l’instance. Si un redémarrage est effectué, alors les
connexions seront coupées et devront être refaites à l’instance
PostgreSQL par les applications. Vous devez donc bien savoir ce que vous
devez faire. Des précautions sont donc plus que nécessaires.
Modifier le paramètre work_mem
et réappliquer la
définition YAML avec
kubectl apply -f ~/postgresql-demo.yaml
.
[ ... ]
postgresql :
parameters :
shared_buffers : 256MB
max_connections : '10'
work_mem : '8MB'
[ ... ]
Le paramètre work_mem
ne nécessite pas un redémarrage de
l’instance. Voici un exemple de trace obtenue lors de la modification de
ce paramètre.
{
"level" : "info" ,
"ts" : "2024-11-29T06:02:03Z" ,
"logger" : "postgres" ,
"msg" : "record" ,
"logging_pod" : "postgresql-demo-1" ,
"record" : {
"log_time" : "2024-11-29 06:02:03.388 UTC" ,
"process_id" : "936" ,
"session_id" : "67495814.3a8" ,
"session_line_num" : "7" ,
"session_start_time" : "2024-11-29 05:58:44 UTC" ,
"transaction_id" : "0" ,
"error_severity" : "LOG" ,
"sql_state_code" : "00000" ,
"message" : "received SIGHUP, reloading configuration files" ,
"backend_type" : "postmaster" ,
"query_id" : "0"
}
}
La ligne message
indique que seul un rechargement de la
configuration a été nécessaire.
Vérifier que la modification a bien été prise en compte. Vous pouvez
le voir dans les traces ou alors directement en vous connectant à
l’instance et en utilisant show work_mem
dans le prompt
psql
;
Dans les traces, regarder le champ message
.
{
"level" : "info" ,
"ts" : "2024-11-29T06:02:03Z" ,
"logger" : "postgres" ,
"msg" : "record" ,
"logging_pod" : "postgresql-demo-1" ,
"record" : {
"log_time" : "2024-11-29 06:02:03.390 UTC" ,
"process_id" : "936" ,
"session_id" : "67495814.3a8" ,
"session_line_num" : "8" ,
"session_start_time" : "2024-11-29 05:58:44 UTC" ,
"transaction_id" : "0" ,
"error_severity" : "LOG" ,
"sql_state_code" : "00000" ,
"message" : "parameter \" work_mem \" changed to \" 8MB \" " ,
"backend_type" : "postmaster" ,
"query_id" : "0"
}
}
En lignes de commande :
kubectl exec -it postgresql-demo-1 -- psql
ou, via le plugin :
kubectl cnpg psql postgresql-demo
Defaulted container "postgres" out of: postgres, bootstrap-controller ( init )
psql ( 17.0 ( Debian 17.0-1.pgdg110+1))
Type "help" for help.
postgres = # show work_mem ;
work_mem
----------
8MB
( 1 row)
Certains paramètres PostgreSQL ne sont pas modifiables. C’est le
parti pris des développeurs de CloudNativePG. La liste se trouve dans la
documentation du projet (ici ).
Créer un rôle
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 rôle dba
ayant les droits
SUPERUSER
dans l’instance.
L’ajout d’un rôle se fait avec la section managed
du
fichier yaml. Par exemple dans notre fichier
~/postgresql-demo.yaml
, cela donnerait :
[ ... ]
spec :
[ ... ]
managed :
roles :
- name : dba
ensure : present
comment : Administrateur
login : true
superuser : true
Appliquer la modification avec
kubectl apply -f ~/postgresql-demo.yaml
.
Vérifier que le rôle a bien été créé.
kubectl exec -it postgresql-demo-1 -- psql -c '\du'
Defaulted container "postgres" out of: postgres, bootstrap-controller ( init )
List of roles
Role name | Attributes
-------------------+------------------------------------------------------------
app |
dba | Superuser
postgres | Superuser, Create role, Create DB, Replication, 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 ... SET 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
là qui sera utilisé dans le fichier yaml.
Encoder le nom du rôle en base64.
printf "dba" | base64
ZGJh
Encoder le mot de passe en base64.
Vous pouvez ajouter un espace avant echo
pour que la
commande n’apparaisse pas dans l’historique de l’utilisateur
dalibo
.
printf "ilovemydba" | base64
aWxvdmVteWRiYQ = =
Créer un fichier ~/secret.yaml
avec le contenu suivant
puis créer le Secret
.
apiVersion : v1
data :
username : ZGJh
password : aWxvdmVteWRiYQ==
kind : Secret
metadata :
name : secret-password-dba
labels :
cnpg.io/reload : "true"
type : kubernetes.io/basic-auth
kubectl apply -f ~/secret.yaml
secret/secret-password-dba created
Ajouter ce mot de passe à la définition du rôle dba
dans
le fichier ~/postgresql-demo.yaml
, via l’information
passwordSecret
.
[ ... ]
managed :
roles :
- name : dba
ensure : present
comment : Administrateur
login : true
superuser : true
passwordSecret :
name : secret-password-dba
Appliquer les modifications.
kubectl apply -f ~/postgresql-demo.yaml
cluster.postgresql.cnpg.io/postgresql-demo configured
Le rôle dba
peut désormais se connecter avec son super
mot de passe. Par exemple :
kubectl exec -it postgresql-demo-1 -- psql -d postgres -U dba -h 127.0.0.1
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.
postgres = #
Créer une base de données
Au moment de l’écriture du workshop la version 1.25
n’était pas encore disponible …
Cette version permet désormais, via la nouvelle CRD
Database
, de manipuler et gérer des bases de données de
manière déclarative avec l’opérateur.
apiVersion : postgresql.cnpg.io/v1
kind : Database
metadata :
name : db1
spec :
name : db1
owner : app
cluster :
name : cluster-dalidemo
Pour les besoins du TP, nous resteront en version
1.24.0.
Jusqu’en 1.24.0, il n’était pas possible de créer une base de données
dans une instance de manière déclarative via le fichier yaml.
Il est toujours possible de créer des bases de données
supplémentaires plus tard en utilisant l’ordre SQL
CREATE DATABASE
.
CNPG offre tout de même deux possibilités supplémentaires
intéressantes. Voici quelques explications à titre indicatif (pas besoin
de les faire dans le cadre du workshop ) :
Vous pouvez modifier la base de données créée automatiquement
(par défaut app
). Pour cela, il faut modifier sa définition
dans la section bootstrap
avant de créer le cluster. Par
exemple :
[ ... ]
bootstrap :
initdb :
database : app
owner : app
secret :
name : app-secret
[ ... ]
Si vous souhaitez conserver la base app
et en créer
une supplémentaire, il existe le paramètre postInitSQL
dans
la section initdb
qui permet d’exécuter des ordres SQL dès
la fin de création de l’instance. Vous pouvez alors indiquer un ordre
SQL CREATE DATABASE ...
. Les requêtes indiquées à ce niveau
seront exécutées uniquement et une seule
fois après la création de l’instance. Autrement dit, si la
modification est apportée a une instance déjà créée, rien ne se
passera.
[ ... ]
spec :
[ ... ]
bootstrap :
initdb :
postInitSQL :
- create database my_database