Développement Web

Replica sets MongoDB et élections : haute disponibilité pas-à-pas

12 min de lecture

Un MongoDB en production ne tourne jamais en standalone. Le replica set est l’unité de déploiement standard : trois nœuds qui répliquent les mêmes données, élisent un primary automatiquement, basculent en 10 à 30 secondes si le primary tombe, et débloquent les transactions multi-documents que MongoDB n’autorise que sur un replica set. Ce tutoriel détaille comment monter un replica set sur MongoDB 8.0 LTS, comprendre le mécanisme d’élection Raft-like, gérer les votes et priorités, et tester un failover de bout en bout.

Prérequis

  • Trois machines (ou trois ports sur la même machine pour le développement) avec MongoDB 8.0 LTS installé.
  • Accès root ou sudo sur chaque hôte.
  • Connectivité réseau bidirectionnelle entre les nœuds sur le port choisi (27017 par défaut).
  • Une horloge synchronisée via NTP — les élections dépendent du timestamp.
  • Temps estimé : 120 minutes pour traverser les 9 étapes.

Étape 1 — Pourquoi un replica set, pas un standalone

Un standalone est un MongoDB seul : une machine, un processus, une base. Tout est sur ce serveur. S’il tombe, le service est interrompu jusqu’au redémarrage. S’il est corrompu, la donnée est perdue jusqu’à la dernière sauvegarde. C’est acceptable pour un poste de développement, jamais pour un applicatif servant des utilisateurs.

Un replica set est un groupe de processus MongoDB qui se répliquent en continu via un journal appelé l’oplog. À tout moment, un seul nœud est primary — il accepte les écritures ; les autres sont secondaries et appliquent l’oplog en streaming. Si le primary disparaît, les secondaries détectent l’absence par heartbeat manqué et déclenchent une élection : l’un d’entre eux devient primary, le client est notifié, le service reprend. Sans oublier trois bénéfices supplémentaires : les transactions multi-documents (réservées aux replica sets), la lecture distribuée via readPreference, et les sauvegardes faites depuis un secondary sans charger le primary.

Étape 2 — Configurer trois processus mongod

Pour un environnement de production, chaque nœud tourne sur sa propre machine ou VM. Pour un environnement de développement local, on peut lancer trois processus mongod sur la même machine avec des ports et des répertoires de données distincts. La configuration partage le nom du replica set — c’est ce nom qui identifie les membres entre eux.

# Trois fichiers de configuration distincts
# /etc/mongod-rs0-a.conf
storage:
  dbPath: /var/lib/mongodb-rs0-a
net:
  port: 27017
  bindIp: 127.0.0.1,192.168.1.10
replication:
  replSetName: rs0
security:
  keyFile: /etc/mongodb-keyfile

# /etc/mongod-rs0-b.conf — pareil, dbPath /var/lib/mongodb-rs0-b, bindIp 192.168.1.11
# /etc/mongod-rs0-c.conf — pareil, dbPath /var/lib/mongodb-rs0-c, bindIp 192.168.1.12

# Démarrer chaque service
sudo systemctl start mongod-rs0-a mongod-rs0-b mongod-rs0-c

Le keyFile partagé entre les trois nœuds joue deux rôles : il authentifie les membres entre eux (sans lui, n’importe quelle machine du réseau pourrait rejoindre le set), et il active automatiquement security.authorization: enabled. Générez-le une seule fois avec openssl rand -base64 756 > /etc/mongodb-keyfile && chmod 400 /etc/mongodb-keyfile, puis copiez-le à l’identique sur les trois hôtes.

Étape 3 — Initier le replica set

Une fois les trois processus démarrés, ils tournent isolément. Pour les faire travailler ensemble, on se connecte à n’importe lequel et on appelle rs.initiate() avec la liste des membres. C’est l’étape qui matérialise le set : la configuration est écrite dans une collection système locale et propagée aux autres nœuds.

