Magie héritée : personne ne sait comment ça marche, alors n’y touchez pas

Cet article vous a aidé ?

Vous héritez d’un système qui paie des salaires, expédie des commandes ou règle des transactions. Il a la disponibilité d’un phare et la lisibilité d’une lettre de rançon.
Tout le monde l’appelle « stable ». Ce qu’ils veulent dire : personne n’a osé le modifier depuis des années.

Puis un disque meurt, un certificat expire, un fournisseur disparaît ou la facture cloud double. Soudain on attend de vous que vous touchiez la « magie ».
Voilà comment le faire sans transformer la production en session de thérapie collective.

Ce qu’est vraiment la « magie héritée »

« Personne ne sait comment ça marche, alors n’y touchez pas » n’est pas une évaluation technique. C’est une cicatrice organisationnelle. Cela signifie généralement :
le système est critique pour le business, mal instrumenté, sous-documenté, et a accumulé des correctifs qui fonctionnent seulement sous
un ensemble de contraintes spécifiques et en grande partie oubliées.

La partie la plus dangereuse n’est pas le code ancien. Ce sont les contrats invisibles : timeouts réglés pour un profil de latence de stockage précis,
des cron planifiés autour de fenêtres batch, des bizarreries de schéma exploitées par un unique job d’export, des paramètres kernel définis par quelqu’un
qui est maintenant instructeur de plongée. Les systèmes hérités tournent sur des hypothèses. Quand ces hypothèses se dégradent, le « stable » devient « hanté ».

Il existe une mythologie autour de ces systèmes : le « sorcier » qui l’a écrit, le trigger de base de données qu’« il ne faut jamais toucher »,
le serveur spécial qui « a besoin » d’une certaine carte réseau. La mythologie rend les équipes prudentes ; la prudence est utile. La mythologie rend aussi les équipes paresseuses ; la paresse
est la façon dont les incidents deviennent des traditions.

Pourquoi elle survit (et pourquoi elle échoue)

La magie héritée survit parce que c’était autrefois la meilleure option

La plupart des stacks « mystères » n’ont pas été créés par des incompétents. Ils ont été créés par des personnes sous pression, avec des outils limités,
et souvent des contraintes matérielles qui forçaient à être ingénieux. Si vous êtes jeune au point d’avoir toujours connu des SSD bon marché et des bases gérées,
vous vivez dans le luxe. Les anciens systèmes devaient tirer des performances de disques rotatifs, de RAM réduite, de réseaux lents,
et parfois d’un budget qui ressemblait à une erreur de frappe.

Le système survit parce qu’il est aligné avec le business : il fait une chose suffisamment bien, et le risque de le changer semble plus élevé
que le risque de le laisser tel quel. Ce calcul est rationnel—jusqu’à ce qu’il ne le soit plus.

La magie héritée échoue parce que l’environnement change, pas parce qu’elle « expire »

Les systèmes ne meurent pas de vieillesse. Ils meurent de dérive d’interface : les bibliothèques changent de comportement, les exigences TLS se renforcent, des problèmes DNS ou NTP sont amplifiés par des vérifications strictes de l’heure, le stockage virtualisé se comporte différemment des disques locaux, et votre nouveau type d’instance « plus rapide » livre une topologie CPU complètement différente. Puis le système qui fonctionnait depuis des années s’effondre en une semaine.

L’ingénierie de la fiabilité consiste surtout à maintenir vraies les hypothèses, ou à les rendre non pertinentes. Si vous traitez la magie héritée comme un artefact sacré,
vous garantissez que vous finirez par la casser—juste au pire moment possible.

Faits et contexte historique que vous pouvez utiliser

  • « Écrire une fois, exécuter partout » avait une ombre : la portabilité précoce cachait souvent des hypothèses de performance, notamment autour des sémantiques de système de fichiers et de la gestion du temps.
  • Les systèmes de fichiers POSIX n’étaient pas conçus pour votre essaim de microservices : beaucoup d’apps héritées supposent des opérations de fichiers locales, à faible latence et fortement cohérentes.
  • NFS est devenu la colle par défaut en entreprise : il a aussi normalisé le « c’est lent parfois » comme réalité opérationnelle—nombre d’apps ont discrètement codé des contournements.
  • Les caches des contrôleurs RAID ont modelé les réglages de bases : l’ancien tuning de base de données supposait souvent un cache d’écriture sur batterie ; le retirer et votre système « stable » devient une machine à latence.
  • Les VM ont changé le temps : la dérive d’horloge et le jitter d’ordonnancement dans les environnements virtualisés ont cassé des logiciels plus anciens qui supposaient un temps monotone ou des timers prévisibles.
  • Les fichiers de log étaient autrefois la pile d’observabilité : beaucoup de « magie » repose sur du scraping de logs ou des pipelines grep ad hoc que personne n’admet être critiques en production.
  • Les scripts init contenaient la logique opérationnelle : avant systemd, beaucoup d’organisations ont incorporé des règles de séquençage fragiles dans des scripts init personnalisés.
  • Les barrières de système de fichiers et l’ordre d’écriture ont évolué : des options comme les write barriers ont fait évoluer les compromis sécurité/performance, et de vieux conseils peuvent être activement nuisibles aujourd’hui.
  • Les « fenêtres batch » étaient une primitive de conception : le traitement nocturne a façonné les modèles de données, les stratégies de verrouillage et les plans de sauvegarde ; les charges toujours actives les stressent différemment.

