Grands systèmes, petites erreurs : pourquoi « une ligne » peut coûter une fortune

Cet article vous a aidé ?

Les pannes les plus coûteuses auxquelles j’ai participé n’ont jamais été causées par la « complexité ». Elles ont été causées par la confiance.
Quelqu’un a modifié une ligne — parfois un flag, parfois une valeur par défaut, parfois un « correctif temporaire » — et le système a fait exactement ce qu’on lui disait.
Voilà le problème.

Dans de grands environnements de production, vous n’obtenez pas une panne partielle en guise de faveur. Vous obtenez un comportement en cascade, amplifié par l’automatisation, les caches, les retries et l’auto-réparation bien intentionnée.
Une erreur d’une ligne devient un événement touchant toute la flotte, et la facture arrive sous forme de revenus perdus, d’utilisateurs en colère et d’une semaine d’archéologie médico-légale.

Pourquoi « une ligne » est dangereuse dans les grands systèmes

Le mythe : une ligne ne peut pas être si grave. La réalité : une ligne est souvent la seule chose qui sépare votre système de ses pires instincts.
Les grands systèmes sont chargés de facteurs multiplicatifs : échelle, automatisation, retries, coordination distribuée, infrastructure partagée et valeurs par défaut « utiles ».
Une petite mauvaise configuration ne casse pas seulement une machine ; elle change le comportement partout où cette ligne est appliquée.

Un changement d’une ligne est rarement un effet d’une seule ligne. Il modifie le timing. Le timing modifie la mise en file d’attente. La mise en file d’attente modifie la latence.
La latence déclenche des timeouts. Les timeouts déclenchent des retries. Les retries augmentent la charge. La charge augmente la latence. Félicitations : vous avez construit une boucle de rétroaction.

La plupart des post-mortems finissent par admettre la même vérité gênante : le système était techniquement « sain » jusqu’à ce que des humains lui apprennent une nouvelle, pire leçon.
Et parce que le changement était petit, il a contourné le filtre de scepticisme de tout le monde. Les petits changements paraissent sûrs. Ils ne le sont pas.

Voici la règle opérationnelle qui vous fera économiser de l’argent : traitez les petits changements comme à haut risque lorsque le rayon d’impact est grand.
Une modification d’une ligne dans un magasin de configuration global n’est pas « petite ». C’est une diffusion.

La seule ligne qui compte est celle que le système exécute

Les gens argumentent sur l’intention. Les ordinateurs argumentent avec des tables de vérité.
Vous pouvez « vouloir » fixer un timeout à 30 secondes et le régler accidentellement à 30 millisecondes. Le système obéira instantanément, comme un stagiaire zélé sans contexte.

Blague n°1 (courte, pertinente) : La production, c’est l’endroit où vos hypothèses passent un audit par la réalité — à l’échelle.

Pourquoi les ingénieurs stockage deviennent nerveux devant un « simple ajustement de config »

Le stockage se trouve sur le chemin critique de presque tout : bases de données, journaux, files d’attente, conteneurs et la machinerie invisible de l’« état ».
Le stockage a aussi des spécificités : write amplification, comportement des caches, sémantiques d’fsync et ordonnancement I/O du noyau. Une ligne peut basculer un modèle de comportement entier :
synchronous vs. asynchronous writes, direct I/O vs. buffered, compression on/off, recordsize, options de montage, queue depth, ou un seul sysctl qui change les seuils de pages sales.

Pire : les défaillances de stockage sont souvent des catastrophes au ralenti. Le système ne plante pas ; il rampe.
C’est comme ça qu’on gaspille de l’argent : on laisse la flotte tourner pendant qu’elle brûle silencieusement du CPU sur des retries et attend des I/O.

Faits & contexte historique : petits changements, grosses conséquences

