Retour au blog
31 août 2026Sergei Solod11 min de lecture

Mon VPS était en ligne. Puis Linux a commencé à attendre 18 secondes pour lire le disque

Un cas réel de diagnostic Linux en production : le VPS restait en ligne alors que le travail utile était presque à l'arrêt. La pression I/O approchait 100 %, certaines lectures duraient 18,7 secondes et Nginx se bloquait dans EXT4, tandis que le backend répondait toujours en environ 3 ms.

LinuxDevOpsVPSPerformanceDébogage

Avant d'entrer dans le diagnostic, je veux clarifier un point : mon impression générale de FDCServers n'est pas négative. Cet article n'est pas une recommandation d'éviter le fournisseur et ne prétend pas juger l'ensemble de son infrastructure. J'avais un seul VPS, pendant une période précise, avec un problème particulièrement difficile.

Pendant longtemps, ce VPS a fonctionné normalement et servi du trafic réel de production. Puis quelque chose a changé. Des requêtes ordinaires ont commencé à prendre un temps absurde. Les pages s'ouvraient lentement, puis parfois plus du tout. À d'autres moments, la machine redevenait parfaitement normale avant que je puisse examiner correctement la panne.

Au pire, j'ai enregistré :

CPU iowait:       97–100%
I/O PSI full:     ~95–98%
read latency:     up to 18.7 seconds
flush latency:    up to 53.6 seconds
I/O queue depth:  128+

Nginx tournait. Le backend tournait. La VM était en ligne. Pourtant, presque aucun travail utile n'avançait.

La panne en production était frustrante, mais le diagnostic lui-même m'a réellement plu. C'est précisément ce que j'aime avec Linux : un système peut sembler vivant de l'extérieur alors que plusieurs interfaces indépendantes du noyau permettent de voir où la progression utile s'est arrêtée. Cet article parle de ces indices, pas de décider si FDCServers est un bon ou un mauvais fournisseur.

active (running) a fini par ne presque plus rien signifier

J'ai commencé par les vérifications habituelles :

top
free -h
df -h
systemctl status nginx

Dans une période saine, j'observais environ :

D-state processes: 0
CPU iowait:        ~0%
disk latency:      ~2–4 ms
I/O PSI some:      0.04
I/O PSI full:      0.04

Si je m'étais connecté uniquement à ce moment-là, j'aurais facilement conclu que tout allait bien. Puis le trafic normal revenait et l'état pouvait changer complètement. Une capture saine d'un problème intermittent apprend très peu sur son état défaillant. Il fallait mesurer pendant la panne.

L'échantillon iostat qui a changé l'enquête

r_await = 18744 ms
f_await = 53561 ms
aqu-sz  = 128.54
util    ≈ 100%
read    = 88 KB/s

Les lectures terminées prenaient environ 18,7 secondes. Les flush atteignaient 53,6 secondes. La file moyenne dépassait 128, alors que le débit de lecture utile n'était que de 88 KB/s.

Ce n'était pas simplement un stockage occupé parce qu'il servait efficacement un workload exigeant. Le chemin de bloc virtuel était pratiquement saturé tout en accomplissant très peu de travail. Une utilisation élevée seule n'est pas une panne ; associée à une latence énorme, de longues files, des tâches bloquées, un débit minuscule et des requêtes en échec, elle raconte une toute autre histoire.

J'ai cessé de considérer iowait comme un diagnostic

blocked processes: 4–9
CPU iowait:        97–100%
CPU idle:          0%

Il serait facile de résumer cela en disant que le CPU passe 100 % de son temps à attendre le disque. L'intuition aide, mais la comptabilité Linux est plus subtile et iowait ne mesure pas directement le disque. Je l'ai donc traité comme un symptôme parmi d'autres et cherché des signaux indépendants.

PSI montrait que l'I/O empêchait réellement le travail d'avancer

cat /proc/pressure/io

Pendant une période sévère :

some avg10=99.14
full avg10=95.55

Dans d'autres reproductions, full s'est approché de 98 %. Pour la pression I/O, some représente le temps où au moins une partie du travail non idle est bloquée sur l'I/O, tandis que full représente le temps où toutes les tâches non idle sont bloquées simultanément. Près de 100 %, le problème n'est plus simplement un disque occupé : le workload obtient à peine l'occasion de progresser.

D-state m'a fait arrêter d'accuser un seul processus

ps -eo state,pid,ppid,etime,wchan:50,comm,args | awk 'NR==1 || $1 ~ /^D/'

Voir brièvement un processus en D-state ne prouve pas une panne de stockage. L'important était de savoir quels processus attendaient ensemble. J'ai vu jbd2, systemd-journald, des workers Nginx, des processus de cache Nginx et d'autres activités liées au système de fichiers.

