Haute disponibilité native PostgreSQL : stratégies Patroni, réplication en streaming et basculement de production
PostgreSQL Native HA avec Patroni, Streaming Replication et HAProxy
L'exécution d'une seule instance PostgreSQL est simple jusqu'à ce que la première panne imprévue vous rappelle qu'une base de données n'a autant de valeur que sa disponibilité. Les pannes de disque, les paniques du noyau, les partitions réseau et les mises à niveau bâclées ne sont pas des risques théoriques : ce sont des certitudes opérationnelles sur une période suffisamment longue. PostgreSQL n'est pas livré avec un basculement automatique intégré, mais il fournit toutes les primitives de réplication nécessaires pour créer un cluster hautement disponible. Patroni, un framework HA open source maintenu par Zalando, orchestre ces primitives dans un système de basculement de niveau production qui a été testé à grande échelle sur des milliers de clusters PostgreSQL dans le monde.
Cet article est un guide d'ingénierie approfondi. Nous couvrirons la réplication en streaming PostgreSQL (synchrone et asynchrone), l'architecture et la configuration Patroni, etcd en tant que magasin de configuration distribué, HAProxy pour le routage des connexions avec fractionnement en lecture-écriture, PgBouncer pour le pooling de connexions, l'archivage WAL et la récupération ponctuelle, la réplication logique pour la synchronisation sélective des données, pg_basebackup pour l'approvisionnement initial en veille, repmgr comme alternative à Patroni, les modèles de déploiement spécifiques au cloud pour AWS, Azure et GCP, déploiement k3s nu avec Rancher et Longhorn, surveillance avec pg_stat_replication et Prometheus/Grafana, procédures de basculement ou de basculement, prévention du split-brain, réglage de la production et ingénierie du chaos pour la validation du basculement.
PostgreSQL Principes fondamentaux de la réplication en continu
La réplicationStreaming est l’épine dorsale de la haute disponibilité PostgreSQL. Il fonctionne en envoyant en continu les enregistrements WAL (Write-Ahead Log) d'un serveur principal vers un ou plusieurs serveurs de secours. Le serveur de secours applique ces enregistrements WAL en temps réel, conservant une copie presque identique des données du serveur principal. Ce mécanisme a été introduit dans PostgreSQL 9.0 et a été affiné dans chaque version ultérieure.
Il existe deux modes de réplication en streaming :asynchroneetsynchrone. En mode asynchrone, le serveur principal n'attend pas que les serveurs de secours confirment la réception des enregistrements WAL avant de valider une transaction. Cela donne des performances d'écriture maximales mais introduit une fenêtre de perte potentielle de données : si le serveur principal échoue avant qu'un serveur de secours n'ait reçu le WAL le plus récent, ces transactions sont perdues. En mode synchrone, le serveur principal attend qu'au moins un serveur de secours confirme que les enregistrements WAL ont été écrits dans un stockage durable avant de signaler une transaction comme validée. Cela élimine la perte de données au prix d'une latence de validation accrue, puisque chaque écriture doit faire un aller-retour vers une réserve.
Le choix entre réplication synchrone et asynchrone n'est pas binaire. PostgreSQL prend en chargesynchronous_commitau niveau de la session, de sorte que les charges de travail sensibles à la latence peuvent opter pour des validations asynchrones tandis que les transactions financières critiques utilisent des validations synchrones au sein du même cluster.
Configuration du serveur principal pour la réplication
Le serveur principal doit être configuré pour générer des enregistrements WAL à un niveau suffisant pour la réplication et pour autoriser les connexions de secours. Les paramètrespostgresql.confsuivants sont essentiels.
# postgresql.conf on the primary
wal_level = replica # minimum for streaming replication
max_wal_senders = 10 # max concurrent replication connections
max_replication_slots = 10 # prevent WAL removal before standby consumption
wal_keep_size = 2GB # retain WAL as fallback if slots are unused
hot_standby = on # allow read queries on standbys
synchronous_commit = on # 'on' for sync, 'off' for pure async
synchronous_standby_names = 'ANY 1 (standby1, standby2)' # sync replication targets
archive_mode = on # enable WAL archiving for PITR
archive_command = 'test ! -f /archive/%f && cp %p /archive/%f'
listening_addresses = '*'
port = 5432L'authentificationpour les connexions de réplication est gérée danspg_hba.conf. Les connexions de réplication utilisent un type de connexion dédié.
# pg_hba.conf — replication entries
# TYPE DATABASE USER ADDRESS METHOD
host replication replicator 10.0.1.0/24 scram-sha-256
host replication replicator 10.0.2.0/24 scram-sha-256
host all all 10.0.0.0/16 scram-sha-256Créez l'utilisateur de réplication sur le serveur principal.
CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'strong_replication_password';Provisionnement d'une réserve avec pg_basebackup
L'utilitairepg_basebackupcrée une copie physique du répertoire de données du serveur principal, qui devient le point de départ d'un nouveau répertoire de données de secours. Il gère la sauvegarde de base et le streaming WAL de manière atomique, de sorte que la copie résultante est cohérente.
# On the standby server
pg_basebackup -h primary-host -U replicator -D /var/lib/postgresql/16/main \
-Fp -Xs -P -R
# -Fp: plain format
# -Xs: stream WAL during backup
# -P: show progress
# -R: create standby.signal and configure primary_conninfo in postgresql.auto.confL'indicateur-Rest critique : il écritprimary_conninfodanspostgresql.auto.confet créestandby.signal, qui indique à PostgreSQL de démarrer en mode veille. Dans PostgreSQL 12 et versions ultérieures,recovery.confest remplacé par ces deux mécanismes.
# postgresql.auto.conf (generated by pg_basebackup -R)
primary_conninfo = 'host=primary-host port=5432 user=replicator password=strong_replication_password application_name=standby1'
primary_slot_name = 'standby1_slot'Créez l'emplacement de réplication sur le serveur principal avant de démarrer le serveur de secours, pour empêcher le nettoyage des WAL avant que le serveur de secours puisse le consommer.
SELECT pg_create_physical_replication_slot('standby1_slot');
SELECT pg_create_physical_replication_slot('standby2_slot');Patroni : Orchestration HA automatisée
La réplicationStreaming vous offre une redondance des données, mais elle ne vous permet pas de basculement automatique. Si le serveur principal tombe en panne, quelqu'un (un opérateur humain ou un système d'automatisation) doit promouvoir un serveur de secours au rang de serveur principal, reconfigurer les serveurs de secours restants pour qu'ils suivent le nouveau serveur principal et mettre à jour le routage des connexions. Patroni automatise tout cela.
Patroni est un démon Python qui s'exécute avec chaque instance PostgreSQL. Il utilise un magasin de configuration distribué (DCS) – généralement etcd, mais aussi ZooKeeper ou Consul – pour coordonner l'élection du leader et l'état du cluster. Chaque nœud Patroni écrit en permanence son état de santé dans le DCS. Lorsque le leader (primaire) ne parvient pas à renouveler sa clé DCS dans le TTL configuré, Patroni lance une élection de leader parmi les serveurs de secours sains. Le gagnant est promu au rang de nœud principal et les nœuds restants se reconfigurent en tant que nœuds de secours du nouveau nœud principal, le tout automatiquement, généralement dans un délai de 10 à 30 secondes.
ConfigurationPatroni YAML
Patroni est configuré via un fichier YAML qui définit la connexion DCS, les paramètres PostgreSQL, le comportement de réplication et les paramètres d'amorçage. Ce qui suit est une configuration de niveau production pour le nœud principal.
# /etc/patroni/patroni.yml — Node 1 (Primary)
scope: pg-ha-cluster
namespace: /postgresql-ha/
name: node1
restapi:
listen: 0.0.0.0:8008
connect_address: 10.0.1.10:8008
etcd3:
hosts:
- 10.0.2.10:2379
- 10.0.2.11:2379
- 10.0.2.12:2379
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576 # 1MB — only promote standbys within this lag
synchronous_mode: true
synchronous_mode_strict: false
postgresql:
use_pg_rewind: true
use_slots: true
parameters:
wal_level: replica
hot_standby: 'on'
max_connections: 200
max_wal_senders: 10
max_replication_slots: 10
wal_keep_size: 2GB
synchronous_commit: 'on'
archive_mode: 'on'
archive_command: 'test ! -f /archive/%f && cp %p /archive/%f'
archive_timeout: 60
wal_log_hints: 'on'
shared_preload_libraries: 'pg_stat_statements'
track_commit_timestamp: 'on'
pg_hba:
- host replication replicator 10.0.0.0/16 scram-sha-256
- host all all 10.0.0.0/16 scram-sha-256
- host all all 0.0.0.0/0 scram-sha-256
initdb:
- encoding: UTF8
- data-checksums
users:
admin:
password: 'admin_secure_password'
options:
- createrole
- createdb
replicator:
password: 'repl_secure_password'
options:
- replication
postgresql:
listen: 0.0.0.0:5432
connect_address: 10.0.1.10:5432
data_dir: /var/lib/postgresql/16/main
bin_dir: /usr/lib/postgresql/16/bin
config_dir: /var/lib/postgresql/16/main
pgpass: /tmp/pgpass0
authentication:
superuser:
username: postgres
password: 'postgres_secure_password'
replication:
username: replicator
password: 'repl_secure_password'
rewind:
username: postgres
password: 'postgres_secure_password'
parameters:
unix_socket_directories: '/var/run/postgresql'
create_replica_methods:
- basebackup
basebackup:
max-rate: 100M
checkpoint: fast
tags:
nofailover: false
noloadbalance: false
clonefrom: false
nosync: falseLes nœuds de secours utilisent une configuration identique avec leurs propres valeursname,connect_addressetlisten. Patroni s'occupe du reste : il détecte si un nœud doit être le leader ou une réplique en fonction de l'état DCS et configure PostgreSQL en conséquence.
etcd en tant que magasin de configuration distribué
etcd est le système nerveux d'un cluster Patroni. Il stocke l'identité actuelle du leader, la topologie du cluster, la configuration souhaitée et l'état de santé de chaque nœud. Un cluster etcd à trois nœuds est le minimum pour la production, tolérant la panne d'un nœud tout en maintenant le quorum.
# Install and configure etcd on three dedicated nodes
# /etc/etcd/etcd.conf.yml — Node etcd1 (10.0.2.10)
name: etcd1
data-dir: /var/lib/etcd
listen-client-urls: http://0.0.0.0:2379
listen-peer-urls: http://0.0.0.0:2380
advertise-client-urls: http://10.0.2.10:2379
initial-advertise-peer-urls: http://10.0.2.10:2380
initial-cluster: etcd1=http://10.0.2.10:2380,etcd2=http://10.0.2.11:2380,etcd3=http://10.0.2.12:2380
initial-cluster-state: new
initial-cluster-token: patroni-etcd-cluster
# Start etcd
systemctl enable --now etcd
# Verify cluster health
etcdctl endpoint health --cluster \
--endpoints=http://10.0.2.10:2379,http://10.0.2.11:2379,http://10.0.2.12:2379Pour les déploiements de production, activez TLS entre les pairs etcd et entre les clients etcd et Patroni. Le trafic etcd non chiffré expose les informations d'identification et la configuration du cluster aux attaquants au niveau du réseau.
HAProxy pour le routage de connexion
Patroni expose un REST API sur chaque nœud (port 8008 par défaut) qui indique si le nœud est le leader actuel ou une réplique. HAProxy utilise ces points de terminaison de vérification de l'état pour acheminer le trafic : les écritures sont dirigées vers le leader, les lectures sont dirigées vers des réplicas sains. Cela vous permet de diviser automatiquement les lectures en écriture sans aucune modification au niveau de l'application.
# /etc/haproxy/haproxy.cfg
global
maxconn 2000
log /dev/log local0
stats socket /var/run/haproxy.sock mode 660 level admin
defaults
mode tcp
log global
retries 3
timeout client 30m
timeout connect 4s
timeout server 30m
timeout check 5s
maxconn 1000
listen pg_write
bind *:5000
option httpchk GET /primary
http-check expect status 200
default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions
server node1 10.0.1.10:5432 check port 8008
server node2 10.0.1.11:5432 check port 8008
server node3 10.0.1.12:5432 check port 8008
listen pg_read
bind *:5001
balance roundrobin
option httpchk GET /replica
http-check expect status 200
default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions
server node1 10.0.1.10:5432 check port 8008
server node2 10.0.1.11:5432 check port 8008
server node3 10.0.1.12:5432 check port 8008
listen stats
bind *:7000
mode http
stats enable
stats uri /
stats refresh 10sLe point de terminaison/primaryrenvoie HTTP 200 uniquement sur le leader Patroni actuel. Le point de terminaison/replicarenvoie 200 en veille saine. Lorsqu'un basculement se produit, le nouveau serveur principal commence à renvoyer 200 sur/primaryet HAProxy redirige automatiquement le trafic d'écriture, généralement dans un seul intervalle de vérification de l'état (3 secondes). La directiveon-marked-down shutdown-sessionsmet immédiatement fin aux connexions existantes à un serveur principal défaillant, obligeant les clients à se reconnecter au nouveau leader.
PgBouncer pour le pooling de connexions
PostgreSQL crée un nouveau processus backend pour chaque connexion client. À grande échelle (des centaines ou des milliers de microservices, chacun maintenant des pools de connexions), la surcharge de création de processus et de consommation de mémoire devient importante. PgBouncer se situe entre l'application et PostgreSQL, maintenant un pool de connexions côté serveur et multiplexant les connexions client sur celles-ci.
# /etc/pgbouncer/pgbouncer.ini
[databases]
* = host=127.0.0.1 port=5432 dbname=appdb
[pgbouncer]
listen_addr = 0.0.0.0
listen_port = 6432
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 50
min_pool_size = 10
reserve_pool_size = 10
reserve_pool_timeout = 3
server_lifetime = 3600
server_idle_timeout = 600
server_connect_timeout = 5
server_login_retry = 3
log_connections = 1
log_disconnections = 1
stats_period = 60Lorsqu'il est utilisé avec Patroni, PgBouncer est généralement colocalisé sur chaque nœud PostgreSQL ou sur les nœuds HAProxy. Le mode pool dutransactionest le meilleur choix pour la plupart des charges de travail : il attribue une connexion au serveur pendant la durée d'une transaction et la renvoie au pool entre les transactions. C'est bien plus efficace que le modesession, qui maintient une connexion pendant toute la session client.
Archivage WAL et récupération ponctuelle
La réplicationStreaming protège contre les pannes de serveur, mais elle ne protège pas contre les erreurs logiques : unDROP TABLEaccidentel ou une mauvaise migration d'application est immédiatement répliqué sur tous les serveurs de secours. L'archivage WAL combiné à la récupération à un moment précis (PITR) vous permet de restaurer à tout moment avant que l'erreur ne se produise.
WAL copie les segments WAL terminés dans une archive durable - généralement un compartiment S3, un montage NFS ou un serveur de sauvegarde dédié. Des outils tels quepgBackRestetWAL-Ggèrent efficacement l'archivage avec la compression, le cryptage et le transfert parallèle.
# pgBackRest configuration — /etc/pgbackrest/pgbackrest.conf
[global]
repo1-type=s3
repo1-s3-bucket=pg-wal-archive
repo1-s3-endpoint=s3.eu-west-1.amazonaws.com
repo1-s3-region=eu-west-1
repo1-s3-key=AKIAIOSFODNN7EXAMPLE
repo1-s3-key-secret=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
repo1-path=/pgbackrest
repo1-retention-full=4
repo1-retention-diff=7
repo1-cipher-type=aes-256-cbc
repo1-cipher-pass=strong_encryption_passphrase
compress-type=zst
compress-level=3
process-max=4
[pg-ha-cluster]
pg1-path=/var/lib/postgresql/16/main
pg1-port=5432
# In postgresql.conf
archive_command = 'pgbackrest --stanza=pg-ha-cluster archive-push %p'
restore_command = 'pgbackrest --stanza=pg-ha-cluster archive-get %f "%p"'Pour effectuer une récupération à un moment précis, spécifiez un horodatage cible.
# Restore to a specific point in time
pgbackrest --stanza=pg-ha-cluster --type=time \
--target="2026-04-12 11:25:00" \
--target-action=promote \
restoreRéplication logique pour la synchronisation sélective des données
Alors que la réplication en streaming crée une copie physique exacte de l'ensemble du cluster de bases de données, la réplication logique fonctionne au niveau des tables, répliquant des tables individuelles ou des sous-ensembles de données entre des instances PostgreSQL indépendantes. Ceci est utile pour les mises à niveau de versions majeures sans temps d'arrêt, les réplicas en lecture interrégionaux qui n'ont besoin que de tables spécifiques, l'alimentation d'entrepôt de données et la distribution de données multi-locataires.
# On the publisher (source database)
wal_level = logical # must be 'logical' — higher than 'replica'
CREATE PUBLICATION app_pub FOR TABLE orders, customers, products;
# On the subscriber (target database)
CREATE SUBSCRIPTION app_sub
CONNECTION 'host=publisher-host port=5432 dbname=appdb user=replicator password=pass'
PUBLICATION app_pub;La réplication logiquepeut s'exécuter parallèlement à la réplication en continu. Un modèle courant consiste à utiliser la réplication en continu pour la haute disponibilité (basculement physique rapide) et la réplication logique pour les réplicas d'analyse interrégionaux qui n'ont besoin que d'un sous-ensemble de tables.
Réplication en continu multirégion
Pour la reprise après sinistre et les performances de lecture globales, les clusters PostgreSQL peuvent s'étendre sur plusieurs régions. Le modèle standard est la réplication synchrone au sein d'une région (pour aucune perte de données lors du basculement local) et la réplication asynchrone entre les régions (pour éviter les pénalités de latence entre régions à chaque écriture). Chaque région possède son propre HAProxy pour le routage de lecture local.
Procédures de basculement et de basculementIl est essentiel de comprendre la différence entre le basculement et le basculement. Un basculementest une promotion imprévue déclenchée par l'échec du serveur principal actuel. Un basculementest un changement de rôle planifié et progressif, généralement effectué avant la maintenance. Patroni soutient les deux.
Commutation planifiée
# List cluster members
patronictlctl -c /etc/patroni/patroni.yml list
# Perform switchover to a specific node
patronictlctl -c /etc/patroni/patroni.yml switchover \
--master node1 --candidate node2 --force
# Or use the Patroni REST API
curl -s http://10.0.1.10:8008/switchover -XPOST \
-d '{"leader": "node1", "candidate": "node2"}'Lors d'un basculement, Patroni rétrograde le primaire actuel en tant que serveur de secours, promeut le candidat cible et reconfigure tous les autres serveurs de secours pour suivre le nouveau primaire. Le processus prend 5 à 15 secondes. HAProxy détecte le changement grâce à des contrôles de santé et redirige automatiquement le trafic.
Séquence de basculement automatique
Lorsque le serveur principal tombe en panne de manière inattendue, Patroni suit une séquence précise pour restaurer le service. Le diagramme suivant illustre les étapes.
Prévention des divisions cérébrales
Split-brain — où deux nœuds croient simultanément qu'ils sont les principaux — est le mode de défaillance le plus dangereux de tout système haute disponibilité. Patroni prévient la division du cerveau grâce à plusieurs mécanismes :
- Verrouillage leader basé sur DCS :Un seul nœud peut détenir la clé leader dans etcd à tout moment. La clé a une durée de vie et le leader doit la renouveler en permanence. Si une partition réseau isole le leader d'etcd, la clé expire et le leader se rétrograde.
- Watchdog :Patroni peut configurer un périphérique de surveillance Linux (
/dev/watchdog). Si Patroni perd l'accès au DCS et ne peut pas confirmer qu'il doit rester leader, le chien de garde redémarrera ou éteindra le nœud – un mécanisme de clôture rigide qui garantit que l'ancien principal ne continue pas à accepter les écritures. - pg_rewind :Lorsqu'un ancien serveur principal revient en ligne, il peut contenir des enregistrements WAL qui n'ont jamais été répliqués.
pg_rewindrembobine la chronologie jusqu'au point de divergence, permettant au nœud de se rejoindre en mode veille sans sauvegarde de base complète. Le paramètreuse_pg_rewind: truede Patroni automatise cela.
# Enable watchdog in Patroni config
bootstrap:
dcs:
postgresql:
use_pg_rewind: true
parameters:
wal_log_hints: 'on' # required for pg_rewind
# Watchdog configuration
watchdog:
mode: required # 'off', 'automatic', or 'required'
device: /dev/watchdog
safety_margin: 5 # seconds before TTL expiry to trigger watchdogrepmgr comme alternative à Patroni
repmgrest un autre outil HA populaire pour PostgreSQL. Il offre des fonctionnalités de gestion de veille, de basculement automatique et de basculement. Cependant, il adopte une approche fondamentalement différente de celle de Patroni. repmgr utilise un nœud témoin et un démon (repmgrd) pour la détection des pannes plutôt qu'un magasin de consensus distribué. Cela le rend plus simple à déployer mais plus susceptible de se diviser dans des scénarios de partition réseau complexes.
# repmgr.conf on the primary
node_id=1
node_name='node1'
conninfo='host=10.0.1.10 user=repmgr dbname=repmgr connect_timeout=2'
data_directory='/var/lib/postgresql/16/main'
failover=automatic
promote_command='repmgr standby promote -f /etc/repmgr.conf --log-to-file'
follow_command='repmgr standby follow -f /etc/repmgr.conf --log-to-file --upstream-node-id=%n'
monitoring_history=yes
monitor_interval_secs=5
reconnect_attempts=6
reconnect_interval=10Pour les nouveaux déploiements, Patroni est le choix recommandé en raison de ses garanties plus solides de prévention des divisions cérébrales et de sa communauté de développement plus active. repmgr reste une option raisonnable pour les configurations plus simples ou les organisations déjà investies dans l'outil.
Modèles de déploiement cloudDéploiement AWS : EC2, EBS et Route53
Sur AWS, déployez chaque nœud PostgreSQL + Patroni sur une instance EC2 avec des volumes EBS gp3 ou io2. Utilisez des instances distinctes sur plusieurs zones de disponibilité pour la haute disponibilité. Les nœuds etcd doivent également s’étendre sur les AZ.
# Terraform sketch for PostgreSQL HA on AWS
resource "aws_instance" "pg_node" {
count = 3
ami = "ami-0abcdef1234567890" # Ubuntu 22.04
instance_type = "r6g.2xlarge" # 8 vCPU, 64GB RAM
subnet_id = aws_subnet.private[count.index].id
vpc_security_group_ids = [aws_security_group.pg_sg.id]
availability_zone = element(["eu-west-1a", "eu-west-1b", "eu-west-1c"], count.index)
root_block_device {
volume_size = 50
volume_type = "gp3"
}
tags = {
Name = "pg-node-${count.index + 1}"
Role = "patroni"
}
}
resource "aws_ebs_volume" "pg_data" {
count = 3
availability_zone = element(["eu-west-1a", "eu-west-1b", "eu-west-1c"], count.index)
size = 500
type = "gp3"
iops = 6000
throughput = 250
encrypted = true
tags = {
Name = "pg-data-${count.index + 1}"
}
}
resource "aws_route53_health_check" "pg_primary" {
count = 3
ip_address = aws_instance.pg_node[count.index].private_ip
port = 8008
type = "HTTP"
resource_path = "/primary"
failure_threshold = 3
request_interval = 10
}Utilisez un Network Load Balancer (NLB) au lieu de HAProxy si vous préférez une solution gérée par AWS. Le NLB peut utiliser les vérifications de l'état du groupe cible par rapport au Patroni REST API pour acheminer le trafic vers le serveur principal actuel.
Déploiement Azure : machines virtuelles, disques gérés et Azure LB
Sur Azure, utilisez des machines virtuelles Standard_E8s_v5 (mémoire optimisée) avec des disques gérés SSD Premium pour les volumes de données. Déployez dans les zones de disponibilité. Azure Load Balancer fournit l'équivalent de HAProxy avec des sondes de santé contre le Patroni REST API.
# Azure CLI — create PostgreSQL VM with Managed Disk
az vm create \
--resource-group pg-ha-rg \
--name pg-node-1 \
--image Canonical:0001-com-ubuntu-server-jammy:22_04-lts:latest \
--size Standard_E8s_v5 \
--zone 1 \
--vnet-name pg-vnet \
--subnet pg-subnet \
--nsg pg-nsg \
--admin-username pgadmin \
--ssh-key-value ~/.ssh/id_rsa.pub
az disk create \
--resource-group pg-ha-rg \
--name pg-data-1 \
--size-gb 512 \
--sku Premium_LRS \
--zone 1
az vm disk attach \
--resource-group pg-ha-rg \
--vm-name pg-node-1 \
--name pg-data-1
# Azure Load Balancer health probe for Patroni
az network lb probe create \
--resource-group pg-ha-rg \
--lb-name pg-lb \
--name patroni-primary-probe \
--protocol Http \
--port 8008 \
--path /primary \
--interval 5 \
--threshold 3Déploiement GCP: Compute Engine et équilibrage de charge cloud
Sur GCP, utilisez des instances n2-highmem-8 (8 vCPU, 64 Go de RAM) avec des disques SSD persistants. Répartir entre les zones d'une région. Utilisez un équilibreur de charge TCP/UDP interne avec les vérifications de l'état Patroni.
# GCP — create instance and persistent disk
gcloud compute instances create pg-node-1 \
--zone=europe-west1-b \
--machine-type=n2-highmem-8 \
--image-family=ubuntu-2204-lts \
--image-project=ubuntu-os-cloud \
--boot-disk-size=50GB \
--network=pg-network \
--subnet=pg-subnet
gcloud compute disks create pg-data-1 \
--zone=europe-west1-b \
--size=500GB \
--type=pd-ssd
gcloud compute instances attach-disk pg-node-1 \
--disk=pg-data-1 \
--zone=europe-west1-b
# Health check for Patroni primary endpoint
gcloud compute health-checks create http patroni-primary-check \
--port=8008 \
--request-path=/primary \
--check-interval=5s \
--timeout=5s \
--unhealthy-threshold=3 \
--healthy-threshold=2Déploiement k3s Bare Metal avec Rancher et Longhorn
Pour les organisations qui exploitent leur propre matériel, le déploiement de PostgreSQL HA sur bare metal avec k3s, Rancher et Longhorn fournit une infrastructure entièrement open source et indépendante du cloud. k3s est une distribution Kubernetes légère qui fonctionne efficacement sur des serveurs nus sans la surcharge d'une distribution Kubernetes complète.
k3s et configuration Longhorn
# Install k3s on the first server node
curl -sfL https://get.k3s.io | K3S_TOKEN=my-cluster-token \
INSTALL_K3S_EXEC="server --cluster-init --disable traefik --disable servicelb" sh -
# Join additional server nodes
curl -sfL https://get.k3s.io | K3S_TOKEN=my-cluster-token \
K3S_URL=https://10.0.0.1:6443 \
INSTALL_K3S_EXEC="server" sh -
# Install Longhorn for distributed block storage
helm repo add longhorn https://charts.longhorn.io
helm install longhorn longhorn/longhorn \
--namespace longhorn-system --create-namespace \
--set defaultSettings.defaultDataPath=/mnt/longhorn \
--set defaultSettings.replicaCount=3 \
--set defaultSettings.storageMinimalAvailablePercentage=15
# Deploy PostgreSQL with Patroni using the Zalando Postgres Operator
helm repo add postgres-operator-charts https://opensource.zalando.com/postgres-operator/charts/postgres-operator
helm install postgres-operator postgres-operator-charts/postgres-operator \
--namespace postgres-system --create-namespace# PostgreSQL cluster manifest for the Zalando Postgres Operator
apiVersion: acid.zalan.do/v1
kind: postgresql
metadata:
name: pg-ha-cluster
namespace: production
spec:
teamId: "platform"
numberOfInstances: 3
volume:
size: 500Gi
storageClass: longhorn
users:
appuser:
- superuser
- createdb
replicator: []
databases:
appdb: appuser
postgresql:
version: "16"
parameters:
shared_buffers: "16GB"
effective_cache_size: "48GB"
work_mem: "256MB"
maintenance_work_mem: "2GB"
max_connections: "200"
max_wal_senders: "10"
wal_level: replica
synchronous_commit: "on"
wal_keep_size: "2GB"
archive_mode: "on"
track_commit_timestamp: "on"
patroni:
ttl: 30
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576
synchronous_mode: true
resources:
requests:
cpu: "4"
memory: 32Gi
limits:
cpu: "8"
memory: 64GiKeepalived pour HAProxy VIP
# /etc/keepalived/keepalived.conf on lb1
vrrp_script chk_haproxy {
script "killall -0 haproxy"
interval 2
weight 2
}
vrrp_instance VI_PG {
state MASTER
interface eth0
virtual_router_id 52
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass pgha_vip_pass
}
virtual_ipaddress {
10.0.0.100/24
}
track_script {
chk_haproxy
}
}Surveillance de la réplication PostgreSQL
La surveillance de l’intégrité de la réplication n’est pas négociable en production. PostgreSQL fournit plusieurs vues intégrées à cet effet, et Prometheus avec Grafana offre la visibilité et les alertes à long terme dont vous avez besoin.
Requêtes de surveillance intégrées
-- Check replication status on the primary
SELECT
client_addr,
application_name,
state,
sync_state,
sent_lsn,
write_lsn,
flush_lsn,
replay_lsn,
(sent_lsn - replay_lsn) AS replication_lag_bytes,
write_lag,
flush_lag,
replay_lag
FROM pg_stat_replication;
-- Check replication slot status
SELECT
slot_name,
slot_type,
active,
wal_status,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS slot_lag
FROM pg_replication_slots;
-- Check standby recovery status (run on standby)
SELECT
pg_is_in_recovery() AS is_standby,
pg_last_wal_receive_lsn() AS last_received,
pg_last_wal_replay_lsn() AS last_replayed,
pg_last_xact_replay_timestamp() AS last_replayed_timestamp,
EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp()))::int AS replay_lag_seconds;
-- Monitor WAL generation rate
SELECT
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), '0/0')) AS total_wal_generated,
pg_size_pretty(sum(size)) AS wal_directory_size
FROM pg_ls_waldir();
-- Check for long-running queries that could block replication
SELECT
pid,
now() - pg_stat_activity.query_start AS duration,
query,
state
FROM pg_stat_activity
WHERE (now() - pg_stat_activity.query_start) > interval '5 minutes'
AND state != 'idle'
ORDER BY duration DESC;Pile Prometheus et Grafana
Lepostgres_exporterexpose les métriques PostgreSQL au format Prometheus. Combiné avec lepatroni_exporter, vous obtenez une visibilité complète sur les performances de la base de données et l'état du cluster HA.
# Deploy postgres_exporter as a sidecar or standalone
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
# Custom queries for postgres_exporter
# /etc/postgres_exporter/queries.yaml
pg_replication_lag:
query: |
SELECT
CASE WHEN pg_is_in_recovery() THEN
EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp()))::float
ELSE 0 END AS lag_seconds
master: true
metrics:
- lag_seconds:
usage: "GAUGE"
description: "Replication lag in seconds"
pg_replication_slots:
query: |
SELECT
slot_name,
active::int AS active,
pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)::float AS slot_lag_bytes
FROM pg_replication_slots
master: true
metrics:
- slot_name:
usage: "LABEL"
- active:
usage: "GAUGE"
description: "Whether the slot is active"
- slot_lag_bytes:
usage: "GAUGE"
description: "Slot lag in bytes"# PrometheusRule for PostgreSQL HA alerts
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: postgresql-ha-alerts
namespace: monitoring
spec:
groups:
- name: postgresql-replication
rules:
- alert: PostgreSQLReplicationLagHigh
expr: pg_replication_lag_seconds > 30
for: 5m
labels:
severity: warning
annotations:
summary: "PostgreSQL replication lag exceeds 30s on {{ $labels.instance }}"
- alert: PostgreSQLReplicationSlotInactive
expr: pg_replication_slots_active == 0
for: 5m
labels:
severity: critical
annotations:
summary: "Replication slot {{ $labels.slot_name }} is inactive"
- alert: PostgreSQLReplicationSlotLagHigh
expr: pg_replication_slots_slot_lag_bytes > 1073741824
for: 10m
labels:
severity: warning
annotations:
summary: "Replication slot lag exceeds 1GB on {{ $labels.slot_name }}"
- alert: PatroniClusterUnhealthy
expr: patroni_cluster_members_count < 3
for: 2m
labels:
severity: critical
annotations:
summary: "Patroni cluster has fewer than 3 members"Paramètres de réglage de la production duLa configuration par défaut duPostgreSQL est conservatrice, adaptée à un petit environnement d'hébergement partagé. Les clusters de production HA nécessitent un réglage minutieux des paramètres de réplication, de mémoire et de WAL. Le tableau suivant résume les paramètres les plus importants pour un serveur de 64 Go de RAM avec stockage NVMe.
# postgresql.conf — Production HA tuning
# === Replication ===
wal_level = replica
max_wal_senders = 10
max_replication_slots = 10
wal_keep_size = 4GB
synchronous_commit = on
synchronous_standby_names = 'ANY 1 (standby1, standby2)'
track_commit_timestamp = on
wal_log_hints = on
# === WAL ===
min_wal_size = 1GB
max_wal_size = 8GB
wal_buffers = 64MB
wal_compression = zstd
archive_mode = on
archive_timeout = 300
checkpoint_completion_target = 0.9
checkpoint_timeout = 15min
# === Memory ===
shared_buffers = 16GB # 25% of RAM
effective_cache_size = 48GB # 75% of RAM
work_mem = 256MB # per-operation sort/hash memory
maintenance_work_mem = 2GB # for VACUUM, CREATE INDEX
huge_pages = try
# === Connections ===
max_connections = 200 # use PgBouncer for higher client counts
superuser_reserved_connections = 5
# === Query Performance ===
random_page_cost = 1.1 # SSD storage
effective_io_concurrency = 200 # NVMe SSD
default_statistics_target = 500
jit = on
# === Logging ===
log_min_duration_statement = 500 # log queries > 500ms
log_checkpoints = on
log_connections = on
log_disconnections = on
log_lock_waits = on
log_temp_files = 0
log_autovacuum_min_duration = 0
# === Autovacuum ===
autovacuum_max_workers = 4
autovacuum_naptime = 30s
autovacuum_vacuum_cost_limit = 2000
autovacuum_vacuum_scale_factor = 0.05
autovacuum_analyze_scale_factor = 0.02Réglage des paramètres Patroni DCS
La relation entre les paramètresttl,loop_waitetretry_timeoutde Patroni a un impact direct sur la vitesse de basculement et le risque de faux positifs. Une durée de vie plus courte signifie une détection de basculement plus rapide, mais augmente le risque de basculements inutiles lors de brefs incidents réseau.
# Conservative (production default)
ttl: 30
loop_wait: 10
retry_timeout: 10
# Failover detection: ~30-40 seconds
# Aggressive (low-latency failover)
ttl: 15
loop_wait: 5
retry_timeout: 5
# Failover detection: ~15-20 seconds
# Warning: Higher risk of false failovers in unstable networksIngénierie du chaos et tests de basculement
Un système de basculement qui n'a jamais été testé est un système qui ne fonctionne pas. L'ingénierie du chaos applique des pannes contrôlées pour valider que votre configuration HA se comporte correctement dans des conditions de panne réelles. Chaque cluster Patroni doit être soumis à des exercices de basculement réguliers.
Manuel de test de basculement
# 1. Verify cluster health before testing
patronictlctl -c /etc/patroni/patroni.yml list
+----------+---------+---------+----+-----------+
| Member | Host | Role | TL | Lag in MB |
+----------+---------+---------+----+-----------+
| node1 | 10.0.1.10| Leader | 5 | |
| node2 | 10.0.1.11| Replica | 5 | 0 |
| node3 | 10.0.1.12| Replica | 5 | 0 |
+----------+---------+---------+----+-----------+
# 2. Simulate primary crash (on node1)
sudo systemctl stop patroni
# Or more aggressive: sudo kill -9 $(pgrep -f patroni)
# 3. Monitor failover (from any node with patronictl)
watch -n 1 'patronictl -c /etc/patroni/patroni.yml list'
# 4. Verify new leader is elected (within 30-45 seconds)
# Expected: node2 or node3 promoted to Leader
# 5. Test write availability through HAProxy
PGPASSWORD=app_password psql -h haproxy-host -p 5000 -U appuser -d appdb \
-c "INSERT INTO health_check (ts) VALUES (now()) RETURNING *;"
# 6. Restart the former primary
sudo systemctl start patroni
# Patroni will use pg_rewind to rejoin as a replica
# 7. Verify the former primary rejoins as replica
patronictlctl -c /etc/patroni/patroni.yml listTest de partition réseau
# Simulate network partition on the primary using iptables
# Block all traffic to etcd from the primary
sudo iptables -A OUTPUT -d 10.0.2.10 -j DROP
sudo iptables -A OUTPUT -d 10.0.2.11 -j DROP
sudo iptables -A OUTPUT -d 10.0.2.12 -j DROP
# Expected behaviour:
# 1. Primary loses DCS access
# 2. Leader key TTL expires
# 3. Primary demotes itself (with watchdog, node may reboot)
# 4. Standby acquires leader lock and promotes
# 5. After clearing iptables rules, former primary rejoins as replica
# Clean up
sudo iptables -D OUTPUT -d 10.0.2.10 -j DROP
sudo iptables -D OUTPUT -d 10.0.2.11 -j DROP
sudo iptables -D OUTPUT -d 10.0.2.12 -j DROPTests de chaos automatisés avec Toxiproxy
# Run Toxiproxy alongside your Patroni cluster
# Create proxies for etcd and replication connections
toxiproxy-cli create etcd_proxy -l 0.0.0.0:12379 -u 10.0.2.10:2379
toxiproxy-cli create pg_repl_proxy -l 0.0.0.0:15432 -u 10.0.1.10:5432
# Add latency to etcd connections (simulates degraded network)
toxiproxy-cli toxic add etcd_proxy -t latency -a latency=500 -a jitter=200
# Add bandwidth limit to replication (simulates WAN replication)
toxiproxy-cli toxic add pg_repl_proxy -t bandwidth -a rate=1024
# Completely sever the connection (simulates network partition)
toxiproxy-cli toxic add etcd_proxy -t timeout -a timeout=0
# Monitor Patroni behaviour and verify correct failover
watch -n 2 'curl -s http://10.0.1.10:8008/patroni | python3 -m json.tool'Script de validation continue
#!/bin/bash
# continuous_ha_check.sh — Run during chaos tests to measure availability
HAPROXY_HOST="10.0.0.100"
WRITE_PORT=5000
READ_PORT=5001
DATABASE="appdb"
USER="appuser"
LOGFILE="/var/log/ha_test_$(date +%Y%m%d_%H%M%S).log"
write_count=0
write_fail=0
read_count=0
read_fail=0
while true; do
ts=$(date '+%Y-%m-%d %H:%M:%S.%3N')
# Test write path
if PGPASSWORD=app_password psql -h $HAPROXY_HOST -p $WRITE_PORT \
-U $USER -d $DATABASE -c "SELECT 1" &>/dev/null; then
((write_count++))
else
((write_fail++))
echo "$ts WRITE_FAIL total_fails=$write_fail" >> $LOGFILE
fi
# Test read path
if PGPASSWORD=app_password psql -h $HAPROXY_HOST -p $READ_PORT \
-U $USER -d $DATABASE -c "SELECT 1" &>/dev/null; then
((read_count++))
else
((read_fail++))
echo "$ts READ_FAIL total_fails=$read_fail" >> $LOGFILE
fi
total=$((write_count + write_fail))
if (( total % 100 == 0 )); then
write_avail=$(echo "scale=2; $write_count * 100 / $total" | bc)
read_total=$((read_count + read_fail))
read_avail=$(echo "scale=2; $read_count * 100 / $read_total" | bc)
echo "$ts Writes: ${write_avail}% ($write_count/$total) Reads: ${read_avail}% ($read_count/$read_total)"
fi
sleep 0.5
doneAdvanced : réplication en cascade et mises en veille différées
Pour les grands clusters, la réplication en cascade réduit la charge sur le cluster principal. Au lieu que tous les serveurs de secours soient répliqués directement à partir du serveur principal, certains serveurs de secours se répliquent à partir d'autres serveurs de secours. Cela crée une topologie arborescente dans laquelle le serveur principal alimente deux serveurs de secours, et ces serveurs de secours alimentent des serveurs de secours supplémentaires en aval.
# postgresql.auto.conf on a cascading standby
primary_conninfo = 'host=standby1-host port=5432 user=replicator application_name=cascade1'
primary_slot_name = 'cascade1_slot'Unen veille différéeapplique intentionnellement les enregistrements WAL avec un délai (généralement de 1 à 4 heures). Cela fournit une défense contre les erreurs logiques (suppressions accidentelles, mauvaises migrations) qui sont immédiatement répliquées sur les serveurs de secours synchrones. Si un sinistre survient, vous pouvez arrêter la relecture des WAL en mode veille différée et récupérer les données antérieures à l'erreur.
# postgresql.conf on delayed standby
recovery_min_apply_delay = '1h'Stratégies de chaîne de connexionLes applicationsse connectant à un cluster géré par Patroni doivent toujours se connecter via HAProxy ou utiliser la chaîne de connexion multi-hôtes intégrée de PostgreSQL avectarget_session_attrs. Cela permet un basculement côté client sans dépendre d'un équilibreur de charge.
# Multi-host connection string with target_session_attrs
# The client tries each host in order and connects to the one matching the target attribute
postgresql://appuser:password@node1:5432,node2:5432,node3:5432/appdb?target_session_attrs=read-write&sslmode=require
# For read-only connections
postgresql://appuser:password@node1:5432,node2:5432,node3:5432/appdb?target_session_attrs=prefer-standby&sslmode=requireCette approche fonctionne bien pour les applications qui ne peuvent pas être facilement reconfigurées pour pointer vers un VIP HAProxy. La bibliothèque client PostgreSQL (libpq) gère le basculement de manière transparente.
Renforcement de la sécurité
Un cluster PostgreSQL HA de production doit appliquer le chiffrement en transit et au repos, utiliser une authentification forte et limiter l'exposition du réseau.
# Enable TLS in postgresql.conf
ssl = on
ssl_cert_file = '/etc/postgresql/certs/server.crt'
ssl_key_file = '/etc/postgresql/certs/server.key'
ssl_ca_file = '/etc/postgresql/certs/ca.crt'
ssl_min_protocol_version = 'TLSv1.3'
# Require TLS for all connections in pg_hba.conf
hostssl replication replicator 10.0.0.0/16 scram-sha-256
hostssl all all 10.0.0.0/16 scram-sha-256
# etcd TLS
# In Patroni config
etcd3:
hosts:
- 10.0.2.10:2379
- 10.0.2.11:2379
- 10.0.2.12:2379
protocol: https
cacert: /etc/patroni/certs/etcd-ca.crt
cert: /etc/patroni/certs/etcd-client.crt
key: /etc/patroni/certs/etcd-client.keyStratégie de sauvegardepour les clusters haute disponibilité
Une stratégie de sauvegarde complète pour les clusters Patroni doit inclure un archivage WAL continu, des sauvegardes complètes régulières et des sauvegardes différentielles ou incrémentielles entre les sauvegardes complètes. pgBackRest est l'outil recommandé pour la gestion des sauvegardes PostgreSQL de production.
# Schedule backups via cron
# Full backup weekly (Sunday 2 AM)
0 2 * * 0 pgbackrest --stanza=pg-ha-cluster --type=full backup
# Differential backup daily (2 AM, Mon-Sat)
0 2 * * 1-6 pgbackrest --stanza=pg-ha-cluster --type=diff backup
# Verify backup integrity
pgbackrest --stanza=pg-ha-cluster --set=latest info
# Verify backup can be restored (dry run)
pgbackrest --stanza=pg-ha-cluster --set=latest verify
# List all backups
pgbackrest --stanza=pg-ha-cluster info
full backup: 20260412-020000F
timestamp: 2026-04-12 02:00:00 +0000
wal start/stop: 000000050000000000000040 / 000000050000000000000042
database size: 150GB, backup size: 150GB
repository size: 45GB (compressed)
diff backup: 20260412-020000F_20260413-020000D
timestamp: 2026-04-13 02:00:00 +0000
database size: 151GB, backup size: 2.1GB
repository size: 650MB (compressed)Résumé du Runbook opérationnel duChaque équipe exécutant un cluster Patroni doit conserver un runbook couvrant les scénarios suivants. Des procédures documentées et testées transforment une panne stressante en une opération de routine.
# === Quick Reference Commands ===
# Cluster status
patronictlctl -c /etc/patroni/patroni.yml list
patronictlctl -c /etc/patroni/patroni.yml history
# Planned switchover
patronictlctl -c /etc/patroni/patroni.yml switchover --master node1 --candidate node2
# Restart PostgreSQL on a specific node (rolling restart)
patronictlctl -c /etc/patroni/patroni.yml restart pg-ha-cluster node2
# Reload PostgreSQL configuration without restart
patronictlctl -c /etc/patroni/patroni.yml reload pg-ha-cluster
# Pause automatic failover (during maintenance)
patronictlctl -c /etc/patroni/patroni.yml pause
# Resume automatic failover
patronictlctl -c /etc/patroni/patroni.yml resume
# Edit DCS configuration (applies to all nodes)
patronictlctl -c /etc/patroni/patroni.yml edit-config
# Reinitialise a failed replica
patronictlctl -c /etc/patroni/patroni.yml reinit pg-ha-cluster node3
# Check Patroni REST API directly
curl -s http://10.0.1.10:8008/patroni | python3 -m json.tool
curl -s http://10.0.1.10:8008/cluster | python3 -m json.toolAnalyse comparative des performances duAvant de passer en production, évaluez votre cluster haute disponibilité pour établir des performances de référence et vérifiez que la latence de réplication synchrone est acceptable pour votre charge de travail.
# Benchmark with pgbench — initialise test data
pgbench -i -s 100 -h haproxy-host -p 5000 -U appuser appdb
# Run write-heavy benchmark (measures sync replication impact)
pgbench -h haproxy-host -p 5000 -U appuser -c 32 -j 8 -T 300 appdb
# Compare with async: temporarily set synchronous_commit = off
# Run read-only benchmark through read replica port
pgbench -h haproxy-host -p 5001 -U appuser -c 64 -j 16 -T 300 -S appdb
# Measure failover impact on transactions
# Run pgbench in background, then trigger a failover
pgbench -h haproxy-host -p 5000 -U appuser -c 8 -j 4 -T 600 appdb &
sleep 60 && patronictl switchover --master node1 --candidate node2 --forceConclusion
La haute disponibilité native PostgreSQL avec Patroni n'est pas une solution à outil unique : il s'agit d'un système intégré de réplication en continu, de consensus distribué, de routage des connexions, de regroupement de connexions, d'archivage WAL, de surveillance et de discipline opérationnelle. Chaque couche répond à un mode de défaillance spécifique : la réplication en streaming gère la redondance des données, Patroni gère la coordination du basculement automatique, etcd fournit le consensus distribué nécessaire à l'élection du leader sans split-brain, HAProxy achemine les connexions vers le bon leader, PgBouncer gère la surcharge de connexion à grande échelle et l'archivage WAL avec pgBackRest fournit la dernière ligne de défense contre les erreurs logiques et la reprise après sinistre.
Les modèles de déploiement varient selon les environnements : AWS avec NLB et Route53, Azure avec zones de disponibilité et Azure Load Balancer, GCP avec groupes d'instances gérés régionaux ou bare metal avec k3s, Longhorn et Keepalived – mais l'architecture de base reste la même. Trois nœuds PostgreSQL ou plus gérés par Patroni, soutenus par un cluster etcd à trois nœuds, dirigés par un équilibreur de charge qui suit les points de terminaison de vérification de l'état de Patroni.
L'investissement le plus important que vous puissiez faire n'est pas dans la configuration, mais dans les tests. Exécutez des exercices de basculement tous les mois. Injectez des partitions réseau. Tuez les processus de manière inattendue. Mesurez le temps de récupération et la perte de données. Créez des tableaux de bord qui affichent le délai de réplication, le taux de génération de WAL, la saturation du pool de connexions et l'état du DCS en temps réel. La confiance que vous procurent les tests systématiques est ce qui différencie un cluster qui survit à sa première panne réelle de celui qui transforme une panne de serveur en un incident ayant un impact sur l'activité.
PostgreSQL vous donne toutes les primitives de réplication. Patroni vous confie l'orchestration. etcd vous donne le consensus. Votre travail consiste à les relier correctement, à les adapter à votre charge de travail et à les valider en continu. Ce guide vous a donné les plans : construisez, testez et exploitez désormais en toute confiance.