Règles opérationnelles : comment y toucher en sécurité

Règle 1 : Traitez « inconnu » comme une dépendance, pas comme une honte

Le mystère n’est pas une faute morale. C’est un état du système. Votre travail est de réduire le mystère comme vous réduisez la latence : en mesurant,
isolant et itérant. Si la culture de l’équipe considère le fait de ne pas savoir comme embarrassant, les gens cacheront les incertitudes—et vous livrerez du risque.

Règle 2 : Ne changez pas le comportement tant que vous ne pouvez pas l’observer

Avant de refactorer, mettre à jour ou « nettoyer », vous avez besoin d’une ligne de base. Cette ligne de base n’est pas « ça a l’air bien ». C’est : distribution des latences,
taux d’erreur, points de saturation, profondeurs de queues, et la forme exacte du normal.

Une citation que les opérationnels redécouvrent sans cesse, parce qu’elle reste vraie : « On ne peut pas améliorer ce qu’on ne mesure pas. » — Peter Drucker (idée paraphrasée).
Les mesures ne rendront pas le système sûr, mais elles rendent le changement testable.

Règle 3 : Réduisez le périmètre d’impact avant d’améliorer la performance

Le travail de performance est séduisant : vous voyez un pic sur un graphe, vous voulez le corriger. Mais les « correctifs » de performance sur du legacy dépendent souvent d’un ordre obscur,
du comportement de la backpressure, ou du timing. D’abord, rendez les pannes plus petites : changements canaris, feature flags, fenêtres de maintenance plus courtes,
jeux de données plus petits, périmètres de réplication réduits. Puis optimisez.

Règle 4 : Préférez les changements réversibles

Si votre changement ne peut pas être reverti rapidement, il doit être testé comme un implant chirurgical. En territoire legacy, vous avez rarement ce luxe.
Choisissez des changements qui peuvent être désactivés : flags de config, modifications runtime, sondes en lecture seule, trafic shadow, pipelines parallèles.

Règle 5 : Séparez « comment ça marche » de « comment ça casse »

Vous n’avez pas besoin de comprendre complètement un système hérité pour l’opérer en sécurité. Vous devez comprendre :
à quoi ressemble le bon état, à quoi ressemble le mauvais état, et quels leviers changent les résultats.
Construisez des runbooks autour des modes de défaillance, pas des diagrammes d’architecture.

Blague #1 : Les systèmes hérités sont comme les poêles en fonte—si vous frottez trop fort, la « patine » s’en va et tout le monde s’énerve.

Règle 6 : Documentez l’intention, pas les anecdotes

« Définir vm.dirty_ratio=12 » est une anecdote. « Maintenir la latence d’écriture sous X pour que le checkpoint DB ne bloque pas » est une intention.
La seconde survit aux changements matériels et aux upgrades du kernel. L’intention permet au prochain ingénieur de faire un choix différent mais correct.

Règle 7 : Le stockage est généralement le complice silencieux

En tant qu’ingénieur stockage, je serai franc : quand des services hérités se comportent mal, le stockage est souvent impliqué—parfois comme cause, parfois comme amplificateur.
Les vieilles apps incorporent des hypothèses sur le coût de fsync, la vitesse de lookup d’inodes, l’atomicité de rename, et le comportement d’espace libre.
Mettre cette app sur un backend de stockage différent, c’est changer les lois de la physique sous lesquelles elle a évolué.

Règle 8 : « N’y touchez pas » est une ligne de registre de risques, pas une stratégie

Si un système est trop effrayant pour être modifié, il est trop risqué pour être dépendu. Mettez-le dans le registre de risques avec des déclencheurs concrets :
expiration de certificat, fin de vie de l’OS, arrêt commercial d’un modèle de disque, renouvellement de contrat fournisseur, perte d’une personne clé.
Puis élaborez un plan qui remplace la peur par des étapes.

Guide de diagnostic rapide

Quand le système « magique » ralentit, vous n’avez pas le temps pour la philosophie. Il vous faut une boucle de triage qui trouve rapidement le goulet d’étranglement,
sans l’empirer. Voici l’ordre qui gagne en production.

Première étape : confirmer l’impact et borner le périmètre d’impact

  • Est-ce la latence côté utilisateur, le débit, le taux d’erreur, ou l’intégrité des données ?
  • Est-ce un hôte, une AZ, un shard, un locataire, ou global ?
  • Quelque chose a-t-il changé dans l’heure/jour précédent : déploiement, config, kernel, chemin de stockage, ACL réseau, renouvellement de certificat ?

Deuxième étape : décider si vous êtes limité CPU, mémoire ou I/O

  • Limité CPU : longue file d’attente d’exécution, CPU utilisateur/système élevé, faible iowait, latence stable jusqu’à saturation.
  • Limité mémoire : défauts majeurs en hausse, activité de swap, stalls de reclaim, OOM kills, thrashing du cache.
  • Limité I/O : iowait élevé, utilisation disque élevée, await/temps de service élevés, threads bloqués, tempêtes de fsync.