Le domaine de la fiabilité a une longue histoire d’apprentissage de la même leçon, avec différents costumes.
Voici quelques faits concrets et points de contexte qui importent quand vous décidez si un changement « mineur » mérite un processus lourd.

  1. Les accidents du Therac-25 (années 1980) sont un exemple classique d’effondrement des hypothèses logicielles et de sécurité. De petites erreurs logicielles combinées aux workflows ont conduit à des surdoses mortelles.
    Ce n’est pas « une ligne », mais c’est « petite logique, impact massif ».
  2. La perte du Mars Climate Orbiter (1999) est due à un décalage d’unités (impérial vs. métrique). C’est le mode d’échec canonique d’« hypothèse erronée » : les valeurs semblaient plausibles jusqu’à ce que la physique ne soit pas d’accord.
  3. Des pannes DNS ont à plusieurs reprises mis hors service des services majeurs parce que le DNS est une dépendance minuscule avec un énorme rayon d’impact. Un réglage de TTL ou de résolveur peut devenir rapidement un incident global.
  4. La panne du nord-est en 2003 impliquait des logiciels et des alarmes qui ont masqué le problème jusqu’à sa propagation. Les défaillances d’observabilité sont des cousines des « une ligne » : on ne peut pas réparer ce qu’on ne voit pas.
  5. Le comportement d’écriture différée (dirty page writeback) de Linux a évolué pendant des années parce que les valeurs par défaut peuvent causer soit des pics de latence soit un effondrement du débit sous pression. Un seul sysctl peut transférer la douleur du disque vers les utilisateurs.
  6. Le write hole du RAID et les politiques de cache sont des leçons vieilles de plusieurs décennies : un seul réglage sur un contrôleur (write-back vs write-through) change si une coupure de courant est un événement de performance ou une perte de données.
  7. Les « tempêtes de retry » sont une pathologie connue des systèmes distribués. Elles transforment une lenteur transitoire en surcharge soutenue. De minuscules valeurs de timeout/retry peuvent transformer vos clients en armes contre vos propres serveurs.
  8. La configuration-as-code est devenue populaire surtout parce que la dérive manuelle des configs provoquait des comportements imprévisibles ; la solution a été la répétabilité. Ironiquement, cela a aussi facilité la diffusion d’une mauvaise ligne partout à la fois.

Une citation que les opérateurs finissent par intérioriser après assez de nuits blanches :
« L’espoir n’est pas une stratégie. » — James Cameron

Ce n’est pas une citation romantique. C’est une citation de runbook. Si votre histoire de sécurité est « ça marchera probablement », vous êtes déjà en train de négocier avec la panne.

La physique de l’amplification : comment de petites erreurs deviennent des pannes

1) Les systèmes distribués ne tombent pas poliment

Les pannes mono-nœud sont nettes. Les pannes distribuées sont en tache.
Une erreur de config subtile peut produire des timeouts partiels, une charge inégale et des contentions étranges.
Votre load balancer continue d’envoyer du trafic. Votre autoscaler voit la latence et ajoute des nœuds. Votre base de données voit plus de connexions et commence à se débattre.
Le graphe ressemble à une avalanche au ralenti.

2) Les files d’attente cachent les problèmes jusqu’à ce qu’elles ne le fassent plus

Les files d’attente sont merveilleuses. Elles découplent producteurs et consommateurs.
Elles agissent aussi comme des cartes de crédit : elles repoussent la douleur et ajoutent des intérêts.
Un changement d’une ligne qui ralentit les consommateurs de 20% peut ne pas alerter pendant des heures, jusqu’à ce que l’arriéré atteigne un seuil et que tout commence à expirer.

3) Les caches transforment la correction en probabilité

Un cache masque la latence, amplifie la charge et vous fait oublier à quoi ressemble un cold-start.
Une ligne peut détruire votre hit rate : changer un format de clé de cache, inverser la politique d’éviction, ajuster le TTL ou introduire de la cardinalité.
Soudain, votre origine (souvent une base de données) fait un travail qu’elle n’a pas fait depuis des mois. Elle n’a jamais été provisionnée pour ça. Maintenant c’est le goulot et le bouc émissaire.

4) Le stockage est l’endroit où le « mineur » devient mesurable

En stockage, de petits réglages comptent parce qu’ils changent les patterns I/O :
random vs sequential, sync vs async, petit I/O vs large, metadata-heavy vs streaming, buffered vs direct, compressible vs incompressible.
Un mismatch de recordsize sur ZFS ou une option de montage peut transformer une base de données saine en générateur de latence.

Blague n°2 (courte, pertinente) : La latence de stockage, c’est comme une mauvaise blague — le timing est tout, et vous la ressentez toujours dans la pause.

5) « Par défaut » n’est pas synonyme de « sûr »

Les valeurs par défaut sont des compromis entre charges de travail, matériel et appétit pour le risque. Votre production n’est pas « moyenne ».
Un défaut acceptable pour un serveur web peut être terrible pour une base de données à forte écriture.
Pire, les valeurs par défaut changent selon les versions. Une mise à jour de paquet d’une ligne peut implicitement modifier cinq comportements que vous n’aviez jamais documentés.

Trois mini-récits d’entreprise (anonymisés, douloureusement plausibles)

Mini-récit n°1 : L’incident causé par une mauvaise hypothèse

Une entreprise SaaS de taille moyenne a déplacé une partie de sa flotte d’un type d’instance à un autre. Même famille CPU, NVMe « similaire », noyau plus récent.
Un ingénieur senior a fait un petit changement dans un manifeste de déploiement : augmenter le timeout de connexion pour un client gRPC interne parce que « le réseau est plus rapide maintenant ».
L’hypothèse : réseau plus rapide = latence plus faible ; donc un timeout plus bas est « sûr ».

