Pics de latence disque Debian/Ubuntu : Prouvez que c’est le stockage, pas l’application (Outils + Correctifs)

Cet article vous a aidé ?

Tout va “bien” jusqu’à ce que ça ne le soit plus. Le p99 de l’API fait sauter un disjoncteur, votre base de données cale, les tableaux de bord ressemblent à un sismographe, et quelqu’un prononce la phrase que vous entendrez dans toutes les entreprises pour toujours : « L’application n’a pas changé. »

Les pics de latence disque sont le bouc émissaire classique et un jeu de devinettes. Voici l’antidote : un workflow Debian/Ubuntu qui produit des preuves, pas des impressions—pour que vous puissiez démontrer que c’est le stockage (ou prouver que ce n’est pas le cas), puis corriger la bonne chose.

Playbook de diagnostic rapide

Si vous avez 10 minutes, faites ceci dans l’ordre. L’astuce est de séparer la latencedébit, et le périphérique bloc du système de fichiers de l’application. Un disque occupé peut être acceptable ; un disque avec des pauses occasionnelles de 2–20 secondes ruinera votre journée.

1) Confirmer le symptôme : sommes-nous bloqués sur les E/S ?

  • Vérifier au niveau système : exécutez vmstat 1 et cherchez un wa élevé (iowait) pendant le pic.
  • Vérifier par périphérique : exécutez iostat -x 1 et regardez await et %util augmenter pendant le pic.
  • Vérifier l’enfilement : si avgqu-sz augmente, vous empilez des requêtes plus vite que le périphérique ne les termine.

2) Identifier la victime : quel processus est bloqué ?

  • Capturez les tâches bloquées : ps -eo pid,stat,wchan:25,comm | awk '$2 ~ /D/'.
  • Corrélez avec l’app : des threads DB en état D signifient que le noyau vous dit « j’attends le stockage ».

3) Décidez : disque local, disque virtuel ou stockage distant ?

  • Mapper montage → périphérique bloc : findmnt -no SOURCE,TARGET /your/mount.
  • Puis : lsblk -o NAME,TYPE,SIZE,FSTYPE,MOUNTPOINTS,ROTA,MODEL pour voir si vous êtes sur NVMe, SSD SATA, HDD, dm-crypt, LVM, MD RAID, multipath ou un disque virtuel cloud.

4) Si c’est un pic : tracez, n’agrégerez pas

  • Utilisez biosnoop (bcc) ou bpftrace pour attraper les valeurs aberrantes de latence.
  • Si vous ne pouvez pas, utilisez blktrace/blkparse et cherchez de longs écarts entre envoi et complétion.

5) Validez avec une reproduction contrôlée

  • Exécutez un profil fio sûr contre un fichier de test sur le même système de fichiers et voyez si les p99/p999 de latence correspondent aux symptômes de production.

Ce que signifie réellement « pic de latence disque »

La latence disque est le temps entre « le noyau soumet une requête d’E/S bloc » et « le noyau reçoit la complétion ». Si ce temps grimpe, tout en amont devient mensonger : le thread d’application paraît « lent », les verrous semblent « conflictuels », les files « se construisent mystérieusement », et les humains commencent à réécrire du code au lieu de corriger le goulet d’étranglement.

Il y a trois formes courantes de pics :

  • Pics d’enfilement : la latence augmente parce que vous saturez le périphérique ou l’arrière-plan. Symptômes : %util élevé, avgqu-sz élevé, await en hausse avec des IOPS stables.
  • Pics de pause : la latence passe de quelques ms à des secondes avec peu de changement de débit. Symptômes : arrêts périodiques de plusieurs secondes ; parfois %util ne semble même pas saturé. Causes : bugs de firmware, garbage collection, throttling du backend distant, ou commits de journaux.
  • Pics d’amplification : de petites écritures deviennent de nombreuses écritures (journaling, copy-on-write, parité RAID, chiffrement). Symptômes : l’appli émet des E/S « raisonnables », le stockage effectue beaucoup plus de travail et la latence explose sous charge.

Une citation à garder :

« L’espoir n’est pas une stratégie. » — General Gordon R. Sullivan

Également : votre appli peut être innocente et rester le déclencheur. Un changement de charge sans déploiement (nouveau client, forme de requête différente, construction d’un nouvel index, compactage en arrière-plan) peut pousser le stockage au bord du précipice. Votre travail est de prouver que le précipice existe.

Blague #1 : la latence disque est comme une réunion qui « ne prendra que cinq minutes ». Elle ne prendra pas.