Troisième étape : vérifier la saturation et l’encombrement au niveau le plus bas

  • Stockage : profondeur de queue du périphérique, latence, erreurs, basculement multipath, système de fichiers plein, santé du pool ZFS, resync md RAID.
  • Réseau : retransmissions, pertes, mismatch MTU, problèmes de duplex, latence DNS, pics de handshake TLS.
  • Kernel : avertissements dmesg, tâches bloquées, soft lockups, timeouts de la couche bloc.

Quatrième étape : identifier le processus goulot et sa dépendance

  • Quel PID consomme le CPU, est bloqué en I/O, ou tient des locks ?
  • Sur quels fichiers, sockets ou disques attend-il ?
  • La contention est-elle interne (verrous) ou externe (stockage/réseau) ?

Cinquième étape : appliquer la plus petite mitigation sûre

  • Limiter le débit des jobs bruyants (cron, batch, compactions).
  • Basculer les lectures, drainer le trafic, ou déplacer des charges de travail.
  • Augmenter la marge (capacité temporaire, cache, ou réglage de queues) seulement si vous comprenez les effets secondaires.

Tâches pratiques avec commandes, ce que la sortie signifie, et la décision à prendre

Voici les manœuvres de base quand vous faites face à un service mystère sur Linux avec du stockage impliqué.
Chaque tâche inclut une commande, une sortie d’exemple, ce que ça signifie, et la décision à en tirer.
Exécutez-les en lecture seule d’abord. La curiosité est bien ; les reboots surprises ne le sont pas.

Task 1: Find what changed recently (package and service state)

cr0x@server:~$ journalctl --since "6 hours ago" -p warning..alert | tail -n 20
Jan 12 10:14:03 app01 kernel: nvme nvme0: I/O 123 QID 4 timeout, aborting
Jan 12 10:14:03 app01 kernel: blk_update_request: I/O error, dev nvme0n1, sector 987654 op 0x1:(WRITE) flags 0x0 phys_seg 16 prio class 0
Jan 12 10:18:11 app01 systemd[1]: myapp.service: Main process exited, code=killed, status=9/KILL

Signification : timeouts I/O du kernel plus erreurs, puis un service a été tué. Ce n’est pas « l’app est lente ». C’est « le stockage est en feu ».

Décision : arrêtez de tuner l’application et commencez à protéger les données : vérifiez la santé du périphérique, le multipath, le contrôleur et l’intégrité du système de fichiers. Envisagez de drainer le trafic.

Task 2: Get a one-screen health view (CPU, memory, load, iowait)

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
 2  0      0  81232  41200 621000    0    0   120   210  950 1400 12  6 78  4  0
 4  1      0  50440  39800 612400    0    0   110   980 1100 2100 10  7 55 28  0
 6  3      0  49600  39000 610800    0    0    90  1600 1200 2600  8  6 40 46  0

Signification : l’augmentation de b (bloqués) et de wa (iowait) indique des threads coincés en attente d’I/O.

Décision : pivotez vers des vérifications disque et système de fichiers ; ne faites pas « ajouter du CPU » immédiatement. Le CPU n’est pas encore le goulot.

Task 3: Identify which disks are slow and how (latency vs utilization)

cr0x@server:~$ iostat -xz 1 3
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           9.21    0.00    6.14   31.55    0.00   53.10

Device            r/s     w/s   rkB/s   wkB/s  rrqm/s  wrqm/s  %util  await  svctm
nvme0n1         220.0  310.0  9024.0 12800.0     0.0     0.0   99.5  18.2   0.9
nvme1n1          10.0   12.0   400.0   520.0     0.0     0.0    3.1   1.2   0.2

Signification : %util proche de 100% avec un await élevé signifie que le périphérique est saturé et que les requêtes font la queue.

Décision : trouvez ce qui touche nvme0n1 et réduisez son taux d’écriture, déplacez les données chaudes, ou ajoutez de la capacité/IOPS. Si c’était censé être « miroir », vérifiez que c’est réellement le cas.

Task 4: Find which process is generating I/O

cr0x@server:~$ sudo iotop -o -b -n 3
Total DISK READ: 8.12 M/s | Total DISK WRITE: 45.33 M/s
  PID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN  IO>    COMMAND
 7321 be/4   myapp     1.23 M/s  22.10 M/s   0.00 %  6.12 % myapp --worker
 9012 be/4   postgres  0.00 B/s  18.44 M/s   0.00 %  5.90 % postgres: checkpointer

Signification : l’app et le checkpointer Postgres sont les gros écrivains. Cette combinaison signifie souvent que des pages sales se flushent en rafales.

Décision : envisagez d’adoucir les écritures (tuning des checkpoints DB) et vérifiez si la latence du stockage a changé récemment. Ne tuez pas le checkpointer à l’aveugle ; il gère les dégâts.

Task 5: Check filesystem fullness and inode exhaustion (the dumb outage)

cr0x@server:~$ df -hT
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/nvme0n1p2 ext4  200G  196G  2.1G  99% /
/dev/nvme1n1p1 ext4  500G  120G  356G  26% /data

Signification : le système de fichiers root est à 99%. Attendez des anomalies : échecs de création de fichiers temporaires, logs non écrits, services qui plantent de manière non évidente.

Décision : libérez de l’espace immédiatement (rotation des logs, suppression d’anciens artefacts), puis corrigez la trajectoire de croissance. Si vous avez besoin de temps, redirigez temporairement les écritures vers /data.

