PostgreSQL Haute disponibilité avec Crunchy Data PGO : Déploiement Enterprise Kubernetes
Enterprise PostgreSQL HA avec Crunchy Data PGO sur Kubernetes
L'exécution de PostgreSQL en production sur Kubernetes nécessite plus qu'un StatefulSet avec un volume persistant. Vous avez besoin d'un basculement automatisé, d'une sauvegarde continue et d'une récupération ponctuelle, d'un regroupement de connexions, du cryptage TLS, d'une surveillance et de la possibilité d'un déploiement cohérent entre les fournisseurs de cloud et le bare metal. Crunchy Data PGO (Postgres Operator) v5 offre tout cela via une seule ressource personnalisée native Kubernetes — lePostgresClusterCRD — soutenue par des composants testés au combat : Patroni pour la haute disponibilité basée sur le consensus, pgBackRest pour la sauvegarde d'entreprise, PgBouncer pour le pooling de connexions et pgMonitor pour l'observabilité compatible Prometheus.
Ce guide couvre tout ce qui est nécessaire pour faire passer un cluster PostgreSQL géré par PGO du déploiement initial au fonctionnement en production sur AWS EKS, Azure AKS, GCP GKE et bare metal k3s avec Rancher. Chaque section comprend des valeurs concrètes YAML, Helm et des procédures opérationnelles que vous pouvez adapter à votre environnement.
Données croustillantes Architecture PGO v5
PGO v5 est une réécriture complète de l'opérateur Crunchy Postgres. Il remplace les précédents pgcluster/pgreplica/pgpolicy CRD par une seule ressourcePostgresClusterqui décrit de manière déclarative tous les aspects d'un déploiement PostgreSQL. L'opérateur surveille les modifications apportées à cette ressource et rapproche les objets Kubernetes sous-jacents (StatefulSets, Services, ConfigMaps, Secrets, Jobs) pour qu'ils correspondent à l'état souhaité.
L'architecture est construite sur quatre piliers.Patronifonctionne comme un side-car dans chaque pod PostgreSQL et gère l'élection du leader, la topologie de réplication et le basculement automatique à l'aide du consensus distribué natif Kubernetes.pgBackRestgère les sauvegardes complètes, différentielles et incrémentielles ainsi que l'archivage WAL continu vers le stockage objet (S3, GCS, Azure Blob) ou les PVC locaux.PgBouncerfournit un pool de connexions léger qui protège le PostgreSQL des tempêtes de connexion.pgMonitorexpose les métriques PostgreSQL via un side-car d'exportateur Prometheus pour l'intégration avec votre pile d'observabilité existante.
L'opérateur lui-même est sans état : tous les états persistants résident dans le cluster PostgreSQL et son référentiel de sauvegarde. Cela signifie que vous pouvez mettre à niveau ou redémarrer l'opérateur sans affecter les bases de données en cours d'exécution. La boucle de réconciliation des opérateurs est idempotente : appliquer plusieurs fois la même spécificationPostgresClusterproduit le même ensemble d'objets Kubernetes.
Installation de PGO v5
PGO v5 peut être installé via Helm ou des manifestes kubectl directs. L'approche Helm est préférée pour la production car elle s'intègre parfaitement aux flux de travail GitOps et fournit des mises à niveau contrôlées par les versions.
# Add the Crunchy Data Helm repository
helm repo add crunchydata https://charts.crunchydata.com
helm repo update
# Install PGO into its own namespace
helm install pgo crunchydata/pgo \
--namespace postgres-operator \
--create-namespace \
--set controllerImages.cluster=registry.crunchydata.com/crunchydata/postgres-operator:ubi8-5.7.2-0 \
--set relatedImages.postgres_16=registry.crunchydata.com/crunchydata/crunchy-postgres:ubi8-16.6-1 \
--set relatedImages.pgbackrest=registry.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.54.1-0 \
--set relatedImages.pgbouncer=registry.crunchydata.com/crunchydata/crunchy-pgbouncer:ubi8-1.23.1-0 \
--set relatedImages.pgexporter=registry.crunchydata.com/crunchydata/crunchy-postgres-exporter:ubi8-0.16.0-0Pour les environnements isolés, mettez en miroir les images requises dans votre registre interne et mettez à jour les valeursrelatedImagesen conséquence. PGO n'extrairea que les images spécifiées dans les valeurs Helm ou la spécificationPostgresCluster- il n'atteindra jamais les registres externes au moment de l'exécution, à moins d'être explicitement configuré pour le faire.
# Verify the operator is running
kubectl get pods -n postgres-operator
NAME READY STATUS RESTARTS AGE
pgo-controller-manager-7d8f9b6c4-xk2pt 1/1 Running 0 45sPostgresCluster CRD Spécification
LePostgresClusterCRD est la surface déclarative unique pour l'ensemble de votre déploiement PostgreSQL. Vous trouverez ci-dessous une spécification prête pour la production qui présente les sections clés. Chaque section est abordée en détail dans les parties suivantes de ce guide.
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PostgresCluster
metadata:
name: production-db
namespace: databases
spec:
postgresVersion: 16
image: registry.crunchydata.com/crunchydata/crunchy-postgres:ubi8-16.6-1
instances:
- name: pgha1
replicas: 3
resources:
requests:
cpu: "2"
memory: 8Gi
limits:
cpu: "4"
memory: 16Gi
dataVolumeClaimSpec:
storageClassName: gp3-csi
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Gi
walVolumeClaimSpec:
storageClassName: gp3-csi
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 20Gi
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
postgres-operator.crunchydata.com/cluster: production-db
tolerations:
- key: "workload"
operator: "Equal"
value: "database"
effect: "NoSchedule"
backups:
pgbackrest:
image: registry.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.54.1-0
configuration:
- secret:
name: pgbackrest-s3-creds
global:
repo1-retention-full: "7"
repo1-retention-diff: "14"
repo1-path: /pgbackrest/production-db/repo1
repo1-s3-uri-style: path
repos:
- name: repo1
schedules:
full: "0 2 * * 0" # Sunday 2am
differential: "0 2 * * 1-6" # Mon-Sat 2am
incremental: "0 */4 * * *" # Every 4 hours
s3:
bucket: mycompany-pg-backups
endpoint: s3.eu-west-1.amazonaws.com
region: eu-west-1
proxy:
pgBouncer:
image: registry.crunchydata.com/crunchydata/crunchy-pgbouncer:ubi8-1.23.1-0
replicas: 2
resources:
requests:
cpu: 500m
memory: 256Mi
limits:
cpu: "1"
memory: 512Mi
config:
global:
pool_mode: transaction
max_client_conn: "1000"
default_pool_size: "25"
min_pool_size: "5"
reserve_pool_size: "5"
reserve_pool_timeout: "3"
monitoring:
pgmonitor:
exporter:
image: registry.crunchydata.com/crunchydata/crunchy-postgres-exporter:ubi8-0.16.0-0
resources:
requests:
cpu: 100m
memory: 128Mi
patroni:
dynamicConfiguration:
synchronous_mode: true
postgresql:
parameters:
shared_buffers: 2GB
effective_cache_size: 6GB
work_mem: 32MB
maintenance_work_mem: 512MB
max_connections: 200
wal_buffers: 64MB
checkpoint_completion_target: 0.9
random_page_cost: 1.1
effective_io_concurrency: 200
min_wal_size: 1GB
max_wal_size: 4GB
max_worker_processes: 8
max_parallel_workers_per_gather: 4
max_parallel_workers: 8
log_min_duration_statement: 500
log_checkpoints: "on"
log_connections: "on"
log_disconnections: "on"
log_lock_waits: "on"
users:
- name: appuser
databases: ["appdb"]
options: "NOSUPERUSER"
- name: readonly
databases: ["appdb"]
options: "NOSUPERUSER"
databaseInitSQL:
name: init-sql-configmap
key: init.sqlCette spécification crée un cluster PostgreSQL 16 à trois instances avec des volumes de données et WAL séparés, des sauvegardes sauvegardées par S3 selon une planification complète/différentielle/incrémentale, un regroupement de connexions PgBouncer en mode transaction, une exportation de métriques Prometheus et une réplication synchrone Patroni. La règle d'anti-affinité des pods garantit que les trois instances atterrissent sur des nœuds Kubernetes différents.
HA basée sur Patroni et basculement automatique
Patroni est le framework HA intégré dans chaque pod PostgreSQL géré par PGO. Il utilise le Kubernetes API comme magasin de configuration distribué (DCS) – aucun cluster etcd ou ZooKeeper séparé n'est requis. Patroni surveille en permanence la santé du primaire et des répliques PostgreSQL. Lorsque le serveur principal ne répond plus, Patroni lance un basculement automatique : il promeut la réplique la plus à jour au rang de réplique principale et reconfigure les répliques restantes pour qu'elles suivent le nouveau leader.
Le processus de basculement dans PGO fonctionne comme suit. Patroni sur chaque pod détient un verrou de leader dans Kubernetes (via des objets Endpoints ou ConfigMap). Le primaire actuel doit renouveler ce verrou à un intervalle configurable (TTL par défaut de 10 secondes, attente de boucle de 3 secondes). Si le renouvellement du serveur principal ne parvient pas - parce qu'il est tombé en panne, que le nœud est mort ou que le réseau l'a partitionné - une réplique la plus proche de la position WAL du serveur principal acquerra le verrou et se promouvra. L'ensemble du processus se termine généralement en 10 à 30 secondes.
Le mode de réplication synchrone, activé viasynchronous_mode: truedans la configuration Patroni, garantit zéro perte de données (RPO = 0) au prix d'une latence d'écriture légèrement plus élevée. En mode synchrone, une transaction n'est pas reconnue au client tant qu'au moins une réplique n'a pas confirmé la réception du WAL. Si aucune réplique synchrone n'est disponible, Patroni désactive temporairement le mode synchrone pour maintenir la disponibilité. Vous pouvez remplacer cela avecsynchronous_mode_strict: truesi vous préférez sacrifier la disponibilité par souci de cohérence.
# Check Patroni cluster status
kubectl exec -it production-db-pgha1-0 -n databases -- patronictl list
+----------------------------+-------------------+---------+---------+----+-----------+
| Member | Host | Role | State | TL | Lag in MB |
+----------------------------+-------------------+---------+---------+----+-----------+
| production-db-pgha1-0 | 10.244.0.15 | Leader | running | 3 | |
| production-db-pgha1-1 | 10.244.1.22 | Replica | running | 3 | 0 |
| production-db-pgha1-2 | 10.244.2.18 | Replica | running | 3 | 0 |
+----------------------------+-------------------+---------+---------+----+-----------+
# Manual switchover (planned, zero downtime)
kubectl exec -it production-db-pgha1-0 -n databases -- \
patronictl switchover --master production-db-pgha1-0 --candidate production-db-pgha1-1 --force
# Manual failover (forced, for emergencies)
kubectl exec -it production-db-pgha1-1 -n databases -- \
patronictl failover --candidate production-db-pgha1-1 --forcePGO configure automatiquement les services Kubernetes pour suivre le leader Patroni. Le serviceproduction-db-primarypointe toujours vers le pod qui détient actuellement le verrou principal, de sorte que les applications qui se connectent via ce service bénéficient d'un basculement transparent avec seulement une brève réinitialisation de la connexion.
pgBackRest Sauvegarde et restauration
pgBackRest est le moteur de sauvegarde intégré à PGO. Il prend en charge trois types de sauvegarde : complète, différentielle et incrémentielle, ainsi que l'archivage WAL continu pour la récupération à un moment donné (PITR). Comprendre comment ces éléments s'articulent est essentiel pour concevoir une stratégie de sauvegarde qui équilibre le coût du stockage, la vitesse de sauvegarde et le temps de récupération.
Une sauvegarde complètecopie l'intégralité du répertoire de données PostgreSQL et constitue la référence pour tous les autres types de sauvegarde. Une sauvegarde différentiellecopie uniquement les pages modifiées depuis la dernière sauvegarde complète. Une sauvegarde incrémentiellecopie uniquement les pages modifiées depuis la dernière sauvegarde, quel que soit leur type. Pendant la restauration, pgBackRest enchaîne automatiquement les sauvegardes requises — par exemple, la restauration à partir d'une sauvegarde incrémentielle nécessite la sauvegarde incrémentielle plus la sauvegarde différentielle précédente (ou complète) plus la sauvegarde complète, ainsi que tous les segments WAL nécessaires pour atteindre le point de récupération cible.
Configuration du calendrier de sauvegarde duLa planification de sauvegarde est définie dans la sectionreposde la configuration pgBackRest dans la spécificationPostgresCluster. Un calendrier de production solide exécute généralement des sauvegardes hebdomadaires complètes, différentielles quotidiennes et des sauvegardes incrémentielles plus fréquentes.
backups:
pgbackrest:
global:
repo1-retention-full: "4" # Keep 4 full backups
repo1-retention-full-type: count
repo1-retention-diff: "14" # Keep 14 differentials
repos:
- name: repo1
schedules:
full: "0 2 * * 0" # Weekly full: Sunday 2am
differential: "0 2 * * 1-6" # Daily diff: Mon-Sat 2am
incremental: "0 */6 * * *" # Every 6 hours
s3:
bucket: mycompany-pg-backups
endpoint: s3.eu-west-1.amazonaws.com
region: eu-west-1Sauvegarde et restauration manuelles
# Trigger a manual full backup
kubectl annotate postgrescluster production-db -n databases \
postgres-operator.crunchydata.com/pgbackrest-backup="$(date +%s)" \
--overwrite
# Check backup status
kubectl exec -it production-db-pgha1-0 -n databases -- \
pgbackrest info --stanza=db
# Restore to a specific point in time
# First, update the PostgresCluster spec:
spec:
backups:
pgbackrest:
restore:
enabled: true
repoName: repo1
options:
- --type=time
- --target="2026-04-12 08:30:00+00"
- --target-action=promote
# Apply the restore annotation
kubectl annotate postgrescluster production-db -n databases \
postgres-operator.crunchydata.com/pgbackrest-restore="$(date +%s)" \
--overwriteLors d'une restauration PITR, PGO arrête toutes les instances PostgreSQL, restaure à partir de la sauvegarde complète ou différentielle la plus proche, relit les segments WAL jusqu'à l'heure cible, puis démarre le cluster. L’ensemble de l’opération est orchestré par l’opérateur : aucune intervention manuelle sur les pods individuels n’est nécessaire.
Regroupement de connexions PgBouncer
PostgreSQL crée un nouveau processus pour chaque connexion client. À grande échelle – des centaines ou des milliers de modules d’applications gérant chacun des pools de connexions – ce modèle s’effondre. PgBouncer se situe entre votre application et PostgreSQL, multiplexant de nombreuses connexions client sur un plus petit nombre de connexions serveur.
PGO déploie PgBouncer en tant que déploiement distinct avec son propre service. C'est au serviceproduction-db-pgbouncerque vos applications doivent se connecter, et non directement au service principal PostgreSQL. PgBouncer prend en charge trois modes de pool :
- Regroupement de sessions— une connexion serveur est attribuée à un client pour la durée de vie de la connexion client. Le plus sûr, mais le moins efficace.
- Regroupement de transactions— une connexion au serveur est attribuée uniquement pour la durée d'une transaction. Le plus efficace pour les charges de travail Web. Il s'agit de la valeur par défaut recommandée.
- Regroupement d'instructions— une connexion serveur est attribuée pour une seule instruction. Ne fonctionne que pour les charges de travail simples et non transactionnelles.
proxy:
pgBouncer:
replicas: 3
config:
global:
pool_mode: transaction
max_client_conn: "2000"
default_pool_size: "30"
min_pool_size: "10"
reserve_pool_size: "10"
reserve_pool_timeout: "3"
server_idle_timeout: "300"
server_lifetime: "3600"
client_idle_timeout: "600"
tcp_keepalive: "1"
tcp_keepidle: "30"
tcp_keepintvl: "10"
tcp_keepcnt: "3"
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
postgres-operator.crunchydata.com/role: pgbouncerNotez les paramètrestcp_keepalive: ils sont essentiels lorsque PgBouncer s'exécute derrière un équilibreur de charge cloud ou un service Kubernetes. Sans keepalives agressifs, les connexions inactives peuvent être supprimées silencieusement par les composants réseau intermédiaires, provoquant des erreurs d'application lors de la prochaine tentative de requête.
pgMonitor et surveillance Prometheus/Grafana
L'intégration de surveillance duPGO déploie un side-carcrunchy-postgres-exporterdans chaque pod PostgreSQL. Cet exportateur récupère les vues de statistiques internes de PostgreSQL et les expose en tant que métriques Prometheus sur le port 9187. L'exportateur couvre plus de 150 métriques prêtes à l'emploi, notamment les connexions, le délai de réplication, les taux de transaction, les taux de réussite du cache, les statistiques de tables et d'index, les conflits de verrouillage et les taux de génération de WAL.
# ServiceMonitor for Prometheus Operator auto-discovery
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: postgres-monitoring
namespace: databases
labels:
release: kube-prometheus-stack
spec:
selector:
matchLabels:
postgres-operator.crunchydata.com/cluster: production-db
postgres-operator.crunchydata.com/crunchy-postgres-exporter: "true"
endpoints:
- port: exporter
interval: 15s
scrapeTimeout: 10sCrunchy Data fournit un ensemble de tableaux de bord Grafana prédéfinis que vous pouvez importer directement. Ceux-ci couvrent la présentation de PostgreSQL, l'état de réplication, l'état de sauvegarde de pgBackRest, les statistiques de PgBouncer et l'utilisation des ressources au niveau du pod. Importez-les via le provisionnement du tableau de bord de Grafana ou manuellement à partir du référentiel d'exemples Crunchy Data.
# Install Grafana dashboards as ConfigMaps
kubectl create configmap grafana-pg-overview \
--from-file=pg-overview.json=dashboards/pg-overview.json \
-n monitoring
kubectl label configmap grafana-pg-overview grafana_dashboard=1 -n monitoringRègles d'alerte critiquepour PostgreSQL
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: postgres-alerts
namespace: databases
labels:
release: kube-prometheus-stack
spec:
groups:
- name: postgresql-health
rules:
- alert: PostgresReplicationLagHigh
expr: ccp_replication_lag_size_bytes > 104857600
for: 5m
labels:
severity: warning
annotations:
summary: "Replica {{ $labels.pod }} lag exceeds 100MB"
- alert: PostgresConnectionsExhausted
expr: ccp_connection_stats_active / ccp_connection_stats_max_connections > 0.85
for: 2m
labels:
severity: critical
annotations:
summary: "PostgreSQL connections above 85% capacity"
- alert: PostgresDeadlockDetected
expr: rate(ccp_stat_database_deadlocks[5m]) > 0
for: 1m
labels:
severity: warning
annotations:
summary: "Deadlocks detected in {{ $labels.datname }}"
- alert: PostgresCacheHitRatioLow
expr: ccp_stat_database_blks_hit / (ccp_stat_database_blks_hit + ccp_stat_database_blks_read) < 0.95
for: 10m
labels:
severity: warning
annotations:
summary: "Cache hit ratio below 95% for {{ $labels.datname }}"
- alert: PgBackRestStaleBackup
expr: time() - ccp_backrest_last_full_backup_time_since_completion_seconds > 604800
for: 1h
labels:
severity: critical
annotations:
summary: "No full backup in the last 7 days"AWS Déploiement EKS
Le déploiement de PGO sur AWS EKS nécessite la configuration du pilote EBS CSI pour les volumes persistants et des rôles IAM pour les comptes de service (IRSA) pour l'accès à la sauvegarde S3. Cette approche évite de stocker les informations d’identification AWS de longue durée dans les secrets Kubernetes.
# Install the EBS CSI driver (required for gp3 volumes)
eksctl create addon --name aws-ebs-csi-driver --cluster my-cluster --region eu-west-1 \
--service-account-role-arn arn:aws:iam::111122223333:role/AmazonEKS_EBS_CSI_DriverRole
# Create a gp3 StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gp3-csi
provisioner: ebs.csi.aws.com
parameters:
type: gp3
iops: "3000"
throughput: "125"
encrypted: "true"
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true# IAM policy for pgBackRest S3 access
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::mycompany-pg-backups",
"arn:aws:s3:::mycompany-pg-backups/*"
]
}
]
}
# Create an IRSA-enabled service account
eksctl create iamserviceaccount \
--name pgbackrest-sa \
--namespace databases \
--cluster my-cluster \
--attach-policy-arn arn:aws:iam::111122223333:policy/PGBackRestS3Policy \
--approveDans la spécificationPostgresCluster, faites référence au compartiment et à la région S3. PGO utilisera automatiquement les informations d'identification du compte de service du pod (via IRSA) — aucune clé d'accès explicite n'est nécessaire.
backups:
pgbackrest:
repos:
- name: repo1
s3:
bucket: mycompany-pg-backups
endpoint: s3.eu-west-1.amazonaws.com
region: eu-west-1Pour une résilience multi-AZ, assurez-vous que vos groupes de nœuds EKS couvrent au moins trois zones de disponibilité et utilisez les contraintes de répartition de la topologie présentées précédemment pour distribuer les pods PostgreSQL entre elles.
Azure Déploiement AKS
Azure AKS utilise des disques gérés pour le stockage persistant et Azure Blob Storage pour les sauvegardes pgBackRest. La classe de stockage recommandée utilise Premium SSD v2 ou Premium LRS pour les charges de travail de base de données.
# StorageClass for Azure Premium SSD
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: azure-premium-ssd
provisioner: disk.csi.azure.com
parameters:
skuName: Premium_LRS
kind: Managed
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
---
# pgBackRest with Azure Blob Storage
backups:
pgbackrest:
configuration:
- secret:
name: pgbackrest-azure-creds
global:
repo1-azure-account: mystorageaccount
repo1-retention-full: "7"
repos:
- name: repo1
schedules:
full: "0 2 * * 0"
differential: "0 2 * * 1-6"
azure:
container: pg-backups
---
# Secret with Azure credentials
apiVersion: v1
kind: Secret
metadata:
name: pgbackrest-azure-creds
namespace: databases
stringData:
azure.conf: |
[global]
repo1-azure-key=BASE64_STORAGE_ACCOUNT_KEYPour Workload Identity (l'équivalent Azure de AWS IRSA), configurez le cluster AKS avec l'émetteur OIDC et créez des informations d'identification d'identité fédérée pour le compte de service pgBackRest. Cela élimine le besoin de stocker les clés de compte dans les secrets.
Déploiement GCP GKE
GKE utilise un disque persistant (pd-ssd) pour le stockage et GCS pour les sauvegardes pgBackRest. GKE Workload Identity mappe les comptes de service Kubernetes aux comptes de service Google Cloud pour une authentification sécurisée et sans clé.
# StorageClass for GKE SSD Persistent Disk
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: pd-ssd
provisioner: pd.csi.storage.gke.io
parameters:
type: pd-ssd
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
---
# pgBackRest with GCS
backups:
pgbackrest:
configuration:
- secret:
name: pgbackrest-gcs-creds
repos:
- name: repo1
gcs:
bucket: mycompany-pg-backups
---
# GCS service account key secret
apiVersion: v1
kind: Secret
metadata:
name: pgbackrest-gcs-creds
namespace: databases
stringData:
gcs-key.json: |
{
"type": "service_account",
"project_id": "my-project",
...
}# Enable Workload Identity on GKE
gcloud container clusters update my-cluster \
--workload-pool=my-project.svc.id.goog
# Create and bind IAM service account
gcloud iam service-accounts create pgbackrest-gcs \
--display-name="pgBackRest GCS Access"
gcloud projects add-iam-policy-binding my-project \
--member="serviceAccount:pgbackrest-gcs@my-project.iam.gserviceaccount.com" \
--role="roles/storage.objectAdmin"
gcloud iam service-accounts add-iam-policy-binding \
pgbackrest-gcs@my-project.iam.gserviceaccount.com \
--role="roles/iam.workloadIdentityUser" \
--member="serviceAccount:my-project.svc.id.goog[databases/production-db-pgbackrest]"Reprise après sinistre multi-régions
PGO prend en charge les clusters de secours pour la reprise après sinistre entre régions. Un cluster de secours relit en permanence les WAL à partir du référentiel pgBackRest du cluster principal, conservant une copie chaleureuse qui peut être promue au rang de serveur principal indépendant en cas de panne régionale.
# Standby cluster in Region B
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PostgresCluster
metadata:
name: production-db-standby
namespace: databases
spec:
postgresVersion: 16
image: registry.crunchydata.com/crunchydata/crunchy-postgres:ubi8-16.6-1
standby:
enabled: true
repoName: repo1
instances:
- name: pgha1
replicas: 2
dataVolumeClaimSpec:
storageClassName: gp3-csi
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Gi
backups:
pgbackrest:
image: registry.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.54.1-0
configuration:
- secret:
name: pgbackrest-s3-creds
repos:
- name: repo1
s3:
bucket: mycompany-pg-backups
endpoint: s3.us-east-1.amazonaws.com
region: us-east-1Pour promouvoir le cluster de secours lors d'un sinistre, définissezstandby.enabled: falsedans la spécification et appliquez la modification. PGO promeut le cluster de secours en cluster principal indépendant. Cette promotion est irréversible : vous devrez reconstruire la relation de réplication à partir de zéro une fois la région principale d'origine récupérée.
Déploiement k3s/Rancher sans système d'exploitation
L'exécution de PGO sur k3s nu avec gestion Rancher nécessite une attention particulière au stockage et à la mise en réseau, car vous ne disposez pas de disques gérés par un fournisseur de cloud ou d'équilibreurs de charge.
Rangement Longhorn
Longhorn est un système de stockage en bloc léger et distribué pour Kubernetes, idéal pour les environnements sans système d'exploitation. Il réplique les volumes sur plusieurs nœuds pour plus de durabilité et prend en charge les instantanés, les sauvegardes et l'expansion des volumes.
# Install Longhorn via Helm
helm repo add longhorn https://charts.longhorn.io
helm repo update
helm install longhorn longhorn/longhorn \
--namespace longhorn-system \
--create-namespace \
--set defaultSettings.defaultReplicaCount=3 \
--set defaultSettings.storageOverProvisioningPercentage=100 \
--set defaultSettings.storageMinimalAvailablePercentage=15
---
# Longhorn StorageClass for PostgreSQL
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: longhorn-pg
provisioner: driver.longhorn.io
parameters:
numberOfReplicas: "3"
staleReplicaTimeout: "2880"
fsType: ext4
dataLocality: best-effort
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
reclaimPolicy: RetainMetalLB pour l'équilibrage de charge
# Install MetalLB
helm repo add metallb https://metallb.github.io/metallb
helm install metallb metallb/metallb --namespace metallb-system --create-namespace
---
# Configure IP address pool
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: pg-pool
namespace: metallb-system
spec:
addresses:
- 192.168.1.200-192.168.1.210
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: pg-l2
namespace: metallb-system
spec:
ipAddressPools:
- pg-poolAvec MetalLB configuré, les services PGO de typeLoadBalancerrecevront les adresses IP de votre pool, rendant les points de terminaison principaux PostgreSQL et PgBouncer directement accessibles depuis votre réseau sans redirection manuelle de port.
pgBackRest avec PVC local ou NFS pour Bare Metal
# For environments without S3/GCS/Azure Blob, use a PVC-based repo
backups:
pgbackrest:
repos:
- name: repo1
schedules:
full: "0 2 * * 0"
differential: "0 2 * * 1-6"
volume:
volumeClaimSpec:
storageClassName: longhorn-pg
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 200GiPour les environnements isolés, vous pouvez également configurer un référentiel pgBackRest secondaire qui expédie les sauvegardes vers un partage NFS ou une instance MinIO exécutée sur site, fournissant une copie de sauvegarde hors cluster sans dépendances cloud.
Configuration du cryptage TLS/SSL
PGO génère par défaut des certificats TLS auto-signés pour toutes les communications internes : les instances PostgreSQL, la réplication, pgBackRest et PgBouncer communiquent tous sur des canaux cryptés sans aucune gestion manuelle des certificats. Toutefois, pour les environnements de production, vous souhaitez généralement utiliser des certificats signés par l'autorité de certification de votre organisation.
# Create a TLS secret with your CA-signed certificates
kubectl create secret tls production-db-tls \
--cert=server.crt \
--key=server.key \
-n databases
kubectl create secret generic production-db-ca \
--from-file=ca.crt=ca.crt \
-n databases
---
# Reference in PostgresCluster spec
spec:
customTLSSecret:
name: production-db-tls
customReplicationTLSSecret:
name: production-db-replication-tlsPGO configure PostgreSQL avecssl = onet définit les paramètresssl_cert_file,ssl_key_fileetssl_ca_fileappropriés. PgBouncer est configuré de la même manière pour nécessiter TLS pour les connexions client et pour utiliser TLS lors de la connexion aux backends PostgreSQL.
# Enforce TLS for all client connections via pg_hba.conf
spec:
patroni:
dynamicConfiguration:
postgresql:
pg_hba:
- hostssl all all 0.0.0.0/0 scram-sha-256
- hostssl replication all 0.0.0.0/0 scram-sha-256Configuration PostgreSQL personnalisée
PGO expose la configuration PostgreSQL via la section de configuration dynamique Patroni. Il s'agit de l'approche recommandée car Patroni garantit que toutes les instances maintiennent une configuration cohérente et gère les redémarrages si nécessaire.
patroni:
dynamicConfiguration:
postgresql:
parameters:
# Memory
shared_buffers: 4GB
effective_cache_size: 12GB
work_mem: 64MB
maintenance_work_mem: 1GB
huge_pages: "try"
# WAL
wal_buffers: 128MB
min_wal_size: 2GB
max_wal_size: 8GB
checkpoint_completion_target: 0.9
wal_compression: lz4
# Query Planning
random_page_cost: 1.1
effective_io_concurrency: 200
default_statistics_target: 200
# Parallelism
max_worker_processes: 16
max_parallel_workers_per_gather: 4
max_parallel_workers: 8
max_parallel_maintenance_workers: 4
# Connections
max_connections: 200
idle_in_transaction_session_timeout: 60000
statement_timeout: 300000
# Logging
log_min_duration_statement: 250
log_checkpoints: "on"
log_connections: "on"
log_disconnections: "on"
log_lock_waits: "on"
log_temp_files: 0
log_autovacuum_min_duration: 0
# Autovacuum Tuning
autovacuum_max_workers: 5
autovacuum_naptime: 30
autovacuum_vacuum_cost_limit: 800
autovacuum_vacuum_scale_factor: 0.02
autovacuum_analyze_scale_factor: 0.01Ces paramètres supposent un nœud avec 16 cœurs CPU et 32 Go de RAM dédiés à PostgreSQL. Ajustez leshared_buffersà environ 25 % de la RAM disponible et leeffective_cache_sizeà environ 75 %. Les paramètres WAL et de point de contrôle sont adaptés aux charges de travail lourdes en écriture : réduisez lemax_wal_sizepour les systèmes gourmands en lecture où la fréquence des points de contrôle importe moins.
Gestion des utilisateurs et des bases de données
PGO gère les utilisateurs et les bases de données PostgreSQL de manière déclarative via la sectionusersde la spécificationPostgresCluster. Lorsque vous ajoutez un utilisateur, PGO crée le rôle dans PostgreSQL, génère un mot de passe aléatoire et stocke les informations d'identification dans un secret Kubernetes.
users:
- name: appuser
databases: ["appdb"]
options: "NOSUPERUSER NOCREATEDB NOCREATEROLE"
- name: readonly
databases: ["appdb"]
options: "NOSUPERUSER NOCREATEDB NOCREATEROLE"
- name: migrations
databases: ["appdb"]
options: "NOSUPERUSER CREATEDB"
- name: monitoring
databases: ["postgres"]
options: "NOSUPERUSER LOGIN"
---
# Access credentials from the generated secret
kubectl get secret production-db-pguser-appuser -n databases -o jsonpath='{.data.password}' | base64 -d
# The secret also contains a full connection URI
kubectl get secret production-db-pguser-appuser -n databases -o jsonpath='{.data.uri}' | base64 -d
# postgresql://appuser:PASSWORD@production-db-primary.databases.svc:5432/appdbPour accorder un accès en lecture seule, utilisez la fonctionnalitédatabaseInitSQLpour exécuter SQL lors de la création du cluster qui crée un rôle en lecture seule et lui accorde SELECT sur toutes les tables.
# init.sql ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: init-sql-configmap
namespace: databases
data:
init.sql: |
-- Read-only role for reporting users
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly;
GRANT CONNECT ON DATABASE appdb TO readonly;
GRANT USAGE ON SCHEMA public TO readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;
-- Extensions
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE EXTENSION IF NOT EXISTS pg_trgm;Mises à jour progressives et mises à niveau de versions majeures
PGO gère les mises à niveau de version mineures via des mises à jour progressives. Lorsque vous modifiez la balise d'image dans la spécificationPostgresClusterpar une version de correctif plus récente, PGO met à jour les instances une par une, en commençant par les répliques et en terminant par la principale (ce qui déclenche un basculement Patroni pour minimiser les temps d'arrêt).
# Minor version upgrade: change the image tag
spec:
image: registry.crunchydata.com/crunchydata/crunchy-postgres:ubi8-16.7-0Les mises à niveau des versions majeures de(par exemple, PostgreSQL 15 à 16) nécessitentpg_upgrade, que PGO orchestre via unPGUpgradeCRD distinct.
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PGUpgrade
metadata:
name: production-db-upgrade
namespace: databases
spec:
postgresClusterName: production-db
fromPostgresVersion: 15
toPostgresVersion: 16
image: registry.crunchydata.com/crunchydata/crunchy-upgrade:ubi8-5.7.2-0Le processus de mise à niveau arrête le cluster existant, exécutepg_upgrade --linkpour effectuer une mise à niveau sur place à l'aide de liens physiques (minimisant la copie des données), vérifie la mise à niveau et démarre le cluster sur la nouvelle version. Effectuez toujours une sauvegarde complète avant de démarrer une mise à niveau de version majeure et testez d'abord la procédure sur un cluster hors production.
# Pre-upgrade checklist
# 1. Take a full backup
kubectl annotate postgrescluster production-db -n databases \
postgres-operator.crunchydata.com/pgbackrest-backup="$(date +%s)" \
--overwrite
# 2. Verify backup completed
kubectl exec -it production-db-pgha1-0 -n databases -- pgbackrest info --stanza=db
# 3. Shutdown the cluster (set replicas to 0 or use PGO shutdown annotation)
kubectl annotate postgrescluster production-db -n databases \
postgres-operator.crunchydata.com/shutdown="$(date +%s)" \
--overwrite
# 4. Apply the PGUpgrade resource
kubectl apply -f pg-upgrade.yaml
# 5. Update the PostgresCluster spec with the new version and image
# 6. Remove the shutdown annotation to start the upgraded clusterRecommandations de réglage de production duL'exécution de PGO en production nécessite une attention particulière à plusieurs domaines opérationnels au-delà du déploiement initial.
Performances de stockage
- Données séparées et volumes WAL: Utilisez toujours
walVolumeClaimSpecpour placer WAL sur un PVC dédié. Cela empêche les activités WAL lourdes en écriture d'entrer en conflit avec les E/S de données. - Utilisez le stockage à IOPS élevé: gp3 (AWS), Premium SSD v2 (Azure), pd-ssd (GCP) ou Longhorn soutenu par NVMe sur du métal nu. Les modèles d'E/S aléatoires du PostgreSQL nécessitent un stockage à faible latence.
- Extension de volume: assurez-vous que votre StorageClass dispose de
allowVolumeExpansion: true. PGO peut étendre les PVC sans interruption sur les fournisseurs de stockage pris en charge.
PGO crée automatiquement des PDB pour vos instances PostgreSQL, mais vérifiez qu'elles conviennent à vos exigences HA.
# Check PDBs
kubectl get pdb -n databases
NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
production-db-pgha1 1 N/A 2 7dDemandes et limites de ressources
- Définissez les demandes de mémoire égales aux limites des pods de base de données pour éviter les suppressions de MOO et garantir la classe QoS.
- Réglez les demandes du CPU de manière prudente et limitez-les plus haut pour permettre l'éclatement pendant les opérations de vide ou de maintenance.
- Surveillez l'utilisation réelle via les métriques pgMonitor et ajustez-la tous les trimestres.
Gestion des connexions
- Connectez-vous toujours via PgBouncer, pas directement à PostgreSQL.
- Définissez
max_connectionsdans PostgreSQL sur une valeur qui prend en compte la taille du pool de PgBouncer ainsi que les connexions système (réplication, surveillance, superutilisateur). - Utiliser le mode de regroupement de transactions pour les applications Web. Basculez vers le regroupement de sessions uniquement si votre application utilise des instructions préparées ou un état au niveau de la session (par exemple, commandes
SET, tables temporaires).
Validation de sauvegarde
- Testez régulièrement les restaurations en créant un cluster cloné à partir de la sauvegarde et en exécutant des tests de fumée au niveau de l'application.
- Surveillez l'alerte
PgBackRestStaleBackuppour garantir que les sauvegardes se terminent dans les délais. - Validez PITR en restaurant des horodatages spécifiques et en vérifiant la cohérence des données.
# Clone cluster for backup validation
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PostgresCluster
metadata:
name: backup-validation
namespace: databases
spec:
postgresVersion: 16
image: registry.crunchydata.com/crunchydata/crunchy-postgres:ubi8-16.6-1
dataSource:
postgresCluster:
clusterName: production-db
repoName: repo1
options:
- --type=time
- --target="2026-04-12 06:00:00+00"
instances:
- name: pgha1
replicas: 1
dataVolumeClaimSpec:
storageClassName: gp3-csi
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Gi
backups:
pgbackrest:
repos:
- name: repo1
volume:
volumeClaimSpec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 50GiPolitiques réseau# Restrict PostgreSQL traffic to known namespaces
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: postgres-network-policy
namespace: databases
spec:
podSelector:
matchLabels:
postgres-operator.crunchydata.com/cluster: production-db
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
app-access: database
- podSelector:
matchLabels:
postgres-operator.crunchydata.com/cluster: production-db
ports:
- port: 5432
protocol: TCP
- port: 9187
protocol: TCPListe de contrôle de surveillance du- Retard de réplication: Alerte lorsqu'une réplique dépasse 100 Mo ou 60 secondes de décalage.
- Saturation des connexions: Alerte lorsque les connexions actives dépassent 80% du
max_connections. - Anomalies de taux de transaction: Basez votre TPS et alertez sur les écarts significatifs.
- Utilisation du disque: Alerte aux seuils de 70 % et 85 % avec les runbooks d'extension de volume.
- Fraîcheur de la sauvegarde: Alerte lorsque la dernière sauvegarde complète est plus ancienne que votre fenêtre RPO.
- Conflit de verrous: Alerte sur les verrous maintenus longtemps (> 30 secondes) pouvant indiquer des bugs d'application.
- Santé de l'autovacuum: Alerte lorsque les tables n'ont pas été aspirées depuis plus de 24 heures.
Un plan de reprise après sinistre n'est utile que s'il a été testé. Le runbook suivant décrit les procédures clés pour la récupération après des scénarios de défaillance courants.
Panne d'un seul pod
Patroni et Kubernetes gèrent cela automatiquement. Si le pod principal tombe en panne, Patroni promeut une réplique dans les 10 à 30 secondes. Kubernetes redémarre le pod défaillant, qui rejoint en tant que réplique.
Défaillance d'un nœud unique
Si un nœud exécutant un pod PostgreSQL meurt, Kubernetes replanifie le pod sur un nœud sain. Le pod se connecte à son PVC existant (si le stockage est connecté au réseau) ou restaure à partir d'une sauvegarde (si le stockage local a été utilisé). Les règles d'anti-affinité des pods garantissent que les instances restantes continuent de diffuser le trafic.
Perte complète du cluster
Si l'intégralité du cluster Kubernetes est perdue, déployez un nouveau cluster, installez PGO et créez un nouveauPostgresClusteravec undataSourcepointant vers le référentiel de sauvegarde. PGO restaure la dernière sauvegarde et relit WAL au point disponible le plus récent.
Basculement régional
Si la région principale est perdue, promouvez le cluster de secours en définissantstandby.enabled: false. Mettez à jour votre DNS ou votre équilibreur de charge pour pointer vers la nouvelle région principale. Une fois la région d'origine récupérée, vous pouvez reconstruire la relation de veille à l'envers.
# Promote standby cluster
kubectl patch postgrescluster production-db-standby -n databases --type merge \
-p '{"spec":{"standby":{"enabled":false}}}'
# Verify the cluster is now an independent primary
kubectl exec -it production-db-standby-pgha1-0 -n databases -- patronictl listRéférence rapide des commandes opérationnelles du# Cluster status
kubectl get postgrescluster -n databases
kubectl describe postgrescluster production-db -n databases
# Patroni status
kubectl exec -it production-db-pgha1-0 -n databases -- patronictl list
kubectl exec -it production-db-pgha1-0 -n databases -- patronictl history
# Connect to PostgreSQL
kubectl exec -it production-db-pgha1-0 -n databases -- psql -U postgres
# Backup info
kubectl exec -it production-db-pgha1-0 -n databases -- pgbackrest info --stanza=db
# Manual backup
kubectl annotate postgrescluster production-db -n databases \
postgres-operator.crunchydata.com/pgbackrest-backup="$(date +%s)" --overwrite
# Restart PostgreSQL (rolling)
kubectl annotate postgrescluster production-db -n databases \
postgres-operator.crunchydata.com/restart="$(date +%s)" --overwrite
# Logs
kubectl logs production-db-pgha1-0 -n databases -c database
kubectl logs production-db-pgha1-0 -n databases -c pgbackrest
kubectl logs production-db-pgha1-0 -n databases -c exporter
# PgBouncer status
kubectl exec -it $(kubectl get pod -l postgres-operator.crunchydata.com/role=pgbouncer -n databases -o name | head -1) -n databases -- psql -U pgbouncer pgbouncer -c "SHOW POOLS;"
# Scale replicas
kubectl patch postgrescluster production-db -n databases --type merge \
-p '{"spec":{"instances":[{"name":"pgha1","replicas":5}]}}'Conclusion
Crunchy Data PGO transforme PostgreSQL sur Kubernetes d'une charge opérationnelle en un système déclaratif gérable. LePostgresClusterCRD capture l'intégralité du déploiement (instances, réplication, sauvegarde, pooling, surveillance, TLS) dans une seule ressource contrôlée en version. Patroni propose un basculement automatique éprouvé. pgBackRest offre une sauvegarde de niveau entreprise avec une restauration ponctuelle sur n'importe quel magasin d'objets cloud. PgBouncer gère le regroupement de connexions exigé par le modèle processus par connexion de PostgreSQL à grande échelle. Et pgMonitor alimente les métriques dont vous avez besoin dans Prometheus et Grafana pour une visibilité opérationnelle.
Les modèles de déploiement sur AWS EKS, Azure AKS, GCP GKE et Bare Metal k3s partagent la même spécification de basePostgresCluster: ce qui change, c'est la classe de stockage, la configuration du référentiel de sauvegarde et la couche réseau. Cette cohérence constitue la véritable valeur d'une approche basée sur les opérateurs : votre équipe apprend un seul outil, un seul modèle opérationnel et un seul ensemble de runbooks qui fonctionnent partout.
Commencez avec un cluster à trois instances, PgBouncer en mode pooling de transactions, une planification de sauvegarde hebdomadaire complète et différentielle quotidienne et les alertes Prometheus principales. Validez votre procédure de restauration de sauvegarde dès le premier jour, et non lorsque vous en avez besoin pour la première fois. Étendez-vous aux clusters de secours multirégionaux, à la réplication synchrone et au réglage avancé à mesure que vos besoins en disponibilité et votre maturité opérationnelle augmentent. L'opérateur s'occupe de la mécanique ; votre responsabilité est de bien comprendre l’architecture pour faire les bons compromis en fonction de votre charge de travail.