Debian 13 : gel de l’hôte à 100% iowait — trouver le processus/VM bruyant en 10 minutes

Cet article vous a aidé ?

Votre hôte Debian 13 « gèle », les sessions SSH se bloquent, les graphiques Prometheus ressemblent au tracé plat d’un moniteur cardiaque, et top affiche le CPU à 2%… sauf que l’iowait est bloqué à 100%. On a l’impression que la machine est allumée mais émotionnellement indisponible.

Ce cas est rarement un mystère. Il s’agit généralement d’un processus ou d’une VM bruyante qui fait quelque chose de « parfaitement raisonnable » au pire moment possible, combiné à un stockage qui ne suit pas. Le travail n’est pas de méditer dessus. Le travail est d’identifier le coupable rapidement, décider de tuer, limiter ou réparer, et remettre votre hôte sur pied.

Le modèle mental : ce que signifie vraiment 100% iowait

iowait n’est pas « votre disque est occupé à 100% ». C’est « vos CPU ont du travail exécutable, mais ce travail est bloqué en attendant la fin d’opérations I/O ». Si vous voyez une iowait élevée avec une faible utilisation CPU, le noyau vous rend service en n’épuisant pas les cycles. Vos utilisateurs l’interprètent comme « le serveur est mort », parce que de leur point de vue, il l’est.

Sur Debian 13, le schéma habituel est :

  • Une charge commence à émettre beaucoup d’I/O (les écritures sont le déclencheur classique).
  • La profondeur de file d’attente augmente sur un périphérique bloc.
  • La latence explose (des millisecondes deviennent des secondes).
  • Des threads se retrouvent bloqués en état D (sleep non interruptible), et le système paraît figé.

Ce que vous voulez répondre en moins de 10 minutes :

  1. Quel périphérique ? (nvme0n1 ? md0 ? dm-crypt ? montage NFS ?)
  2. Quel type de pression ? (latence ? saturation ? retries ? flush/sync ?)
  3. Quelle source ? (PID ? conteneur ? VM/invité ?)
  4. Quelle action ? (tuer, limiter, déplacer, réparer le matériel, changer la config)

Vérité brute : « iowait 100% » est un symptôme, pas un diagnostic. Le diagnostic, c’est une file d’attente et une source.

Une citation à garder au mur, parce que c’est tout le travail : « L’espoir n’est pas une stratégie. » — Gene Kranz.

Blague #1 : iowait est la façon Linux de dire « J’adorerais aider, mais j’attends que le stockage termine son long roman. »

Playbook de diagnostic rapide (10 minutes)

Minute 0–1 : confirmer que c’est de l’I/O et non un autre type de misère

  • Vérifier la charge, l’iowait et les tâches bloquées.
  • Si vous voyez beaucoup de tâches en état D et une charge qui monte avec une faible utilisation CPU, vous êtes en territoire I/O.

Minute 1–3 : identifier le périphérique qui s’étouffe

  • Utilisez iostat ou sar -d pour trouver un await élevé et une forte utilisation.
  • Validez avec cat /proc/diskstats si les outils ne sont pas installés ou se bloquent.

Minute 3–6 : identifier la source (PID, cgroup ou VM)

  • Utilisez pidstat -d pour savoir quels PIDs font de l’I/O.
  • Utilisez iotop pour une visibilité en direct si cela fonctionne (il peut manquer les writeback en mémoire tampon).
  • Sur des hôtes virtualisés, mappez les threads QEMU aux invités via les arguments du processus libvirt/QEMU et les stats par disque.
  • Utilisez PSI (/proc/pressure/io) pour confirmer un arrêt I/O à l’échelle du système.

Minute 6–8 : décider : tuer, limiter ou isoler

  • Si c’est un job de sauvegarde, une reconstruction d’index ou une migration : limitez-le.
  • Si c’est un processus incontrôlé : stoppez-le maintenant, trouvez la cause ensuite.
  • Si c’est une VM unique : appliquez des limites I/O au niveau de l’hyperviseur ou de la couche de stockage.

Minute 8–10 : confirmer la récupération et capturer des preuves

  • Surveillez la baisse de await et de l’utilisation.
  • Récupérez journalctl, les logs noyau, et un échantillon iostat/pidstat court pour pouvoir corriger durablement.

Si vous ne faites rien d’autre, faites ceci : trouvez le périphérique, puis trouvez la source. Tout le reste est décoration.

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

Ce sont les actions que j’utilise réellement quand un hôte « gèle » mais n’est pas mort. Chaque tâche inclut ce que la sortie signifie et quelle décision en découle. Exécutez-les dans l’ordre jusqu’à obtenir un nom (PID ou VM) et un périphérique.

Task 1 — Voir iowait, la charge et les tâches bloquées en un coup d’œil

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
 1 12      0 312844  98124 4021180   0    0     0  8240  220  410  2  1  3 94  0
 0 18      0 311200  98124 4019900   0    0     0 12032  210  390  1  1  0 98  0
 0 16      0 310980  98124 4019500   0    0     0 11024  205  372  1  1  1 97  0
 0 20      0 310500  98124 4018400   0    0     0 14080  198  360  1  1  0 98  0
 0 17      0 310300  98124 4018100   0    0     0 13056  200  365  1  1  0 98  0

Signification : la colonne b (processus bloqués) est élevée, et wa (iowait) est ~98%. C’est la signature classique d’un « stockage est le goulot d’étranglement ».

Décision : ne discutez pas. Passez immédiatement à la latence par périphérique (iostat).

Task 2 — Trouver le périphérique chaud avec latence et utilisation

