Skip to content

Latest commit

 

History

History
1344 lines (1041 loc) · 47.4 KB

File metadata and controls

1344 lines (1041 loc) · 47.4 KB

🔝 Retour au Sommaire

19.5.3. Géo-réplication et Multi-Region

Introduction

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.


Qu'est-ce que la Géo-réplication ?

Définition

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 vs Géo-réplication

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

Pourquoi mettre en place une Géo-réplication ?

1. Protection contre les catastrophes régionales (Disaster Recovery)

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.

2. Réduction de la latence pour les utilisateurs globaux

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.

3. Conformité réglementaire (Data Residency)

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)

4. Haute disponibilité à l'échelle planétaire

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

Architectures de Géo-réplication

Architecture 1 : Primary-Standby Multi-régions (Active-Passive)

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

Architecture 2 : Multi-Primary avec Répartition Géographique

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

Architecture 3 : Multi-Primary avec Résolution de Conflits (Active-Active)

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 :

  1. Patroni + Citus : Sharding distribué
  2. BDR (Bi-Directional Replication) : Solution commerciale de EDB
  3. Bucardo : Réplication asynchrone avec résolution de conflits
  4. 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)

⚠️ Complexité très élevée - À éviter sauf besoin absolument critique


Les défis de la Géo-réplication

1. Le théorème CAP

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.

2. Latence réseau et bande passante

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.

3. Coûts

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.

4. Complexité opérationnelle

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)

Mise en place d'une Géo-réplication PostgreSQL

Étape 1 : Planification et Design

Questions à vous poser

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 ?

Dimensionnement

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

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

Étape 2 : Configuration de la Réplication Physique Géo-distribuée

Scénario : Primary à Paris, Standby à New York

Sur le Primary (Paris)

# 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  

Sur le Standby (New York)

# 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

Étape 3 : Vérification de la Réplication

-- 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)

Étape 4 : Monitoring spécifique Géo-réplication

Métriques critiques à surveiller :

  1. 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
  1. 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)
  1. 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 eth0

Étape 5 : Configuration du Failover

Pour 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: 2GB

Dé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    |
# +-------------+-----------------+---------+---------+----+-----------+

Stratégies de Routage des Requêtes

1. DNS-based Routing

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"
)

2. Proxy-based Routing avec HAProxy

┌──────────────────────────────────────────────────┐
│               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

3. Application-level Routing

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];
}

Bonnes Pratiques

1. Tester régulièrement les failovers

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 monitoring

2. Monitorer les métriques critiques

Dashboard 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

3. Optimiser les transferts inter-régions

Techniques de réduction des coûts :

  1. Compression WAL
-- Dans postgresql.conf
wal_compression = on

Réduction typique : 30-50% du volume WAL.

  1. 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;  
  1. 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  

4. Documenter les procédures d'urgence

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

Communication (minutes 10-15)

  • Status page: "Service dégradé - Latence augmentée Europe"
  • Email clients critiques
  • Tweet @CompanyStatus

Stabilisation (minutes 15-60)

  • Monitoring intensif des métriques
  • Vérifier les queues applicatives
  • Investiguer la cause racine

Post-incident (J+1)

  • 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

Alternatives et Solutions Managées

Solutions Cloud Natives

Si gérer la géo-réplication vous semble trop complexe, considérez des solutions managées :

1. AWS Aurora Global Database

┌──────────────────────┐          ┌──────────────────────┐
│  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

2. Google Cloud Spanner (pas PostgreSQL natif)

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

3. CockroachDB (PostgreSQL-compatible)

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

4. Crunchy Data Bridge (Patroni as a Service)

Solution managée basée sur Patroni.

✅ PostgreSQL natif
✅ HA automatisée
✅ Multi-cloud (AWS, Azure, GCP)
❌ Coût intermédiaire

Checklist de Mise en Production

✅ Avant le lancement

  • 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

✅ Validation pré-production

  • 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

✅ En production

  • 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

Conclusion

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.

Quand mettre en place une géo-réplication ?

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é

Les clés du succès

  1. Commencez simple : Primary-Standby asynchrone dans 2 régions
  2. Testez régulièrement : Failovers, DR drills
  3. Monitorez intensément : Latence réseau, replication lag
  4. Documentez tout : Runbooks, architectures, procédures
  5. Automatisez : Patroni pour failover, Terraform pour infra
  6. Préparez l'équipe : Formation, astreinte, post-mortems

Évolution progressive recommandée

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

Pour aller plus loin

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 :


"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

⏭️ Checklist de mise en production (Best Practices)