Ce qu’ils ont manqué : le service derrière le endpoint gRPC dépendait d’un cluster Postgres qui avait occasionnellement des stalls fsync sous charge en rafale.
Ces stalls étaient généralement absorbés par l’ancien timeout plus généreux. Avec le nouveau timeout bas, le client commença à expirer rapidement et à retryer agressivement.
Les retries augmentèrent la charge sur le service, ce qui augmenta la contention en base, ce qui augmenta les stalls fsync. Retour de boucle positive classique.

L’astreinte a vu des « erreurs réseau » et a chassé des pertes de paquets. Le réseau était sain.
Pendant ce temps, les graphiques de la base montraient une hausse du nombre de connexions, une montée du temps d’attente des verrous et des pics de latence I/O.
L’incident n’était pas une panne isolée ; c’était un nouveau mode d’oscillation introduit par un changement de timeout bien intentionné.

Le correctif fut brutalement simple : revenir au timeout précédent, limiter les retries avec jitter et ajouter un circuit breaker.
L’apprentissage fut plus important : la latence n’est pas un scalaire. C’est une distribution. Quand vous ajustez des timeouts, vous choisissez quelle queue vous pouvez tolérer.

Mini-récit n°2 : L’optimisation qui s’est retournée contre eux

Une autre organisation faisait tourner un grand cluster Elasticsearch pour les logs et l’analytics sécurité. L’ingestion était lourde, le stockage coûteux, et quelqu’un décida « d’optimiser le disque ».
Ils ont inversé un réglage lié à la compression et modifié des options de filesystem sur les nœuds de données — une ligne dans un repo de gestion de config, déployée progressivement.
Le changement paraissait raisonnable dans un test labo avec des données synthétiques.

En production, l’ensemble de données avait une forme très différente. Certains index se comp primaient bien ; d’autres non.
L’utilisation CPU monta. Pas un peu — assez pour étirer les cycles de GC et augmenter la latence d’indexation.
La latence d’indexation entraîna des retries de bulk requests depuis les shippers. Les retries augmentèrent la charge d’ingestion. L’ingestion augmenta les merges de segments.
Les merges de segments augmentèrent la write amplification disque. Maintenant les disques étaient plus occupés qu’avant « l’optimisation disque ».

Les graphiques racontaient une histoire confuse : l’utilisation disque montait, le CPU montait, et la latence montait. Qu’est‑ce qui causait quoi ?
La réponse fut : oui.
Un changement visant à réduire le coût disque augmenta le coût CPU, ce qui augmenta la pression des merges, ce qui augmenta le coût disque. Le système trouva un nouvel équilibre : pire.

Ils ont rollbacké. Puis ils ont exécuté un vrai canary avec des données proches de la production, en se focalisant sur les latences de queue (tail) et les taux de merge plutôt que le débit moyen.
La leçon n’était pas « ne pas optimiser ». C’était « n’optimisez pas à l’aveugle ». Votre workload est le test.

Mini-récit n°3 : La pratique ennuyeuse mais correcte qui a sauvé la mise

Une plateforme de services financiers avait la réputation d’être douloureusement conservatrice. Les ingénieurs se plaignaient des fenêtres de changement, des canaries et des « trop de checklists ».
Puis un vendeur de stockage a livré une mise à jour de firmware pour corriger un bug connu. La mise à jour nécessitait une mise à jour du driver côté hôte et un petit changement de règle udev.
Une ligne. Inoffensive, non ?

Leur processus força le déploiement à démarrer sur un seul nœud non critique avec une charge synthétique plus du trafic shadow réel.
En quelques minutes, la latence sur ce nœud montra des pics périodiques. Pas assez pour faire tomber quoi que ce soit, mais suffisant pour apparaître en p99.
Ils arrêtèrent le déploiement avant qu’il n’atteigne la flotte principale.

La cause racine se révéla être une interaction de queue-depth entre le nouveau driver et leur ordonnanceur I/O du noyau.
L’ancien driver limitait silencieusement les I/O en cours ; le nouveau permit des queues plus profondes, ce qui améliora le débit mais dégrada la latence tail sous charge mixte lecture/écriture.
Leur workload se souciait plus du p99 que du débit.

Ils ajustèrent explicitement la profondeur de queue et fixèrent l’ordonnanceur par périphérique. Puis ils répétèrent le canary et déployèrent en sécurité.
Personne n’écrivit un postmortem héroïque. Ils évitèrent juste une panne. Voilà à quoi ressemble le « ennuyeux et correct ».

Playbook de diagnostic rapide : quoi vérifier en premier/deuxième/troisième

Quand les choses dérapent, vous n’avez pas le temps pour la philosophie. Vous avez besoin d’une séquence qui trouve le goulot avant que la salle ne se remplisse d’opinions.
C’est le playbook que j’utilise pour les incidents « le système est lent / timeout / échoue de manière intermittente », surtout quand le stockage peut être impliqué.