Si seul mon backend bloque, j'examine le backend. Si seul Nginx bloque, j'examine Nginx. Mais si Nginx, le journal système et le thread de journal EXT4 cessent d'avancer en même temps, leur dépendance commune devient beaucoup plus intéressante. Ici, c'était le système de fichiers et le chemin de stockage situé en dessous.

Les stacks du noyau ont montré la couche suivante

Le thread de journal EXT4 apparaissait dans des chemins comme :

wait_on_buffer
jbd2_log_wait_commit
jbd2_journal_commit_transaction

Les workers Nginx se trouvaient dans des chemins classiques de lecture :

folio_wait_bit_common
filemap_read
generic_file_read_iter
ext4_file_read_iter
vfs_read
pread64

À un moment, le noyau a signalé :

INFO: task nginx blocked for more than 122 seconds.

systemctl status nginx pouvait toujours afficher active (running). Les deux informations étaient vraies : le processus existait, mais une tâche Nginx avait passé plus de deux minutes sans pouvoir terminer son travail utile. Un processus en cours d'exécution n'est pas synonyme de service sain.

Mon test le plus propre a pris environ trois millisecondes

Une requête via Nginx échouait :

HTTP=000
SSL connection timeout

J'ai ensuite contourné Nginx et contacté directement le backend local :

connect = 0.000423 s
TTFB    = 0.003063 s
total   = 0.003139 s

Environ 3 ms. Le code HTTP précis n'avait pas d'importance ici. Le backend acceptait la connexion, traitait la requête et répondait presque immédiatement. Au même moment, les workers Nginx étaient visibles dans des chemins de lecture EXT4.

application execution       → progressing normally
filesystem-backed web path  → not progressing normally

Plus j'avançais, moins une cause applicative était crédible.

Le même VPS pouvait basculer en une trentaine de secondes

Avant une reproduction :

HTTP:          200
D-state:       0
CPU iowait:    3%
r_await:       ~1.18 ms
I/O PSI full:  ~2.95%

Environ trente secondes plus tard :

D-state:       4
CPU iowait:    91%
CPU idle:      0%
I/O PSI some:  86.11%
I/O PSI full:  78.02%
r_await:       236.50 ms
HTTP:          000

Puis :

CPU iowait:    96–100%
I/O PSI full:  ~98%
HTTPS queue:   512
HTTP:          000

Après retrait de la charge :

D-state:      0
CPU iowait:   6%
r_await:      ~0.98 ms
queue depth:  ~0.07
HTTP:         200

C'est ce qui rend les incidents intermittents si difficiles. Dix minutes plus tard, quelqu'un peut mesurer honnêtement une latence disque inférieure à la milliseconde sur la même VM. Il n'a pas forcément tort : il observe simplement un autre état.

Une latence nulle peut être étonnamment peu informative

J'ai aussi observé un intervalle iostat avec r_await = 0 alors que le système était clairement en mauvais état : iowait élevé, tâches en D-state, I/O en cours, presque aucune lecture terminée et presque aucun débit.

Les moyennes calculées à partir des opérations terminées deviennent moins utiles lorsque presque rien ne se termine pendant la fenêtre d'observation. Zéro ne signifie donc pas forcément que les lectures ont été instantanées ; il peut simplement ne pas y avoir assez d'opérations terminées pour décrire celles qui restent bloquées.

Depuis, je regarde ensemble la latence, les IOPS, le débit, la profondeur de file, les I/O en vol, le D-state, PSI et la fin réelle des requêtes.

Un incident est devenu une longue enquête avec le support

Le premier événement sévère coïncidait avec une sauvegarde planifiée sur l'infrastructure d'origine et FDCServers a confirmé qu'elle était en cours. C'était une explication plausible au départ. Mais j'ai ensuite reproduit le même type de stall après la sauvegarde et en dehors de sa fenêtre initiale.

Le problème est revenu plusieurs jours. Lors d'un incident, le VPS a ensuite été indisponible pendant 4 heures, 41 minutes et 15 secondes d'après mes logs. Je ne peux pas prouver que le stall de stockage a provoqué cet état de la VM ; cette conclusion aurait nécessité des informations côté hôte auxquelles je n'avais pas accès.

application
    ↓
Linux VFS
    ↓
EXT4
    ↓
virtual block device
    ↓
?

Derrière ce point d'interrogation peuvent se trouver la virtualisation, les files côté hôte, le réseau de stockage, le stockage distribué, les supports physiques, les ordonnanceurs et d'autres couches invisibles depuis la VM. Je voyais où le problème se manifestait, pas sa cause physique exacte.

FDCServers a escaladé le dossier et a finalement migré le VPS vers un autre nœud. Après la migration, j'ai capturé un autre stall sévère depuis le guest. Cela ne prouve pas que tous les nœuds FDCServers avaient un problème de stockage ; seulement que, de mon point de vue, le problème de mon VPS n'avait pas été éliminé.

Pourquoi je ne considère toujours pas cela comme une histoire négative sur FDCServers