Faits et contexte intéressants (parce que l’histoire se répète)

  • « iowait » n’est pas « le disque est lent ». C’est du temps CPU passé inactif alors que le système a des E/S en attente. Une appli gourmande en CPU peut avoir un faible iowait et une latence de stockage terrible.
  • L’ordonnanceur Linux était autrefois un argument clé. Les anciens planificateurs comme anticipatory et CFQ étaient conçus pour les disques rotatifs et la réactivité interactive ; les SSD et NVMe ont fait évoluer la préférence vers mq-deadline/none.
  • NCQ et les files profondes ont changé les modes de défaillance. NCQ SATA a permis aux périphériques de réordonner les requêtes ; cela a aussi rendu plus visible le cas où « une mauvaise commande bloque la file » quand le firmware se trompe.
  • Les SSD peuvent faire des pauses pour se nettoyer. La garbage collection et l’équilibrage d’usure peuvent provoquer des pics périodiques de latence, surtout quand le disque est presque plein ou manque de surprovisionnement.
  • Le journaling a échangé perte de données contre prévisibilité de latence. ext3/ext4 ont rendu les crashs moins intéressants, mais le comportement de commit peut générer des rafales d’écriture périodiques et des blocages liés aux sync.
  • Les barrières d’écriture sont devenues la valeur par défaut pour une raison. Les barrières (flush/FUA) empêchent le réordonnancement qui peut corrompre les métadonnées après une panne de courant ; elles peuvent aussi exposer un chemin de vidage de cache lent.
  • Les disques virtuels sont des frontières politiques. Dans le cloud, votre « disque » est une tranche d’un backend partagé ; le throttling et les crédits de burst peuvent faire apparaître des pics de latence de nulle part.
  • Le RAID masque mieux les problèmes de débit que de latence. Vous pouvez ajouter des plateaux et obtenir plus de MB/s, mais les petites écritures aléatoires sur un RAID parité paient toujours un tribut.

Tâches pratiques : commandes, signification des sorties, et actions suivantes

Cette section est volontairement pratique. Vous voulez des artefacts reproductibles : journaux, horodatages, et un récit qui survive à la prochaine réunion.

Task 1: Établir les faits (noyau, périphérique, virtualisation)

cr0x@server:~$ uname -a
Linux db-01 6.5.0-28-generic #29~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC x86_64 GNU/Linux
cr0x@server:~$ systemd-detect-virt
kvm
cr0x@server:~$ lsb_release -ds
Ubuntu 22.04.4 LTS

Signification : La version du noyau et la virtualisation influencent fortement le comportement du stockage (multi-queue, réglages du scheduler, virtio). Si c’est une VM, pensez aussi aux voisins bruyants et au throttling du backend.

Décision : Si virtualisé, prévoyez de collecter des preuves qui résistent à la discussion « c’est votre guest » : latence par périphérique, profondeur de file, signaux de throttling et corrélation temporelle.

Task 2: Mapper les montages aux périphériques bloc (ne pas deviner)

cr0x@server:~$ findmnt -no SOURCE,TARGET,FSTYPE,OPTIONS /var/lib/postgresql
/dev/mapper/vg0-pgdata /var/lib/postgresql ext4 rw,relatime,discard
cr0x@server:~$ lsblk -o NAME,TYPE,SIZE,FSTYPE,MOUNTPOINTS,ROTA,MODEL
NAME            TYPE   SIZE FSTYPE      MOUNTPOINTS              ROTA MODEL
vda             disk   500G                                      1    QEMU HARDDISK
└─vda2          part 499.5G LVM2_member                          1
  ├─vg0-root    lvm     50G ext4        /                         1
  └─vg0-pgdata  lvm    449G ext4        /var/lib/postgresql       1

Signification : L’application est sur LVM sur vda. Le flag rotational indique « 1 » (HDD) même en environnement virtuel ; traitez-le comme « pas NVMe-rapide ». Notez aussi discard—cela peut avoir de l’importance.

Décision : Votre goulet d’étranglement peut être sous LVM (virtio, stockage hôte, bloc réseau). Continuez la cartographie : les empilements device-mapper ajoutent de l’enfilement et de la complexité.

Task 3: Surveillez la pression système, pas seulement les moyennes

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 612304  64216 931224    0    0   120   410  410  880 12  4 82  2  0
 1  0      0 610992  64216 931260    0    0   180   512  398  860 10  4 84  2  0
 1  5      0 610120  64216 930800    0    0   140  2100  520 1200  6  3 60 31  0
 0  6      0 609880  64216 930744    0    0    90  1800  540 1300  5  3 58 34  0
 1  0      0 610400  64216 931100    0    0   110   600  420  900  9  4 84  3  0

Signification : Pendant le pic, b (processus bloqués) augmente et wa bondit à 31–34%. C’est une signature réelle de blocage.

Décision : Si les processus bloqués augmentent pendant les pics de latence, passez aux métriques par périphérique. Si wa est faible mais que la p99 est élevée, l’appli peut être liée au CPU ou attendre des verrous, pas des E/S.

Task 4: Obtenir la latence par périphérique et les signaux de file

cr0x@server:~$ iostat -x 1 5
Linux 6.5.0-28-generic (db-01) 	12/30/2025 	_x86_64_	(8 CPU)

Device            r/s     w/s   rkB/s   wkB/s  rrqm/s  wrqm/s  %rrqm  %wrqm r_await w_await aqu-sz rareq-sz wareq-sz  svctm  %util
vda              8.00   95.00  512.0  8240.0    0.00   12.00   0.00  11.21   3.10  18.40   1.90    64.0    86.7   1.20  12.40
vda              7.00  110.00  448.0  9100.0    0.00   10.00   0.00   8.33   3.40 120.50  14.20    64.0    82.7   1.50  17.50
vda              6.00  105.00  384.0  9000.0    0.00   11.00   0.00   9.48   3.80 240.10  28.30    64.0    85.7   1.60  18.00
vda              9.00   98.00  576.0  8600.0    0.00   14.00   0.00  12.50   3.20  22.00   2.30    64.0    87.8   1.30  13.50
vda              8.00   92.00  512.0  8100.0    0.00   12.00   0.00  11.54   3.10  19.30   2.00    64.0    88.0   1.20  12.60