Premier : confirmer le symptôme et sa forme (latence vs erreurs vs saturation)

  • Est-ce global ou limité à une AZ/rack/pool de nœuds ?
  • p50 correct mais p99 mauvais (latence tail), ou les moyennes sont-elles aussi mauvaises ?
  • Les erreurs corrèlent-elles avec des timeouts (côté client) ou avec des échecs côté serveur ?

Deuxième : trouver la classe de goulot (CPU, mémoire, I/O, réseau, contention de locks)

  • CPU : user/system élevé, run queue, throttling, steal time.
  • Mémoire : reclaim, swap, OOM kills, churn du page cache.
  • I/O : iowait, latence disque, queue depth, filesystem stalls, pics d’fsync.
  • Réseau : pertes de paquets, retransmissions, limites conntrack, timeouts DNS.
  • Locks : attentes de verrous base, contention de mutex, pauses GC.

Troisième : déterminer si vous voyez la cause ou la conséquence

  • Un CPU élevé peut venir de compression, chiffrement, retries ou boucles de logging.
  • Un I/O élevé peut venir de compaction, merges, checkpoints ou d’une tempête de cache miss.
  • Les erreurs réseau peuvent être réelles, ou être des timeouts causés par des serveurs lents.

Quatrième : chercher le déclencheur « une ligne »

  • Déploiements récents, changements de config, feature flags, mises à jour noyau/firmware.
  • Les changements côté client (timeouts, retries, concurrency) sont des coupables fréquents.
  • Les bascules liées au stockage (sync, cache, options de montage, scheduler) sont silencieuses mais tranchantes.

Cinquième : réduire le rayon d’impact avant de comprendre parfaitement

  • Pausez les rollouts, geler les changements d’autoscaling, désactivez les retries agressifs.
  • Choisissez délibérément d’échouer ouvert/fermé selon l’impact business.
  • Préférez revenir en arrière plutôt que déboguer en live, sauf si le rollback est risqué.

Tâches pratiques : commandes, sorties et décisions (12+)

Ce sont des tâches réelles que vous pouvez exécuter pendant un incident. Chacune inclut une commande, une sortie d’exemple, ce que cela signifie et la décision suivante.
Ne les mémorisez pas. Gardez-les dans un runbook et utilisez-les de façon cohérente.

Task 1: Check basic load, uptime, and whether the node is thrashing

cr0x@server:~$ uptime
 14:22:10 up 31 days,  2:03,  3 users,  load average: 22.14, 18.09, 12.77

Ce que cela signifie : Une load average bien supérieure au nombre de CPU signifie souvent des tâches exécutables (contention CPU) ou du sleep non interruptible (iowait).

Décision : Vérifier immédiatement la saturation CPU et l’iowait (Tasks 2 et 3). Si la charge monte vite, commencer à réduire le trafic ou drainer des nœuds.

Task 2: See if CPU is the bottleneck or if “idle” is hiding I/O wait

cr0x@server:~$ mpstat -P ALL 1 3
Linux 6.2.0 (server) 	01/22/2026 	_x86_64_	(16 CPU)

12:22:12 PM  CPU   %usr %nice %sys %iowait %irq %soft %steal %idle
12:22:13 PM  all   12.4  0.0   6.1   41.8    0.0   0.7    0.0   39.0

Ce que cela signifie : Un %iowait élevé suggère que le CPU attend le disque, pas qu’il est occupé à calculer.

Décision : Traitez cela comme un incident I/O jusqu’à preuve du contraire. Passez à iostat et vérifications I/O par processus.

Task 3: Check disk latency and queueing quickly

cr0x@server:~$ iostat -x 1 3
Linux 6.2.0 (server) 	01/22/2026 	_x86_64_	(16 CPU)

Device            r/s     w/s   rkB/s   wkB/s  await  aqu-sz  %util
nvme0n1         220.0  1100.0  8800.0 64000.0  38.2    9.40   99.7

Ce que cela signifie : Un await autour de 38ms avec %util proche de 100% indique que le périphérique est saturé et que les requêtes sont mises en file.

Décision : Identifier qui émet des I/O (Task 4/5). Envisager de throttler, déplacer la charge ou rollbacker un changement qui a augmenté la write amplification.

Task 4: Find top I/O processes (fast triage)

cr0x@server:~$ iotop -o -b -n 3
Total DISK READ: 2.10 M/s | Total DISK WRITE: 85.30 M/s
  PID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN  IO%  COMMAND
18722 be/4  elastic    0.00 B/s  62.40 M/s    0.00 45.6 elasticsearch
 9421 be/4  postgres   0.00 B/s  18.70 M/s    0.00 21.0 postgres: checkpointer

Ce que cela signifie : Deux processus génèrent les écritures ; l’un est un checkpointer de base de données (en rafales) et l’autre est Elasticsearch (écritures lourdes et constantes).

Décision : Si cela correspond à un déploiement/config récent, revert. Sinon, chercher des changements de comportement en amont (cache misses, retries, pics d’ingestion).