cr0x@server:~$ iostat -xz 1 3
Linux 6.12.0-1-amd64 (server) 	12/29/2025 	_x86_64_	(32 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          1.20    0.00    1.10   92.40    0.00    5.30

Device            r/s     rkB/s   rrqm/s  %rrqm  r_await rareq-sz     w/s     wkB/s   wrqm/s  %wrqm  w_await wareq-sz  aqu-sz  %util
nvme0n1          0.20      8.00     0.00   0.00    2.10    40.0  420.00  67136.00    12.00   2.78  280.30   159.8  118.2  99.70
nvme1n1         18.00   2048.00     0.00   0.00    1.80   113.8   15.00   1024.00     0.00   0.00    2.40    68.3    0.1   4.10

Signification : nvme0n1 est à ~100% d’utilisation avec un énorme w_await et une file massive (aqu-sz). C’est votre périphérique goulot.

Décision : identifiez qui écrit sur nvme0n1. Envisagez aussi si c’est un pic de latence (firmware, throttling thermique) ou une saturation (charge légitime).

Task 3 — Confirmer l’arrêt I/O système avec PSI

cr0x@server:~$ cat /proc/pressure/io
some avg10=78.43 avg60=65.12 avg300=40.02 total=184563218
full avg10=52.10 avg60=44.90 avg300=22.31 total=105331982

Signification : la pression I/O « full » indique que des tâches sont en attente parce que l’I/O ne peut pas se compléter. Ce n’est pas « quelques requêtes lentes », c’est systémique.

Décision : vous êtes autorisé à prendre des mesures agressives (limiter/tuer), car tout l’hôte en souffre.

Task 4 — Trouver rapidement les PID I/O principaux (et ne faites pas confiance à votre intuition)

cr0x@server:~$ pidstat -d 1 5
Linux 6.12.0-1-amd64 (server) 	12/29/2025 	_x86_64_	(32 CPU)

03:14:01      UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
03:14:02        0     21492      0.00  65432.00      0.00      48  qemu-system-x86
03:14:02        0     31811      0.00  12160.00      0.00      20  rsync
03:14:02        0      1092      0.00   4096.00      0.00      10  jbd2/nvme0n1p2-8
03:14:02        0     25170      0.00   2048.00      0.00       8  postgres

03:14:02      UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
03:14:03        0     21492      0.00  70000.00      0.00      52  qemu-system-x86
03:14:03        0     31811      0.00  11008.00      0.00      18  rsync

Signification : le plus gros écrivain est qemu-system-x86 PID 21492. Cela signifie généralement « une VM est le voisin bruyant », pas que QEMU s’est réveillé et a choisi la violence.

Décision : mappez ce processus QEMU à un nom d’invité et à un disque. Ensuite, limitez l’I/O de l’invité ou corrigez ce que fait l’invité.

Task 5 — Si ce n’est pas évident, cherchez des accumulations en état D

cr0x@server:~$ ps -eo pid,state,comm,wchan:32 --sort=state | head -n 20
  PID S COMMAND         WCHAN
 8921 D qemu-system-x86 blk_mq_get_tag
 8922 D qemu-system-x86 io_schedule
 8923 D qemu-system-x86 bit_wait_io
31811 D rsync           io_schedule
25170 D postgres        io_schedule
 1092 D jbd2/nvme0n1p2-8 bit_wait_io

Signification : les threads en état D sont bloqués dans la couche bloc (blk_mq_get_tag) ou en attente d’ordonnancement I/O. Cela correspond à une saturation de la file sur le périphérique.

Décision : vous ne traitez pas une fuite CPU. Vous devez réduire la charge I/O ou augmenter la capacité I/O, et rapidement identifier la source.

Task 6 — Mapper le PID QEMU à un nom de VM (libvirt)

cr0x@server:~$ ps -p 21492 -o pid,cmd --no-headers | sed 's/ -/\n-/g' | head -n 20
21492 /usr/bin/qemu-system-x86_64
-name guest=acct-db-03,debug-threads=on
-uuid 3bce8b54-8d13-4c4a-bd19-5e3dfe7e8a0e
-drive file=/var/lib/libvirt/images/acct-db-03.qcow2,format=qcow2,if=virtio,cache=none,aio=native
-drive file=/var/lib/libvirt/images/acct-db-03-data.raw,format=raw,if=virtio,cache=none,aio=native

Signification : vous avez maintenant une identité VM : acct-db-03, et au moins un chemin de disque. C’est de l’or quand vous devez expliquer l’impact aux humains.

Décision : confirmez quel disque est le plus chaud et si le stockage sous-jacent est sur nvme0n1. Si oui, limitez cette VM ou mettez en pause la charge à l’intérieur.

Task 7 — Trouver quel périphérique bloc sous-tend le fichier disque de la VM

cr0x@server:~$ df -h /var/lib/libvirt/images
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme0n1p2  1.8T  1.2T  526G  70% /var

Signification : les images VM sont sur /dev/nvme0n1p2, qui est sur le NVMe chaud. La cartographie est cohérente.

Décision : l’atténuation peut se faire sur l’hôte : limiter l’I/O de cette VM (cgroup ou réglage libvirt), ou côté invité : arrêter/réduire le job.

Task 8 — Vérifier les statistiques de bloc par domaine (libvirt) pour trouver le disque le plus bruyant

cr0x@server:~$ virsh domblklist acct-db-03
Target     Source
------------------------------------------------
vda        /var/lib/libvirt/images/acct-db-03.qcow2
vdb        /var/lib/libvirt/images/acct-db-03-data.raw

cr0x@server:~$ virsh domblkstat acct-db-03 vdb --human
rd_bytes              1.2 GiB
wr_bytes              842.6 GiB
rd_operations         12034
wr_operations         55210321
flush_operations      431920
wr_total_times        10421863423

Signification : vdb est le gros écrivain avec beaucoup de flushes. Les flushes amplifient souvent la latence car elles forcent l’ordre et les garanties de complétion.

Décision : si l’invité effectue des écritures sync-intensive (bases de données, fsync storm, journaling), envisagez de limiter ou de déplacer vers un stockage plus rapide/moins contendu. Vérifiez aussi si les réglages hôte (cache d’écriture, barrières, dm-crypt) rendent les flushes coûteux.

Task 9 — Vérifier si le périphérique vous rejette avec des erreurs ou des resets

cr0x@server:~$ journalctl -k -n 80 --no-pager
Dec 29 03:12:48 server kernel: nvme nvme0: I/O 1234 QID 6 timeout, aborting
Dec 29 03:12:48 server kernel: nvme nvme0: Abort status: 0x371
Dec 29 03:12:49 server kernel: nvme0n1: I/O error, dev nvme0n1, sector 1937422336 op 0x1:(WRITE) flags 0x800 phys_seg 32 prio class 0
Dec 29 03:12:50 server kernel: EXT4-fs warning (device nvme0n1p2): ext4_end_bio:345: I/O error 10 writing to inode 5513245 starting block 242177834)