Signification : Les écritures posent problème (w_await bondit à 120–240ms) alors que %util n’est pas extrême. C’est le classique « le backend est devenu lent » ou « chemin de vidage/commit » plutôt que « périphérique saturé ». aqu-sz augmente pendant les pics : il y a de l’enfilement.

Décision : Quand await saute mais que l’utilisation ne plafonne pas, suspectez des pauses : flushs de cache, thin provisioning, throttling distant, ou contention sur l’hôte. Passez au traçage et à l’investigation des flushs/files.

Task 5: Confirmer quels processus sont en sommeil ininterruptible (état D)

cr0x@server:~$ ps -eo pid,stat,wchan:25,comm | awk '$2 ~ /D/'
18423 D    io_schedule             postgres
18431 D    io_schedule             postgres
21102 D    ext4_writepages         postgres

Signification : Des workers Postgres sont bloqués dans des chemins d’attente noyau liés aux E/S. Ce n’est pas seulement du « SQL lent » ; c’est la complétion stockage qui est en retard.

Décision : Si des threads d’application sont en état D pendant les fenêtres de pic, priorisez les preuves au niveau bloc et les causes côté stockage. S’ils sont exécutables (R) mais lents, concentrez-vous sur le CPU, les verrous, GC ou le réseau.

Task 6: Lire l’information de pression d’E/S (PSI) pour prouver la contention I/O système

cr0x@server:~$ cat /proc/pressure/io
some avg10=0.28 avg60=0.22 avg300=0.15 total=184329210
full avg10=0.07 avg60=0.05 avg300=0.03 total=40210299

Signification : PSI vous dit combien de fois des tâches sont retardées en attendant des E/S. full indique des périodes où le système n’avait aucune tâche exécutable parce que tout le monde attendait des E/S. C’est un signal fort « le stockage bride la machine ».

Décision : Si full de PSI augmente pendant les pics, traitez cela comme une affaire d’infra, pas d’appli. Si PSI est calme, votre latence peut être à l’intérieur de l’appli (verrous) ou à l’intérieur du cache du système de fichiers (défauts de page) plutôt qu’à l’E/S bloc.

Task 7: Inspecter le scheduler du périphérique bloc et les réglages de file

cr0x@server:~$ cat /sys/block/vda/queue/scheduler
[mq-deadline] none
cr0x@server:~$ cat /sys/block/vda/queue/nr_requests
256
cr0x@server:~$ cat /sys/block/vda/queue/read_ahead_kb
128

Signification : Le choix du scheduler compte pour la latence. mq-deadline est souvent un bon défaut pour les périphériques qui ont besoin d’équité. La profondeur de file (nr_requests) influence le comportement en rafale et la latence de queue tail.

Décision : Ne « tunez » pas à l’aveugle. Si vous voyez une longue latence tail, vous devrez peut-être réduire l’enfilement pour garder la latence bornée, surtout sur des backends partagés. Planifiez des tests contrôlés.

Task 8: Vérifier les options de montage du système de fichiers qui peuvent forcer un comportement synchrone

cr0x@server:~$ findmnt -no TARGET,FSTYPE,OPTIONS /var/lib/postgresql
/var/lib/postgresql ext4 rw,relatime,discard
cr0x@server:~$ tune2fs -l /dev/mapper/vg0-pgdata | egrep 'Filesystem features|Journal features'
Filesystem features:      has_journal ext_attr resize_inode dir_index filetype extent 64bit flex_bg sparse_super large_file huge_file dir_nlink extra_isize metadata_csum
Journal features:         journal_incompat_revoke journal_64bit journal_checksum_v3

Signification : discard peut provoquer des pics de latence selon le backend. La recommandation moderne est souvent d’utiliser fstrim périodiquement plutôt que le discard inline pour des charges sensibles à la latence.

Décision : Si vous voyez des pics pendant des suppressions/vacuum/compactage, essayez de désactiver discard et d’utiliser fstrim programmé. Validez via contrôle de changement et mesures.

Task 9: Observer les flushs et le comportement de writeback (throttling des dirty)

cr0x@server:~$ sysctl vm.dirty_background_ratio vm.dirty_ratio vm.dirty_expire_centisecs vm.dirty_writeback_centisecs
vm.dirty_background_ratio = 10
vm.dirty_ratio = 20
vm.dirty_expire_centisecs = 3000
vm.dirty_writeback_centisecs = 500
cr0x@server:~$ grep -E 'Dirty:|Writeback:' /proc/meminfo
Dirty:             182340 kB
Writeback:           2048 kB

Signification : Les seuils de pages dirty dictent quand le noyau force le writeback. Quand vous atteignez dirty_ratio, les écritures d’appli peuvent être fortement throttlées, ce qui ressemble à des pics de latence aléatoires.