Task 6: Validate inode availability (small files can kill you)

cr0x@server:~$ df -i
Filesystem      Inodes  IUsed   IFree IUse% Mounted on
/dev/nvme0n1p2 1310720 1310100     620  100% /
/dev/nvme1n1p1 3276800  210000 3066800    7% /data

Signification : 100% d’utilisation des inodes : vous pouvez avoir de l’espace libre et être pourtant incapable de créer des fichiers. Les systèmes batch hérités adorent créer des petits fichiers.

Décision : trouvez le répertoire responsable de l’explosion (souvent logs, spool, temp, cache). Nettoyez et ajoutez des garde-fous (logrotate, politiques de rétention).

Task 7: Identify mount options and risky behaviors (barriers, noatime, etc.)

cr0x@server:~$ findmnt -no TARGET,SOURCE,FSTYPE,OPTIONS /
/ /dev/nvme0n1p2 ext4 rw,relatime,errors=remount-ro

Signification : relatime est correct ; errors=remount-ro signifie que le système de fichiers passera en lecture seule sur erreurs, ce qui peut ressembler à « l’app a gelé ».

Décision : si vous avez vu des erreurs I/O, prévoyez le basculement en lecture seule et assurez-vous que les services le gèrent. Sur les systèmes hérités, ils ne le font souvent pas.

Task 8: Check kernel for hung tasks and block layer pain

cr0x@server:~$ dmesg -T | tail -n 30
[Sun Jan 12 10:14:03 2026] nvme nvme0: I/O 123 QID 4 timeout, aborting
[Sun Jan 12 10:14:09 2026] INFO: task myapp:7321 blocked for more than 120 seconds.
[Sun Jan 12 10:14:09 2026]       Tainted: G        W  OE     5.15.0-91-generic #101-Ubuntu
[Sun Jan 12 10:14:09 2026] Call Trace:
[Sun Jan 12 10:14:09 2026]  __schedule+0x2c5/0x6f0
[Sun Jan 12 10:14:09 2026]  io_schedule+0x46/0x70

Signification : tâches bloquées dans la planification I/O : symptôme classique de stalls de stockage.

Décision : escaladez vers la couche stockage : vérifiez SMART/NVMe logs du périphérique, contrôleur, multipath, SAN, et tout changement de firmware récent.

Task 9: Map hot files to devices (which mount is the culprit)

cr0x@server:~$ sudo lsof -p 7321 | head -n 10
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
myapp   7321 myapp  cwd    DIR  259,2     4096  131073 /
myapp   7321 myapp  10w   REG  259,2  1048576  262200 /var/log/myapp/worker.log
myapp   7321 myapp  11w   REG  259,2  5242880  262199 /var/lib/myapp/spool/events.dat

Signification : le processus écrit sous /var sur le disque root (DEVICE 259,2), pas sur /data. Cela peut être accidentel et catastrophique.

Décision : relocalisez le spool/logs vers un volume de données approprié, idéalement avec un symlink ou bind mount pour un changement minimal. Vérifiez d’abord qu’aucune logique n’attend l’ancien chemin.

Task 10: Check service restart loops and dependency ordering

cr0x@server:~$ systemctl status myapp.service --no-pager
● myapp.service - MyApp Worker
     Loaded: loaded (/etc/systemd/system/myapp.service; enabled; vendor preset: enabled)
     Active: activating (auto-restart) (Result: exit-code) since Sun 2026-01-12 10:18:12 UTC; 6s ago
    Process: 9441 ExecStart=/usr/local/bin/myapp --worker (code=exited, status=1/FAILURE)
   Main PID: 9441 (code=exited, status=1/FAILURE)
        CPU: 210ms

Jan 12 10:18:12 app01 myapp[9441]: FATAL: cannot create /var/lib/myapp/spool/lock: No space left on device

Signification : plus de mystère : l’échec est dû à l’espace, et systemd déclenche des redémarrages en boucle.

Décision : stoppez la tempête de redémarrages si elle aggrave l’I/O (temporairement systemctl stop), libérez de l’espace, puis redémarrez. Ajoutez du monitoring/alerting pour disque/inodes pour prévenir les récurrences.

Task 11: Inspect cron/system timers (the invisible workload)

cr0x@server:~$ systemctl list-timers --all --no-pager | head -n 12
NEXT                        LEFT     LAST                        PASSED   UNIT                         ACTIVATES
Sun 2026-01-12 10:30:00 UTC 7min left Sun 2026-01-12 10:00:00 UTC 23min ago logrotate.timer              logrotate.service
Sun 2026-01-12 11:00:00 UTC 37min left Sun 2026-01-12 10:00:03 UTC 23min ago fstrim.timer                fstrim.service
Sun 2026-01-12 12:00:00 UTC 1h 37min Sun 2026-01-12 00:00:00 UTC 10h ago    apt-daily-upgrade.timer      apt-daily-upgrade.service

Signification : fstrim sur du stockage thin-provisionné ou très utilisé peut causer des pics de latence ; logrotate peut causer des rafales de rename/compress I/O.

Décision : si les pics corrèlent, replanifiez les timers sur des fenêtres à faible trafic ou ajustez-les (ex. fréquence trim). Validez avec des métriques avant de changer.

Task 12: Network sanity for “storage” that is actually remote