Signification : timeouts et erreurs I/O ne sont pas de la « charge ». Ce sont des problèmes de périphérique ou de transport. En condition d’erreur, la latence peut monter en flèche tandis que le débit chute. Votre iowait ressemblera toujours à la même chose.

Décision : cessez de blâmer les charges. Passez au triage matériel/firmware : vérifiez SMART/logs NVMe, câblage/backplane, logs du contrôleur. Envisagez d’extraire le périphérique (RAID/ZFS) si possible.

Task 10 — Santé NVMe rapide et compteurs d’erreurs

cr0x@server:~$ sudo nvme smart-log /dev/nvme0
Smart Log for NVME device:nvme0 namespace-id:ffffffff
critical_warning                    : 0x00
temperature                         : 78 C
available_spare                     : 100%
available_spare_threshold           : 10%
percentage_used                     : 3%
media_errors                        : 12
num_err_log_entries                 : 45

Signification : la température est élevée et il y a des erreurs média. Le throttling thermique et la récupération d’erreurs interne peuvent provoquer de grosses hausses de latence.

Décision : traitez cela comme un incident de performance et de fiabilité. Planifiez le remplacement du périphérique si les erreurs persistent. Améliorez le refroidissement si la température est constamment élevée.

Task 11 — Distinguer « périphérique saturé » vs « périphérique inactif mais lent »

cr0x@server:~$ iostat -xz /dev/nvme0n1 1 2
Linux 6.12.0-1-amd64 (server) 	12/29/2025 	_x86_64_	(32 CPU)

Device            r/s     rkB/s   rrqm/s  %rrqm  r_await rareq-sz     w/s     wkB/s   wrqm/s  %wrqm  w_await wareq-sz  aqu-sz  %util
nvme0n1           0.00      0.00     0.00   0.00    0.00     0.0  380.00  62000.00     0.00   0.00  310.00   163.2  120.0  99.90

Device            r/s     rkB/s   rrqm/s  %rrqm  r_await rareq-sz     w/s     wkB/s   wrqm/s  %wrqm  w_await wareq-sz  aqu-sz  %util
nvme0n1           0.00      0.00     0.00   0.00    0.00     0.0  390.00  64000.00     0.00   0.00  295.00   164.1  118.5  99.80

Signification : c’est de la saturation (util ~100%, grosse file). Si l’utilisation était faible mais l’await élevé, vous suspecteriez des stalls intermittents, des bugs de firmware, ou des couches en amont (dm-crypt, resync RAID, serveur NFS) plutôt qu’une charge pure.

Décision : si c’est de la saturation, réduisez la charge ou ajoutez de la capacité (autre disque, plus de disques, NVMe meilleur, séparer les charges bruyantes). Si ce sont des stalls, trouvez la couche qui bloque.

Task 12 — Trouver quels fichiers sont martelés (quand c’est nécessaire)

cr0x@server:~$ sudo lsof -p 21492 | grep -E '/var/lib/libvirt/images/|deleted' | head
qemu-system 21492 root  103u   REG  259,2 34359738368  1310720 /var/lib/libvirt/images/acct-db-03-data.raw
qemu-system 21492 root  104u   REG  259,2 21474836480   983040 /var/lib/libvirt/images/acct-db-03.qcow2

Signification : confirme quels fichiers d’image sont en jeu. Cela ne vous montrera pas quels blocs sont chauds, mais ancre la responsabilité à un chemin concret.

Décision : si vous devez déplacer : vous savez quoi déplacer. Si vous devez snapshotter : vous savez quoi snapshotter. Si vous devez expliquer l’impact : vous avez un fichier et un nom de VM.

Task 13 — Identifier le writeback majeur et la pression des pages dirty

cr0x@server:~$ grep -E 'Dirty|Writeback|MemAvailable' /proc/meminfo
MemAvailable:   10432124 kB
Dirty:           982432 kB
Writeback:       210944 kB
WritebackTmp:         0 kB

Signification : un Dirty/Writeback élevé suggère que le noyau pousse des écritures tamponnées vers le disque et peut être bloqué derrière un I/O lent. Si la mémoire dirty explose et que le disque ne suit pas, tout devient « lent de façons étranges ».

Décision : considérez si une charge génère des écritures soutenues dépassant le débit du périphérique, et si le throttling (ionice, cgroup io.max) est la bonne atténuation.

Task 14 — Vérifier la douleur au niveau système de fichiers (EXT4/XFS) et le comportement du journal

cr0x@server:~$ mount | grep ' on /var '
/dev/nvme0n1p2 on /var type ext4 (rw,relatime,errors=remount-ro)

