🔝 Retour au Sommaire
Dans le chapitre précédent, nous avons vu que les StatefulSets fournissent les briques de base pour exécuter PostgreSQL sur Kubernetes : identité stable, persistance des données et ordre de déploiement. Cependant, ils ne gèrent pas la logique métier spécifique à PostgreSQL.
C'est là qu'interviennent les Operators. Un Operator est un composant qui automatise la gestion complète d'une application sur Kubernetes, en encapsulant l'expertise d'un administrateur de base de données dans du code.
Dans ce chapitre, nous explorerons trois Operators PostgreSQL majeurs : Zalando Postgres Operator, CloudNativePG et Crunchy Postgres Operator. Vous comprendrez leurs forces, leurs différences et comment choisir celui qui convient à votre projet.
Un Operator est un logiciel qui étend Kubernetes pour gérer des applications complexes de manière automatisée. Il encode les connaissances d'un expert humain dans un contrôleur logiciel.
┌─────────────────────────────────────────────────────────────────────┐
│ LE CONCEPT D'OPERATOR │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ SANS OPERATOR (Gestion manuelle) │
│ ──────────────────────────────── │
│ │
│ Administrateur humain doit : │
│ • Configurer la réplication │
│ • Surveiller la santé du cluster │
│ • Gérer les failovers manuellement │
│ • Planifier et exécuter les backups │
│ • Gérer les mises à jour │
│ • Réagir aux pannes (souvent la nuit !) │
│ │
│ │
│ AVEC OPERATOR (Gestion automatisée) │
│ ─────────────────────────────────── │
│ │
│ L'Operator fait tout automatiquement : │
│ • Configure la réplication ✓ │
│ • Surveille en continu ✓ │
│ • Failover automatique en secondes ✓ │
│ • Backups planifiés ✓ │
│ • Rolling updates ✓ │
│ • Self-healing 24/7 ✓ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Un Operator fonctionne selon un cycle de réconciliation continu :
BOUCLE DE RÉCONCILIATION
────────────────────────
┌─────────────────────────────────────────────────────┐
│ │
│ 1. OBSERVER │
│ L'Operator surveille l'état actuel │
│ du cluster PostgreSQL │
│ │
└─────────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ │
│ 2. COMPARER │
│ État actuel vs État désiré │
│ (défini dans le Custom Resource) │
│ │
└─────────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ │
│ 3. AGIR │
│ Si différence détectée : │
│ → Appliquer les changements nécessaires │
│ → Créer/modifier/supprimer des ressources │
│ │
└─────────────────────┬───────────────────────────────┘
│
└──────────► Retour à l'étape 1
(boucle continue)
Les Operators introduisent de nouveaux types de ressources dans Kubernetes :
# Exemple de Custom Resource pour PostgreSQL
apiVersion: postgresql.example.com/v1
kind: PostgresCluster # ← Nouveau type de ressource !
metadata:
name: my-postgres
spec:
instances: 3
storage: 100Gi
version: "16"
backup:
schedule: "0 2 * * *" # Backup à 2h du matinAu lieu de gérer manuellement des StatefulSets, Services, ConfigMaps, etc., vous déclarez simplement ce que vous voulez et l'Operator s'occupe du comment.
SANS OPERATOR AVEC OPERATOR
───────────── ─────────────
Vous gérez : Vous gérez :
• 1 StatefulSet • 1 Custom Resource
• 1 Service Headless (PostgresCluster)
• 1 Service ClusterIP
• 1 ConfigMap L'Operator gère :
• 1 Secret • Tout le reste
• N PersistentVolumeClaims automatiquement !
• Scripts de réplication
• Scripts de backup
• Monitoring config
• ...
~500 lignes de YAML ~30 lignes de YAML
Exécuter PostgreSQL en production sur Kubernetes implique de nombreux défis :
| Défi | Complexité sans Operator |
|---|---|
| Réplication streaming | Configuration manuelle de primary/replica |
| Failover | Détection de panne + promotion manuelle |
| Backups | Scripts cron + stockage + rétention |
| Restauration | Procédure manuelle, risque d'erreur |
| Mises à jour | Rolling update délicat |
| Scaling | Ajout de replicas avec réplication |
| Monitoring | Configuration Prometheus/exporters |
| TLS | Génération et rotation des certificats |
┌─────────────────────────────────────────────────────────────────────┐
│ FONCTIONNALITÉS D'UN OPERATOR POSTGRESQL │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ HAUTE DISPONIBILITÉ │ │ BACKUPS │ │
│ │ │ │ │ │
│ │ • Réplication auto │ │ • Backups planifiés │ │
│ │ • Failover auto │ │ • Stockage S3/GCS │ │
│ │ • Leader election │ │ • Rétention auto │ │
│ │ • Health checks │ │ • PITR │ │
│ └──────────────────────┘ └──────────────────────┘ │
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ SÉCURITÉ │ │ OBSERVABILITÉ │ │
│ │ │ │ │ │
│ │ • TLS automatique │ │ • Métriques Prom. │ │
│ │ • Rotation secrets │ │ • Logs centralisés │ │
│ │ • RBAC intégré │ │ • Alertes │ │
│ │ • Network policies │ │ • Dashboards │ │
│ └──────────────────────┘ └──────────────────────┘ │
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ CYCLE DE VIE │ │ SCALING │ │
│ │ │ │ │ │
│ │ • Provisioning │ │ • Horizontal (repl.) │ │
│ │ • Updates │ │ • Vertical (ressour.)│ │
│ │ • Minor upgrades │ │ • Connection pooling │ │
│ │ • Major upgrades │ │ • Read replicas │ │
│ └──────────────────────┘ └──────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Le Zalando Postgres Operator a été développé par Zalando, le géant européen du e-commerce. En production chez Zalando depuis 2017, il gère des milliers de clusters PostgreSQL.
┌─────────────────────────────────────────────────────────────────────┐
│ ZALANDO POSTGRES OPERATOR │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Créateur : Zalando SE (Allemagne) │
│ Depuis : 2017 │
│ Licence : MIT │
│ GitHub : github.com/zalando/postgres-operator │
│ Maturité : Production-ready, très utilisé │
│ │
│ Points forts : │
│ • Simplicité de configuration │
│ • Intégration AWS native (S3, IAM) │
│ • Connection pooling intégré (PgBouncer) │
│ • Interface Web (UI) optionnelle │
│ • Large communauté │
│ │
└─────────────────────────────────────────────────────────────────────┘
ARCHITECTURE ZALANDO OPERATOR
─────────────────────────────
┌───────────────────────────────────────────────────────────────────┐
│ KUBERNETES CLUSTER │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ POSTGRES OPERATOR (Deployment) │ │
│ │ │ │
│ │ Surveille les ressources "postgresql" │ │
│ │ Crée et gère les clusters PostgreSQL │ │
│ └─────────────────────────┬────────────────────────────────┘ │
│ │ │
│ │ Gère │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ CLUSTER POSTGRESQL "my-db" │ │
│ │ │ │
│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │
│ │ │my-db-0 │ │my-db-1 │ │my-db-2 │ │ │
│ │ │(Primary) │ │(Replica) │ │(Replica) │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │┌─────────┐│ │┌─────────┐│ │┌─────────┐│ │ │
│ │ ││Postgres ││ ││Postgres ││ ││Postgres ││ │ │
│ │ │└─────────┘│ │└─────────┘│ │└─────────┘│ │ │
│ │ │┌─────────┐│ │┌─────────┐│ │┌─────────┐│ │ │
│ │ ││ Patroni ││ ││ Patroni ││ ││ Patroni ││ │ │
│ │ │└─────────┘│ │└─────────┘│ │└─────────┘│ │ │
│ │ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ │ │
│ │ │ │ │ │ │
│ │ └──────────────┼──────────────┘ │ │
│ │ ▼ │ │
│ │ ┌─────────────────┐ │ │
│ │ │ Spilo │ Image Docker contenant │ │
│ │ │ (PostgreSQL + │ PostgreSQL + Patroni │ │
│ │ │ Patroni + ...) │ + outils │ │
│ │ └─────────────────┘ │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────┘
| Composant | Rôle |
|---|---|
| Postgres Operator | Contrôleur qui gère le cycle de vie des clusters |
| Spilo | Image Docker contenant PostgreSQL + Patroni + outils |
| Patroni | Gestion du failover et de la haute disponibilité |
| PgBouncer | Connection pooling (optionnel) |
# Exemple de cluster Zalando
apiVersion: "acid.zalan.do/v1"
kind: postgresql
metadata:
name: my-postgres-cluster
namespace: default
spec:
teamId: "myteam" # Préfixe pour les ressources
# Configuration du cluster
numberOfInstances: 3 # 1 primary + 2 replicas
# Version PostgreSQL
postgresql:
version: "16"
parameters:
max_connections: "100"
shared_buffers: "256MB"
# Ressources
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "1"
memory: "2Gi"
# Stockage
volume:
size: 50Gi
storageClass: fast-ssd
# Utilisateurs et bases
users:
myapp_user:
- superuser
- createdb
readonly_user: []
databases:
myapp_db: myapp_user # base: owner
# Connection pooling
enableConnectionPooler: true
connectionPooler:
numberOfInstances: 2
mode: "transaction"
# Backups vers S3
backup:
enableWALBackup: trueZalando utilise Patroni pour la haute disponibilité :
FAILOVER AUTOMATIQUE AVEC PATRONI
─────────────────────────────────
État normal :
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ my-db-0 │ │ my-db-1 │ │ my-db-2 │
│ (Primary) │────►│ (Replica) │ │ (Replica) │
│ │────►│ │ │ │
└─────────────┘ └─────────────┘ └─────────────┘
│ ▲ ▲
└───────────────────┴───────────────────┘
Réplication streaming
Le Primary tombe :
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ my-db-0 │ │ my-db-1 │ │ my-db-2 │
│ ╳ DOWN │ │ (Replica) │ │ (Replica) │
└─────────────┘ └─────────────┘ └─────────────┘
│
│ Patroni détecte (~10s)
│ Élection du nouveau leader
▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ my-db-0 │ │ my-db-1 │ │ my-db-2 │
│ (à recréer)│◄────│ (NOUVEAU │────►│ (Replica) │
│ │ │ PRIMARY) │ │ │
└─────────────┘ └─────────────┘ └─────────────┘
Temps de failover : ~10-30 secondes
# Activer PgBouncer
spec:
enableConnectionPooler: true
connectionPooler:
numberOfInstances: 2
mode: "transaction" # ou "session"
maxDBConnections: 100L'Operator crée automatiquement :
- Des Pods PgBouncer
- Un Service pour accéder au pooler
- La configuration appropriée
spec:
users:
admin_user:
- superuser
- createdb
app_user:
- login
readonly_user:
- login
databases:
production_db: admin_user
analytics_db: admin_userLes mots de passe sont automatiquement générés et stockés dans des Secrets Kubernetes.
# Configuration dans l'Operator (ConfigMap)
configuration:
aws_region: eu-west-1
wal_s3_bucket: my-postgres-backups
# Ou dans le cluster
spec:
backup:
enableWALBackup: true# Cloner le dépôt
git clone https://github.com/zalando/postgres-operator.git
cd postgres-operator
# Installer avec kubectl
kubectl create -f manifests/configmap.yaml
kubectl create -f manifests/operator-service-account-rbac.yaml
kubectl create -f manifests/postgres-operator.yaml
# Ou avec Helm
helm repo add postgres-operator-charts \
https://opensource.zalando.com/postgres-operator/charts/postgres-operator
helm install postgres-operator postgres-operator-charts/postgres-operatorCloudNativePG (anciennement CloudNative-PG) est un Operator développé initialement par EDB (EnterpriseDB) puis donné à la CNCF (Cloud Native Computing Foundation). Il est conçu nativement pour Kubernetes, sans dépendances externes.
┌─────────────────────────────────────────────────────────────────────┐
│ CLOUDNATIVEPG │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Créateur : EDB, maintenant projet CNCF │
│ Depuis : 2020 │
│ Licence : Apache 2.0 │
│ GitHub : github.com/cloudnative-pg/cloudnative-pg │
│ Maturité : Production-ready, en forte croissance │
│ │
│ Points forts : │
│ • Architecture Kubernetes-native pure │
│ • Pas de dépendances externes (pas de Patroni/etcd) │
│ • Excellente documentation │
│ • Support officiel disponible (EDB) │
│ • Projet CNCF (gouvernance neutre) │
│ │
└─────────────────────────────────────────────────────────────────────┘
CloudNativePG se distingue par son approche Kubernetes-native : il utilise les mécanismes natifs de Kubernetes (leases, endpoints) plutôt que des outils externes.
ARCHITECTURE CLOUDNATIVEPG
──────────────────────────
┌──────────────────────────────────────────────────────────────────┐
│ KUBERNETES CLUSTER │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ CLOUDNATIVEPG OPERATOR (Deployment) │ │
│ │ │ │
│ │ • Surveille les ressources "Cluster" │ │
│ │ • Gère le failover via Kubernetes Leases │ │
│ │ • Pas de dépendance externe (Patroni, etcd) │ │
│ └─────────────────────────┬───────────────────────────────┘ │
│ │ │
│ │ Gère │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ CLUSTER "my-cluster" │ │
│ │ │ │
│ │ ┌───────────────┐ │ │
│ │ │my-cluster-1 │ ← Primary │ │
│ │ │ │ │ │
│ │ │ ┌───────────┐ │ │ │
│ │ │ │ PostgreSQL│ │ │ │
│ │ │ │ 16 │ │ │ │
│ │ │ └───────────┘ │ │ │
│ │ │ ┌───────────┐ │ │ │
│ │ │ │ Instance │ │ Gestionnaire intégré │ │
│ │ │ │ Manager │ │ (pas de Patroni) │ │
│ │ │ └───────────┘ │ │ │
│ │ └───────┬───────┘ │ │
│ │ │ Réplication │ │
│ │ ▼ │ │
│ │ ┌───────────────┐ ┌───────────────┐ │ │
│ │ │my-cluster-2 │ │my-cluster-3 │ │ │
│ │ │ (Replica) │ │ (Replica) │ │ │
│ │ └───────────────┘ └───────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────┘
# Exemple de cluster CloudNativePG
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: my-cluster
namespace: default
spec:
# Nombre d'instances (1 primary + N-1 replicas)
instances: 3
# Version PostgreSQL
imageName: ghcr.io/cloudnative-pg/postgresql:16.2
# Configuration PostgreSQL
postgresql:
parameters:
max_connections: "100"
shared_buffers: "256MB"
effective_cache_size: "1GB"
pg_hba:
- host all all 10.0.0.0/8 scram-sha-256
# Stockage
storage:
size: 50Gi
storageClass: fast-ssd
# Ressources
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "1"
# Bootstrap (initialisation)
bootstrap:
initdb:
database: myapp
owner: myapp_user
secret:
name: myapp-credentials
# Backups
backup:
barmanObjectStore:
destinationPath: s3://my-bucket/backups
s3Credentials:
accessKeyId:
name: s3-creds
key: ACCESS_KEY_ID
secretAccessKey:
name: s3-creds
key: SECRET_ACCESS_KEY
retentionPolicy: "7d"
# Monitoring
monitoring:
enablePodMonitor: trueCloudNativePG utilise les Leases Kubernetes pour l'élection du leader :
FAILOVER AVEC LEASES KUBERNETES
───────────────────────────────
┌─────────────────────┐
│ Kubernetes Lease │
│ "my-cluster-leader"│
│ │
│ holder: cluster-1 │
└──────────┬──────────┘
│
┌─────────────────────┼─────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌───────────────┐ ┌───────────────┐
│ cluster-1 │ │ cluster-2 │ │ cluster-3 │
│ (Primary) │ │ (Replica) │ │ (Replica) │
│ │ │ │ │ │
│ "J'ai la │ │ "Je surveille │ │ "Je surveille │
│ Lease !" │ │ la Lease" │ │ la Lease" │
└─────────────┘ └───────────────┘ └───────────────┘
Si cluster-1 tombe :
1. La Lease expire
2. cluster-2 ou cluster-3 l'acquiert
3. Le nouveau holder devient Primary
Pas besoin de Patroni, etcd ou autre composant externe !
CloudNativePG utilise Barman pour les backups :
spec:
backup:
barmanObjectStore:
destinationPath: s3://my-bucket/backups
s3Credentials:
accessKeyId:
name: s3-creds
key: ACCESS_KEY_ID
secretAccessKey:
name: s3-creds
key: SECRET_ACCESS_KEY
wal:
compression: gzip
retentionPolicy: "7d"Créer un backup à la demande :
apiVersion: postgresql.cnpg.io/v1
kind: Backup
metadata:
name: my-backup
spec:
cluster:
name: my-clusterRestaurer à un moment précis :
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: restored-cluster
spec:
instances: 3
bootstrap:
recovery:
source: my-cluster
recoveryTarget:
targetTime: "2024-01-15 10:30:00"
externalClusters:
- name: my-cluster
barmanObjectStore:
destinationPath: s3://my-bucket/backups
s3Credentials:
accessKeyId:
name: s3-creds
key: ACCESS_KEY_ID
secretAccessKey:
name: s3-creds
key: SECRET_ACCESS_KEYCréer un cluster replica dans une autre région :
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: dr-cluster
namespace: default
spec:
instances: 3
# Ce cluster réplique depuis un autre cluster
replica:
enabled: true
source: main-cluster
externalClusters:
- name: main-cluster
connectionParameters:
host: main-cluster-rw.primary-region.svc
user: streaming_replica
password:
name: replica-credentials
key: passwordCloudNativePG expose des métriques Prometheus automatiquement :
spec:
monitoring:
enablePodMonitor: true # Crée un PodMonitor pour Prometheus
customQueriesConfigMap:
- name: custom-queries
key: queries# Installation avec kubectl
kubectl apply -f \
https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.22/releases/cnpg-1.22.0.yaml
# Ou avec Helm
helm repo add cnpg https://cloudnative-pg.github.io/charts
helm upgrade --install cnpg \
--namespace cnpg-system \
--create-namespace \
cnpg/cloudnative-pgLe Crunchy Postgres Operator (aussi appelé PGO) est développé par Crunchy Data, un des principaux contributeurs à PostgreSQL. C'est l'un des Operators les plus complets et les plus matures.
┌─────────────────────────────────────────────────────────────────────┐
│ CRUNCHY POSTGRES OPERATOR (PGO) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Créateur : Crunchy Data │
│ Depuis : 2017 │
│ Licence : Apache 2.0 │
│ GitHub : github.com/CrunchyData/postgres-operator │
│ Maturité : Production-ready, très mature │
│ │
│ Points forts : │
│ • Fonctionnalités très complètes │
│ • pgBackRest pour backups (robuste) │
│ • Support commercial disponible │
│ • Excellent pour les environnements enterprise │
│ • Intégration avec l'écosystème Crunchy │
│ │
└─────────────────────────────────────────────────────────────────────┘
ARCHITECTURE CRUNCHY PGO
────────────────────────
┌──────────────────────────────────────────────────────────────────┐
│ KUBERNETES CLUSTER │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ PGO OPERATOR │ │
│ │ │ │
│ │ Surveille les ressources "PostgresCluster" │ │
│ └─────────────────────────┬───────────────────────────────┘ │
│ │ │
│ │ Gère │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ POSTGRESCLUSTER "hippo" │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ INSTANCE SET "instance1" │ │ │
│ │ │ │ │ │
│ │ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │ │
│ │ │ │ hippo- │ │ hippo- │ │ hippo- │ │ │ │
│ │ │ │ instance1 │ │ instance1 │ │ instance1 │ │ │ │
│ │ │ │ -xxxx │ │ -yyyy │ │ -zzzz │ │ │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ │ │┌──────────┐│ │┌──────────┐│ │┌──────────┐│ │ │ │
│ │ │ ││PostgreSQL││ ││PostgreSQL││ ││PostgreSQL││ │ │ │
│ │ │ │└──────────┘│ │└──────────┘│ │└──────────┘│ │ │ │
│ │ │ │┌──────────┐│ │┌──────────┐│ │┌──────────┐│ │ │ │
│ │ │ ││ Patroni ││ ││ Patroni ││ ││ Patroni ││ │ │ │
│ │ │ │└──────────┘│ │└──────────┘│ │└──────────┘│ │ │ │
│ │ │ └────────────┘ └────────────┘ └────────────┘ │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ PgBouncer │ │ pgBackRest │ │ pg_exporter │ │ │
│ │ │ (Pooler) │ │ (Backup) │ │ (Metrics) │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────┘
# Exemple de cluster Crunchy PGO
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PostgresCluster
metadata:
name: hippo
spec:
# Version PostgreSQL
postgresVersion: 16
# Instances PostgreSQL
instances:
- name: instance1
replicas: 3
dataVolumeClaimSpec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 50Gi
storageClassName: fast-ssd
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "1"
memory: "2Gi"
# Configuration PostgreSQL
patroni:
dynamicConfiguration:
postgresql:
parameters:
max_connections: "100"
shared_buffers: "256MB"
# Utilisateurs
users:
- name: hippo
databases:
- hippo
options: "SUPERUSER"
- name: app_user
databases:
- hippo
# Backups avec pgBackRest
backups:
pgbackrest:
image: registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.47-2
repos:
- name: repo1
schedules:
full: "0 1 * * 0" # Full backup dimanche 1h
differential: "0 1 * * 1-6" # Diff backup autres jours
volume:
volumeClaimSpec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Gi
- name: repo2
s3:
bucket: "my-postgres-backups"
endpoint: "s3.amazonaws.com"
region: "us-east-1"
# Connection pooling
proxy:
pgBouncer:
replicas: 2
config:
global:
pool_mode: transaction
# Monitoring
monitoring:
pgmonitor:
exporter:
image: registry.developers.crunchydata.com/crunchydata/crunchy-postgres-exporter:ubi8-5.5.0-0Crunchy utilise pgBackRest, l'outil de backup le plus robuste pour PostgreSQL :
TYPES DE BACKUPS pgBackRest
───────────────────────────
┌─────────────────────────────────────────────────────────────────┐
│ │
│ FULL BACKUP (Dimanche) │
│ ═══════════════════════ │
│ Copie complète de la base │
│ ~50 Go │
│ │
│ DIFFERENTIAL BACKUP (Lundi-Samedi) │
│ ═════════════════════════════════ │
│ Seulement les changements depuis le dernier FULL │
│ ~5-10 Go │
│ │
│ INCREMENTAL BACKUP (Optionnel, plus fréquent) │
│ ═════════════════════════════════════════════ │
│ Seulement les changements depuis le dernier backup (any) │
│ ~1-2 Go │
│ │
│ + WAL Archiving en continu │
│ Permet PITR à n'importe quelle seconde │
│ │
└─────────────────────────────────────────────────────────────────┘
Configuration multi-repo (local + S3) :
backups:
pgbackrest:
repos:
- name: repo1
volume: # Repo local (rapide)
volumeClaimSpec:
storage: 100Gi
- name: repo2
s3: # Repo S3 (DR)
bucket: "pg-backups"
endpoint: "s3.amazonaws.com"
region: "us-east-1"Cloner un cluster existant :
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PostgresCluster
metadata:
name: hippo-clone
spec:
postgresVersion: 16
dataSource:
postgresCluster:
clusterName: hippo
repoName: repo1
instances:
- name: instance1
replicas: 3
dataVolumeClaimSpec:
storage: 50GiRestauration PITR :
dataSource:
postgresCluster:
clusterName: hippo
repoName: repo1
options:
- --type=time
- --target="2024-01-15 10:30:00"spec:
proxy:
pgBouncer:
replicas: 2
config:
global:
pool_mode: transaction
max_client_conn: "1000"
default_pool_size: "20"
resources:
requests:
cpu: "100m"
memory: "128Mi"Contrôle fin du placement des Pods :
instances:
- name: instance1
replicas: 3
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
topologyKey: "topology.kubernetes.io/zone"
labelSelector:
matchLabels:
postgres-operator.crunchydata.com/cluster: hippoPGO génère et gère automatiquement les certificats TLS :
spec:
customTLSSecret:
name: hippo-tls # Optionnel : utiliser vos propres certificats
# Sinon, certificats auto-générés# Avec kubectl (méthode recommandée)
kubectl apply -k \
github.com/CrunchyData/postgres-operator-examples/kustomize/install/default
# Ou avec Helm
helm repo add crunchydata https://crunchydata.github.io/postgres-operator
helm install pgo crunchydata/pgo | Aspect | Zalando | CloudNativePG | Crunchy PGO |
|---|---|---|---|
| Licence | MIT | Apache 2.0 | Apache 2.0 |
| Maturité | Très mature (2017) | Mature (2020) | Très mature (2017) |
| HA Solution | Patroni | Kubernetes-native | Patroni |
| Backup Tool | WAL-E/WAL-G | Barman | pgBackRest |
| Connection Pooler | PgBouncer intégré | Externe (PgBouncer) | PgBouncer intégré |
| Dépendances externes | Patroni, S3 | Aucune | Patroni |
| Complexité config | Simple | Moyenne | Avancée |
| Support commercial | Non (Zalando interne) | Oui (EDB) | Oui (Crunchy) |
| Projet CNCF | Non | Oui (Sandbox) | Non |
| Documentation | Bonne | Excellente | Excellente |
| Communauté | Grande | En croissance | Grande |
┌─────────────────────────────────────────────────────────────────────┐
│ ZALANDO OPERATOR │
├─────────────────────────────────────────────────────────────────────┤
│ ✅ Forces │ ⚠️ Faiblesses │
│ ───────────────────────────── │ ───────────────────────────── │
│ • Configuration simple │ • Moins flexible │
│ • UI web disponible │ • Pas de support commercial │
│ • PgBouncer natif │ • Moins de features backup │
│ • Battle-tested chez Zalando │ • Dépend de Patroni/etcd │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ CLOUDNATIVEPG │
├─────────────────────────────────────────────────────────────────────┤
│ ✅ Forces │ ⚠️ Faiblesses │
│ ───────────────────────────── │ ───────────────────────────── │
│ • Kubernetes-native pur │ • Plus récent │
│ • Pas de dépendances externes │ • Moins de plugins/intégrations│
│ • Documentation excellente │ • Courbe d'apprentissage │
│ • Projet CNCF │ • PgBouncer externe │
│ • Architecture élégante │ │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ CRUNCHY PGO │
├─────────────────────────────────────────────────────────────────────┤
│ ✅ Forces │ ⚠️ Faiblesses │
│ ───────────────────────────── │ ───────────────────────────── │
│ • Fonctionnalités complètes │ • Complexité de config │
│ • pgBackRest (best backup) │ • Images plus lourdes │
│ • Support commercial │ • Courbe d'apprentissage │
│ • Multi-repo backup │ • Verbosité YAML │
│ • Entreprise-ready │ │
└─────────────────────────────────────────────────────────────────────┘
QUEL OPERATOR CHOISIR ?
───────────────────────
Avez-vous besoin d'un support commercial ?
│
├── OUI ─────────────────────────────────────────────┐
│ │
│ Préférence pour l'écosystème ? │
│ │ │
│ ├── EDB / PostgreSQL officiel ──► CloudNativePG │
│ │ │
│ └── Crunchy Data ──────────────► Crunchy PGO │
│ │
└── NON │
│ │
Complexité de vos besoins ? │
│ │
├── Simple (petit/moyen projet) │
│ │ │
│ └── Zalando Operator │
│ (facile à démarrer) │
│ │
├── Moyen (features avancées, K8s-native) │
│ │ │
│ └── CloudNativePG │
│ (pas de dépendances) │
│ │
└── Avancé (enterprise, DR, multi-région) │
│ │
└── Crunchy PGO │
(le plus complet) │
Pour mieux comprendre les différences, voici comment créer un cluster PostgreSQL similaire avec chaque Operator.
- 3 instances (1 primary + 2 replicas)
- 50 Gi de stockage
- PostgreSQL 16
- Backups vers S3
apiVersion: "acid.zalan.do/v1"
kind: postgresql
metadata:
name: my-cluster
spec:
teamId: "myteam"
numberOfInstances: 3
postgresql:
version: "16"
volume:
size: 50Gi
enableConnectionPooler: trueLignes de YAML : ~15
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: my-cluster
spec:
instances: 3
imageName: ghcr.io/cloudnative-pg/postgresql:16.2
storage:
size: 50Gi
backup:
barmanObjectStore:
destinationPath: s3://my-bucket/backups
s3Credentials:
accessKeyId:
name: s3-creds
key: ACCESS_KEY_ID
secretAccessKey:
name: s3-creds
key: SECRET_ACCESS_KEYLignes de YAML : ~20
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PostgresCluster
metadata:
name: my-cluster
spec:
postgresVersion: 16
instances:
- name: instance1
replicas: 3
dataVolumeClaimSpec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 50Gi
backups:
pgbackrest:
repos:
- name: repo1
s3:
bucket: "my-bucket"
endpoint: "s3.amazonaws.com"
region: "us-east-1"
proxy:
pgBouncer:
replicas: 2Lignes de YAML : ~30
# Quel que soit l'Operator, configurez les backups DÈS LE DÉBUT
backup:
retentionPolicy: "7d" # Ou selon vos besoins
schedule: "0 2 * * *" # Quotidien à 2hresources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "2"
memory: "4Gi"- Minimum 3 instances pour la HA
- Utiliser des anti-affinity rules pour distribuer sur plusieurs nodes/zones
# Exposer les métriques pour Prometheus
monitoring:
enabled: true- Utiliser TLS pour les connexions
- Stocker les mots de passe dans des Secrets
- Configurer les network policies
Testez régulièrement :
- La restauration de backup
- Le failover automatique
- Le PITR
Si vous souhaitez changer d'Operator, voici la stratégie générale :
MIGRATION ENTRE OPERATORS
─────────────────────────
1. BACKUP
Effectuer un backup complet avec pg_dump ou backup natif
2. DÉPLOYER
Installer le nouvel Operator
Créer un nouveau cluster vide
3. RESTAURER
Restaurer les données dans le nouveau cluster
(pg_restore ou restauration native)
4. TESTER
Vérifier l'intégrité des données
Tester les applications
5. BASCULER
Mettre à jour les connexions applicatives
6. NETTOYER
Supprimer l'ancien cluster
Désinstaller l'ancien Operator
Les trois Operators présentés dans ce chapitre sont tous production-ready et largement utilisés. Le choix dépend de vos besoins spécifiques :
| Choisissez | Si vous... |
|---|---|
| Zalando | Voulez démarrer rapidement avec une solution simple et éprouvée |
| CloudNativePG | Préférez une architecture Kubernetes-native sans dépendances |
| Crunchy PGO | Avez besoin de fonctionnalités enterprise avancées |
Dans tous les cas, l'utilisation d'un Operator est fortement recommandée pour exécuter PostgreSQL en production sur Kubernetes. Les StatefulSets seuls ne suffisent pas pour gérer la complexité d'un SGBD en production.
Les Operators transforment des heures d'administration manuelle en quelques lignes de YAML déclaratif, tout en assurant une haute disponibilité et des backups automatisés.
- Zalando : Getting Started Guide
- CloudNativePG : Quickstart
- Crunchy : Tutorial avec exemples
- GitHub Issues de chaque projet
- Slack Kubernetes (#postgresql)
- Mailing lists PostgreSQL
- kubectl plugins pour PostgreSQL
- Lens (IDE Kubernetes)
- k9s (terminal UI)