// Se connecter au premier nœud
mongosh "mongodb://192.168.1.10:27017"

// Lancer l'initiation avec les trois membres
rs.initiate({
  _id: "rs0",
  members: [
    { _id: 0, host: "192.168.1.10:27017", priority: 2 },
    { _id: 1, host: "192.168.1.11:27017", priority: 1 },
    { _id: 2, host: "192.168.1.12:27017", priority: 1 }
  ]
});

// Vérifier le statut une fois l'élection terminée (5-10 secondes)
rs.status();

La sortie de rs.status() est riche : pour chaque membre, vous voyez son state (1 = PRIMARY, 2 = SECONDARY, 7 = ARBITER), son health (1 = OK), sa dernière opTime, et le nœud qui a été élu primary. La priorité 2 sur le premier membre n’est pas un ordre : c’est une préférence — toutes choses égales par ailleurs, ce nœud sera élu primary en premier. Mettez la priorité plus haute sur la machine la plus puissante du set.

Étape 4 — Mécanique d’une élection

Le protocole d’élection MongoDB est inspiré de Raft. Chaque membre échange un heartbeat avec ses pairs toutes les deux secondes. Quand un secondary ne reçoit plus de heartbeat du primary pendant 10 secondes (configurable via electionTimeoutMillis), il déclenche une élection : il s’auto-candidate, demande aux autres de voter pour lui.

Trois règles déterminent qui gagne. Une : le candidat doit avoir l’oplog le plus à jour parmi les votants disponibles — un secondary en retard ne peut pas devenir primary. Deux : il faut une majorité stricte des votants — sur trois nœuds, deux votes suffisent ; sur cinq, trois. Trois : en cas d’égalité (rare), c’est la priorité qui départage. Le primary élu prend ses fonctions, les autres deviennent ou restent secondaries, le set est de nouveau cohérent.

// Forcer une élection manuelle (utile pour les tests)
rs.stepDown(60);   // le primary actuel se met en pause 60 secondes
                   // → une élection est déclenchée immédiatement

// Voir le membre actuellement primary
db.hello().primary;

// Voir la configuration actuelle du replica set
rs.conf();

rs.stepDown() est la manière propre de déplacer le primary pour une maintenance — un kill -9 brutal fonctionne aussi mais provoque une fenêtre de quelques secondes pendant laquelle les écritures échouent. La version 8.0 a introduit le read preference subset : même pendant une élection, les lectures avec readPreference: primaryPreferred peuvent basculer transparently sur un secondary, ce qui réduit la fenêtre d’erreur perçue côté application. Référence officielle : Replication — Manual.

Étape 5 — Nombre de votants et configurations valides

La règle de la majorité impose un nombre impair de votants : 3, 5 ou 7. Avec 4 votants, une partition réseau 2-vs-2 paralyse l’élection — aucun camp n’a la majorité. Avec 3 votants, une partition 2-vs-1 désigne le camp à 2 comme gagnant.

Nombre de nœuds Quorum requis Pannes tolérées Usage
3 2 1 Le minimum production
5 3 2 Tolérance accrue, lecture distribuée
7 4 3 Géo-réplication multi-régions
3 (2 data + 1 arbitre) 2 1 Économie disque — arbitre = membre votant sans données

L’arbitre est un membre du replica set qui participe aux votes sans stocker les données. Il existe pour les budgets serrés mais a deux limitations sérieuses : il ne peut pas devenir primary (donc en cas de perte de deux nœuds-data, le set s’arrête en lecture seule), et il pénalise les écritures avec writeConcern: majority. La recommandation 2026 est sans ambiguïté : préférer trois data nodes à deux data + un arbitre, sauf contrainte de coût explicite.

Étape 6 — Lectures distribuées avec readPreference

Par défaut, toutes les lectures partent vers le primary. C’est cohérent mais ne profite pas des secondaries. Le paramètre readPreference du driver permet de répartir les lectures sur les secondaries, à condition d’accepter une éventuelle staleness de quelques millisecondes.