cr0x@server:~$ dmesg | tail -n 20
[12345.678901] EXT4-fs (nvme0n1p2): Delayed block allocation failed for inode 5513245 at logical offset 123456 with max blocks 2 with error 5
[12345.678910] EXT4-fs (nvme0n1p2): This should not happen!! Data will be lost

Signification : avertissements du système de fichiers plus erreurs I/O sont des événements « stop the world ». Même sans erreurs, les threads de journal (comme jbd2) dans pidstat indiquent que des écritures forcent des mises à jour de métadonnées et des commits.

Décision : si des erreurs existent, traitez comme une défaillance de stockage. Sinon, et si vous avez beaucoup de flushes/commits, concentrez-vous sur le motif de la charge (fsync storms, petites écritures aléatoires) et la configuration du stockage (cache d’écriture, barrières, mode de cache en virtualisation).

Task 15 — Quand il vous faut une preuve irréfutable : échantillonner l’I/O bloc avec blktrace

cr0x@server:~$ sudo blktrace -d /dev/nvme0n1 -w 10 -o - | sudo blkparse -i - | head -n 20
  8,0    0        1     0.000000000  21492  Q   WS 1937422336 + 128 [qemu-system-x86]
  8,0    0        2     0.000012345  21492  G   WS 1937422336 + 128 [qemu-system-x86]
  8,0    0        3     0.000056789  21492  I   WS 1937422336 + 128 [qemu-system-x86]
  8,0    0        4     0.310123456  21492  D   WS 1937422336 + 128 [qemu-system-x86]
  8,0    0        5     0.650987654  21492  C   WS 1937422336 + 128 [qemu-system-x86]

Signification : vous pouvez attribuer l’I/O au niveau bloc à un PID (ici, QEMU). Remarquez l’écart temporel entre l’émission et la complétion ; c’est votre latence brute.

Décision : si vous devez convaincre quelqu’un que « la VM l’a vraiment fait », c’est de la preuve de qualité judiciaire. Utile aussi quand plusieurs PIDs sont impliqués et que les outils userland divergent.

Task 16 — Atténuation rapide : limiter un cgroup bruyant (systemd) avec io.max

cr0x@server:~$ systemctl status libvirtd | head -n 5
● libvirtd.service - Virtualization daemon
     Loaded: loaded (/lib/systemd/system/libvirtd.service; enabled)
     Active: active (running) since Mon 2025-12-29 02:11:03 UTC; 1h 03min ago

cr0x@server:~$ sudo systemctl set-property --runtime machine-qemu\\x2dacct\\x2ddb\\x2d03.scope IOReadBandwidthMax=/dev/nvme0n1 10M IOWriteBandwidthMax=/dev/nvme0n1 20M

Signification : vous venez d’appliquer des limites I/O de bande passante à l’exécution sur le scope systemd de cette VM (le nommage varie ; confirmez le scope exact sur votre hôte). Cela réduit la capacité de la VM à affamer l’hôte.

Décision : utilisez ceci quand la stabilité de l’hôte prime sur la vitesse d’un invité. Ensuite, planifiez une correction permanente : isoler les charges, tuner les paramètres DB, ou déplacer la VM vers un stockage dédié.

Blague #2 : Limiter une VM bruyante, c’est comme remettre une roue de caddie. Ça ne la rendra pas élégante, mais au moins elle arrêtera de crier.

Faits et historique pour accélérer le débogage

Connaître quelques faits « pourquoi c’est comme ça » vous évite de courir après des fantômes. En voici qui comptent vraiment quand vous fixez les yeux sur des graphiques iowait à 03:00.

  1. iowait Linux est un état de comptabilisation CPU, pas une métrique disque. Il peut être élevé même quand le disque n’est pas entièrement utilisé, surtout avec des stalls intermittents ou des files noyau profondes.
  2. Le vieux champ svctm d’iostat est devenu peu fiable avec les périphériques modernes et le multi-queue. Les gens le citent encore comme un évangile. Ne le faites pas.
  3. blk-mq (couche bloc multi-queue) a changé l’intuition « une file par périphérique ». NVMe et le stockage moderne peuvent avoir de nombreuses files ; le comportement de saturation diffère du vieux SATA.
  4. CFQ est mort, BFQ est arrivé, et mq-deadline est devenu un choix par défaut pour beaucoup de SSD. Le choix de l’ordonnanceur peut changer la latence de queue sous workloads mixtes.
  5. PSI (Pressure Stall Information) est relativement nouveau comparé aux outils classiques. Il vous dit « à quel point le système est bloqué », pas seulement « à quel point il est occupé », ce qui se rapproche davantage de la douleur utilisateur.
  6. Le cache de writeback peut cacher le début d’un incident. Vous pouvez bufferiser des écritures en RAM jusqu’à ce que vous ne le puissiez plus, puis vous payez la dette avec intérêts : de longues tempêtes de flush.
  7. La virtualisation ajoute une pile d’ordonnanceur supplémentaire. L’invité pense faire un fsync « raisonnable » ; l’hôte le transforme en vrais flushes ; le stockage sous-jacent décide qu’il est temps de faire du garbage collection.
  8. Le throttling thermique NVMe est un vrai mode de panne en production. Des écritures séquentielles élevées peuvent pousser les disques en throttling, causant des augmentations dramatiques de latence sans erreurs évidentes au début.
  9. Le resync RAID et les scrub peuvent créer un déni de service I/O parfaitement légitime. Aucun bug requis. Juste le timing.

Si c’est une VM : cartographier l’I/O hôte vers l’invité bruyant

Sur un hôte de virtualisation, l’erreur la plus courante est de traiter QEMU comme le coupable. QEMU est le messager. Votre vraie question : quelle charge invité sature quel périphérique hôte, et est-ce un job attendu ou un piège accidentel ?