Décision : Si les pics se corrèlent avec des rafales de mémoire dirty et du writeback, ajustez soigneusement les réglages dirty ou réduisez l’amplification d’écriture (regroupement côté appli, réglages DB). Soyez conservateur ; ces réglages peuvent aggraver la situation.

Task 10: Capturer les valeurs aberrantes de latence stockage avec BPF (biosnoop des outils bcc)

cr0x@server:~$ sudo biosnoop -Q -d vda
TIME(s) COMM           PID    DISK    T  SECTOR    BYTES   LAT(ms)
12.4321 postgres      18423  vda     W  9132456   16384   14.92
12.4398 postgres      18431  vda     W  9132488   16384   18.77
12.9802 postgres      18423  vda     W  9132520   16384   942.51
13.0054 postgres      18431  vda     W  9132552   16384   1103.44

Signification : C’est la preuve ultime : de vraies requêtes d’E/S avec la latence mesurée, attribuées à un processus. Ces écritures à 900–1100ms expliquent les timeouts p99 côté appli.

Décision : Si BPF montre des valeurs aberrantes au niveau du périphérique bloc, vous pouvez arrêter de discuter de l’appli. Il faut maintenant comprendre pourquoi : flushs, throttling, pauses backend, enfilement, ou erreurs de périphérique.

Task 11: Tracer le chemin du layer bloc avec blktrace pour des timings plus fins

cr0x@server:~$ sudo blktrace -d /dev/vda -o - | blkparse -i -
  8,0    1        1     0.000000000 18423  Q  WS 9132520 + 32 [postgres]
  8,0    1        2     0.000310215 18423  G  WS 9132520 + 32 [postgres]
  8,0    1        3     0.000482906 18423  I  WS 9132520 + 32 [postgres]
  8,0    1        4     0.000650102 18423  D  WS 9132520 + 32 [postgres]
  8,0    1        5     0.942912433 18423  C  WS 9132520 + 32 [0]

Signification : Cela montre le cycle de vie de la requête : mise en file (Q), dispatch (D), complétion (C). Ici, la complétion est ~0.942s après le dispatch. C’est du temps périphérique/backend, pas votre parser SQL.

Décision : Si le temps se passe principalement entre D et C, concentrez-vous sur le périphérique/backend. Si le délai est entre Q et D, vous êtes en train de vous enfilez dans l’OS (scheduler/profondeur de file), souvent dû à la saturation ou à des couches device-mapper empilées.

Task 12: Vérifier les problèmes signalés par le noyau (les logs ennuyeux comptent)

cr0x@server:~$ sudo dmesg -T | egrep -i 'blk|I/O error|timeout|reset|nvme|scsi|ext4|xfs' | tail -n 8
[Mon Dec 30 10:12:41 2025] blk_update_request: I/O error, dev vda, sector 9132520 op 0x1:(WRITE) flags 0x0 phys_seg 2 prio class 0
[Mon Dec 30 10:12:41 2025] Buffer I/O error on dev dm-1, logical block 1141568, lost async page write
[Mon Dec 30 10:12:41 2025] EXT4-fs warning (device dm-1): ext4_end_bio:345: I/O error 10 writing to inode 262411 starting block 1141568)

Signification : Si vous avez des erreurs I/O ou des resets, la latence n’est plus l’histoire principale. Vous avez un risque d’intégrité. Les pics peuvent être des tentatives de nouvelle transmission, des remappages, ou des timeouts backend.

Décision : Escaladez immédiatement : équipe stockage/fournisseur cloud/propriétaire de l’hyperviseur. Commencez à planifier un basculement et des vérifications d’intégrité des données, pas des micro-optimisations.

Task 13: Vérifier le comportement TRIM (discard vs trim programmé)