// Driver Node.js — URI avec readPreference
const uri = "mongodb://192.168.1.10:27017,192.168.1.11:27017,192.168.1.12:27017/" +
            "?replicaSet=rs0&readPreference=secondaryPreferred";

// Ou par opération
db.collection("commandes").find(
  { statut: "payee" },
  { readPreference: "secondary", maxStalenessSeconds: 90 }
);

Cinq modes possibles : primary (défaut, lectures cohérentes), primaryPreferred (primary, fallback secondary si primary HS), secondary (lecture toujours sur secondary), secondaryPreferred (secondary, fallback primary), nearest (le nœud avec la plus basse latence réseau). Le paramètre maxStalenessSeconds exclut les secondaries trop en retard — utile pour les tableaux de bord temps réel qui tolèrent quelques secondes mais pas une heure.

Étape 7 — Transactions multi-documents

Les transactions ACID multi-documents sont disponibles depuis MongoDB 4.0 (replica set) et 4.2 (cross-shard). Elles n’existent pas en standalone — le replica set est la condition d’éligibilité parce que la transaction utilise l’oplog comme journal de rollback.

// Driver Node.js — transaction
const session = client.startSession();
session.startTransaction({
  readConcern: { level: "snapshot" },
  writeConcern: { w: "majority" }
});

try {
  await db.collection("comptes").updateOne(
    { _id: idDebit },
    { $inc: { solde: -10000 } },
    { session }
  );
  await db.collection("comptes").updateOne(
    { _id: idCredit },
    { $inc: { solde: +10000 } },
    { session }
  );
  await db.collection("operations").insertOne(
    { type: "virement", debit: idDebit, credit: idCredit, montant: 10000 },
    { session }
  );
  await session.commitTransaction();
} catch (err) {
  await session.abortTransaction();
  throw err;
} finally {
  await session.endSession();
}

Le motif est canonique : démarrer une session, démarrer une transaction, passer la session à chaque opération, commiter ou annuler en bloc. Trois règles à retenir. Une : une transaction doit durer moins de 60 secondes (transactionLifetimeLimitSeconds par défaut), sinon elle est abortée par le moteur. Deux : les transactions sont chères — n’en abusez pas, le pattern documentaire de base est conçu pour les éviter quand un document unique suffit. Trois : avec writeConcern: majority, l’écriture est confirmée quand une majorité de nœuds porteurs de données a durablement écrit l’entrée dans son oplog (l’application sur les secondaires se fait ensuite de manière asynchrone — comportement modifié en MongoDB 8.0 qui n’attend plus l’application complète avant d’acquitter).

Étape 8 — Tester un failover en conditions réelles

Tant qu’un failover n’a pas été testé, il n’est pas garanti. La procédure prend cinq minutes et révèle quasi systématiquement deux ou trois choses à corriger : timeout réseau du driver mal réglé, alerting absent, application qui retente les écritures à l’aveugle pendant l’élection.

# Sur le primary, simuler une panne brutale
sudo systemctl stop mongod-rs0-a
# Ou plus brutal : sudo kill -9 $(pidof mongod)

# Lancer un script applicatif qui boucle insertions
# Observer côté logs application :
#   - 5 à 15 secondes d'erreurs "no primary"
#   - reprise automatique des écritures sur le nouveau primary

# Vérifier la nouvelle topologie
mongosh "mongodb://192.168.1.11:27017"
rs.status();   // un autre nœud doit être PRIMARY

# Redémarrer l'ancien primary, il revient en SECONDARY
sudo systemctl start mongod-rs0-a

La fenêtre d’erreur typique est de 10 à 30 secondes selon electionTimeoutMillis. Pendant ce temps, les drivers officiels MongoDB 6.x et 7.x retournent automatiquement les opérations idempotentes (retryable writes, activé par défaut). Le test démontre aussi la nécessité d’un health check applicatif : si le driver ne reconnecte pas dans les 60 secondes, c’est qu’il a un cache de topologie obsolète — vérifiez la configuration serverSelectionTimeoutMS.