Task 5: Confirm if you’re getting blocked in D state (I/O) and which syscall is stuck

cr0x@server:~$ ps -eo pid,stat,wchan:32,cmd | awk '$2 ~ /D/ {print}' | head
18722 D    io_schedule                    /usr/share/elasticsearch/jdk/bin/java ...
 9421 D    jbd2_log_wait_commit           postgres: checkpointer

Ce que cela signifie : Les processus en état D sont bloqués en attente d’I/O. Le canal d’attente indique les chemins noyau (journaling, scheduler).

Décision : Examiner la pression sur le filesystem/journal, la santé du périphérique et vérifier si une option de montage ou un changement d’ordonnanceur est intervenu.

Task 6: Check filesystem space and inode exhaustion (boring, frequent)

cr0x@server:~$ df -hT
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/nvme0n1p2 ext4  900G  865G   35G  97% /

Ce que cela signifie : 97% d’utilisation est dangereux : fragmentation, allocations plus lentes et risque d’atteindre 100% lors d’écritures en rafale.

Décision : Arrêter l’hémorragie : rotation des logs, suppression de fichiers temporaires ou ajout de capacité. Vérifier aussi les inodes :

cr0x@server:~$ df -ih
Filesystem     Inodes IUsed IFree IUse% Mounted on
/dev/nvme0n1p2   56M   56M     0  100% /

Ce que cela signifie : L’épuisement des inodes provoque « No space left on device » même quand des octets restent disponibles.

Décision : Identifier les explosions de petits fichiers (répertoires temp, caches) et corriger le workload. L’épuisement d’inodes est souvent le symptôme d’un processus en fuite ou d’une rétention mal configurée.

Task 7: Detect kernel memory pressure and reclaim storms

cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
12  6      0  10240  12000  80000    0    0   120  9800 8200 9900 12  6 38 44  0

Ce que cela signifie : Un b élevé (bloqué), un wa élevé (I/O wait) et une forte sortie bo indiquent une pression de writeback.

Décision : Si un sysctl d’une ligne a changé les limites dirty ou le timing de writeback, revert. Sinon, identifier l’écrivain et réduire son débit.

Task 8: Check recent reboots, OOM kills, and storage errors in the journal

cr0x@server:~$ journalctl -p warning -S -2h | tail -n 20
Jan 22 13:41:08 server kernel: nvme nvme0: I/O 123 QID 4 timeout, aborting
Jan 22 13:41:08 server kernel: EXT4-fs warning (device nvme0n1p2): ext4_end_bio:343: I/O error 10 writing to inode 262911

Ce que cela signifie : Les timeouts et erreurs I/O filesystem ne sont pas des « problèmes de performance ». Ce sont des incidents de fiabilité.

Décision : Commencer le diagnostic matériel : vérifier les logs SMART/NVMe (Task 9), envisager de retirer le nœud du service, et éviter les redémarrages forcés répétés qui peuvent aggraver la corruption.

Task 9: Inspect NVMe health and error counters

cr0x@server:~$ sudo nvme smart-log /dev/nvme0
SMART/Health Information (NVMe Log 0x02)
critical_warning                    : 0x00
temperature                         : 57 C
available_spare                     : 100%
percentage_used                     : 12%
media_errors                        : 3
num_err_log_entries                 : 27

Ce que cela signifie : Des media_errors non nuls et une augmentation des entrées de log d’erreur suggèrent de vrais problèmes de périphérique, pas seulement des réglages.

Décision : Drainer et remplacer le périphérique/nœud. Si cela a commencé après un changement de firmware, rollbacker le firmware sur les nœuds restants et ouvrir un dossier auprès du fournisseur.

Task 10: Verify mount options that change durability or latency behavior

cr0x@server:~$ findmnt -no TARGET,SOURCE,FSTYPE,OPTIONS /var/lib/postgresql
/var/lib/postgresql /dev/nvme0n1p2 ext4 rw,relatime,data=writeback,barrier=0

Ce que cela signifie : barrier=0 désactive les write barriers ; data=writeback change les sémantiques de journaling. Cela peut améliorer la vitesse et ruiner votre semaine.

Décision : Si c’est involontaire, revenir à des options plus sûres (pour ext4, les barriers sont généralement activées par défaut ; le mode journaling dépend du profil de risque). Confirmer avec vos exigences de durabilité de stockage.

Task 11: Check TCP retransmits and packet loss (don’t blame storage prematurely)

cr0x@server:~$ ss -s
Total: 19432 (kernel 20110)
TCP:   14210 (estab 1200, closed 12650, orphaned 8, timewait 3200)

Transport Total     IP        IPv6
RAW	  0         0         0
UDP	  90        70        20
TCP	  1560      1400      160
INET	  1650      1470      180
FRAG	  0         0         0
cr0x@server:~$ netstat -s | egrep -i 'retrans|listen|drops' | head
    12847 segments retransmitted
    320 listen queue overflows