cr0x@server:~$ systemctl status fstrim.timer
● fstrim.timer - Discard unused blocks once a week
     Loaded: loaded (/lib/systemd/system/fstrim.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Mon 2025-12-30 09:00:01 UTC; 1h 12min ago
     Trigger: Mon 2026-01-06 00:00:00 UTC; 6 days left
cr0x@server:~$ sudo fstrim -v /var/lib/postgresql
/var/lib/postgresql: 94.3 GiB (101251604480 bytes) trimmed

Signification : Si vous pouvez planifier le trim, vous pouvez souvent supprimer le discard inline. Cela peut réduire les pics de latence lors de suppressions massives.

Décision : Préférez fstrim.timer pour de nombreux backends. Si vous êtes sur un SAN thin-provisionné ou certains disques cloud, validez avec votre fournisseur/équipe stockage.

Task 14: Reproduire avec fio et regarder la latence tail, pas seulement les IOPS

cr0x@server:~$ fio --name=latcheck --filename=/var/lib/postgresql/fio.test --size=2G --direct=1 --ioengine=libaio --rw=randwrite --bs=16k --iodepth=16 --numjobs=1 --time_based --runtime=30 --group_reporting --output-format=normal
latcheck: (g=0): rw=randwrite, bs=(R) 16.0KiB-16.0KiB, (W) 16.0KiB-16.0KiB, ioengine=libaio, iodepth=16
fio-3.33
latcheck: Running 1 job
  write: IOPS=1420, BW=22.2MiB/s (23.3MB/s)(666MiB/30001msec)
    slat (usec): min=6, max=421, avg=14.52, stdev=9.30
    clat (msec): min=0, max=1840, avg=10.62, stdev=41.10
     lat (msec): min=0, max=1840, avg=10.65, stdev=41.11
    clat percentiles (msec):
     |  50.00th=[   1],  90.00th=[   3],  99.00th=[ 120],  99.90th=[ 820],  99.99th=[1700]

Signification : La médiane est correcte ; la queue tail est catastrophique. C’est exactement la sensation en production : la plupart du temps OK, parfois catastrophe. Les percentiles tail confirment le comportement de pic même sous test contrôlé.

Décision : Si fio reproduit la tail, ce n’est pas votre application. Vous pouvez maintenant tester des correctifs (scheduler, discard, profondeur de file, réglages dirty) et mesurer l’amélioration.

Construire le dossier : stockage vs application (comment prouver sans déclencher une guerre)

« Prouver que c’est le stockage » signifie produire une chaîne de preuves du délai visible par l’utilisateur jusqu’au temps de complétion I/O au niveau noyau. Vous voulez corrélation, attribution et un mécanisme plausible.

Echelle de preuves (utilisez-la comme une cour)

  1. Symptôme utilisateur : pics de latence p95/p99, timeouts, tempêtes de retries, croissance de files.
  2. Symptôme hôte : processus bloqués, PSI I/O, iowait élevé pendant les fenêtres d’incident.
  3. Symptôme périphérique : iostat -x montre des pics d’await et croissance de file ; parfois sans saturation.
  4. Preuve par E/S : outils BPF ou blktrace montrent des E/S individuelles prenant 200ms–secondes, attribuées au processus et au périphérique.
  5. Mécanisme : quelque chose explique pourquoi : rafale de flush, contention metadata thin pool, throttling cloud, discard, pénalité RAID, GC firmware, basculement multipath, etc.

Ce qu’il ne faut pas faire

  • Ne pas utiliser « CPU iowait est élevé » comme seul argument. C’est suggestif, pas définitif.
  • Ne pas considérer svctm comme l’évangile. Sur les noyaux modernes et les périphériques empilés, il est souvent trompeur.
  • Ne pas lisser pour masquer la latence tail. Les pics vivent dans p99/p999, pas dans la moyenne.

Comment les problèmes d’app se déguisent en stockage (et comment les séparer)

Parfois l’appli est coupable, et le stockage en est le témoin. Voici les imposteurs courants :

  • Contention de verrous : des threads appli bloqués sur des mutex semblent « tout est lent », mais le noyau montre des tâches exécutables, pas un état D d’attente E/S.
  • Pauses GC ou de compactage : l’appli s’arrête mais les métriques disque restent stables ; le CPU peut crasher ou montrer des motifs de pause périodiques.
  • Dépendance réseau : des appels distants causent de la latence ; le disque reste sain ; les processus bloqués ne sont pas en attente E/S.
  • Défauts de cache du système de fichiers : de gros défauts de page peuvent ressembler à des E/S, mais vous verrez un motif dans vmstat et les compteurs perf ; c’est toujours du stockage, mais à un autre niveau.

Blague #2 : l’équipe appli dira que c’est le stockage ; l’équipe stockage dira que c’est l’appli. Félicitations, vous gérez maintenant la diplomatie comme un service.

Trois mini-histoires en entreprise (comment ça échoue en pratique)

Mini-histoire 1 : l’incident causé par une mauvaise hypothèse

L’entreprise avait un service de checkout sur des VMs Ubuntu, appuyé par un volume géré. Une nouvelle intégration partenaire a été lancée un lundi. Aucun déploiement, aucun changement de schéma, aucun flag évident. À midi, les latences p99 montaient à plusieurs secondes et le chat on-call faisait ce que font les chats on-call : produire des théories plus vite que des données.

L’hypothèse dominante : « Si le stockage posait problème, on verrait 100% d’utilisation disque. » Les graphiques montraient %util avoisinant les dizaines. Quelqu’un a décrété le stockage innocent et a blâmé l’API partenaire. Les ingénieurs ont commencé à ajouter du cache, ajuster les timeouts, et la logique de retry—ce qui a augmenté la charge sur la base de données.

Plus tard dans l’après-midi, un SRE a exécuté biosnoop et a capturé des écritures périodiques à 800–1500ms sur le volume. Le taux de requêtes n’était pas élevé ; le backend était juste occasionnellement lent. Le concept manquant était que des pics de latence peuvent se produire sans saturation locale quand vous êtes sur un backend partagé ou throttlé.

La correction n’a pas été héroïque : la charge s’était décalée vers plus de petites écritures. Ils ont augmenté la classe de volume vers une option avec une latence de base meilleure et ont supprimé une option de montage qui provoquait des discards synchrones lors de rafales de suppressions. L’API partenaire était fine. L’hypothèse initiale ne l’était pas.

Mini-histoire 2 : l’optimisation qui a mal tourné

Une équipe voulait accélérer des jobs batch nocturnes. Quelqu’un a remarqué les réglages des pages dirty du noyau et a décidé de « laisser Linux buffer davantage », en augmentant vm.dirty_ratio et vm.dirty_background_ratio. Le job batch s’est accéléré la première heure. Slack a fêté. Une demande de changement a été rédigée a posteriori. Vous voyez où ça mène.

En production, le trafic diurne écrit aussi. Avec des seuils dirty plus hauts, le noyau accumulait plus de données dirty puis les vidait en rafales d’écriture plus larges. Le stockage n’était pas saturé en moyenne, mais la rafale créait des arrêts de plusieurs secondes quand le système atteignait la limite dirty et throttlait les écrivains.

La base n’a pas juste ralenti ; elle a commencé à timeouter des requêtes clients, ce qui a déclenché des retries et amplifié la pression d’écriture. L’équipe appli a vu des timeouts et blâmé les plans de requête. L’équipe infra voyait des IOPS moyens acceptables et haussait les épaules. La métrique qui importait était la latence tail, et elle était en feu.

Le rollback a restauré la stabilité immédiatement. La correction durable a été des tests de charge disciplinés avec suivi p99/p999, et une fenêtre batch séparée avec des limites de débit. L’« optimisation » avait bien existé pour le débit et été catastrophique pour la latence. Les deux peuvent être vrais.

Mini-histoire 3 : la pratique ennuyeuse mais correcte qui a sauvé la mise

Ailleurs, l’équipe plateforme stockage avait l’habitude qui paraissait ennuyeuse mais efficace : chaque hôte émettait un petit ensemble de métriques SLO stockage—await du périphérique, full PSI I/O, et un histogramme de latence bloc depuis un programme eBPF échantillonné pendant les pics. Ils tenaient aussi un inventaire des options de montage et des empilements device mapper.

Un jeudi, plusieurs services ont commencé à montrer des pics p99 synchronisés à travers des applis non liées. Parce que la télémétrie était cohérente, l’on-call n’a pas commencé par « qu’est-ce qui a changé dans l’appli ». Ils ont commencé par : « qu’est-ce qui est commun à ces hôtes ? » L’histogramme montrait de longues écritures tail sur des volumes attachés à un cluster d’hyperviseurs.

Ils ont extrait des snapshots de processus en état D et ont confirmé plusieurs processus bloqués dans io_schedule sur différents services. Cela a déplacé la conversation de « bug appli » vers « backend de stockage partagé ». Le propriétaire de l’hyperviseur a trouvé un événement de maintenance qui avait mis une pool de stockage en état dégradé. Aucun VM unique n’était « saturée ». Le backend l’était.

Le résultat a été anticlimactique : les charges ont été migrées hors des hôtes affectés, et la pool de stockage a été réparée. Le salut n’était pas du génie. C’était une mesure ennuyeuse plus l’habitude de corréler temps, périphérique et processus. En exploitation, l’ennuyeux est une fonctionnalité.

Correctifs qui font réellement la différence (classés par fréquence d’efficacité)

Une fois que vous avez prouvé que la latence est sur le chemin stockage, vos correctifs doivent cibler le mécanisme observé. Ne lancez pas des réglages sysctl au hasard et espérez. La latence tail punit la superstition.

1) Corriger la classe/les limites du backend (cloud et environnements virtualisés)