Étape 9 — Sauvegarder depuis un secondary

Lancer mongodump contre le primary impose une charge IO et CPU pendant la durée de l’opération. Sur une base de 50 Go, c’est plusieurs heures pendant lesquelles les requêtes applicatives ralentissent. La solution propre est de cibler un secondary : il copie ses données pendant que le primary continue sa charge normale, sans impact perceptible côté utilisateurs.

mongodump \
  --uri="mongodb://user:pass@192.168.1.12:27017/?replicaSet=rs0&readPreference=secondary" \
  --gzip \
  --archive=/backup/dump_$(date +%Y%m%d).gz

# Restauration côté primary (toujours)
mongorestore \
  --uri="mongodb://user:pass@192.168.1.10:27017/?replicaSet=rs0" \
  --gzip \
  --archive=/backup/dump_20260515.gz

La règle de stratégie : écritures de restauration sur le primary, lectures de sauvegarde sur un secondary. Pour aller plus loin, sur Atlas, les snapshots sont pris côté secondaire automatiquement par la plateforme et déposés sur un stockage objet — opération zero-impact pour l’applicatif.

Erreurs fréquentes

Erreur Cause Solution
Aucun primary élu Pas de majorité connectée (panne réseau partielle) Réparer la connectivité, ou ajouter un membre votant supplémentaire
Élection tourne en boucle Horloges désynchronisées Activer NTP partout, timedatectl set-ntp true
Secondary en RECOVERING permanent Retard supérieur à la fenêtre oplog Resync complet : arrêter, vider dbPath, redémarrer
Transaction numbers are only allowed on a replica set Tentative de transaction sur standalone Convertir en replica set même mono-nœud : replSetName: rs0 + rs.initiate()
Driver perd 30 secondes au failover serverSelectionTimeoutMS trop haut Réduire à 5000 ms côté URI
Écritures lentes en w:majority Un secondary distant tire la latence Le placer en delayed ou réduire la priorité, ne pas l’inclure dans le quorum d’écriture critique

FAQ

Q : Peut-on lancer un replica set mono-nœud ?
R : Oui, et c’est même obligatoire pour activer les transactions sur un environnement de développement. replSetName: rs0 + rs.initiate() avec un seul membre, le nœud est immédiatement primary. Pas de tolérance aux pannes, mais le mécanisme transactionnel est dispo.

Q : Combien de temps dure une élection ?
R : 10 à 30 secondes typiquement. Le electionTimeoutMillis par défaut est 10 000 ms, plus le temps de campagne et de propagation. Sur Atlas, c’est proche de 10 à 15 secondes.

Q : Atlas dispense-t-il de comprendre les replica sets ?
R : Non. Atlas gère le déploiement et la maintenance, mais la sémantique des transactions, du writeConcern, du readPreference et du maxStalenessSeconds reste de votre responsabilité côté driver applicatif.

Q : Un replica set étendu sur plusieurs datacenters ?
R : Possible et recommandé pour la tolérance aux désastres. Placer 3 nœuds sur 3 régions différentes garantit la survie même à la perte d’une région entière. Inconvénient : la latence des écritures avec w: majority dépend de la région la plus lointaine.

Q : Faut-il préférer replica set ou sharding ?
R : Replica set d’abord, sharding ensuite. Un replica set monte jusqu’à 30 à 50 K écritures/seconde sur des machines correctes. Le sharding s’introduit quand un seul replica set sature, pas avant. Sharding sans replica set sous-jacent n’existe pas — chaque shard est lui-même un replica set.

Pour aller plus loin

Service ITSkillsCenter

Site ou application web sur mesure

Conception Pro + Nom de domaine 1 an + Hébergement 1 an + Formation + Support 6 mois. Accès et code livrés. À partir de 350 000 FCFA.

Demander un devis
Publicité