Torna al blog
31 agosto 2026Sergei Solod11 min di lettura

Il mio VPS era online. Poi Linux ha iniziato ad aspettare 18 secondi per una lettura dal disco

Un caso reale di debugging Linux in produzione: il VPS restava online mentre il lavoro utile era quasi fermo. La pressione I/O sfiorava il 100%, le letture arrivavano a 18,7 secondi e Nginx restava bloccato in EXT4, mentre il backend rispondeva ancora in circa 3 ms.

LinuxDevOpsVPSPrestazioniDebugging

Prima di entrare nel debugging voglio chiarire una cosa: la mia impressione complessiva di FDCServers non è negativa. Questo non è un articolo che invita a evitare il provider e non è un giudizio sulla qualità dell'intera infrastruttura. Avevo un solo VPS, in un periodo preciso, con un problema insolito e particolarmente difficile.

Per parecchio tempo il VPS ha funzionato normalmente e ha gestito traffico reale di produzione. Poi qualcosa è cambiato. Le richieste ordinarie hanno iniziato a richiedere tempi assurdi. Le pagine si aprivano lentamente e a volte smettevano del tutto di aprirsi. In altri momenti la macchina tornava ad apparire normale prima che riuscissi a ispezionare bene il guasto.

Nel momento peggiore ho registrato:

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 era in esecuzione. Il backend era in esecuzione. La VM era online. Eppure quasi nessun lavoro utile veniva completato.

Il problema in produzione era frustrante, ma il debugging in sé è stato davvero interessante. È uno dei motivi per cui amo Linux: una macchina può sembrare viva dall'esterno mentre più interfacce indipendenti del kernel mostrano dove il progresso utile si è realmente fermato. Questo articolo riguarda quelle tracce, non il tentativo di stabilire se FDCServers sia un provider buono o cattivo.

active (running) ha finito per significare molto poco

Ho iniziato dai controlli ovvi:

top
free -h
df -h
systemctl status nginx

Nei periodi sani vedevo più o meno:

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

Se mi fossi collegato solo in quei momenti, avrei facilmente concluso che il VPS era sano. Poi tornava il traffico normale e lo stato poteva cambiare completamente. Uno snapshot sano di un problema intermittente dice molto poco sullo stato durante il guasto. Dovevo raccogliere prove mentre il problema era realmente presente.

Il campione di iostat che ha cambiato l'indagine

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

Le letture completate richiedevano circa 18,7 secondi. Le operazioni di flush arrivavano a circa 53,6 secondi. La coda media superava 128, mentre il throughput di lettura utile era soltanto 88 KB/s.

Non sembrava storage semplicemente impegnato perché stava servendo in modo efficiente un workload pesante. Il percorso di blocco virtuale era praticamente saturo e concludeva pochissimo lavoro. Un utilizzo elevato da solo non indica un guasto, ma utilizzo elevato insieme a latenza enorme, coda lunga, task bloccati, throughput minimo e request fallite è un segnale molto diverso.

Ho smesso di trattare iowait come una diagnosi

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

È facile ridurre tutto alla frase “la CPU passa il 100% del tempo ad aspettare il disco”. Come intuizione può essere utile, ma l'accounting di Linux è più complesso e iowait non misura direttamente il disco. Per questo l'ho trattato come un sintomo e ho cercato prove indipendenti.

PSI mostrava che l'I/O stava fermando il lavoro utile

cat /proc/pressure/io

Durante un periodo grave:

some avg10=99.14
full avg10=95.55

Nelle riproduzioni successive, full si è avvicinato al 98%. Per la pressione I/O, some rappresenta il tempo in cui almeno una parte del lavoro non idle è bloccata sull'I/O, mentre full rappresenta il tempo in cui tutti i task non idle sono bloccati contemporaneamente sull'I/O. Vicino al 100% non significa semplicemente che il disco è occupato: il workload riceve appena la possibilità di avanzare.

D-state mi ha fatto smettere di accusare un solo processo

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

Vedere brevemente un processo in D-state non dimostra un problema storage. Importava capire quali processi fossero bloccati insieme. Ho osservato componenti non correlati come jbd2, systemd-journald, worker Nginx, processi cache di Nginx e altre attività del filesystem.