Si vous êtes sur un volume cloud ou un SAN partagé, vous pouvez atteindre du throttling, l’épuisement des crédits burst, ou de la contention de voisin bruyant. Le pattern de preuve : await qui monte sans saturation locale, fio montrant des tails, et souvent une périodicité répétable.

  • Action : passer à une classe de volume avec une meilleure latence de base / IOPS provisionnées ; réduire la variance en payant pour cela.
  • Action : répartir les données chaudes sur plusieurs volumes (striping côté appli/DB ou LVM) si approprié.
  • Avoid : « juste ajouter des retries. » Les retries transforment de petits incidents en pannes.

2) Supprimer le discard inline pour les systèmes sensibles à la latence (utiliser fstrim)

Le discard inline peut transformer des suppressions en opérations trim synchrones. Sur certains backends c’est acceptable ; sur d’autres c’est un piège de latence.

  • Action : retirer discard du fstab pour le volume de données, remonter, et compter sur fstrim.timer.
  • Valider : exécuter fio et observer l’amélioration de la latence tail.

3) Limiter l’enfilement pour réduire la latence tail

Les files profondes améliorent le débit jusqu’à ce qu’elles détruisent le p99. Pour les backends partagés, de grandes files peuvent transformer une courte pause en longue pause en empilant le travail derrière elle.

  • Action : tester mq-deadline vs none pour NVMe/virtio. Choisissez celui qui réduit la latence tail pour votre charge, pas celui qui gagne des microbenchmarks.
  • Action : envisager de réduire la profondeur de file (spécifique au fournisseur en VM ; en Linux, la file du périphérique et l’iodepth applicatif comptent).

4) Éliminer l’amplification d’écriture

Si votre appli émet de petites écritures aléatoires, chaque couche peut les multiplier en plus d’E/S que vous ne l’imaginez.

  • Action : pour les bases de données, aligner le comportement de checkpoint/flush avec les caractéristiques du stockage ; éviter les réglages qui créent d’énormes rafales de flush périodiques.
  • Action : vérifier si vous êtes involontairement sur un RAID parité pour des charges d’écritures petites et fréquentes ; envisager miroir/striped mirrors pour les écritures sensibles à la latence.
  • Action : éviter d’empiler LVM + dm-crypt + MD RAID sauf nécessité ; chaque couche ajoute de l’enfilement et des modes de panne.

