🔝 Retour au Sommaire
Imaginez cette situation : vous gérez une application e-commerce internationale. Vos clients sont aux États-Unis, en Europe et en Asie. À 3h du matin, heure de Paris, un incendie détruit votre datacenter principal situé en France.
Sans géo-réplication :
- ❌ Votre site est totalement hors ligne
- ❌ Vous perdez potentiellement des heures ou jours de données
- ❌ Vos clients du monde entier ne peuvent plus commander
- ❌ Votre réputation est ruinée
- ❌ Les pertes financières sont catastrophiques
Avec géo-réplication :
- ✅ Un datacenter aux États-Unis prend automatiquement le relais
- ✅ Vos clients européens subissent une légère hausse de latence (300ms au lieu de 50ms)
- ✅ Aucune donnée n'est perdue
- ✅ Le service continue de fonctionner
- ✅ Vous avez le temps de reconstruire votre infrastructure européenne
La géo-réplication et l'architecture multi-région sont des stratégies qui protègent votre base de données PostgreSQL contre les catastrophes régionales et améliorent simultanément les performances pour vos utilisateurs dispersés géographiquement.
Ce chapitre vous explique pourquoi, quand et comment mettre en place une architecture PostgreSQL distribuée géographiquement.
La géo-réplication est la technique consistant à maintenir des copies de votre base de données PostgreSQL dans plusieurs datacenters situés dans différentes régions géographiques (pays, continents).
C'est une extension du concept de réplication que nous avons vu précédemment, mais à l'échelle planétaire.
RÉPLICATION LOCALE (même datacenter ou ville)
┌─────────────────────────────────────┐
│ Datacenter Paris (France) │
│ │
│ ┌─────────┐ ┌─────────┐ │
│ │ Primary │───→│ Standby │ │
│ └─────────┘ └─────────┘ │
│ │
│ Latence: <1ms │
│ Distance: quelques km │
└─────────────────────────────────────┘
GÉO-RÉPLICATION (multi-régions)
┌────────────────────┐ ┌────────────────────┐
│ Datacenter Paris │ │ Datacenter Virginie│
│ (Europe) │ │ (US-East) │
│ │ │ │
│ ┌─────────┐ │ │ ┌─────────┐ │
│ │ Primary │──────┼────────┼───→│ Standby │ │
│ └─────────┘ │ │ └─────────┘ │
│ │ │ │
└────────────────────┘ └────────────────────┘
Latence: 80-150ms
Distance: ~6000 km
Différences clés :
| Aspect | Réplication locale | Géo-réplication |
|---|---|---|
| Latence | <1ms | 50-300ms |
| Bande passante | Très élevée (10+ Gbps) | Limitée (100 Mbps - 1 Gbps) |
| Protection contre | Pannes matérielles | Catastrophes régionales |
| Coût | Modéré | Élevé |
| Complexité | Moyenne | Élevée |
Risques régionaux auxquels vous êtes exposés :
- 🔥 Incendies : OVH Strasbourg (2021), 3,6 millions de sites affectés
- 🌊 Inondations : Datacenter de Bangkok (2011)
- ⚡ Pannes électriques massives : Texas (2021), plusieurs jours sans électricité
- 🌪️ Catastrophes naturelles : Tremblements de terre, ouragans, tornades
- 💣 Événements géopolitiques : Guerre, terrorisme, cyberattaque d'État
- 🏛️ Problèmes légaux : Saisie de serveurs, fermeture administrative
Exemple concret :
En mars 2021, l'incendie du datacenter OVH à Strasbourg a détruit complètement plusieurs bâtiments. Des milliers d'entreprises qui n'avaient qu'un seul site ont tout perdu :
- Sites web hors ligne pendant des semaines
- Données perdues définitivement pour certaines
- Entreprises en difficulté financière, certaines ont fermé
Avec une géo-réplication, ces entreprises auraient pu basculer instantanément sur un datacenter aux Pays-Bas ou en Allemagne.
Le problème de la latence intercontinentale :
Temps de latence depuis Paris :
- Paris → Paris : 1 ms
- Paris → Londres : 15 ms
- Paris → New York : 80 ms
- Paris → Tokyo : 250 ms
- Paris → Sydney : 350 ms
Pour un utilisateur à Sydney, chaque requête SQL prend au minimum 700ms (350ms aller + 350ms retour), ce qui rend l'application très lente.
Solution avec géo-réplication :
┌─────────────┐ ┌─────────────┐
│ User à │ │ User à │
│ Paris │ │ Sydney │
└──────┬──────┘ └──────┬──────┘
│ 1ms │ 1ms
↓ ↓
┌─────────────┐ ┌─────────────┐
│ PostgreSQL │←──── Sync ────────→│ PostgreSQL │
│ Paris │ 250ms │ Sydney │
└─────────────┘ └─────────────┘
Chaque utilisateur accède à un serveur proche géographiquement, réduisant la latence de 350ms à 1-10ms.
Certaines législations imposent que les données des citoyens restent dans leur pays ou région :
- RGPD (Europe) : Données des citoyens européens
- LGPD (Brésil) : Données des citoyens brésiliens
- FedRAMP (USA) : Données gouvernementales américaines
- PDPA (Singapour) : Données à Singapour
Exemple :
Une banque européenne doit stocker les données de ses clients européens en Europe, mais peut servir des clients asiatiques depuis des serveurs en Asie.
┌──────────────────┐ ┌───────────────────┐
│ Europe (RGPD) │ │ Asie (PDPA) │
│ │ │ │
│ Clients: 🇫🇷🇩🇪🇮🇹 │ │ Clients: 🇯🇵🇸🇬🇰🇷 │
│ │ │ │
│ ┌──────────────┐ │ │ ┌───────────────┐ │
│ │ PostgreSQL │ │ │ │ PostgreSQL │ │
│ │ (données EU) │ │ │ │ (données Asia)│ │
│ └──────────────┘ │ │ └───────────────┘ │
└──────────────────┘ └───────────────────┘
↑ ↑
└─ Réplication sélective ────┘
(uniquement métadonnées globales)
Objectif : Atteindre une disponibilité de 99,99% ou plus (moins de 53 minutes d'indisponibilité par an).
Avec plusieurs régions :
- Si une région tombe, les autres prennent le relais
- Maintenance sans interruption (rolling updates)
- Pas de single point of failure géographique
C'est l'architecture la plus simple pour débuter.
┌──────────────────────────────────────────────────┐
│ WRITES │
│ ↓ │
│ ┌──────────────────┐ │
│ │ PRIMARY │ │
│ │ Paris (EU) │ │
│ │ Read + Write │ │
│ └────────┬─────────┘ │
│ │ │
│ Streaming Replication │
│ (Asynchronous) │
│ │ │
│ ┌────────────┴────────────┐ │
│ ↓ ↓ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ STANDBY │ │ STANDBY │ │
│ │ Virginia │ │ Tokyo │ │
│ │ (US-East) │ │ (Asia) │ │
│ │ Read-only │ │ Read-only │ │
│ └──────────────┘ └──────────────┘ │
│ │
│ READS: Primary + Standbys (load balancing) │
└──────────────────────────────────────────────────┘
Caractéristiques :
- ✅ Simple à mettre en place
- ✅ Une seule source de vérité (pas de conflit)
- ✅ Standbys peuvent servir les lectures (lecture locale rapide)
- ❌ Écritures centralisées (latence pour utilisateurs lointains)
- ❌ Failover manuel en cas de panne du primary
Configuration PostgreSQL :
-- Sur le Primary (Paris)
-- postgresql.conf
wal_level = replica
max_wal_senders = 10
wal_keep_size = 1GB
archive_mode = on
archive_command = 'aws s3 cp %p s3://pg-wal-archive/%f'
-- Sur les Standbys (Virginia, Tokyo)
-- postgresql.conf
hot_standby = on
primary_conninfo = 'host=paris-primary.example.com port=5432 user=replicator'
restore_command = 'aws s3 cp s3://pg-wal-archive/%f %p' Cas d'usage :
- Application dont les écritures proviennent principalement d'une région
- Budget limité
- Équipe technique de taille moyenne
Chaque région a son propre serveur primary qui gère ses données locales.
┌────────────────────┐ ┌────────────────────┐ ┌────────────────────┐
│ PRIMARY Europe │ │ PRIMARY US │ │ PRIMARY Asia │
│ Paris │ │ Virginia │ │ Tokyo │
│ │ │ │ │ │
│ ┌──────────────┐ │ │ ┌──────────────┐ │ │ ┌──────────────┐ │
│ │ customers_eu │ │ │ │ customers_us │ │ │ │customers_asia│ │
│ │ orders_eu │ │ │ │ orders_us │ │ │ │ orders_asia │ │
│ │ products (R) │←─┼─────┼──│ products (W) │──┼─────┼→ │ products (R) │ │
│ └──────────────┘ │ │ └──────────────┘ │ │ └──────────────┘ │
│ │ │ │ │ │
└────────────────────┘ └────────────────────┘ └────────────────────┘
↑ ↑ ↑
└──────── Logical ─────────┴──────── Replication ─────┘
(Selective tables)
Principe :
- Chaque région gère ses propres données clients
- Certaines tables (produits, configuration) sont répliquées partout
- Évite les conflits en partitionnant les données géographiquement
Configuration avec Logical Replication :
-- Sur le serveur US (primary pour customers_us)
CREATE PUBLICATION pub_products FOR TABLE products;
-- Sur le serveur EU (subscribe aux products US)
CREATE SUBSCRIPTION sub_products_from_us
CONNECTION 'host=us-primary.example.com dbname=mydb user=replicator'
PUBLICATION pub_products;
-- Sur le serveur Asia (subscribe aux products US)
CREATE SUBSCRIPTION sub_products_from_us
CONNECTION 'host=us-primary.example.com dbname=mydb user=replicator'
PUBLICATION pub_products; Avantages :
- ✅ Écritures locales rapides (faible latence)
- ✅ Résilience élevée (chaque région autonome)
- ✅ Conformité réglementaire (données localisées)
Inconvénients :
- ❌ Complexité élevée de la logique applicative
- ❌ Gestion des conflits si données partagées
- ❌ Coût important (3+ serveurs primaries)
Cas d'usage :
- Application SaaS multi-tenant avec clients par région
- Conformité stricte (RGPD, etc.)
- Budget conséquent
Attention : Non supporté nativement par PostgreSQL !
PostgreSQL ne supporte pas nativement le multi-primary avec écritures concurrentes sur les mêmes données. Vous aurez besoin d'extensions tierces.
Solutions externes :
- Patroni + Citus : Sharding distribué
- BDR (Bi-Directional Replication) : Solution commerciale de EDB
- Bucardo : Réplication asynchrone avec résolution de conflits
- Pglogical : Base pour BDR, open-source
Schéma avec BDR :
┌────────────────────┐ ┌────────────────────┐
│ Node Europe │ │ Node US │
│ (Primary) │ │ (Primary) │
│ │ │ │
│ Write: users │←────────→│ Write: users │
│ Write: orders │ BDR │ Write: orders │
│ │ │ │
└────────────────────┘ └────────────────────┘
↑ ↑
│ │
└───────────┬───────────────────┘
↓
Conflict Resolution:
- Last Write Wins (LWW)
- Custom Rules
- Application Logic
Gestion des conflits :
-- Exemple de conflit
-- À Paris (11:00:00 UTC): UPDATE users SET email='alice@paris.com' WHERE id=1;
-- À New York (11:00:00 UTC): UPDATE users SET email='alice@ny.com' WHERE id=1;
-- Stratégies de résolution:
-- 1. Last Write Wins (plus récent gagne)
-- 2. First Write Wins (premier gagne)
-- 3. Merge (fusion des changements)
-- 4. Custom logic (règle métier)Vous ne pouvez avoir que 2 des 3 propriétés suivantes dans un système distribué :
Consistency (Cohérence)
/\
/ \
/ \
/ \
/ \
/ \
/ Choose \
/ 2 \
/________________\
Availability Partition
(Disponibilité) Tolerance
(Tolérance aux
pannes réseau)
En pratique pour PostgreSQL géo-répliqué :
| Mode | C | A | P | Description |
|---|---|---|---|---|
| Réplication synchrone | ✅ | ❌ | ✅ | Cohérence forte, mais indisponibilité si liaison réseau coupée |
| Réplication asynchrone | ❌ | ✅ | ✅ | Haute disponibilité, mais perte de données possible (RPO > 0) |
Vous devez choisir votre compromis selon votre contexte métier.
Latences typiques inter-régions (ping) :
| Route | Latence moyenne | Latence 95th percentile |
|---|---|---|
| Paris → Londres | 15 ms | 25 ms |
| Paris → Francfort | 20 ms | 30 ms |
| Paris → New York | 80 ms | 120 ms |
| Paris → São Paulo | 200 ms | 300 ms |
| Paris → Tokyo | 250 ms | 350 ms |
| Paris → Sydney | 350 ms | 450 ms |
Impact sur la réplication synchrone :
-- Chaque COMMIT doit attendre la confirmation du standby
BEGIN;
INSERT INTO orders VALUES (...);
COMMIT; -- Attend 80ms (Paris → New York) avant de confirmer
-- Impact sur le débit:
-- Avec 80ms de latence, max théorique = 1000/80 = 12,5 transactions/seconde
-- (si transactions séquentielles)Pour les charges importantes, la réplication asynchrone est souvent nécessaire.
Exemple de coûts mensuels (AWS) :
Architecture Single-Region (Paris uniquement):
- 1× RDS PostgreSQL db.r6g.2xlarge : 550€/mois
- 500 GB stockage SSD : 70€/mois
- 1 TB backup S3 : 25€/mois
TOTAL: ~645€/mois
Architecture Multi-Region (Paris + Virginia + Tokyo):
- 3× RDS PostgreSQL db.r6g.2xlarge : 1650€/mois
- 3× 500 GB stockage SSD : 210€/mois
- 3× 1 TB backup S3 : 75€/mois
- Data transfer inter-régions (~500GB/mois) : 400€/mois
TOTAL: ~2335€/mois
Surcoût: +262% (×3,6)
Le coût augmente considérablement, surtout à cause des transferts de données inter-régions.
Tâches additionnelles :
- Monitoring de la latence de réplication sur chaque région
- Gestion des failovers multi-régions
- Tests DR sur plusieurs régions
- Coordination des déploiements (migrations de schéma)
- Gestion des fuseaux horaires
- Conformité multi-juridictions
Taille d'équipe recommandée :
- Single-region : 1-2 personnes
- Multi-region : 3-5 personnes (équipe DevOps/SRE dédiée)
Technique :
- Quelle latence acceptable pour les écritures ?
- Quel RPO est acceptable (0, 1 seconde, 1 minute) ?
- Quel volume de données à répliquer ?
- Quelle bande passante disponible entre régions ?
Métier :
- Quelles régions géographiques cibler ?
- Quels utilisateurs dans quelle région ?
- Quelles données doivent rester localisées (RGPD) ?
- Quel budget disponible ?
Organisationnel :
- Avez-vous l'expertise technique ?
- Qui sera on-call pour les incidents multi-régions ?
- Avez-vous les outils de monitoring adaptés ?
Calculer le volume de réplication :
-- Calculer le taux de génération de WAL (Write-Ahead Log)
SELECT
pg_size_pretty(
pg_wal_lsn_diff(pg_current_wal_lsn(), '0/0')
) as wal_generated;
-- Exécuter à T0, puis à T0+1h
-- Exemple de résultat: 2.5 GB en 1 heure
-- = 60 GB/jour de WAL à transférer entre régionsCalculer la bande passante nécessaire :
Volume WAL: 60 GB/jour = 2.5 GB/heure
Converti en Mbps: (2.5 × 8 × 1024) / 3600 = 5.7 Mbps minimum
Marge de sécurité ×3 = 17 Mbps recommandé
Scénario : Primary à Paris, Standby à New York
# 1. Éditer postgresql.conf
cat >> /etc/postgresql/18/main/postgresql.conf << EOF
# Réplication
wal_level = replica
max_wal_senders = 5
max_replication_slots = 5
wal_keep_size = 2GB
# Archive des WAL (vers S3 pour DR)
archive_mode = on
archive_command = 'aws s3 cp %p s3://pg-wal-paris/%f --region eu-west-1'
# Réplication synchrone (optionnel - impact performance)
# synchronous_commit = remote_apply
# synchronous_standby_names = 'newyork_standby'
EOF
# 2. Créer un utilisateur de réplication
sudo -u postgres psql << EOF
CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'SecureP@ssw0rd!';
EOF
# 3. Configurer pg_hba.conf
cat >> /etc/postgresql/18/main/pg_hba.conf << EOF
# Autoriser réplication depuis New York
host replication replicator 54.123.45.67/32 scram-sha-256
EOF
# 4. Redémarrer PostgreSQL
sudo systemctl restart postgresql
# 5. Créer un slot de réplication
sudo -u postgres psql << EOF
SELECT pg_create_physical_replication_slot('newyork_slot');
EOF # 1. Faire une sauvegarde de base depuis Paris
sudo -u postgres pg_basebackup \
-h paris-primary.example.com \
-D /var/lib/postgresql/18/main \
-U replicator \
-v \
-P \
-W \
-R \
--slot=newyork_slot
# L'option -R crée automatiquement standby.signal et configure
# primary_conninfo dans postgresql.auto.conf
# 2. Vérifier la configuration auto-générée
cat /var/lib/postgresql/18/main/postgresql.auto.conf
# primary_conninfo = 'host=paris-primary.example.com port=5432 user=replicator password=...'
# primary_slot_name = 'newyork_slot'
# 3. Configurer le restore_command (optionnel, pour PITR)
cat >> /var/lib/postgresql/18/main/postgresql.conf << EOF
restore_command = 'aws s3 cp s3://pg-wal-paris/%f %p --region eu-west-1'
EOF
# 4. Démarrer PostgreSQL
sudo systemctl start postgresql-- Sur le Primary (Paris)
-- Vérifier les connexions de réplication
SELECT
client_addr,
application_name,
state,
sync_state,
pg_wal_lsn_diff(pg_current_wal_lsn(), sent_lsn) as sending_lag_bytes,
pg_wal_lsn_diff(pg_current_wal_lsn(), write_lsn) as write_lag_bytes,
replay_lag
FROM pg_stat_replication;
-- Résultat attendu:
-- client_addr | application_name | state | sync_state | sending_lag_bytes | replay_lag
-- ---------------+------------------+-----------+------------+-------------------+------------
-- 54.123.45.67 | newyork_standby | streaming | async | 0 | 00:00:00.5
-- Sur le Standby (New York)
-- Vérifier qu'on est bien en mode standby
SELECT pg_is_in_recovery();
-- Résultat: t (true)
-- Vérifier le lag de réplication
SELECT
NOW() - pg_last_xact_replay_timestamp() AS replication_lag;
-- Résultat: 00:00:00.247 (247 millisecondes de retard)Métriques critiques à surveiller :
- Replication Lag (Retard de réplication)
-- Query à exécuter sur le Primary
SELECT
application_name,
client_addr,
pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) / 1024 / 1024 as lag_mb,
EXTRACT(EPOCH FROM replay_lag) as replay_lag_seconds
FROM pg_stat_replication
ORDER BY replay_lag DESC; Seuils d'alerte recommandés :
⚠️ Warning : Lag > 100 MB ou > 10 secondes- 🚨 Critical : Lag > 1 GB ou > 60 secondes
- Slot de réplication non consommé
-- Sur le Primary
SELECT
slot_name,
pg_size_pretty(
pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)
) as retained_wal,
active
FROM pg_replication_slots;Alerte si :
- Slot inactif pendant > 30 minutes
- WAL retenus > 10 GB (risque de remplissage disque)
- Bande passante réseau
# Monitoring de la bande passante utilisée
# Exemple avec iftop
sudo iftop -i eth0 -f "dst port 5432"
# Ou avec nethogs
sudo nethogs eth0Pour automatiser le basculement en cas de panne du primary, utilisez Patroni.
Architecture avec Patroni :
┌──────────────────────────────────────────────────┐
│ etcd Cluster │
│ (Consensus distribué) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │etcd Paris│ │etcd NY │ │etcd Tokyo│ │
│ └──────────┘ └──────────┘ └──────────┘ │
└───────┬──────────────┬───────────────┬───────────┘
│ │ │
↓ ↓ ↓
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Patroni │ │ Patroni │ │ Patroni │
│ Paris │ │ NY │ │ Tokyo │
│ (Leader) │ │(Replica) │ │(Replica) │
└────┬─────┘ └────┬─────┘ └────┬─────┘
↓ ↓ ↓
┌──────────┐ ┌──────────┐ ┌──────────┐
│ PG │ │ PG │ │ PG │
│ Primary │──→│ Standby │ │ Standby │
└──────────┘ └──────────┘ └──────────┘
Installation de Patroni (simplifiée) :
# /etc/patroni/patroni.yml (Paris)
scope: postgres-cluster
namespace: /service/
name: paris-node
restapi:
listen: 0.0.0.0:8008
connect_address: paris.example.com:8008
etcd:
hosts:
- paris-etcd.example.com:2379
- ny-etcd.example.com:2379
- tokyo-etcd.example.com:2379
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576 # 1 MB
initdb:
- encoding: UTF8
- data-checksums
postgresql:
listen: 0.0.0.0:5432
connect_address: paris.example.com:5432
data_dir: /var/lib/postgresql/18/main
pgpass: /tmp/pgpass
authentication:
replication:
username: replicator
password: SecureP@ssw0rd!
superuser:
username: postgres
password: SuperSecureP@ss!
parameters:
wal_level: replica
hot_standby: on
max_wal_senders: 10
max_replication_slots: 10
wal_keep_size: 2GBDémarrage :
# Sur chaque nœud (Paris, NY, Tokyo)
sudo systemctl start patroni
sudo systemctl enable patroni
# Vérifier le cluster
patronictl -c /etc/patroni/patroni.yml list
# Résultat:
# + Cluster: postgres-cluster ----+--------+---------+----+-----------+
# | Member | Host | Role | State | TL | Lag in MB |
# +-------------+-----------------+---------+---------+----+-----------+
# | paris-node | 10.0.1.10:5432 | Leader | running | 1 | |
# | ny-node | 10.0.2.10:5432 | Replica | running | 1 | 0 |
# | tokyo-node | 10.0.3.10:5432 | Replica | running | 1 | 0 |
# +-------------+-----------------+---------+---------+----+-----------+Test de failover :
# Simuler une panne du leader (Paris)
sudo systemctl stop postgresql
# Patroni détecte automatiquement et élit un nouveau leader
# (typiquement NY si c'est le plus à jour)
# Après ~30 secondes:
patronictl -c /etc/patroni/patroni.yml list
# Résultat:
# + Cluster: postgres-cluster ----+--------+---------+----+-----------+
# | Member | Host | Role | State | TL | Lag in MB |
# +-------------+-----------------+---------+---------+----+-----------+
# | paris-node | 10.0.1.10:5432 | Replica | stopped | 1 | |
# | ny-node | 10.0.2.10:5432 | Leader | running | 2 | |
# | tokyo-node | 10.0.3.10:5432 | Replica | running | 2 | 0 |
# +-------------+-----------------+---------+---------+----+-----------+Utiliser des enregistrements DNS avec détection géographique.
# Configuration AWS Route 53 (exemple)
postgres-primary.example.com
├─ Geolocation: Europe → paris-pg.example.com (2.3.4.5)
├─ Geolocation: Americas → ny-pg.example.com (54.123.45.67)
└─ Geolocation: Asia → tokyo-pg.example.com (13.234.56.78)
postgres-read.example.com (load balanced)
├─ Geolocation: Europe → paris-read.example.com
├─ Geolocation: Americas → ny-read.example.com
└─ Geolocation: Asia → tokyo-read.example.com
Dans l'application :
# Python avec psycopg2
import psycopg2
# Connexion pour écritures (toujours vers le primary actuel)
write_conn = psycopg2.connect(
host="postgres-primary.example.com", # Résolu géographiquement
database="mydb",
user="app_user",
password="secret"
)
# Connexion pour lectures (vers replica le plus proche)
read_conn = psycopg2.connect(
host="postgres-read.example.com", # Résolu vers replica local
database="mydb",
user="app_user",
password="secret"
)┌──────────────────────────────────────────────────┐
│ HAProxy (Load Balancer) │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Port 5432 │ │ Port 5433 │ │
│ │ (Write) │ │ (Read) │ │
│ └────────┬────────┘ └────────┬────────┘ │
└───────────┼────────────────────────┼─────────────┘
│ │
↓ ↓
┌──────────┐ ┌─────────────────┐
│ Primary │ │ Round-robin: │
│ (Master) │ │ - Primary │
└──────────┘ │ - Standby 1 │
│ - Standby 2 │
└─────────────────┘
Configuration HAProxy :
# /etc/haproxy/haproxy.cfg
global
maxconn 10000
defaults
mode tcp
timeout connect 5s
timeout client 1h
timeout server 1h
# Backend pour écritures (vers primary uniquement)
backend postgres_write
option pgsql-check user haproxy
server paris-primary 10.0.1.10:5432 check
server ny-standby 10.0.2.10:5432 check backup
server tokyo-standby 10.0.3.10:5432 check backup
# Backend pour lectures (load balancing sur tous)
backend postgres_read
balance roundrobin
option pgsql-check user haproxy
server paris-primary 10.0.1.10:5432 check weight 30
server ny-standby 10.0.2.10:5432 check weight 35
server tokyo-standby 10.0.3.10:5432 check weight 35
# Frontend
frontend postgres_write_frontend
bind *:5432
default_backend postgres_write
frontend postgres_read_frontend
bind *:5433
default_backend postgres_read
L'application décide elle-même où router les requêtes.
// Node.js avec pg (node-postgres)
const { Pool } = require('pg');
// Pools séparés par région
const pools = {
paris: new Pool({
host: 'paris-pg.example.com',
database: 'mydb',
max: 20
}),
ny: new Pool({
host: 'ny-pg.example.com',
database: 'mydb',
max: 20
}),
tokyo: new Pool({
host: 'tokyo-pg.example.com',
database: 'mydb',
max: 20
})
};
// Fonction de routing géographique
function getPoolForUser(userRegion) {
switch(userRegion) {
case 'EU': return pools.paris;
case 'US': return pools.ny;
case 'ASIA': return pools.tokyo;
default: return pools.paris; // fallback
}
}
// Utilisation
async function getUser(userId, userRegion) {
const pool = getPoolForUser(userRegion);
const result = await pool.query(
'SELECT * FROM users WHERE id = $1',
[userId]
);
return result.rows[0];
}Calendrier suggéré :
- Failover manuel : Trimestriel
- Chaos engineering (panne simulée) : Annuel
- Review des runbooks : Après chaque incident
Script de test de failover :
#!/bin/bash
# test_failover.sh
# Test de failover contrôlé avec Patroni
echo "=== TEST DE FAILOVER GÉOGRAPHIQUE ==="
echo "Date: $(date)"
# 1. Identifier le leader actuel
CURRENT_LEADER=$(patronictl -c /etc/patroni/patroni.yml list --format=json | \
jq -r '.[] | select(.Role == "Leader") | .Member')
echo "Leader actuel: $CURRENT_LEADER"
# 2. Effectuer un switchover contrôlé vers un autre nœud
TARGET_NODE="ny-node" # Choisir le nœud cible
echo "Déclenchement du switchover vers $TARGET_NODE..."
patronictl -c /etc/patroni/patroni.yml switchover \
--master $CURRENT_LEADER \
--candidate $TARGET_NODE \
--force
# 3. Attendre la stabilisation
echo "Attente de stabilisation (30s)..."
sleep 30
# 4. Vérifier le nouveau leader
NEW_LEADER=$(patronictl -c /etc/patroni/patroni.yml list --format=json | \
jq -r '.[] | select(.Role == "Leader") | .Member')
echo "Nouveau leader: $NEW_LEADER"
# 5. Test de connectivité
echo "Test de connectivité..."
psql -h postgres-primary.example.com -U app_user -d mydb -c "SELECT NOW();"
if [ $? -eq 0 ]; then
echo "✅ Failover réussi"
else
echo "❌ Failover échoué"
exit 1
fi
# 6. Mesurer le downtime
echo "Downtime mesuré: ~5 secondes" # À mesurer réellement avec monitoringDashboard Grafana recommandé :
┌─────────────────────────────────────────────────────────┐
│ PostgreSQL Geo-Replication Dashboard │
├─────────────────────────────────────────────────────────┤
│ │
│ Replication Lag by Region │
│ ┌─────────────────────────────────────────┐ │
│ │ Paris: 0 MB / 0.1s │ │
│ │ NY: 2 MB / 0.3s │ │
│ │ Tokyo: 5 MB / 0.8s │ │
│ └─────────────────────────────────────────┘ │
│ │
│ Network Throughput (Replication) │
│ ┌─────────────────────────────────────────┐ │
│ │ Paris→NY: 15 Mbps │ │
│ │ Paris→Tokyo: 12 Mbps │ │
│ └─────────────────────────────────────────┘ │
│ │
│ WAL Generated vs Replicated │
│ ┌─────────────────────────────────────────┐ │
│ │ Generated: 60 GB/day │ │
│ │ Replicated: 58 GB/day (96%) │ │
│ └─────────────────────────────────────────┘ │
│ │
│ Alerts (Last 24h) │
│ ┌─────────────────────────────────────────┐ │
│ │ 🚨 Tokyo lag > 10s (3 occurrences) │ │
│ │ ⚠️ Slot NY retention > 5GB (1x) │ │
│ └─────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
Queries Prometheus :
# Replication lag en secondes
pg_replication_lag_seconds{job="postgres"}
# Taille du lag en bytes
pg_replication_lag_bytes{job="postgres"}
# Slots non actifs
pg_replication_slots_active{job="postgres"} == 0
Techniques de réduction des coûts :
- Compression WAL
-- Dans postgresql.conf
wal_compression = onRéduction typique : 30-50% du volume WAL.
- Réplication sélective (Logical Replication)
Ne répliquer que les tables nécessaires.
-- Sur le primary
CREATE PUBLICATION pub_critical_tables
FOR TABLE orders, customers, products;
-- Sur le standby
CREATE SUBSCRIPTION sub_critical
CONNECTION 'host=primary.example.com dbname=mydb'
PUBLICATION pub_critical_tables; - Tuning de la réplication
-- Augmenter la taille des WAL segments pour réduire la fréquence
-- postgresql.conf
max_wal_size = 2GB
min_wal_size = 1GB
-- Tuning des paramètres réseau
tcp_keepalives_idle = 60
tcp_keepalives_interval = 10
tcp_keepalives_count = 6 Runbook : Panne région complète
# RUNBOOK: Panne Datacenter Région Europe
## Détection
- Monitoring détecte: paris-primary.example.com DOWN
- Alertes: PagerDuty, Slack #incidents
## Actions immédiates (5 premières minutes)
1. Vérifier que c'est bien une panne régionale (ping, traceroute)
2. Déclarer l'incident (war room Zoom)
3. Vérifier les standbys: ny-node et tokyo-node UP?
## Failover vers NY (minutes 5-10)
```bash
# 1. Vérifier lag avant promotion
patronictl -c /etc/patroni/patroni.yml list
# 2. Promouvoir NY comme nouveau primary
patronictl -c /etc/patroni/patroni.yml failover \
--candidate ny-node --force
# 3. Mettre à jour DNS (TTL: 60s)
# Pointer postgres-primary.example.com vers ny-node
aws route53 change-resource-record-sets ...
# 4. Vérifier l'application
curl https://app.example.com/health- Status page: "Service dégradé - Latence augmentée Europe"
- Email clients critiques
- Tweet @CompanyStatus
- Monitoring intensif des métriques
- Vérifier les queues applicatives
- Investiguer la cause racine
- Post-mortem
- Amélioration runbook
- Test de restauration région Europe
### 5. Gérer les migrations de schéma
**Procédure de migration zero-downtime multi-régions :**
```sql
-- PHASE 1: Ajouter une colonne (compatible backwards)
-- Déployer sur toutes les régions simultanément
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
-- PHASE 2: Déployer le code applicatif qui utilise la colonne
-- Déploiement progressif par région (Paris → NY → Tokyo)
-- PHASE 3: Backfill des données (optionnel)
UPDATE users SET phone = '...' WHERE phone IS NULL;
-- PHASE 4: Ajouter contrainte NOT NULL (une fois backfill terminé)
ALTER TABLE users ALTER COLUMN phone SET NOT NULL;
Outils recommandés :
- Flyway : Migrations versionnées
- Liquibase : Migrations avec rollback
- Alembic (Python) : Migrations SQLAlchemy
Si gérer la géo-réplication vous semble trop complexe, considérez des solutions managées :
┌──────────────────────┐ ┌──────────────────────┐
│ Primary Region │ │ Secondary Region │
│ (us-east-1) │ │ (eu-west-1) │
│ │ │ │
│ ┌────────────────┐ │ │ ┌────────────────┐ │
│ │ Aurora Primary │──┼──────────┼─→│ Aurora Read │ │
│ │ (Write) │ │ Async │ │ Replica │ │
│ └────────────────┘ │ <1s lag │ └────────────────┘ │
│ │ │ │
└──────────────────────┘ └──────────────────────┘
Features:
✅ Réplication < 1 seconde
✅ Failover global automatique
✅ Compatible PostgreSQL
❌ Coût élevé (~3× RDS standard)
❌ Vendor lock-in AWS
Solution distribuée globalement, fortement cohérente.
✅ Cohérence forte (ACID)
✅ Distribution automatique
✅ Scalabilité horizontale infinie
❌ Pas PostgreSQL (SQL propriétaire)
❌ Très coûteux
Base de données distribuée, compatible PostgreSQL wire protocol.
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Node EU │────→│ Node US │────→│ Node Asia│
│ (Paris) │ │ (NY) │ │ (Tokyo) │
└──────────┘ └──────────┘ └──────────┘
↓ ↓ ↓
Multi-active - Writes partout
Consensus: Raft protocol
✅ Multi-region native
✅ Failover automatique
✅ Compatible PostgreSQL (90%)
❌ Limitations SQL (pas 100% compatible)
❌ Coût significatif
Solution managée basée sur Patroni.
✅ PostgreSQL natif
✅ HA automatisée
✅ Multi-cloud (AWS, Azure, GCP)
❌ Coût intermédiaire
- Architecture multi-région définie et validée
- Calcul du budget (serveurs, réseau, équipe) approuvé
- Régions géographiques sélectionnées
- Infrastructure provisionnée (serveurs, réseau)
- PostgreSQL installé et configuré sur tous les nœuds
- Réplication configurée et testée
- Monitoring déployé (Prometheus, Grafana)
- Alertes configurées (PagerDuty, Slack)
- HAProxy ou DNS geo-routing configuré
- Tests de failover réussis
- Runbooks documentés
- Équipe formée aux procédures
- Test de charge avec réplication active
- Test de failover sous charge
- Mesure du lag de réplication sous charge
- Test de restauration PITR multi-régions
- Test de panne réseau inter-régions
- Validation des performances applicatives
- Test de migration de schéma
- Disaster recovery drill complet
- Monitoring 24/7 actif
- Astreinte configurée
- Communication stakeholders préparée
- Plan de rollback documenté
- Observability (logs, metrics, traces)
- Tests DR trimestriels planifiés
- Post-mortems après chaque incident
- Revue architecture semestrielle
La géo-réplication et les architectures multi-régions apportent une résilience exceptionnelle et de meilleures performances pour vos utilisateurs globaux, mais au prix d'une complexité et d'un coût significatifs.
Vous DEVRIEZ mettre en place une géo-réplication si :
- ✅ Votre entreprise ne peut tolérer une panne régionale (e-commerce, finance)
- ✅ Vous avez des utilisateurs sur plusieurs continents
- ✅ Vous devez respecter des réglementations de résidence des données
- ✅ Votre budget le permet (× 2-3 votre coût infrastructure)
- ✅ Vous avez une équipe technique expérimentée
Vous DEVRIEZ attendre si :
- ❌ Votre traffic est principalement dans une seule région
- ❌ Vous êtes une startup en early-stage
- ❌ Votre équipe technique est réduite (< 3 personnes)
- ❌ Une panne de quelques heures est acceptable
- ❌ Votre budget infrastructure est limité
- Commencez simple : Primary-Standby asynchrone dans 2 régions
- Testez régulièrement : Failovers, DR drills
- Monitorez intensément : Latence réseau, replication lag
- Documentez tout : Runbooks, architectures, procédures
- Automatisez : Patroni pour failover, Terraform pour infra
- Préparez l'équipe : Formation, astreinte, post-mortems
Phase 1 (Mois 1-3):
├─ Single-region avec HA locale (Patroni)
└─ Tests et stabilisation
Phase 2 (Mois 4-6):
├─ Ajout d'une 2ème région (Standby asynchrone)
├─ Réplication physique
└─ Tests de failover
Phase 3 (Mois 7-12):
├─ Ajout d'une 3ème région
├─ Optimisation du routing (HAProxy/DNS)
└─ Monitoring avancé
Phase 4 (Année 2+):
├─ Réplication logique pour données partitionnées
├─ Multi-primary pour certaines tables
└─ Amélioration continue
Prochains chapitres :
- 19.6. Checklist de mise en production : Best practices complètes
- 20. Drivers et connexion applicative : Intégration avec les applications
- 20bis. Architectures modernes : Microservices, Event Sourcing, Serverless
Ressources recommandées :
- PostgreSQL High Availability
- Patroni Documentation
- Livre : PostgreSQL High Availability Cookbook - Shaun Thomas
- Conférence : PGConf - Sessions sur HA et réplication
"La meilleure haute disponibilité est celle qu'on a testée. La meilleure géo-réplication est celle qu'on a failovée."
— Proverbe DBA