cr0x@server:~$ ip -s link show dev eth0 | sed -n '1,12p'
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:ab:cd:ef brd ff:ff:ff:ff:ff:ff
    RX:  bytes packets errors dropped  missed   mcast
    987654321  1234567      0    1241       0       0
    TX:  bytes packets errors dropped carrier collsns
    123456789  2345678      0       0       0       0

Signification : pertes RX : peuvent causer des problèmes NFS/iSCSI qui ressemblent à une « lenteur disque » au niveau applicatif.

Décision : investiguez la mise en file des NIC, driver/firmware, MTU, et la congestion en amont. Si le stockage est distant, traitez le réseau comme faisant partie du disque.

Task 13: Confirm what storage you’re actually on (LVM/MD/DM)

cr0x@server:~$ lsblk -o NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT
NAME          TYPE  SIZE FSTYPE MOUNTPOINT
nvme0n1       disk  238G
├─nvme0n1p1   part    1G vfat   /boot/efi
└─nvme0n1p2   part  200G ext4   /
nvme1n1       disk  477G
└─nvme1n1p1   part  477G ext4   /data

Signification : aucun RAID/LVM affiché. Si quelqu’un vous a dit « c’est en miroir », il se peut qu’il se souvienne d’une autre époque.

Décision : vérifiez les affirmations de redondance. Si rien n’existe, planifiez une atténuation du risque : sauvegardes, réplication, ou RAID/mirroring réel.

Task 14: Measure latency distribution to separate “slow” from “spiky”

cr0x@server:~$ sudo fio --name=latcheck --filename=/data/fio.test --size=1G --direct=1 --rw=randread --bs=4k --iodepth=32 --runtime=30 --time_based --group_reporting
latcheck: (g=0): rw=randread, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=32
fio-3.28
Run status group 0 (all jobs):
   READ: bw=85.2MiB/s (89.3MB/s), 85.2MiB/s-85.2MiB/s (89.3MB/s-89.3MB/s), io=2556MiB (2681MB), run=30001-30001msec
    slat (nsec): min=800, max=21000, avg=3200.4, stdev=900.1
    clat (usec): min=180, max=12000, avg=340.2, stdev=410.7
     lat (usec): min=190, max=12010, avg=344.0, stdev=411.0
    clat percentiles (usec):
     |  1.00th=[  210],  5.00th=[  230], 10.00th=[  245], 50.00th=[  290],
     | 90.00th=[  520], 95.00th=[  780], 99.00th=[ 2100], 99.90th=[ 7200]

Signification : la latence médiane est correcte, la latence tail est moche. Les systèmes hérités meurent souvent à cause du 99.9e percentile.

Décision : investiguez les stalls périodiques (GC, compaction, sauvegardes, trim, snapshots, hiccups de contrôleur). Optimisez pour les queues, pas seulement les moyennes.

Task 15: Validate backup capability without trusting the story

cr0x@server:~$ sudo -n test -r /var/backups/myapp/latest.sql.gz && echo "backup readable" || echo "backup missing"
backup readable

Signification : existence et lisibilité sont le strict minimum. Beaucoup d’affirmations « nous avons des backups » s’arrêtent là.

Décision : planifiez un test de restauration sur un hôte scratch. Si vous ne pouvez pas restaurer, vous n’avez pas de sauvegardes ; vous avez des regrets compressés.

Task 16: Check for silent data corruption signals (ZFS example)

cr0x@server:~$ sudo zpool status -v
  pool: tank
 state: DEGRADED
status: One or more devices has experienced an error resulting in data corruption.
action: Restore the file in question if possible.  Otherwise restore the entire pool from backup.
  scan: scrub repaired 0B in 00:12:44 with 2 errors on Sun Jan 12 09:40:11 2026
config:

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

errors: Permanent errors have been detected in the following files:
        /data/myapp/index/segment-00017

Signification : les checksums ont détecté de la corruption. Ce n’est pas ZFS qui dramatise ; c’est ZFS qui fait son travail et vous dit la vérité inconfortable.

Décision : restaurez les données affectées depuis une réplique/sauvegarde connue bonne, remplacez le matériel suspect, et planifiez des scrubs réguliers. Si votre app héritée ne tolère pas la corruption de fichiers, traitez cela comme un sev-1.

Trois mini-histoires du monde de l’entreprise

1) Incident causé par une mauvaise hypothèse : « C’est redondant, non ? »

Une entreprise de taille moyenne exécutait un pipeline de facturation sur un seul hôte Linux. Tout le monde croyait que la base de données était « en RAID miroir »
car historiquement, elle l’avait été. Le constructeur initial était parti, et l’équipe actuelle voyait deux disques dans le châssis et supposait la sécurité.
Personne n’avait vérifié depuis des années, parce que vérifier semblait tenter le sort.

Ils ont migré l’hôte vers une nouvelle baie lors d’un nettoyage du datacenter. Après un reboot, le système est monté, a fonctionné pendant un jour, puis
a commencé à renvoyer des erreurs de système de fichiers sous charge d’écriture. Les logs kernel montraient des timeouts I/O. L’on-call a fait la chose raisonnable :
basculer « vers » le second disque. Il n’y a pas eu de bascule. Il n’y avait qu’un second disque, formaté pour autre chose, silencieusement inutilisé.