5) Tuning des pages dirty (seulement avec mesure)

Le tuning du writeback du noyau peut réduire la rafale, mais il est facile d’empirer les choses. La posture sûre : petits changements, testés sous charge, en surveillant la latence d’écriture p99 et les SLO appli.

  • Action : si vous voyez des arrêts périodiques liés au seuil dirty, baissez légèrement vm.dirty_ratio pour forcer un writeback plus lisse.
  • Action : envisager vm.dirty_background_bytes et vm.dirty_bytes au lieu de ratios sur des hôtes à empreinte mémoire variable.

6) Corriger les chemins d’erreur et les problèmes de firmware

Si dmesg montre resets/timeouts/erreurs, traitez cela comme un incident de fiabilité. Les pics de latence sont souvent des retries et des resets de contrôleur déguisés.

  • Action : mettre à jour le firmware des disques / pilotes de stockage d’hyperviseur quand applicable.
  • Action : remplacer les périphériques défaillants ; cessez d’essayer de régler la physique avec des réglages software.

Erreurs courantes : symptôme → cause racine → correction

1) Pics p99 appli, mais %util disque bas

Symptôme : Les utilisateurs voient des timeouts ; iostat montre un %util faible mais await bondit.

Cause racine : pause du backend ou throttling (volume cloud, SAN, chemin de flush cache) plutôt que saturation soutenue.

Correction : capturez la latence par E/S avec BPF/blktrace ; passez à une classe de volume meilleure ou réduisez les déclencheurs de flush/trim ; ajustez l’enfilement pour la latence tail.

2) Iowait élevé mène à « blâmer le disque », mais await est correct

Symptôme : vmstat montre un wa élevé, mais iostat -x montre un await faible.

Cause racine : le système attend autre chose (hiccup serveur NFS, système de fichiers réseau, swap I/O, ou une appli provoquant des lectures bloquées via des défauts de page sur un autre périphérique).

Correction : mappez les montages aux périphériques ; vérifiez les stats NFS si applicable ; identifiez les processus bloqués et leurs canaux d’attente ; tracez le bon périphérique.

3) Pics périodiques toutes les quelques secondes/minutes

Symptôme : la cadence du pic ressemble à un métronome.

Cause racine : commits de journal, checkpoints, timers de writeback, trim périodique, ou housekeeping du backend de stockage.

Correction : corréler avec les logs FS/DB ; supprimer discard inline ; ajuster le comportement de checkpoint/commit ; mesurer avec fio et BPF.

4) Pics lors de charges heavy en suppressions

Symptôme : vacuum/compaction/suppression coïncide avec des arrêts I/O.

Cause racine : discard synchrone, pression metadata thin provisioning, ou pression GC SSD due aux rafales d’invalidation.

Correction : utiliser trim programmé ; garantir un espace libre/surprovisionnement suffisant ; envisager une classe SSD ou configuration backend meilleure.

5) « On a activé le chiffrement et maintenant c’est lent »

Symptôme : latence plus élevée et débit moindre après activation de dm-crypt.

Cause racine : surcharge CPU, tailles de requête effectives plus petites, perte d’offloads périphérique, ou interactions d’enfilement dans la pile device-mapper.

Correction : confirmer avec perf et l’usage CPU ; s’assurer que AES-NI est disponible ; envisager d’optimiser les tailles d’E/S et l’iodepth ; garder les piles minimales.

6) RAID « fonctionne » jusqu’à ce que des petites écritures aléatoires arrivent

Symptôme : les lectures vont bien ; les écritures ont une latence tail épouvantable sous charge.

Cause racine : pénalité d’écriture sur RAID parité et cycles read-modify-write ; comportement de flush cache.

Correction : utiliser des miroirs pour des écritures sensibles à la latence ; s’assurer que le cache d’écriture est sûr (BBU/PLP) et que les flushs sont corrects ; augmenter l’alignement de stripe si applicable.

Listes de contrôle / plan pas à pas

Étape par étape : de l’alerte à la cause racine de manière contrôlée

  1. Capturer les horodatages. Notez début/fin des fenêtres de pic. La corrélation meurt sans temps.
  2. Confirmer l’impact hôte. Enregistrez vmstat 1 et la sortie PSI I/O pendant le pic.
  3. Collecter les stats par périphérique. Exécutez iostat -x 1 pendant au moins 60 secondes couvrant un pic.
  4. Identifier les processus bloqués. Snapshot des tâches en état D et leurs canaux d’attente.
  5. Cartographier le chemin des données. Montage → système de fichiers → dm-crypt/LVM/MD → disque physique/virtuel.
  6. Tracer les valeurs aberrantes. Utilisez biosnoop ou blktrace pour capturer quelques E/S de pire latence.
  7. Vérifier les logs pour risques de correction. Scanner dmesg pour erreurs/timeouts/resets liés au stockage.
  8. Reproduire en sécurité. Lancer fio sur un fichier de test pour valider les pics tail hors appli.
  9. Formuler une hypothèse. « Les pics sont causés par X parce que la preuve Y montre de la latence entre D et C et se corrèle avec Z. »
  10. Appliquer un seul changement. Un seul. Pas cinq. Mesurez de nouveau avec les mêmes outils.
  11. Verrouiller la surveillance. Conserver PSI I/O, await iostat, et une sonde de latence tail comme signaux standards.