Il est facile de transformer un incident d'infrastructure en verdict sur un fournisseur entier. Ce n'est pas ce que je veux faire.

Je distingue un service dont le fonctionnement normal serait fondamentalement incompatible avec mon workload d'un problème d'infrastructure intermittent, difficile à reproduire et long à isoler. Mon expérience avec FDCServers ressemblait davantage au second cas.

Le VPS avait fonctionné normalement avant l'incident et servi du trafic réel de production. Le support a enquêté et essayé de résoudre le problème. Finalement, j'avais assez d'éléments pour décider que je ne voulais plus dépendre de ce VPS précis en production.

J'ai demandé l'annulation et un remboursement. FDCServers m'a remboursé. Cela fait partie de mon impression globale.

Je n'ai pas retesté leur infrastructure actuelle, je ne peux donc pas dire comment un VPS FDCServers fonctionne aujourd'hui. Je n'ai pas non plus de preuve que mon cas était représentatif de leur plateforme. L'infrastructure change constamment. Je ne transforme pas un incident difficile sur un VPS en jugement permanent sur le fournisseur. Je ne recommande pas non plus FDCServers ici ; je raconte simplement mon expérience.

La partie que j'ai préférée, c'était Linux lui-même

La panne était frustrante, mais l'enquête était réellement intéressante. J'ai pris plaisir à trouver la frontière du problème.

Je n'avais pas assez de visibilité pour identifier la cause physique finale. La question qui m'importait était plus simple : à quelle couche le travail utile cesse-t-il de se terminer ?

Le backend répondait en environ trois millisecondes. Nginx était actif, mais les stacks du noyau le montraient bloqué dans des lectures EXT4. vmstat montrait des processus bloqués et un I/O wait extrême. PSI montrait que les stalls I/O consommaient presque tout le workload. iostat montrait une latence énorme et du queueing. Le D-state montrait plusieurs processus sans rapport attendant en même temps. Le noyau a même signalé une tâche Nginx bloquée pendant plus de 122 secondes.

Aucune métrique n'a résolu le problème seule. C'est leur concordance qui l'a fait. C'est aussi l'une des raisons pour lesquelles j'aime Linux : on peut partir de quelque chose de vague comme « mon site ne s'ouvre parfois pas » et arriver progressivement à une description précise de la couche où le travail utile cesse de progresser.

Le workflow de diagnostic que j'utilise maintenant

top
free -h
df -h

date -u
uptime
cat /proc/pressure/io
cat /proc/pressure/memory
cat /proc/pressure/cpu
vmstat 1 10
iostat -x 1 10
ps -eo state,pid,ppid,etime,wchan:50,comm,args | awk 'NR==1 || $1 ~ /^D/'
ss -lntp
journalctl -k --since "30 min ago" --no-pager

Quand c'est possible, je teste aussi chaque partie du chemin de requête séparément :

public request
      ↓
reverse proxy
      ↓
direct backend
      ↓
filesystem
      ↓
block device

Je ne demande plus seulement pourquoi le serveur est lent. Je demande : à quelle couche le travail utile cesse-t-il d'aboutir ? Cette question produit de bien meilleurs tests.

Dernière règle : recueillir les preuves avant de redémarrer

Un reboot peut être exactement ce dont la production a besoin, mais il peut aussi effacer l'état le plus précieux pour le diagnostic.

Before reboot:
D-state:     high
I/O PSI:     ~97%
iowait:      ~100%
queues:      large
requests:    failing

After reboot:
D-state:     0
latency:     milliseconds
requests:    healthy

Quand la disponibilité et l'impact métier le permettent, je capture d'abord l'heure UTC, PSI, vmstat, iostat, D-state, wchan, les messages du noyau, les files de sockets et les timings de requêtes, puis je restaure le service.

Le serveur tournait. Le workload, non.

Je n'ai jamais su quel composant côté hôte avait finalement provoqué l'incident. Je ne peux pas affirmer qu'un SSD précis était défaillant, désigner un nœud de stockage ou prouver ce qui se passait derrière le périphérique de bloc virtuel.

Ce que j'ai pu établir depuis Linux suffisait :

read latency:       up to 18.7 s
flush latency:      up to 53.6 s
I/O PSI full:       almost 100%
iowait:             almost 100%
I/O queue:          128+
Nginx:              blocked in filesystem reads
EXT4/jbd2:          blocked waiting for I/O
direct backend:     ~3 ms
HTTP through Nginx: timing out

C'était suffisant pour séparer mon application de la couche en panne et prendre une décision opérationnelle. FDCServers m'a remboursé, j'ai continué ailleurs et je ne transforme pas un incident difficile en jugement permanent sur le fournisseur.

La leçon qui m'est restée est plus utile : un processus peut être running, un service active, une VM online et le ping peut fonctionner, alors que la machine n'accomplit presque aucun travail utile.

Linux donne assez d'indices pour voir la différence. Il faut simplement poser les bonnes questions tant que la panne est encore visible.