La récupération est devenue de l’archéologie. La dernière sauvegarde connue bonne était plus ancienne que ce que la direction voulait admettre, parce que les jobs de backup étaient
« verts » mais sauvegardaient en fait un répertoire vide après un changement de chemin. L’incident n’était pas causé par un disque mort ; les disques meurent tout le temps.
Il a été causé par une hypothèse survivant plus longtemps que son auteur.

La correction n’a pas été héroïque. Elle a été ennuyeuse : inventaire avec lsblk, vérification de la redondance, restauration des backups sur un environnement scratch mensuellement,
et ajout d’une alerte pour « sauvegarde produisant une sortie suspectement petite ». Le nouveau runbook incluait une ligne : « Si quelqu’un dit ‘mirrored’, montrez la sortie de la commande. »

2) Optimisation qui a mal tourné : « On a activé la chose plus rapide »

Une autre organisation avait un service Java hérité qui écrivait de petits enregistrements sur disque et comptait sur fsync pour la durabilité. Il tournait sur des SSD locaux
et se comportait de façon prévisible. Une initiative de performance l’a déplacé sur une plateforme de stockage réseau partagée offrant une meilleure utilisation
et des snapshots centralisés. La migration a rempli le KPI : le débit moyen a augmenté et l’équipe plateforme a déclaré victoire.

Deux semaines plus tard, le système a commencé à subir des pics de latence périodiques. Pas une lenteur constante—des pics. Toutes les quelques heures, les écritures se bloquaient,
les files d’attente de requêtes grossissaient, et les services en amont tombaient en timeout. Les ingénieurs ont chassé le GC, les pools de threads, et des flags de config au hasard.
Quelqu’un a « optimisé » encore en augmentant la concurrence applicative pour « masquer la latence ». Cela a augmenté la taille des rafales d’écriture, ce qui a empiré la latence tail de la plateforme de stockage, ce qui a généré des arriérés encore plus gros. Une boucle de rétroaction nette—mais mauvaise.

La cause racine était un mauvais appariement : le modèle de durabilité de l’app supposait un fsync bon marché ; la plateforme de stockage implémentait la durabilité avec des sémantiques différentes et un travail d’arrière-plan périodique. L’app n’avait pas tort. La plateforme n’avait pas tort. L’association avait tort.

Ils ont stabilisé en limitant la concurrence, en remettant les journaux chauds sur NVMe local, et en laissant les données volumineuses sur le backend partagé.
Puis ils ont mesuré le p99.9 comme métrique de première classe, pas comme note de postmortem. La leçon d’« optimisation » a été retenue :
si vous ne mesurez pas les queues, vous mesurez votre optimisme.

3) Pratique ennuyeuse mais correcte qui a sauvé la mise : « Le runbook a marché »

Une équipe de services financiers avait un processeur batch ancien que certains ingénieurs avaient connu avant d’être embauchés. Il n’a jamais été réécrit parce qu’il fonctionnait.
Mais l’équipe le traitait quand même comme un service de production de première classe : dashboards, test de restauration hebdomadaire, et un runbook décrivant
les modes de défaillance avec des vérifications simples et des baselines connues bonnes.

Un soir, le temps de traitement a doublé. Pas d’erreurs. Juste lent. L’on-call a suivi le runbook : vérifier l’utilisation disque, l’iowait,
NTP, la concurrence des jobs, et les événements timer récents. En dix minutes ils ont trouvé le coupable : un job logrotate compressant un log très volumineux sur le même volume que le dataset d’entrée, causant une contention I/O périodique. Le système n’était pas cassé ; il était en compétition.

La correction a été aussi palpitante qu’un tableur : déplacer les logs sur un volume séparé et limiter la CPU utilisée pour la compression. Ils ont aussi ajouté une alerte :
« le fichier de log dépasse la taille de rétention » et « la compression chevauche la fenêtre batch ». La prochaine exécution est redevenue normale.

La partie intéressante est ce qui ne s’est pas produit : aucun tuning panique, aucun redémarrage aléatoire, aucun « peut-être que le SAN est hanté ». La pratique ennuyeuse a fonctionné parce qu’elle a transformé les inconnues en vérifications. En production, l’ennui est une fonctionnalité.

Blague #2 : Si votre monitoring ne vérifie que les moyennes, félicitations—vous avez construit un système qui est toujours sain en rétrospective.

Erreurs courantes : symptômes → cause profonde → correctif

1) « Ça ralentit après migration » → hypothèses fsync cachées → déplacer les journaux ou changer la stratégie de durabilité

Symptômes : pics de latence d’écriture p99, threads bloqués, timeouts applicatifs, surtout pendant des rafales.

Cause profonde : workload déplacé de SSD local vers stockage réseau/NFS/iSCSI avec des sémantiques de flush et une latence tail différentes.

Correctif : garder les WAL/journaux sensibles à la latence sur NVMe local ; benchmarker avec fio ; mesurer p99.9 ; limiter la concurrence et ajouter de la backpressure.

2) « Plantages aléatoires » → disque plein ou inodes épuisés → faire respecter la rétention et l’alerte

Symptômes : services quittent avec des erreurs étranges, fichiers de lock échouent, logs arrêtent, mises à jour de paquets échouent.

Cause profonde : système de fichiers à 95–100% ou épuisement d’inodes ; les apps héritées créent beaucoup de petits fichiers.