À quoi ressemble une « VM bruyante » sur l’hôte

  • pidstat -d montre un ou quelques PIDs qemu-system-* dominant les écritures.
  • iostat affiche un périphérique avec une énorme file (aqu-sz) et un await élevé.
  • La moyenne de charge monte, mais le CPU est principalement idle sauf l’iowait.
  • D’autres VMs se plaignent : « le disque est lent » ou « la base de données est bloquée » même si elles ne font pas grand-chose.

Comment relier proprement les threads QEMU à un domaine

Si vous utilisez libvirt, vous pouvez mapper par :

  • la ligne de commande du processus QEMU (-name guest=...)
  • virsh domblkstat par périphérique disque
  • les unités scope systemd sous machine.slice (le nommage varie)

Une fois que vous avez un nom d’invité, vous avez des options :

  • Atténuation côté invité : mettre la sauvegarde en pause, réduire la concurrence, tuner les checkpoints DB, arrêter une reconstruction d’index, limiter la compaction.
  • Atténuation côté hôte : appliquer des limites I/O via cgroup v2 (io.max / propriétés systemd), ou ajuster le tuning I/O libvirt (si déjà en place).
  • Correction structurelle : déplacer la VM vers un stockage dédié, séparer disque OS et disque de données, ou séparer les jobs batch des VMs sensibles à la latence.

Quand la VM ne « fait pas tant que ça », mais tue quand même l’hôte

C’est là que les flushes, écritures sync et motifs lourds en métadonnées comptent. Une VM effectuant un nombre modeste de transactions avec fsync peut créer une pression énorme si la pile sous-jacente transforme chaque fsync en flushs coûteux via dm-crypt, RAID et un disque en garbage collection interne.

Surveillez :

  • un flush_operations élevé dans virsh domblkstat
  • des changements de configuration DB qui augmentent les garanties de durabilité
  • des options de montage système de fichiers invité modifiées
  • des changements de mode de cache hôte (par ex. cache=none vs writeback)

Triage de la couche stockage : NVMe, RAID, dm-crypt, ZFS, réseau

Après avoir identifié le processus/VM bruyant, vous devez encore décider si la vraie correction est « arrêter la charge » ou « la pile stockage est malade ». Le moyen le plus rapide est de reconnaître les modes de panne courants par leur télémétrie.

NVMe : rapide jusqu’à ce que ça ne le soit plus

Les incidents NVMe tombent souvent dans deux catégories : saturation (charge légitime) et pics de latence (matériel/firmware/thermique). La saturation est ennuyeuse. Les pics de latence sont dramatiques.

Ce qu’il faut vérifier :

  • Température et erreurs via nvme smart-log
  • Logs noyau pour timeouts/resets
  • Util vs await : si util est faible mais await élevé, suspectez des stalls plutôt qu’une limitation de débit

mdadm RAID : le resync est un incident planifié si vous le laissez faire

Le RAID logiciel peut bien se comporter pendant des mois, puis une reconstruction ou un scrub démarre et soudain votre « hôte stable » devient une leçon sur les ressources partagées. L’I/O de rebuild concurrence tout le reste.

cr0x@server:~$ cat /proc/mdstat
Personalities : [raid1] [raid10]
md0 : active raid1 sda1[0] sdb1[1]
      976630336 blocks super 1.2 [2/2] [UU]
      [===>.................]  resync = 18.3% (178944000/976630336) finish=123.4min speed=107000K/sec

unused devices: <none>

Signification : un resync est actif. Cela peut provoquer de l’iowait même si la charge applicative n’a pas changé.

Décision : limitez la vitesse de rebuild si cela nuit à la production, ou planifiez les fenêtres de rebuild. Ne « optimisez » pas en laissant le rebuild tourner sans retenue sur des disques partagés.

dm-crypt : le chiffrement n’est pas gratuit, et les sémantiques de flush comptent

dm-crypt ajoute un coût CPU et peut affecter la latence sous des workloads sync-heavy. En général c’est acceptable sur des CPU modernes, mais cela peut empirer la latence tail dans certains motifs.

cr0x@server:~$ lsblk -o NAME,TYPE,FSTYPE,SIZE,MOUNTPOINTS
nvme0n1         disk         1.8T
├─nvme0n1p1     part vfat     512M /boot/efi
└─nvme0n1p2     part crypto_LUKS 1.8T
  └─cryptvar    crypt ext4    1.8T /var

Signification : votre système de fichiers chaud est au-dessus de dm-crypt. Ce n’est pas automatiquement mauvais. C’est un indice que les charges lourdes en flush peuvent payer un surcoût.

Décision : si les tempêtes de flush vous tuent, testez avec des workloads réalistes et envisagez de séparer les charges sync-heavy sur des périphériques dédiés ou de revoir le tuning de cache/I/O. Ne désactivez pas le chiffrement en panique à moins d’aimer les réunions de conformité.

ZFS : le coup en pleine face de txg sync

ZFS peut offrir d’excellentes performances et aussi vous apprendre l’amplification d’écriture. Un motif courant d’iowait est des stalls périodiques pendant le txg sync quand le pool est surchargé ou mal tuné pour la charge.

cr0x@server:~$ zpool iostat -v 1 3
                              capacity     operations     bandwidth
pool                        alloc   free   read  write   read  write
rpool                       1.20T   600G      5    950   200K  120M
  mirror                    1.20T   600G      5    950   200K  120M
    nvme0n1p3                   -      -      2    480   100K   60M
    nvme1n1p3                   -      -      3    470   100K   60M

Signification : écritures lourdes ; si la latence est mauvaise, vérifiez les motifs d’écritures sync, l’utilisation de SLOG, recordsize, compression, et si le pool est presque plein.

Décision : si une DB invitée fait des écritures sync, envisagez un appareil SLOG séparé (avec protection perte d’énergie) ou ajustez le comportement applicatif. Ne « réglez » pas ça en désactivant sync à moins d’accepter la perte de données.

