Workstation Logo
Produits
Labs IAAgents OpenAIAgents ClaudeGrok BotWorkstation CRM (WSL CRM)MarketingTous les Produits
Solutions IA
Stations de Travail IAAI SME PackagesIA PrivéeClusters GPUIA EdgeLaboratoire IA EntrepriseIA par Industrie
Services
Modernisation de plateformeIngénierie numériqueDonnées et activation IAOpérations autonomesConseil IAAutomatisation DevOpsCybersécuritéDéveloppement logicielCréation d'agentsMise en place MLOps
À Propos
PartenairesTémoignages Clients
Articles
Documentation
WSL ProxyRing PromoterWSL VaultJobshoutSysOps 24/7
Blog
Nous ContacterLogin
Workstation

Stations de travail IA, logiciels multi-agents IA, infrastructure GPU et solutions d'agents intelligents pour les entreprises modernes.

Nous Contacter

Solutions IA

Stations de Travail IAAI SME PackagesIA PrivéeClusters GPUIA EdgeLaboratoire IA EntrepriseIA par Industrie

Produits

Tous les ProduitsWSL CRM & ERPMarketingAgents OpenAIWSL ProxyRing PromoterWSL VaultJobshoutSysOps 24/7

Entreprise

À ProposPourquoi WorkstationPartenairesTémoignages ClientsTarificationContact

Ressources

ArticlesDocumentationBlogRechercherPlan du Site
Bureau Royaume-Uni
77-79 Marlowes, Hemel Hempstead HP1 1LFItinéraire : prenez la sortie 20 de la M25, Outer LondonN° d'entreprise: 11641870Lun - Ven : 9h00 - 18h00 GMT
+44 7515 356 146
Bureau Belgique
Workstation SRL, Rue Vanderkindere 34, 1180 Uccle, BrusselsBE 0751.518.683Lun - Ven : 9h00 - 18h00 CET
+32 492 45 67 46
Bureau Inde
#159 Sector 9, Pocket 1, DDA Flats, 110077 Dwarka, New Delhi
+91 98881 98841

© 2026 Workstation AI. Tous droits réservés.

ConfidentialitéCookiesConditions d'UtilisationPlan du site web

Loading blog...

Home / Blog
DatabaseKubernetesDevOpsBackend

Haute disponibilité native PostgreSQL : stratégies Patroni, réplication en streaming et basculement de production

PostgreSQL Native HA avec Patroni, Streaming Replication et HAProxy

Balinder Walia12 avril 202634 min read

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éplication

Streaming 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 = 5432
L'authentification

pour 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-256

Cré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.conf

L'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éplication

Streaming 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.

Architecture Patroni HA— Cluster PostgreSQL à 3 nœudsClients d'applicationÉquilibreur de charge HAProxyport 5000 (RW) · port 5001 (RO)Primaire (chef)PostgreSQL 16 + Patronnœud1 — 10.0.1.10:5432Veille 1 (synchronisation)PostgreSQL 16 + Patronnœud2 — 10.0.1.11:5432Veille 2 (Async)PostgreSQL 16 + Patronnœud3 — 10.0.1.12:5432Synchronisation du streamingdiffusion asynchroneGrappe etcd (DCS)3 nœuds — élection du leader et amp; magasin de configurationPrimaireVeilleetc. DCSHAProxyConfiguration

Patroni 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: false

Les 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:2379

Pour 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 10s

Le 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 = 60

Lorsqu'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éplication

Streaming 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.

L'archivage

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 \
  restore

Ré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 logique

peut 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.

Réplication en streaming PostgreSQL multirégionRégion 1 — AWS eu-west-1Primairepg-node1 (RW)Synchronisation en veillepg-node2 (RO)SynchronisationHAProxy (local)Région 2 — Azure ouesteuropeVeille asynchronepg-node3 (RO)Veille asynchronepg-node4 (RO)SynchronisationHAProxy (local)Région 3 — GCP Europe-Ouest1Veille asynchronepg-node5 (RO)Veille asynchronepg-node6 (RO)SynchronisationHAProxy (local)WAL asynchroneexpédition WAL asynchroneArchive WAL partagée (S3 / Blob / GCS)pgBackRest ou WAL-G — PITR inter-régionGlobal DNS (Route53 / Gestionnaire de trafic / Cloud DNS)Légende :Réplication synchronisée (dans la région)Réplication asynchrone (entre régions)Archivage WAL vers le stockage objetPrimaireVeilleProcédures de basculement et de basculement

Il 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.

Séquence de basculement automatique Patroni1Échec du primaireCrash du, partition réseau ouPanne de disquesur le nœud 12Patroni détecte via DCSLa durée de vie de la clé leaderexpire dans etcd(TTL 30 s par défaut)3Élection du chefStandbys course pour acquérirVerrouillage du leaderdans etcd4Veille promueWinner exécute pg_ctl pour promouvoiret devient le nouveauprincipal5Mises à jour HAProxyLes contrôles de santé dudétectent un nouveauprincipal/primary renvoie 200 sur le nouveau leader6ClientsreconnectésLes applicationsse reconnectent via HAProxyau nouveau primaire de manière transparenteTemps de basculement total typiqueExpiration TTL DCS du: ~15-30 sÉlection: ~2-5sDétection HAProxy : ~3-10 sReconnecter≈ 20-45 secondes au totalParamètres clés duaffectant la vitesse de basculement :• ttl (30 s par défaut) — Combien de temps avant l'expiration de la clé leader dans DCS• loop_wait (10 s par défaut) — À quelle fréquence Patroni vérifie l'état du cluster• retry_timeout (10 s par défaut) — Délai d'expiration pour les opérations DCS et PostgreSQL• maximum_lag_on_failover (1 Mo) : favorise uniquement les mises en veille pendant ce délai de réplication.

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 :

  1. 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.
  2. 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.
  3. 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 watchdog

repmgr 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=10

Pour 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 cloud

Dé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 3
Dé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=2

Dé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.

Bare Metal k3s HA — PostgreSQL avec PatroniIP virtuelle(VRRP conservé) — 10.0.0.100VIP flottant pour HAProxy actif/veilleHAProxy (actif)métal nu-lb1HAProxy (veille)métal nu-lb2Cluster k3s (géré par Rancher)k3s Nœud 1 (serveur)PostgreSQL PrimairePatroni + PgBouncerVolume Longhorn (500 Go)SSD NVMe— serveur nu-métal1k3s Nœud 2 (serveur)PostgreSQL Veille 1Patroni + PgBouncerVolume Longhorn (500 Go)SSD NVMe— serveur nu-metal-srv2k3s Nœud 3 (serveur)PostgreSQL Veille 2Patroni + PgBouncerVolume Longhorn (500 Go)SSD NVMe– bare-metal-srv3Cluster etcd externe (3 nœuds dédiés)etcd1 (10.0.3.10) · etcd2 (10.0.3.11) · etcd3 (10.0.3.12)Gestion des éleveursInterface utilisateur+ cycle de vie du clusterPrimaireVeilleetc.Longue corneHAProxyRéplication en streaming représentée par des flèches vertes en pointillés

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: 64Gi

Keepalived 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 du

La configuration par défaut du

PostgreSQL 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.02

Ré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 networks

Ingé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 list

Test 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 DROP

Tests 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
done

Advanced : 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 connexion

Les applications

se 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=require

Cette 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.key
Stratégie de sauvegarde

pour 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 du

Chaque é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.tool
Analyse comparative des performances du

Avant 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 --force

Conclusion

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.