Correctif : alertes df -hT et df -i ; logrotate réellement exécuté ; déplacer les spools hors de root ; définir des quotas si possible.

3) « Le CPU est élevé, donc c’est le calcul » → le CPU est élevé à cause d’iowait ou compression → séparer le signal du bruit

Symptômes : moyenne de charge élevée, plaintes utilisateurs, mais ajouter du CPU n’aide pas.

Cause profonde : threads bloqués gonflent la charge ; cycles CPU passés dans le kernel ou la compression ; iowait masqué sous la charge.

Correctif : utiliser vmstat et iostat -xz ; examiner les tâches bloquées dans dmesg ; déplacer les jobs de compression en dehors des fenêtres de pointe.

4) « On a rebooté et c’est pire » → ordre des dépendances + récupération stateful → arrêter les tempêtes de redémarrages

Symptômes : service qui redémarre en boucle, disques qui thrashent, logs qui inondent, récupération qui prend plus de temps.

Cause profonde : boucles de restart systemd plus tâches de warm-up longues (reindex, rebuild cache) plus stockage saturé.

Correctif : arrêter temporairement le service ; confirmer la marge disque ; augmenter le backoff de restart ; ajouter des dépendances explicites et des checks de readiness.

5) « Les backups sont verts » → mauvais chemin ou dataset vide → tester les restaurations

Symptômes : backups « réussissent », mais les restaurations échouent ou restaurent des données vides.

Cause profonde : chemin déplacé, permissions changées, ou job de backup capture le mauvais mount ; les contrôles ne vérifient que le code de sortie.

Correctif : exercices de restauration périodiques ; valider les plages de taille des sauvegardes ; stocker des manifests de backup ; alerter sur des sorties anormalement petites.

6) « Le stockage dit sain » → latence tail et micro-stalls → observer les percentiles et la profondeur de queue

Symptômes : le débit global semble correct ; les utilisateurs voient des gels ; les graphes montrent des falaises périodiques.

Cause profonde : les checks de santé de la plateforme se concentrent sur les moyennes ; les tâches d’arrière-plan (scrubs, trims, snapshots) créent des pics tail.

Correctif : instrumenter p95/p99/p99.9 ; monitorer await du périphérique, profondeur de queue ; replanifier les tâches d’arrière-plan.

7) « On a tuné le kernel comme dans un blog » → vieux conseils sur de nouveaux kernels → revenir à la baseline et tester un changement à la fois

Symptômes : latence imprévisible, problèmes de reclaim mémoire, stalls bizarres après « sysctl tuning ».

Cause profonde : sysctls cargo-cultés qui entrent en conflit avec des kernels modernes ou le comportement des cgroups.

Correctif : tracer les changements sysctl ; revenir aux valeurs par défaut de la distribution ; introduire les changements avec une hypothèse et un plan de mesure.

8) « Ça ne tombe que sur un nœud » → divergence matérielle/firmware → faire respecter l’homogénéité et capturer les versions

Symptômes : un hôte montre plus d’erreurs, une latence différente, ou des resets étranges.

Cause profonde : firmware NVMe différent, état du cache du contrôleur RAID, driver NIC, réglages BIOS différents.

Correctif : inventorier firmware et modules kernel ; standardiser ; isoler le nœud ; remplacer les composants suspects.

Listes de contrôle / plan étape par étape

Checklist A: « Toucher la magie » sans la casser

  1. Définir la frontière de sécurité : quelles données ne doivent jamais être perdues, quelle indisponibilité est acceptable, ce que signifie rollback en minutes.
  2. Capturer une ligne de base : CPU, mémoire, percentiles de latence disque, taux d’erreur, profondeurs de queues. Sauvegardez des captures d’écran si nécessaire ; sauvegardez quelque chose.
  3. Inventorier la réalité : disques, mounts, RAID/LVM/ZFS, chemins réseau, timers, cron, et sauvegardes. « On pense » ne compte pas.
  4. Écrire la première page du runbook : comment reconnaître sain vs malsain, et les cinq commandes qui le prouvent.
  5. Choisir d’abord un changement réversible : logging, métriques, sondes en lecture seule, ou déplacement d’écritures non critiques.
  6. Faire un seul changement : un seul. Pas trois « tant qu’on y est ».
  7. Mesurer à nouveau : si vous ne voyez ni amélioration ni dégradation, vous n’avez pas contrôlé l’expérience.
  8. Documenter l’intention : pourquoi le changement existe, quelle métrique il affecte, et comment revenir en arrière.

Checklist B: Étapes de sécurité spécifiques au stockage (parce que le stockage est l’endroit où les carrières vont mourir)

  1. Vérifier l’espace libre et les inodes sur tous les mounts concernés ; appliquer des cibles de marge.
  2. Valider les affirmations de redondance avec des commandes, pas le folklore.
  3. Confirmer les attentes de durabilité d’écriture (fréquence fsync, mode de journaling, réglages WAL de la DB).
  4. Inspecter les logs kernel pour timeouts de périphérique, resets et remounts du système de fichiers.
  5. Tester la latence tail avec un benchmark contrôlé hors-pointe, pas pendant un incident.
  6. Effectuer un test de restauration avant toute migration risquée.

