Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Architecture physique phys Décidé

Trois datacenters géographiquement séparés, zéro RAID : chaque disque est une brique autonome, la redondance appartient à l'application — erasure coding 8+3 local, réplication asynchrone des segments scellés entre sites, quorum PostgreSQL à trois pour rendre le split-brain impossible. Une seule couche de redondance, du disque au continent.

En clair — pourquoi trois, et pas deux Avec deux sites, le jour où le lien tombe, chacun se demande : « l'autre est-il mort, ou juste injoignable ? ». S'ils décident tous les deux de continuer seuls, on obtient deux vérités divergentes — le split-brain, la pire panne des systèmes distribués. Le troisième site est l'arbitre : la majorité (2 sur 3) tranche toujours, personne ne peut se croire légitime tout seul. Et aucun des trois n'est un site « de secours » qui dort : chacun est primaire pour un tiers des tenants, réplique pour un autre tiers, et arbitre pour tous. Trois coffres-forts dans trois villes, tous au travail.

Vue d’ensemble — les trois datacenters

Topologie idéale — trois DC actifs, quorum synchrone, données asynchrones

AGENTS — HTTPS 443 · GSLB / multi-endpoints dans le bootstrap QUORUM POSTGRESQL SYNCHRONE (2/3) — SPLIT-BRAIN IMPOSSIBLE DC-1 PostgreSQL membre quorum API · front OpenBao STOCKAGE · EC 8+3 6 nœuds · 72 disques JBOD NVMe méta ×3 · HDD données segments scellés immuables primaire : tenants ⅓ A réplique : tenants ⅓ C bastion · IPMI hors bande DC-2 PostgreSQL membre quorum API · front OpenBao STOCKAGE · EC 8+3 6 nœuds · 72 disques JBOD NVMe méta ×3 · HDD données segments scellés immuables primaire : tenants ⅓ B réplique : tenants ⅓ A bastion · IPMI hors bande DC-3 PostgreSQL membre quorum API · front OpenBao STOCKAGE · EC 8+3 6 nœuds · 72 disques JBOD NVMe méta ×3 · HDD données segments scellés immuables primaire : tenants ⅓ C réplique : tenants ⅓ B bastion · IPMI hors bande réplication asynchrone des segments scellés — A→2, B→3, C→1 · vérifiée par rehash liens inter-DC dédiés · latence indifférente pour les données (asynchrone) · quelques ms tolérées pour le quorum domaines de panne réellement indépendants : villes différentes, opérateurs électriques différents — la leçon OVH Strasbourg

Les trois DC sont actifs — chacun primaire pour un tiers des tenants, réplique pour un autre tiers, arbitre pour tous. Le plan de contrôle (minuscule) est synchrone ; les données (massives) sont asynchrones : chaque plan au régime que sa taille permet.

Décision 1 — Zéro RAID : la brique disque

En clair — on ne paie la redondance qu'une seule fois À l'échelle, il faut de toute façon survivre à la perte d'un nœud entier, puis d'un site entier — une redondance inter-machines est donc obligatoire. Si chaque nœud porte en plus du RAID local, on paie la redondance deux fois (1,2× RAID × 2-3× réplication). La seule couche capable de couvrir tous les étages — disque, nœud, salle, ville — c'est celle de l'application : c'est elle qu'on garde, et on licencie l'intermédiaire. C'est le modèle Backblaze / MinIO / Ceph — et notre modèle de données y est exceptionnellement adapté.
En clair — JBOD, HBA : le portier plutôt que le majordome JBOD (Just a Bunch Of Disks — « juste une pile de disques ») : les disques sont présentés au système un par un, tels quels, sans être fusionnés en un volume — l'exact opposé du RAID. HBA (Host Bus Adapter) : la carte qui relie les disques au serveur en simple passe-plat — elle transmet, point. Le contrôleur RAID, lui, est un petit ordinateur intermédiaire — firmware propriétaire, cache, batterie — qui prend des décisions sur vos octets à votre place : un majordome qui range vos affaires selon son propre système sans vous dire où. Quand l'application sait exactement où est chaque chose (notre index), on n'a pas besoin du majordome — juste d'un portier qui ouvre la porte.