Stockage réseau (NFS/iSCSI) : quand le « disque » est en réalité un problème réseau

NFS peut produire de l’iowait qui ressemble à une saturation de disque local. L’hôte attend des complétions I/O, mais ces complétions dépendent de la latence réseau, de la charge du serveur ou des retransmissions.

cr0x@server:~$ mount | grep nfs
10.0.0.20:/export/vm-images on /var/lib/libvirt/images type nfs4 (rw,relatime,vers=4.2,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2)

cr0x@server:~$ nfsstat -c | head -n 20
Client rpc stats:
calls      retrans    authrefrsh
1234567    3456       123

Client nfs v4:
null         read         write        commit       open
0            234567       345678       4567         1234

Signification : les retransmissions sont non nulles et en hausse ; des commits existent ; un montage « hard » signifie que les tâches peuvent rester bloquées tant que le serveur ne répond pas.

Décision : vérifiez les erreurs réseau, la charge du serveur NFS, et envisagez d’isoler les VMs sensibles à la latence loin du stockage réseau. Si votre hôte « gèle » à cause de NFS, traitez-le comme une défaillance de dépendance, pas comme un problème de disque local.

Trois mini-récits d’entreprise du terrain

1) Incident causé par une mauvaise hypothèse : « iowait signifie que le disque est saturé »

Une équipe plateforme avait un cluster de virtualisation basé sur Debian qui entrait occasionnellement en « tout le monde est lent ». L’astreinte voyait l’iowait à 90–100%, regardait un tableau de bord montrant un débit disque modéré, et supposait que l’array de stockage allait bien parce que « ce n’est même pas saturé en bande passante ». Ils se sont donc concentrés sur le CPU et la mémoire. Ils ont ajusté la swappiness du noyau. Ils ont redémarré quelques services. Ils ont même blâmé un déploiement applicatif récent, parce que les humains font ça sous stress.

Pendant ce temps, le cluster continuait de geler par rafales : sessions SSH bloquées, VMs en timeout, puis récupération magique. Cela ressemblait à un problème externe transitoire. L’équipe a ouvert un ticket chez le fournisseur de stockage. Le fournisseur a demandé des chiffres de latence. Personne n’en avait dans l’alerte.

Le vrai problème était la latence tail due à des stalls intermittents sur un périphérique NVMe à cause d’un throttling thermique. L’utilisation et le débit n’étaient pas en cause ; c’était le temps de complétion. Pendant les stalls, les files s’accumulaient, les tâches se bloquaient, l’iowait montait, puis l’arriéré se vidait. Les graphes de bande passante restaient « corrects » parce que les MB/s moyens ne captaient pas les pauses de plusieurs secondes.

Une fois qu’ils ont regardé iostat -xz et les logs smart NVMe, c’était évident : températures élevées, logs d’erreurs en augmentation, await en centaines de ms. La correction a été prosaïque : améliorer la circulation d’air dans le châssis, mettre à jour le firmware, et remplacer le pire lecteur. Ils ont aussi ajouté des alertes sur la latence et PSI, pas seulement le débit. La fois suivante où l’iowait a augmenté, l’alerte disait « IO full pressure is high; nvme0 await 280ms. » La discussion s’est arrêtée avant de commencer.

2) Optimisation qui s’est retournée contre eux : « rendons les sauvegardes plus rapides »

Un groupe opérations voulait réduire la fenêtre de sauvegarde des images VM. Ils sont passés d’une méthode rsync lente à une approche utilisant plusieurs flux parallèles et des tailles de bloc agressives. Sur le papier, c’était excellent : sauvegardes plus rapides, moins de temps, plus « moderne ». Ils l’ont déployé sur tous les hyperviseurs un vendredi, naturellement.

Le premier weekend, le cluster est entré dans un état bizarre : les sauvegardes finissaient rapidement, mais pendant la fenêtre de sauvegarde, les VMs orientées client devenaient non réactives. Les services sensibles à la latence déclaraient des timeouts. Les hyperviseurs affichaient l’iowait bloqué. Quelques invités ont été marqués « paused » par leurs watchdogs parce que les disques virtuels ne répondaient plus à temps.

L’« optimisation » était en fait un déni de service distribué contre leur propre stockage. Les flux parallèles ont saturé la profondeur de queue du périphérique sous-jacent et poussé le contrôleur/array dans un comportement pire pour les charges mixtes. Les sauvegardes ont fini vite parce que le cluster a sacrifié tout le reste pour les accélérer.

La correction a été de traiter les sauvegardes comme un travail en arrière-plan, pas une course. Ils ont mis en place un throttling I/O pour les jobs de sauvegarde et imposé une limite de concurrence par hôte. Les sauvegardes ont de nouveau pris plus de temps, mais l’entreprise a cessé de perdre ses weekends. Ils ont aussi séparé les « golden image backups » des « VMs BD chaudes » sur des couches de stockage différentes. La leçon : accélérer un job en empruntant la latence à tout le monde n’est pas une optimisation, c’est une redistribution de la douleur.

3) La pratique ennuyeuse mais correcte qui a sauvé la mise : garde-fous I/O par hôte

Une autre entreprise gérait une flotte de virtualisation Debian avec une règle stricte : chaque scope VM avait des garde-fous I/O définis. Rien de dramatique, juste des valeurs par défaut sensées en cgroup v2 : un plafond de bande passante et un plafond d’IOPS, ajustés par classe de workload. L’équipe a essuyé quelques roulements d’yeux. Les développeurs voulaient « pas de limites ». La direction voulait « utilisation maximale ». Les SRE voulaient « pas de surprises ». Les SRE ont gagné, discrètement.