Ce que cela signifie : Les retransmissions et débordements de file d’écoute peuvent imiter une lenteur de stockage en créant des tempêtes de timeout.

Décision : Si les retransmissions ont grimpé après un changement (paramètres TLS, MTU, load balancer), traiter le réseau en priorité. Si des overflows se produisent, tuner le backlog et réduire la contention de la boucle d’acceptation.

Task 12: Detect DNS or resolver issues that look like “the app is slow”

cr0x@server:~$ resolvectl status | sed -n '1,80p'
Global
       Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
resolv.conf mode: stub
Current DNS Server: 10.0.0.53
       DNS Servers: 10.0.0.53 10.0.0.54
cr0x@server:~$ dig +stats example.internal A | tail -n 5
;; Query time: 1200 msec
;; SERVER: 10.0.0.53#53(10.0.0.53) (UDP)
;; WHEN: Thu Jan 22 14:24:10 UTC 2026
;; MSG SIZE  rcvd: 86

Ce que cela signifie : Une requête DNS à 1.2s fera paraître tout cassé, surtout les services qui résolvent fréquemment.

Décision : Arrêter l’hémorragie immédiate : mettre en cache les résultats DNS côté client quand c’est approprié, réduire la fréquence de résolution ou basculer les résolveurs. Puis corriger le problème DNS.

Task 13: Check systemd for restart storms (a one-line failure multiplier)

cr0x@server:~$ systemctl status api.service
● api.service - Example API
     Loaded: loaded (/etc/systemd/system/api.service; enabled)
     Active: activating (auto-restart) since Thu 2026-01-22 14:21:02 UTC; 3s ago
    Process: 23110 ExecStart=/usr/local/bin/api --config /etc/api/config.yaml (code=exited, status=1/FAILURE)
cr0x@server:~$ journalctl -u api.service -S -10m | tail -n 10
Jan 22 14:21:01 server api[23110]: FATAL: config: unknown field "reties"
Jan 22 14:21:02 server systemd[1]: api.service: Scheduled restart job, restart counter is at 58.

Ce que cela signifie : Une faute de frappe provoque des redémarrages continus, ce qui ajoute de la charge (logs, churn de connexions) et consume des ressources.

Décision : Arrêter la tempête de redémarrages (désactiver ou mettre backoff), revert la config et ajouter une validation de schéma dans la CI pour que les fautes de frappe ne passent pas en prod.

Task 14: For ZFS environments, check pool health and latency suspects

cr0x@server:~$ sudo zpool status
  pool: tank
 state: ONLINE
  scan: scrub repaired 0B in 02:11:33 with 0 errors on Wed Jan 21 03:00:14 2026
config:

	NAME        STATE     READ WRITE CKSUM
	tank        ONLINE       0     0     0
	  mirror-0  ONLINE       0     0     0
	    sda     ONLINE       0     0     0
	    sdb     ONLINE       0     0     0

errors: No known data errors
cr0x@server:~$ sudo zfs get -o name,property,value -H compression,recordsize,atime,sync tank/db
tank/db	compression	zstd
tank/db	recordsize	128K
tank/db	atime	off
tank/db	sync	standard

Ce que cela signifie : L’état du pool est correct ; regardez maintenant les propriétés du dataset. Un mismatch de recordsize ou un réglage sync peut changer dramatiquement la latence d’une base de données.

Décision : Si le workload est une base avec de petits écrits aléatoires, envisager d’ajuster recordsize et vérifier les attentes de sync. Ne pas activer sync=disabled à moins d’aimer expliquer une perte de données.

Erreurs courantes : symptômes → cause profonde → correctif

C’est la partie où on arrête de prétendre que ces pannes sont rares.
La plupart des incidents « une ligne » entrent dans des schémas répétables. Apprenez l’odeur, et vous les attraperez plus tôt.

1) Timeouts soudains après « réduction des timeouts pour une UX plus réactive »

Symptômes : Plus de 499/504, les retries clients explosent, les serveurs semblent « corrects » mais le p99 explose.

Cause profonde : Timeout en dessous de la latence tail d’une dépendance ; le comportement de retry amplifie la charge.

Correctif : Augmenter les timeouts pour couvrir des p99.9 réalistes ; limiter les retries avec backoff exponentiel + jitter ; ajouter des circuit breakers et des bulkheads.

2) « Le disque est devenu plus lent » juste après activation de la compression ou du chiffrement

Symptômes : CPU monte, pauses GC augmentent, queue depth I/O monte, le débit peut chuter ou devenir en pics.

Cause profonde : Le CPU devient le goulot caché ; la maintenance de fond (merges/compaction) augmente la write amplification.

Correctif : Faire un canary avec des données proches de la production. Mesurer p95/p99 et l’activité de fond. Envisager accélération matérielle, algorithmes différents ou compression sélective.

3) Pics de latence fsync en base après une « option de montage performante »