Checklist opérationnelle : quoi joindre au ticket d’incident

  • Sortie iostat couvrant le pic (texte brut).
  • Snapshot PSI I/O et sortie vmstat.
  • Liste des processus en état D avec canaux d’attente.
  • Un artefact de traçage (lignes biosnoop ou extrait blktrace) montrant des latences I/O aberrantes.
  • Extrait dmesg pour tout avertissement/erreur lié au stockage.
  • Topologie stockage : sortie lsblk montrant les empilements (dm-crypt/LVM/MD/multipath).
  • Note de charge : ce que l’appli faisait (checkpoint, vacuum, compaction, job batch).

FAQ

1) Un iowait élevé suffit-il à prouver que le stockage est le goulot ?

Non. C’est un indice. Prouvez-le avec la latence par périphérique (iostat -x) et le traçage par E/S (BPF ou blktrace). iowait peut être faible pendant de courts pics.

2) Pourquoi la latence monte quand %util n’est pas proche de 100% ?

Parce que l’utilisation est une moyenne et souvent locale à la guest. Des pauses backend, du throttling, des flushs de cache, ou la contention distante peuvent créer un temps de complétion élevé sans saturation locale.

3) Quelle est la manière la plus rapide d’attribuer une E/S lente à un processus ?

Utilisez biosnoop (bcc) ou un outil eBPF similaire. Il enregistre la latence I/O et montre quel processus l’a initiée. Ça règle rapidement les débats.

4) Dois-je passer le scheduler à « none » pour SSD/NVMe ?

Parfois, mais mesurez. « none » peut réduire la surcharge, mais peut aussi permettre de l’injustice et pire latence tail sur des backends partagés. Testez avec fio et une charge proche de la production.

5) Le discard inline est-il vraiment si mauvais ?

Il peut l’être. Sur certains périphériques c’est peu coûteux ; sur d’autres, ça déclenche du travail onéreux à des moments horribles. Si vous voyez des pics pendant des suppressions, essayez le trim programmé à la place.

6) Mon test fio montre un p99 terrible, mais l’appli est « normale » la plupart du temps. Et maintenant ?

Vous avez un problème de latence tail qui se manifestera sous la mauvaise concurrence ou l’activité background. Corrigez-le maintenant, avant une panne qui n’arrive que certains jours.

7) Le choix du système de fichiers (ext4 vs xfs) peut-il corriger les pics de latence ?

Parfois, mais rarement comme premier levier. La plupart des pics viennent de la variance du backend, de l’amplification d’écriture, de l’enfilement, ou du comportement de flush. Les changements de FS sont perturbateurs ; épuisez d’abord les correctifs plus simples.

8) Comment distinguer l’enfilement côté OS de la lenteur de complétion du périphérique ?

Utilisez le timing du cycle de vie avec blktrace. Si le délai est entre Q et D, vous avez de l’enfilement avant dispatch. Si le délai est entre D et C, le périphérique/backend est lent.

9) Pourquoi les pics s’empirent quand on ajoute des retries ?

Les retries ajoutent de la charge précisément quand le système est le plus faible. Ils augmentent la concurrence, creusent les files, et prolongent le pic. Préférez le backoff, du jitter, des coupe-circuits, et corrigez la cause racine.

Conclusion : étapes suivantes qui ne vous feront pas perdre une semaine

Quand des pics de latence disque apparaissent, le danger n’est pas seulement la performance. C’est le mauvais diagnostic. Les gens réécrivent du code, « optimisent » le mauvais chemin, et publient des changements qui rendent l’incident plus grand et plus difficile à comprendre.

Faites ceci ensuite :

  1. Instrumentez : conservez PSI I/O et des métriques de latence périphérique à la manière de iostat -x dans vos tableaux de bord standards.
  2. Pendant le prochain pic, capturez un artefact de traçage (BPF ou blktrace) montrant une E/S aberrante et sa latence.
  3. Supprimez les multiplicateurs évidents de latence (discard inline, enfilement pathologique) et retestez avec fio en regardant les percentiles.
  4. Si vous êtes sur du stockage partagé/virtualisé et que vous pouvez reproduire les tails, cessez de négocier avec la physique : montez en classe backend ou redesign du layout.

Une fois que vous pouvez montrer une E/S spécifique qui a pris 1,1 seconde et nommer le processus qui l’a émise, la conversation change. C’est l’objectif. La preuve bat l’opinion, et cela vous rendra aussi moins populaire en réunion—ce qui, honnêtement, est parfois un avantage.

← Précédent
Ubuntu 24.04 : PHP-FPM plante sans cesse — la ligne de journal à trouver (et les correctifs)
Suivant →
Ubuntu 24.04 : mises à jour ont cassé les modules noyau — reconstruire correctement l’initramfs (cas n°28)

Laisser un commentaire