Un après-midi, une équipe a lancé une migration de données dans une VM. Elle a commencé une grosse transformation write-heavy avec des appels fsync fréquents. Dans la plupart des environnements, c’est à ce moment que l’hôte devient une brique et que tout le monde apprend du nouveau vocabulaire. Ici, la VM de migration a ralenti parce qu’elle a atteint son plafond. Les autres VMs sont restées normales. L’hôte est resté réactif. Personne n’a appelé.

La migration a fini plus tard que prévu, alors l’ingénieur a demandé ce qu’il s’était passé. Le SRE a affiché les stats cgroup et montré le throttling I/O. Il a expliqué que l’alternative était que l’hyperviseur entier tombe en enfer iowait et emporte des services non liés avec lui.

Rien d’héroïque. C’est le point. La pratique ennuyeuse était les garde-fous, et elle a transformé un incident potentiel de cluster en un ingénieur un peu agacé et une migration plus lente. C’est le genre de victoire qu’on apprécie après s’être brûlé.

Erreurs fréquentes : symptômes → cause racine → correctif

C’est là que la plupart des équipes gaspillent des heures. Les schémas se répètent, et les corrections aussi.

1) « iowait est à 100%, donc le disque est saturé »

Symptôme : iowait élevé, débit modéré, système « gèle » par rafales.
Cause racine : pics de latence (throttling thermique, bug de firmware, resets de chemin) plutôt que saturation soutenue de bande passante.
Correctif : regardez await, aqu-sz, PSI, logs noyau ; vérifiez la température/erreurs NVMe ; mettez à jour le firmware et le refroidissement ; remplacez les médias défaillants.

2) « iotop ne dit rien n’écrit, donc ce n’est pas de l’I/O »

Symptôme : iowait élevé, iostat montre des écritures, iotop semble calme.
Cause racine : écritures tamponnées poussées par des threads noyau (writeback), ou I/O attribuée à d’autres threads que vous n’attendez pas (QEMU, jbd2, kswapd).
Correctif : utilisez pidstat -d, vérifiez Dirty/Writeback dans /proc/meminfo, et inspectez les threads noyau en état D.

3) « La moyenne de charge est haute, donc c’est le CPU »

Symptôme : la charge augmente, le CPU est idle, l’iowait est élevé.
Cause racine : la charge inclut les tâches bloquées en sleep I/O non interruptible ; ce n’est pas une métrique CPU-only.
Correctif : corrélez la charge avec les tâches bloquées (colonne b de vmstat) et l’état D (ps). Traitez comme de l’I/O.

4) « C’est la base de données » (dit sans preuve)

Symptôme : les requêtes DB timeout pendant l’incident ; on pointe la BD du doigt.
Cause racine : la BD est victime d’une saturation de la file disque causée par une autre charge (backup, migration, rotation de logs) ou un voisin VM partagé.
Correctif : prouvez la source avec pidstat et des stats par VM ; isolez le stockage DB ; implémentez des limites I/O pour les jobs batch non-DB.

5) « On va régler ça en changeant l’ordonnanceur I/O »

Symptôme : pics de latence aléatoires sous charge mixte ; l’équipe débat des ordonnanceurs.
Cause racine : le choix d’ordonnanceur peut aider la latence tail, mais ne réparera pas un disque défaillant, une tempête de resync, ou une VM saturant le périphérique.
Correctif : identifiez d’abord le périphérique et le coupable ; testez ensuite les changements d’ordonnanceur avec des workloads réalistes et un plan de rollback.

6) « On peut juste mettre la VM en pause et tout reviendra »

Symptôme : mettre en pause/tuer le plus gros écrivain ne restaure pas immédiatement la réactivité.
Cause racine : l’arriéré est encore en train de se vider ; commits de journal ; resync RAID ; writeback ; ou récupération d’erreurs système de fichiers.
Correctif : attendez que les files se vident en surveillant iostat ; vérifiez resync/scrub en cours ; confirmez les logs noyau pour les erreurs.

7) « Désactiver le sync, c’est bien, c’est plus rapide »

Symptôme : la performance s’améliore en désactivant le sync dans les réglages stockage/app.
Cause racine : vous avez échangé la durabilité contre la vitesse ; la consistance en cas de crash peut disparaître.
Correctif : utilisez du matériel approprié (PLP NVMe, SLOG), tunez la charge, ou acceptez un I/O plus lent. Ne changez pas la durabilité en production sans décision explicite.

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

Checklist A : exercice de 10 minutes « nommer le coupable »

  1. Exécutez vmstat 1 5 : confirmez wa élevé et des tâches bloquées b.
  2. Exécutez iostat -xz 1 3 : identifiez le périphérique chaud par await, aqu-sz et %util.
  3. Vérifiez /proc/pressure/io : confirmez l’impact système (« full » élevé).
  4. Exécutez pidstat -d 1 5 : identifiez les PIDs lecteurs/écrivains principaux.
  5. Si le PID principal est QEMU : mappez-le au nom VM via la ligne de commande ps et/ou les outils libvirt.
  6. Validez la cartographie stockage : df sur le chemin d’image, et/ou lsblk pour la pile (dm-crypt, LVM).
  7. Atténuer :
    • Arrêter ou limiter la charge.
    • Appliquer des limites I/O cgroup pour le scope.
    • Mettre la VM en pause seulement si nécessaire, et prévoir le temps de vidage de l’arriéré.
  8. Confirmer la récupération : iostat await et util normalisés ; PSI full en baisse.
  9. Capturer des preuves : échantillon iostat/pidstat, logs noyau, stats VM.