Checklist C: Réponse à incident sur systèmes hérités

  1. Stabiliser : arrêter la boucle de restart, throttle des jobs batch, réduire le trafic si nécessaire.
  2. Identifier la couche goulot : CPU vs mémoire vs stockage vs réseau.
  3. Confirmer le symptôme avec deux sources : kernel + logs app, ou iostat + métriques de latence.
  4. Appliquer la plus petite mitigation : déplacer un hot path, replanifier un timer, ajouter 1 Go de marge, pas une refonte.
  5. Enregistrer ce qui a fonctionné : commandes, sorties, et horodatages. Vous écrivez le runbook de demain sous pression.
  6. Post-incident : convertir une hypothèse en une vérification, et une vérification en alerte.

FAQ

1) Est-ce que « n’y touchez pas » est parfois correct ?

Temporairement, oui—pendant les fenêtres de revenu maximal ou quand vous n’avez pas de rollback. À long terme, non. S’il ne peut pas être modifié, il doit être isolé,
mesuré, et mis sur une trajectoire de remplacement ou de confinement.

2) Comment commencer à documenter un système que je ne comprends pas ?

Commencez par le comportement : entrées, sorties, SLOs, et modes de défaillance. Puis les dépendances : mounts de stockage, bases, endpoints réseau, cron/timers,
et sauvegarde/restauration. Les diagrammes d’architecture sont agréables ; la vérité opérationnelle l’est davantage.

3) Quelle est la première métrique à ajouter à un service hérit

Les percentiles de latence pour l’opération critique, plus le taux d’erreur. Si le stockage est impliqué, ajoutez la latence et l’utilisation des périphériques.
Les moyennes vous mentiront avec un sourire sérieux.

4) Pourquoi les systèmes hérités détestent-ils le stockage partagé ?

Ils supposent souvent les sémantiques du disque local : faible jitter, fsync prévisible, et modes de défaillance simples. Le stockage partagé introduit de la mise en file,
de l’interférence multi-tenant, et des mécanismes de durabilité différents. Parfois ça marche encore—mais pas par défaut.

5) Faut-il le réécrire ?

Pas comme première réponse. D’abord, rendez-le observable et sûr à opérer. Les réécritures se justifient quand le coût de réduction du risque dépasse
le coût du remplacement, ou quand les dépendances (OS, runtime, fournisseur) deviennent véritablement intenables.

6) Comment convaincre la direction que « stable » est risqué ?

Apportez des déclencheurs concrets : dates de fin de vie OS, expirations de certificats, points uniques de défaillance, absence de tests de restauration, et latence tail mesurée.
Le risque qu’on peut mettre en graphe est financé plus vite que la peur.

7) Quel est un premier « touch » sûr ?

Ajoutez de l’observabilité en lecture seule : dashboards, logs avec IDs de corrélation, alertes disque/inodes, et un exercice de restauration de sauvegarde.
Ces changements améliorent la visibilité, pas le comportement de production.

8) Comment gérer « le sorcier » qui garde le système ?

Respectez son expérience, puis extrayez-la en artefacts : runbooks, sorties de base, et procédures de test. Si la connaissance reste dans une seule tête,
le système est déjà en état dégradé—vous n’avez simplement pas encore eu l’incident.

9) Et si le système est trop fragile pour être testé ?

Alors votre premier projet est de créer un environnement de staging ou une réplique shadow en lecture seule. Si vous ne pouvez vraiment pas, réduisez le risque en limitant
les changements à des toggles de config réversibles et des contrôles opérationnels.

10) Comment éviter le tuning cargo-cult ?

Écrivez une hypothèse pour chaque changement : « Cela réduira la rafale de checkpoints et abaissera la latence d’écriture p99. » Mesurez avant/après. Gardez un plan de revert.
Si vous ne pouvez pas expliquer le mode de défaillance que vous pourriez introduire, ne le déployez pas.

Conclusion : prochaines étapes réalisables

La magie héritée n’est que de l’ingénierie non documentée plus du temps. Traitez-la comme de la production, pas comme du folklore. L’objectif n’est pas d’être intrépide.
L’objectif est d’être méthodique au point que la peur cesse d’être votre principal mécanisme de contrôle.

Faites ceci ensuite, dans l’ordre :

  1. Baseliner et observer : capturez des snapshots iostat/vmstat sous charge normale ; ajoutez des percentiles de latence aux tableaux de bord.
  2. Inventorier la réalité du stockage : mounts, redondance, espace libre/inodes, logs kernel d’erreurs. Écrivez-le.
  3. Prouver les sauvegardes par restauration : une restauration sèche sur un hôte scratch. Planifiez-la.
  4. Réduire le périmètre d’impact : séparer logs/spools du root, ajouter des limites de débit aux jobs batch, implémenter des canaris pour les changements.
  5. Convertir les hypothèses en vérifications : si quelqu’un dit « c’est en miroir », automatisez la vérification ; si quelqu’un dit « les sauvegardes sont OK », testez la restauration mensuellement.

Touchez le système, mais touchez-le avec des gants : mesures, réversibilité, et un plan qui valorise la correction ennuyeuse plutôt que les exploits héroïques.
C’est comme ça que le legacy cesse d’être magique et redevient un service que vous exploitez en confiance.

← Précédent
Proxmox snapshot bloqué : nettoyer en toute sécurité les résidus LVM-thin
Suivant →
Réplication Proxmox échouée : pourquoi ça casse et comment récupérer

Laisser un commentaire