Se si blocca solo il backend, indago sul backend. Se si blocca solo Nginx, indago su Nginx. Ma quando Nginx, il journal di sistema e il journal thread EXT4 smettono di avanzare insieme, la dipendenza che condividono diventa molto più interessante. In questo caso era il filesystem e il percorso storage sottostante.

I kernel stack hanno mostrato il livello successivo

Il thread del journal EXT4 appariva in percorsi come:

wait_on_buffer
jbd2_log_wait_commit
jbd2_journal_commit_transaction

I worker Nginx apparivano in normali percorsi di lettura del filesystem:

folio_wait_bit_common
filemap_read
generic_file_read_iter
ext4_file_read_iter
vfs_read
pread64

A un certo punto il kernel ha riportato:

INFO: task nginx blocked for more than 122 seconds.

systemctl status nginx poteva continuare a dire active (running). Entrambe le informazioni erano vere: il processo esisteva, ma un task Nginx era rimasto per oltre due minuti senza riuscire a completare lavoro utile. Un processo in esecuzione e un servizio sano non sono la stessa cosa.

Il test più pulito ha richiesto circa tre millisecondi

Una richiesta tramite Nginx falliva:

HTTP=000
SSL connection timeout

Poi ho bypassato Nginx e contattato direttamente il backend locale:

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

Circa 3 ms. Lo status HTTP esatto non era importante per questo test. Il backend accettava la connessione, eseguiva la richiesta e restituiva una risposta quasi immediatamente. Nello stesso momento i worker Nginx erano visibili nei percorsi di lettura EXT4.

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

Più approfondivo, meno risultava credibile una spiegazione a livello applicativo.

Lo stesso VPS poteva crollare in circa 30 secondi

Prima di una riproduzione:

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

Circa mezzo minuto dopo:

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

Poi peggiorava ulteriormente:

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

Rimuovendo il workload poteva verificarsi rapidamente il passaggio opposto:

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

Ecco perché i guasti intermittenti dell'infrastruttura sono così difficili. Dieci minuti dopo qualcuno può misurare sinceramente una latenza sotto il millisecondo sulla stessa VM. Non è necessariamente in errore: sta semplicemente osservando uno stato diverso.

Una latenza pari a zero può essere sorprendentemente poco utile

Ho visto anche un intervallo di iostat con r_await = 0 mentre il sistema era chiaramente malsano: iowait elevato, processi in D-state, I/O ancora in corso, quasi nessuna lettura completata e throughput quasi nullo.

Le medie basate sulle operazioni completate diventano meno informative quando quasi nulla termina durante la finestra di campionamento. Uno zero non prova necessariamente che le letture siano terminate all'istante; potrebbero esserci semplicemente troppo poche operazioni completate per descrivere quelle ancora bloccate.

Da allora guardo insieme latenza, IOPS, throughput, profondità della coda, I/O in flight, D-state, PSI e completamento reale delle request.

Un incident è diventato un'indagine di supporto molto più lunga

Il primo evento grave coincideva con un backup programmato sull'infrastruttura originale e FDCServers ha confermato che il backup era in esecuzione. Era una spiegazione plausibile. Ma in seguito ho riprodotto lo stesso tipo di storage stall dopo la fine del backup e fuori dalla finestra originale.

Il problema è tornato per più giorni. Durante un incident il VPS è poi rimasto indisponibile per 4 ore, 41 minuti e 15 secondi secondo i miei service log. Non posso dimostrare che lo storage stall abbia causato quello stato della VM; servirebbero informazioni host-side che non avevo.

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

Dietro quel punto interrogativo possono esserci virtualizzazione, code dell'host, rete storage, storage distribuito, supporti fisici, scheduler e altri sistemi invisibili dal guest. Potevo vedere dove si manifestava il guasto, non la causa fisica finale.

FDCServers ha escalato il problema internamente e alla fine ha migrato il VPS su un altro nodo. Dopo la migrazione ho registrato un altro grave storage stall dal guest. Questo non dimostra che tutti i nodi FDCServers avessero un problema storage. Dimostra soltanto che, dal mio punto di vista, il problema del mio VPS non era stato eliminato.