Symptômes : La latence de commit monte, les checkpoints stagnent, les timeouts applicatifs se regroupent.

Cause profonde : Les options de montage ont changé le comportement du journaling/barrier, un mismatch d’ordonnanceur I/O ou un réglage de writeback a nui à la latence tail.

Correctif : Revenir sur les options de montage non sûres ; fixer l’ordonnanceur ; régler la profondeur de queue pour la latence ; s’assurer que les paramètres de durabilité correspondent aux besoins.

4) Tempête de redémarrage sur toute la flotte après une édition de config triviale

Symptômes : Instances qui flapent, logs explosent, les services en amont subissent du churn de connexions.

Cause profonde : Syntaxe de config/champ invalide ; systemd ou orchestrateur redémarre agressivement.

Correctif : Ajouter une validation de config dans la CI, implémenter un backoff de redémarrage et exiger canary + smoke tests avant le déploiement complet.

5) « Tout est lent » mais seulement au cold start ou après rotation de nœuds

Symptômes : Latence de lecture élevée après déploi, hit rate cache chute, bases chauffent.

Cause profonde : Eviction de cache ou changement de clé ; caches locaux de nœud non chauds ; churn agressif des pods réinitialise les working sets.

Correctif : Préserver les caches quand c’est possible, préchauffer les caches critiques, réduire le churn et surveiller le hit rate comme un SLO de première classe.

6) Le stockage semble correct, mais les clients expirent et font la queue

Symptômes : Les métriques serveur semblent stables ; les clients subissent des échecs intermittents ; les retransmissions augmentent.

Cause profonde : Perte réseau, mismatch MTU, ou limites conntrack/écoute surchargées ; « lent » est en réalité « impossible à connecter de façon fiable ».

Correctif : Vérifier retransmissions, drops, conntrack ; valider MTU bout en bout ; tuner le backlog ; réduire le churn de connexions.

7) « Nous n’avons rien changé » (si, vous l’avez fait)

Symptômes : L’incident coïncide avec aucun déploiement applicatif, mais le comportement a changé.

Cause profonde : Mises à jour noyau/firmware, changements d’image de base, upgrades de dépendances, dérive de config management, ou une valeur par défaut qui a changé.

Correctif : Tracer les changements sur toute la pile. Traitez les rollouts « plateforme » avec la même rigueur que les changements applicatifs.

Checklists / plan pas à pas pour des changements d’une ligne plus sûrs

Si vous voulez moins de pannes, arrêtez de compter sur la mémoire et les bonnes intentions.
Utilisez des barrières explicites. Le système se fiche que vous étiez occupé.

Étape par étape : faire un changement d’une ligne sans provoquer un incident d’une semaine

  1. Classifiez le rayon d’impact.
    Demandez-vous : cette ligne s’applique-t-elle à un nœud, un service, une région ou toute l’entreprise ?
    Si c’est global, traitez-le comme un déploiement de code.
  2. Nommez le mode d’échec que vous êtes prêt à accepter.
    Baisser un timeout signifie accepter plus de retries et d’échecs sous lenteur.
    Changer des paramètres de durabilité signifie accepter une perte potentielle de données. Dites-le à voix haute.
  3. Rédigez le plan de rollback avant le changement.
    Si le rollback requiert « plus tard », vous n’avez pas de plan de rollback.
  4. Construisez un canary honnête.
    Un nœud avec aucun trafic réel n’est pas un canary ; c’est un labo.
    Utilisez du vrai trafic (shadow si nécessaire) et mesurez p95/p99.
  5. Définissez des signaux d’arrêt.
    Exemples : p99 latency > X pendant Y minutes, taux d’erreur > Z, profondeur de file > Q, disk await > A.
  6. Livrez le changement avec de l’observabilité.
    Ajoutez des métriques/logs qui confirment l’effet attendu du changement.
    Si vous ne pouvez pas le mesurer, ne touchez pas en production.
  7. Déployez progressivement, avec des paliers.
    Utilisez un rollout en étapes : 1 nœud → 1% → 10% → 50% → 100%.
    Faites une pause entre les étapes suffisamment longue pour observer le comportement tail.
  8. Prévenez les tempêtes de retry.
    Assurez-vous que des budgets de retry existent côté client. Ajoutez du jitter. Caper la concurrency.
  9. Consignez la décision.
    Mettez la raison à côté de la ligne dans un commentaire de code ou la description du changement.
    Le vous futur sera fatigué et suspicieux.
  10. Vérification post-changement.
    Confirmez non seulement « c’est en ligne », mais que la métrique cible a bougé dans la bonne direction sans régression tail.