Chaque HDD est une brique autonome : un XFS nu, une identité (UUID), un inventaire de segments auto-descriptif — un scan du disque suffit à reconstituer ce qu’il contient, dans la droite ligne de la hiérarchie de vérité. Pas de contrôleur RAID : un HBA simple — moins cher, pas de write-hole, pas de batterie de cache, pas de firmware propriétaire entre nous et nos octets. Ce que le sans-RAID apporte structurellement :

  • Pas de reshape, jamais — ajouter un disque = une cible de plus pour les nouveaux segments. Le parc peut être hétérogène (des 16 To et des 24 To cohabitent), on achète le disque du moment au prix du moment.
  • Le scrub RAID est licencié aussi — nos checksums sont de bout en bout (chaque entrée de segment se revérifie en rehachant) là où le RAID ne vérifie que le plateau. Le scrubbing applicatif itère les segments par disque, en tâche de fond throttlée.
  • Le rayon de panne est un disque, pas un array — un disque meurt, les 11 autres ne sont pas dégradés, aucun client ne le sent.

Décision 2 — Le cycle de vie d’un segment : chaud sous escorte, froid dans le béton

Le cycle de vie d'un segment — répliqué chaud, encodé scellé, géo-copié vérifié

OUVERT · CHAUD append streaming répliqué 2× — 2 nœuds fsync groupé ack = durable local fenêtre courte : minutes SCELLÉ · EC 8+3 8 données + 3 parités · Reed-Solomon systématique 11 shards · ≤ 2 par nœud tolère 3 pannes arbitraires lectures = shards de données, en direct répliques d'ouverture libérées GÉO · DC RÉPLIQUE copie asynchrone ré-encodée 8+3 sur place vérifiée par rehash immuabilité indépendante lag typique : minutes scellement minutes Option « commit géo » par tenant : la racine du snapshot n'est committée qu'après réplication — RPO géographique = 0 défaut : commit local (ack rapide), géo-durabilité en minutes — le client re-uploade de toute façon ce qui manque à la reprise

Le chemin chaud reste simple (2 répliques, append, fsync groupé) ; la redondance définitive (EC) ne s'applique qu'aux segments scellés, immuables — l'encodage n'est jamais dans le chemin de latence du client.

Le détail qui compte : Reed-Solomon systématique — les 8 premiers shards contiennent les octets tels quels. Une lecture normale (restauration) lit directement le shard concerné, sans aucun décodage ; les 3 parités ne servent qu’à la réparation. La parité est une roue de secours, pas un pneu de route. Overhead : 1,375× — comparable au RAID6 (1,2×), pour une tolérance de 3 pannes arbitraires et une couche unique.

Décision 3 — Le placement hiérarchique des shards

La règle, formulée une fois pour tous les étages : ne jamais placer dans un même domaine de panne plus de shards que la parité ne peut en absorber.

  • Domaines emboîtés — disque ⊂ nœud ⊂ (rangée/salle) ⊂ site. Le placeur connaît la topologie et répartit les 11 shards en conséquence.
  • Avec 6 nœuds par DC : ≤ 2 shards par nœud → la perte d'un nœud entier + un disque ailleurs reste réparable (2 + 1 = 3 = m). Avec 11+ nœuds : 1 shard par nœud, tolérance de 3 nœuds.
  • Les métadonnées ne sont pas en EC — segments métadonnées répliqués 3× sur les NVMe de 3 nœuds : le volume est faible (le 3× ne coûte rien) et la latence de lecture est critique (pas de décodage sur le chemin des arbres parents ni du mark). L'index LSM : local par nœud, sans redondance — on ne protège pas un cache reconstructible.

Décision 4 — La réparation déclusterisée

Panne d'un disque de 20 To — l'entonnoir RAID contre le maillage déclusterisé

RAID — L'ENTONNOIR tout converge vers UN disque neuf des JOURS · array dégradé · re-panne redoutée DÉCLUSTERISÉ — LE MAILLAGE lu partout, réécrit partout — l'index sait où sont les shards des HEURES · charge diluée · aucun array dégradé RAID : toute la maison attend le seul plombier. Déclusterisé : tout le quartier donne un coup de main.

Un disque de 20 To meurt : l'index liste ses shards, la reconstruction lit les survivants sur des dizaines de disques et réécrit les manquants sur des dizaines d'autres. Heures au lieu de jours, fenêtre de double-panne écrasée, zéro dégradation perçue par les clients.