Perché continuo a non considerarla una storia negativa su FDCServers

È facile trasformare un incident infrastrutturale in un giudizio su un intero provider. Non voglio farlo.

Distinguo tra un servizio il cui normale modello operativo è fondamentalmente incompatibile con il mio workload e un problema infrastrutturale intermittente, difficile da riprodurre e lungo da isolare. La mia esperienza con FDCServers sembrava il secondo caso.

Prima dell'incident il VPS aveva funzionato normalmente e gestito traffico reale di produzione. Il supporto ha investigato e provato a risolvere il problema. Alla fine avevo abbastanza elementi per decidere di non voler più dipendere da quel particolare VPS in produzione.

Ho chiesto la cancellazione e il rimborso. FDCServers mi ha rimborsato. Questo conta nella mia impressione complessiva.

Non ho testato nuovamente la loro infrastruttura attuale, quindi non posso dire come si comporti oggi un VPS FDCServers. Non ho nemmeno prove che ciò che è accaduto alla mia istanza fosse rappresentativo dell'intera piattaforma. L'infrastruttura cambia continuamente. Non trasformo un singolo incident difficile in un giudizio permanente sul provider. E qui non sto nemmeno raccomandando FDCServers: sto semplicemente raccontando ciò che è successo a me.

La parte che ho apprezzato di più è stata Linux stesso

Il downtime era frustrante, ma l'indagine è stata divertente. Mi è piaciuto davvero trovare il confine del problema.

Non avevo abbastanza visibilità per identificare la causa fisica finale. La risposta che cercavo era più semplice: in quale layer smette di completarsi il lavoro utile?

Il backend rispondeva in circa tre millisecondi. Nginx risultava attivo, mentre i kernel stack mostravano che stava aspettando dentro letture EXT4. vmstat mostrava processi bloccati e I/O wait estremo. PSI mostrava stall I/O che consumavano quasi tutto il workload. iostat mostrava latenza e queueing enormi. D-state mostrava processi non correlati in attesa insieme. Il kernel ha persino segnalato un task Nginx bloccato per più di 122 secondi.

Nessuna singola metrica ha risolto l'incident. Lo ha fatto la concordanza tra tutte. È uno dei motivi per cui amo Linux: si può partire da qualcosa di vago come “il mio sito a volte non si apre” e arrivare gradualmente a una descrizione precisa del layer in cui il lavoro utile smette di avanzare.

Il workflow di debugging che uso adesso

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

Quando possibile testo anche ogni parte del percorso della request in modo indipendente:

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

Non chiedo più soltanto perché il server sia lento. Chiedo: in quale layer il lavoro utile smette di completarsi? Questa domanda produce esperimenti molto migliori.

Un'ultima regola: raccogliere le prove prima del reboot

Un reboot può essere esattamente ciò che serve alla produzione, ma può anche cancellare lo stato diagnostico più prezioso che si riuscirà mai a vedere.

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

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

Quando disponibilità e impatto sul business lo permettono, prima raccolgo timestamp UTC, PSI, vmstat, iostat, D-state, wchan, messaggi del kernel, code socket e timing delle request. Poi ripristino la macchina.

Il server era in esecuzione. Il workload no.

Non ho mai saputo quale componente host-side abbia causato infine l'incident. Non posso affermare che abbia fallito uno specifico SSD, indicare uno specifico storage node o provare ciò che accadeva dietro il virtual block device.

Quello che potevo stabilire dall'interno di Linux era sufficiente:

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

Era sufficiente per separare la mia applicazione dal layer guasto e prendere una decisione operativa. FDCServers ha rimborsato il VPS, io sono andato avanti e non trasformo un difficile incident infrastrutturale in un verdetto permanente sul provider.

La lezione rimasta è più utile: un processo può essere running, un servizio active, una VM online e il ping può funzionare, mentre la macchina completa comunque quasi nessun lavoro utile.

Linux offre abbastanza prove per vedere la differenza. Basta fare le domande giuste mentre il guasto è ancora presente.