Ce qu’il faut éviter (opinionné, parce que j’aime dormir)

  • Ne changez pas les timeouts sans modifier la politique de retry.
  • Ne désactivez pas « temporairement » les barriers de durabilité pour respecter une échéance.
  • Ne déployez pas de mises à jour plateforme sans canaries sensibles à l’application.
  • Ne faites pas confiance aux moyennes. Surveillez p95/p99 et la profondeur de file.
  • Ne traitez pas un repo de config comme « sûr » simplement parce que ce n’est pas du code. C’est une intention exécutable.

FAQ

1) Pourquoi de petits changements de config causent-ils plus de pannes que de gros changements de code ?

Parce que les configs tendent à être globales, rapides à appliquer et mal testées. Les changements de code passent souvent par CI, revues et déploiements échelonnés.
Un changement de config peut éviter tout cela et affecter néanmoins tout.

2) Quelle est la façon la plus rapide de dire si la lenteur est liée au stockage ?

Vérifiez mpstat pour un iowait élevé, puis iostat -x pour await, aqu-sz et %util.
Ensuite utilisez iotop pour identifier le writer/reader. Si le disque est saturé, le stockage fait au moins partie de l’histoire.

3) Les retries sont-ils toujours mauvais ?

Les retries sont nécessaires, mais les retries non contrôlés sont une attaque DDoS distribuée que vous lancez par accident contre vous-même.
Utilisez des budgets de retry, backoff exponentiel, jitter et circuit breakers.

4) Devrait-on systématiquement revenir en arrière immédiatement ?

Si l’incident a commencé juste après un changement et que le rollback est sûr, oui.
Déboguer en direct est séduisant et souvent plus lent que revert. Exceptions : migrations de schéma, transformations de données irréversibles ou incidents de sécurité.

5) Comment choisir des timeouts sans deviner ?

Utilisez les distributions de latence en production des dépendances. Fixez les timeouts au-dessus d’un p99.9 réaliste plus une marge.
Ensuite assurez-vous que votre politique de retry ne multiplie pas la charge pendant la dégradation.

6) Quelle est l’erreur de stockage « d’une ligne » la plus courante ?

Changer les sémantiques de durabilité (write barriers, comportement sync) pour la performance.
Cela peut sembler performant dans les benchmarks et se transformer en corruption ou perte de données après un crash ou un événement de coupure d’alimentation.

7) Pourquoi activer la compression aggrave parfois l’utilisation disque ?

La compression peut augmenter l’utilisation CPU, ce qui ralentit l’indexation/compaction, augmentant la write amplification et l’espace temporaire de segment/compaction.
De plus, des données incompressibles peuvent quand même entraîner un surcoût métadonnée et de traitement.

8) Qu’est-ce que le « blast radius » en termes pratiques ?

C’est combien d’utilisateurs et de systèmes sont impactés quand le changement tourne mal.
Un changement d’un nœud est une égratignure. Un changement global de config est un feu de forêt. Planifiez en conséquence.

9) Comment rendre les changements de config plus sûrs sans tout ralentir ?

Traitez la config comme du code : validation, canaries, rollout par paliers, triggers d’rollback automatisés et pistes d’audit.
Vous irez plus vite en global parce que vous passerez moins de temps en incidents.

10) Et si on ne peut pas faire de canary parce que le changement ne fonctionne que globalement ?

Alors vous avez besoin d’un canary synthétique : dupliquer le trafic, utiliser des lectures shadow ou un environnement parallèle qui reflète les dépendances clés.
Si vous ne pouvez vraiment pas valider en sécurité, le changement est intrinsèquement risqué — planifiez-le comme tel.

Conclusion : prochaines étapes qui réduisent vraiment les pannes

Les grands systèmes ne vous punissent pas pour avoir écrit du mauvais code. Ils vous punissent pour avoir expédié des hypothèses non testées dans une machine qui les amplifie parfaitement.
La « une ligne » n’est pas le méchant ; le méchant est le rayon d’impact non borné combiné à des boucles de rétroaction faibles.

Prochaines étapes pratiques :

  • Choisissez une surface de config à haut risque (timeouts, retries, options de montage stockage, caching) et placez-la derrière des rollouts étagés.
  • Ajoutez une validation de config automatisée dans CI/CD pour que les fautes de frappe et mismatch de schéma n’atteignent jamais la prod.
  • Instrumentez la latence tail et la profondeur de file partout où vous avez une frontière de dépendance.
  • Rédigez (et répétez) des procédures de rollback qui n’exigent pas d’héros.
  • Normalisez la « correction ennuyante » : canaries, paliers et signaux d’arrêt. C’est moins cher que l’adrénaline.

Si vous ne retenez rien d’autre : traitez les petits changements comme des événements potentiellement majeurs, parce qu’en production, l’échelle est un multiplicateur et le temps est une taxe.
Payez maintenant.

← Précédent
Debian 13 : NTP fonctionne mais la dérive persiste — Réglages de l’horloge matérielle et de chrony (Cas n°19)
Suivant →
Incompatibilité de version PHP pour WordPress : vérifier et mettre à niveau sans interruption

Laisser un commentaire