PostgreSQL en haute disponibilité : Patroni, etcd et basculement automatique
La plupart des équipes découvrent que leur base PostgreSQL n'est pas réellement en haute disponibilité la nuit où elle tombe. La réplica existait, les sauvegardes tournaient — mais personne n'avait prévu qui promeut la réplica, en combien de temps, et comment empêcher l'ancien primaire de se réveiller en double et de corrompre les données.
Cet article explique ce qu'exige une vraie HA PostgreSQL : la réplication en flux, l'élection de leader via etcd, le basculement automatique orchestré par Patroni — et surtout les pièges opérationnels que personne ne mentionne avant de les vivre en production.
Haute disponibilité n'est pas une sauvegarde — ni une simple réplica
Trois mécanismes différents répondent à trois problèmes différents, et les confondre est la première erreur.
La sauvegarde (idéalement avec restauration à un instant précis, le Point-In-Time Recovery) protège contre la perte ou la corruption de données : un DELETE malheureux, une migration ratée, un disque qui meurt. Elle se mesure en RPO — combien de données vous acceptez de perdre.
La réplica est une copie vivante de votre base sur un autre serveur. Elle protège contre la perte d'une machine, mais à elle seule elle ne décide de rien : si le primaire meurt, la réplica reste sagement en lecture seule en attendant qu'un humain la promeuve.
La haute disponibilité, c'est la continuité automatique du service quand le primaire disparaît. Elle se mesure en RTO — combien de temps votre application reste indisponible. C'est le mécanisme qui détecte la panne, choisit une réplica, la promeut, et redirige les écritures — sans réveiller personne à 3 h du matin.
Une réplica sans bascule automatique, ce n'est pas de la HA : c'est une réplica plus un bipeur. Et une HA sans sauvegarde testée, ce n'est pas une protection : un DELETE FROM users se réplique sur tous vos nœuds en quelques millisecondes. Il vous faut les trois.
Le point de départ : la réplication en flux
PostgreSQL sait nativement répliquer son journal des transactions (les WAL — Write-Ahead Logs) du primaire vers une ou plusieurs réplicas. C'est la fondation, mais le choix du mode a des conséquences directes.
En réplication asynchrone, le primaire confirme la transaction au client sans attendre que la réplica l'ait reçue. C'est rapide, mais en cas de crash brutal du primaire, les dernières transactions non encore répliquées sont perdues : votre RPO n'est pas nul.
En réplication synchrone, le primaire attend qu'au moins une réplica ait confirmé l'écriture avant de valider. Le RPO devient nul — aucune transaction confirmée n'est perdue — mais chaque commit paie le coût d'un aller-retour réseau, et la disponibilité des écritures devient couplée à celle de la réplica.
Ce choix n'est pas neutre, et il n'est pas binaire : un cluster bien réglé combine les deux selon les nœuds. Mais quel que soit le mode, la réplication ne répond toujours pas à la question essentielle : qui devient primaire, et quand ?
Le vrai problème : qui promeut, et comment éviter le split-brain
Quand le primaire tombe, il faut quatre choses, dans l'ordre, sans erreur :
- Détecter la panne (et la distinguer d'un simple ralentissement réseau).
- Choisir la réplica la plus à jour pour la promouvoir.
- Promouvoir ce nœud en nouveau primaire.
- Empêcher l'ancien primaire de revenir en pensant qu'il est toujours le chef.
Le point 4 est le plus dangereux. Si l'ancien primaire réapparaît après un partitionnement réseau et continue d'accepter des écritures pendant qu'un nouveau primaire en accepte d'autres, vous avez deux sources de vérité divergentes : c'est le split-brain, et la réconciliation des données est au mieux douloureuse, au pire impossible.
Faire tout cela à la main est lent et faillible. Il faut une orchestration automatique — et une source de vérité unique sur l'identité du primaire, sur laquelle tous les nœuds s'accordent.
etcd : la source de vérité et le quorum
C'est le rôle du Distributed Configuration Store (DCS). PostgreSQL HA utilise le plus souvent etcd : un magasin clé-valeur distribué qui s'appuie sur le consensus Raft pour garantir que tous ses membres voient la même donnée, même en cas de panne partielle.
Le principe est simple : le primaire détient dans etcd une clé de leader assortie d'une durée de vie (TTL), qu'il doit renouveler en continu. Tant qu'il la renouvelle, il est le chef. S'il cesse — crash, gel, isolement réseau — la clé expire, et les nœuds restants peuvent élire un nouveau leader. Comme un seul nœud peut détenir la clé valide à la fois, le split-brain est structurellement empêché.
Encore faut-il que etcd lui-même reste fiable, et c'est là qu'intervient le quorum. Raft exige une majorité (N/2 + 1) de membres joignables pour prendre une décision. D'où une règle non négociable : un nombre impair de membres. Trois membres tolèrent la perte d'un nœud ; cinq en tolèrent deux. Un nombre pair n'apporte aucun gain — quatre membres tolèrent toujours une seule panne, comme trois, tout en multipliant la surface de défaillance.
C'est aussi pourquoi, quand on n'a que deux serveurs de données, on ajoute un troisième membre etcd léger — un witness — uniquement pour atteindre le quorum impair sans payer un troisième serveur complet.
Patroni : l'orchestrateur
Patroni est le composant qui fait le lien entre PostgreSQL et etcd. Il tourne sur chaque nœud, gère le PostgreSQL local (démarrage, configuration, promotion, rétrogradation) et dialogue avec le DCS.
Concrètement, le Patroni du primaire renouvelle la clé de leader à chaque cycle (loop_wait). Quand un primaire disparaît et que sa clé expire au bout du ttl, les Patroni des réplicas déclenchent une élection : celui dont le WAL est le plus avancé — donc celui qui a le moins de retard sur le primaire défunt — est promu, et les autres se reconfigurent automatiquement pour suivre le nouveau primaire. Patroni expose par ailleurs une API REST (/primary, /replica, /health) précieuse pour le routage, on y revient juste en dessous.
L'installation de base est presque triviale. Ce sont les réglages fins et l'exploitation qui font la différence entre un cluster qui tient et un cluster qui se sabote tout seul.
Router les clients vers le bon nœud
C'est la pièce que la moitié des tutoriels oublie. Après une bascule, le primaire a changé de machine — donc d'adresse IP. Si votre application pointe encore vers l'ancien nœud, votre failover a parfaitement fonctionné… et votre app est quand même tombée.
La solution éprouvée : un HAProxy placé devant le cluster, qui interroge l'API REST de Patroni pour ses contrôles de santé. Il route les écritures vers le nœud qui répond primaire sur /primary, et peut distribuer les lectures sur les réplicas via /replica. À la bascule, HAProxy suit automatiquement le nouveau primaire. On y ajoute fréquemment un PgBouncer pour mutualiser les connexions, PostgreSQL n'aimant pas les multiplier.
Là où ça casse en vrai
Voici la partie que l'on n'apprend qu'en production. Nous avons vu un cluster basculer des dizaines de fois en quelques jours — non pas parce que le matériel lâchait, mais parce que le réglage était trop nerveux.
Les bascules fantômes. Un ttl trop court combiné à une instabilité réseau, et le moindre micro-incident fait expirer la clé de leader : le cluster bascule alors qu'il n'y avait aucune vraie panne. C'est tout l'arbitrage de la triade ttl / loop_wait / retry_timeout. Un ttl bas détecte vite une vraie panne mais multiplie les faux positifs ; un ttl plus élevé (typiquement 60 s) stabilise le cluster au prix d'une bascule un peu plus lente. Il n'y a pas de valeur magique : ça se règle en fonction de la qualité réelle du lien réseau entre les nœuds.
Le mode synchrone et la disponibilité des écritures. Passer en synchronous_mode garantit zéro perte de données, mais une réplica synchrone bloquée peut figer les écritures du primaire. Patroni gère dynamiquement la liste des réplicas synchrones (synchronous_standby_names) pour limiter ce risque, mais c'est un réglage à comprendre, pas à activer à l'aveugle.
La latence inter-datacenter. Étaler un cluster sur plusieurs sites pour survivre à la perte d'un datacenter ajoute de la latence — au quorum etcd comme à la réplication synchrone. Un hoquet réseau entre deux DC peut suffire à déclencher une élection. L'emplacement du quorum et du witness devient alors une décision d'architecture à part entière.
Le split-brain résiduel. Pour empêcher absolument un ancien primaire de continuer à écrire, on s'appuie sur un watchdog qui redémarre un nœud incapable de se rétrograder à temps, et sur le fait que Patroni fait volontairement abdiquer un leader qui perd l'accès au DCS.
Et toujours : la HA n'est pas une sauvegarde. Une mauvaise migration se réplique instantanément sur tous les nœuds. Sans PITR hors-site et sans restauration testée régulièrement, votre cluster ultra-disponible vous servira simplement de la donnée corrompue, en haute disponibilité.
Une architecture de référence
Un cluster PostgreSQL HA sérieux, sans fioriture, ressemble à ceci :
Application
│
HAProxy ──(health check API Patroni)
/ \
Primaire Réplica(s)
[PG+Patroni][PG+Patroni]
\ /
etcd (quorum impair : 3 membres,
ex. 2 nœuds de données + 1 witness)
Réparti sur 2 datacenters
Sauvegardes chiffrées hors-site + restauration testée chaque mois
Les briques : trois nœuds PostgreSQL + Patroni (ou deux nœuds de données plus un witness), un quorum etcd impair, un HAProxy en frontal pour le routage, le tout réparti sur deux datacenters distincts, et des sauvegardes hors-site dont la restauration est vérifiée tous les mois. Aucune de ces briques n'est optionnelle.
La HA est une discipline, pas une installation
Monter Patroni une fois, c'est les 20 % faciles. Les 80 % restants — le réglage des timeouts à la réalité de votre réseau, les exercices de bascule réguliers pour vérifier que le failover marche vraiment, le monitoring qui distingue un ralentissement d'une panne, les tests de restauration, et la réponse humaine quand quelque chose sort du script à 3 h du matin — c'est ce qui sépare une vraie haute disponibilité d'une « HA sur le papier ». La plupart des setups auto-gérés dérivent doucement vers la seconde, sans que personne ne s'en aperçoive, jusqu'à la nuit où ça compte.
C'est précisément ce que nous opérons : un PostgreSQL en haute disponibilité géré comme le nôtre — cluster Patroni + etcd, bascule testée en conditions réelles, sauvegardes vérifiées, monitoring et astreinte par des ingénieurs.
Si vous voulez la continuité sans recruter un SRE ni vous enfermer dans une facturation cloud opaque, c'est l'objet de notre infrastructure managée.