Checklist B : arbre de décision « matériel ou charge ? »

  1. Les logs noyau montrent timeouts/resets/erreurs I/O ? Traitez d’abord comme problème matériel/chemin.
  2. Périphérique util ~100% avec file haute ? Probablement saturation ; trouvez le plus gros écrivain et limitez/isolez.
  3. Util faible mais await élevé ? Cherchez des stalls : throttling thermique, firmware, événements RAID, problèmes de stockage réseau.
  4. Les flushes sont énormes ? Cherchez des apps sync-heavy, barrières, overhead dm-crypt, comportement sync ZFS.
  5. Une seule VM impactée ? Problème spécifique invité ; vérifiez quand même si elle affame les autres via la file partagée.
  6. Tout est impacté y compris services non liés ? Goulot d’étranglement partagé au niveau hôte ; implémentez des garde-fous et isolez les workloads critiques.

Checklist C : prévention permanente (à implémenter après l’incident)

  • Ajouter du monitoring pour : await par périphérique, profondeur de file, PSI I/O « full », et température NVMe.
  • Définir des valeurs I/O cgroup v2 sensées pour VMs et jobs batch.
  • Séparer le stockage pour workloads « sensibles à la latence » vs « écritures massives ».
  • Planifier rebuild/scrub RAID et scrub ZFS hors heures de pointe ; limiter si nécessaire.
  • Documenter quelles charges sont autorisées à faire de l’I/O intensif et quand.

FAQ

1) 100% iowait est-ce toujours un problème de disque ?

Non. C’est un problème de complétion I/O. Le disque peut être local SSD, RAID, pile dm-crypt, stockage réseau, ou même un contrôleur qui stalle de façon intermittente. Identifiez toujours le périphérique et vérifiez la latence.

2) Pourquoi l’hôte « gèle » alors que le CPU est majoritairement idle ?

Parce que les threads dont vous avez besoin sont bloqués en sleep non interruptible en attendant l’I/O. Le CPU étant idle n’aide pas quand le noyau attend la couche bloc pour compléter des requêtes.

3) Quel est le moyen le plus rapide pour trouver le PID coupable ?

pidstat -d 1 est l’outil le plus rapide et fiable pour les taux I/O par PID. iotop est utile, mais peut manquer le writeback et parfois mentir par omission.

4) Comment trouver quelle VM cause le problème sur un hôte KVM/libvirt ?

Utilisez pidstat pour identifier le PID QEMU, inspectez ensuite la ligne de commande QEMU pour -name guest=..., et confirmez avec virsh domblkstat pour voir quel disque écrit.

5) iostat montre un await élevé mais un %util faible. Comment est-ce possible ?

Parce que le périphérique peut staller ou compléter l’I/O par à-coups, ou le goulot est en amont (stockage réseau, resets contrôleur, récupération d’erreurs). Faible util avec haute latence est un signal d’alarme pour des stalls intermittents, pas « trop de charge ».

6) Dois-je changer l’ordonnanceur I/O pour réparer les gels iowait ?

Pas en premier lieu. Les ajustements d’ordonnanceur peuvent aider la latence tail sous workloads mixtes, mais n’auront pas raison d’un matériel défaillant, d’une tempête de resync RAID, ou d’une VM saturant le périphérique. Diagnosez d’abord, testez ensuite avec rollback.

7) Quelle est l’atténuation immédiate la plus sûre quand une VM est bruyante ?

Limitez-la via des limites I/O cgroup ou le tuning I/O libvirt pour que l’hôte reste réactif. Tuer la VM fonctionne aussi, mais c’est une action à risque si vous ne savez pas ce qu’elle faisait (bases de données, écritures en vol).

8) La RAM cache peut-elle masquer les problèmes I/O ?

Oui. Les écritures tamponnées peuvent donner l’impression que tout va bien jusqu’au déclenchement du writeback et à l’incapacité du disque à suivre. Ensuite survient une tempête de flush et tout l’hôte en pâtit.

9) Pourquoi les flushes/fsync provoquent-ils des ralentissements aussi dramatiques ?

Ils forcent l’ordre et les sémantiques de durabilité. Si la pile en dessous (RAID, dm-crypt, firmware SSD) rend un flush coûteux, la charge devient rapidement limitée par la latence, même à des débits modérés.

10) Comment savoir si je dois remplacer le disque ?

Si les logs noyau montrent timeouts/resets, ou si les logs SMART NVMe indiquent des erreurs média et des entrées d’erreur en croissance, traitez cela comme un problème de fiabilité. Les incidents de performance deviennent souvent des incidents de perte de données si on les ignore.

Conclusion : prochaines étapes pratiques

Quand Debian 13 atteint 100% iowait et que l’hôte « gèle », ne le traitez pas comme un mystère insoluble. Traitez-le comme un problème d’identification avec un chronomètre. Nommez le périphérique. Nommez la source. Atténuez. Capturez des preuves. Puis corrigez la structure qui a permis à une charge de monopoliser l’I/O partagée.

Faites ceci ensuite :

  1. Ajoutez des alertes sur await par périphérique et PSI I/O « full », pas seulement sur le débit.
  2. Implémentez des garde-fous I/O pour VMs et jobs batch (cgroup v2) pour qu’un invité ne puisse pas bloquer un hôte.
  3. Séparez les charges d’écriture massive des services sensibles à la latence au niveau stockage.
  4. Auditez régulièrement les logs noyau et les compteurs santé NVMe ; remplacez les disques suspects avant qu’ils ne vous apprennent l’humilité.

Le meilleur incident iowait est celui où rien d’excitant ne se produit parce que vous avez limité le voisin bruyant des mois auparavant. Visez l’ennui.

← Précédent
VPN plus lent que prévu : diagnostiquer CPU du routeur, chiffrement et clamp MSS méthodiquement
Suivant →
Confusion autour de AllowedIPs de WireGuard : pourquoi le trafic n’atteint pas la destination attendue (et comment le corriger)

Laisser un commentaire