Décision 5 — Les trois DC en détail : qui fait quoi

  • Chaque tenant a un DC primaire et un DC réplique, assignés à l'enrôlement du tenant et documentés (la résidence des données par client que la HDS exige). La charge s'équilibre : chaque DC est primaire pour ~⅓ des tenants.
  • Plan de contrôle synchrone, données asynchrones — Postgres réplique en synchrone sur le quorum (l'état est minuscule, quelques ms de latence inter-DC sont indolores) ; les segments (massifs) voyagent en asynchrone une fois scellés. Chaque plan au régime que sa taille permet.
  • Le RPO géographique est plus doux qu'il n'y paraît — spécificité du backup : si le DC primaire brûle avec des segments pas encore répliqués, le client possède toujours ses données sources — la reprise par dédup re-uploade le manquant au run suivant vers la réplique promue. Seuls les snapshots committés non répliqués sont perdus. Pour les tenants qui exigent zéro : l'option commit géo (la racine n'est committée qu'après réplication de ses segments) — RPO géo = 0, au prix de la latence de commit. Par plan, évidemment.
  • Bascule — DC primaire d'un tenant perdu : le quorum élit, la réplique est promue, les agents rebasculent via leurs multi-endpoints (bootstrap) et retrouvent leurs sessions. RTO : minutes.
  • L'immuabilité voyage — le DC réplique applique le plancher WORM indépendamment : compromettre le primaire ne suffit pas à effacer l'historique répliqué.
SinistreRPORTOMécanisme
Disque00 (transparent)EC 8+3, réparation déclusterisée en heures
Nœud entier00 (transparent)placement ≤2 shards/nœud
DC primaire d’un tenantlag async (minutes) — 0 en commit géominutespromotion réplique + quorum + multi-endpoints
Deux DCselon option 3ᵉ copie (par plan)heuresle 3ᵉ DC garde quorum + ses tenants

Le nœud de référence & le dimensionnement

ComposantSpécificationPourquoi
Châssis2U, 12 baies 3,5“l’unité de croissance — on ajoute des châssis, pas des arrays
Données12 × HDD 20-22 To, JBOD sur HBAsegments append-only séquentiels : le cas idéal du disque rotatif
Métadonnées2-3 × NVMe 3,84 Tosegments méta (répliqués 3× inter-nœuds), index LSM, WAL
RAM128 Goblooms GC (~60 Mo/gros tenant), cache métadonnées, LSM
CPU16-24 cœurs, milieu de gammeon est network-bound — BLAKE3 rehache à plusieurs Go/s par cœur
Réseau2 × 25 GbE + IPMI dédiétrois plans : front 443 · stockage interne · management hors bande

L’unité de déploiement par DC : 6 nœuds de stockage + 3 nœuds de contrôle (Postgres, OpenBao, API/front) + 2 switches. Capacité : 6 × 12 × 20 To ÷ 1,375 ≈ 1 Po utile par DC. Comptabilité globale : chaque octet logique coûte 1,375× au primaire + 1,375× à la réplique = 2,75× brut — contre 3× pour de la triple réplication naïve, avec une bien meilleure tolérance aux pannes. Au-delà : on ajoute des châssis (le placement rééquilibre seul), puis des rangées — la règle des domaines de panne absorbe chaque étage sans changement de code.

La durabilité en perspective EC 8+3 avec réparation en heures → la probabilité de perdre 4 shards d'un même segment avant réparation est infinitésimale ; ajoutez la copie indépendante du second DC, et le calcul de « neufs » cesse d'être le facteur limitant. Les risques dominants deviennent logiciels et humains — un bug, une mauvaise commande — et c'est exactement ce que la quarantaine du GC, l'immuabilité et le scrubbing adressent. On dimensionne le matériel contre les pannes, et le logiciel contre nous-mêmes.
L'honnêteté du coût — la complexité change de camp Ce design licencie le contrôleur RAID mais embauche notre code : placement topologique, orchestration de réparation, rééquilibrage, réplication inter-DC — le chantier « Durabilité » devient le cœur du système de stockage, au budget dès le premier jour. C'est le bon échange : cette complexité-là est la nôtre — testable, observable, injectable en fautes — et le même code sert toutes les topologies : un déploiement revendeur mono-site n'est qu'un cas dégénéré de l'architecture à trois DC (EC intra-châssis, pas de géo) — rien à réécrire, des étages en moins.