Volver al blog
31 de agosto de 2026Sergei Solod11 min de lectura

Mi VPS estaba online. Entonces Linux empezó a esperar 18 segundos por una lectura de disco

Un caso real de depuración en Linux en producción: el VPS seguía online mientras el trabajo útil casi se detenía. La presión de I/O rozó el 100 %, las lecturas llegaron a 18,7 segundos y Nginx quedó bloqueado en EXT4, aunque el backend seguía respondiendo en unos 3 ms.

LinuxDevOpsVPSRendimientoDepuración

Antes de entrar en la parte técnica quiero dejar algo claro: mi impresión general de FDCServers no es negativa. Este artículo no es una reseña para decirle a nadie que evite al proveedor ni un juicio sobre toda su infraestructura. Tuve un VPS, durante un periodo concreto, con un problema especialmente difícil.

Durante bastante tiempo el VPS hizo exactamente lo que necesitaba y atendió tráfico real de producción con normalidad. Después cambió algo. Las peticiones ordinarias empezaron a tardar una cantidad absurda de tiempo. Las páginas se abrían lentamente y, en ocasiones, dejaban de abrirse por completo. En otros momentos, la máquina volvía a parecer normal antes de que pudiera inspeccionar bien el fallo.

En el peor momento registré:

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 seguía ejecutándose. El backend seguía ejecutándose. La VM estaba online. Y, aun así, casi no se completaba trabajo útil.

El fallo en producción fue frustrante, pero la investigación me resultó genuinamente interesante. Esta es una de las cosas que más me gustan de Linux: un sistema puede parecer vivo desde fuera mientras varias interfaces independientes del kernel muestran dónde ha dejado de avanzar el trabajo útil. Este artículo trata de seguir esas pistas, no de decidir si FDCServers es un buen o mal proveedor.

active (running) acabó significando muy poco

Empecé con las comprobaciones habituales:

top
free -h
df -h
systemctl status nginx

En momentos sanos veía aproximadamente:

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 hubiera entrado únicamente en ese momento, habría concluido que todo estaba bien. Después volvía el tráfico normal y el estado podía cambiar por completo. Una captura sana de un fallo intermitente apenas dice nada sobre su estado cuando está fallando. Necesitaba observarlo durante el incidente.

La muestra de iostat que cambió la investigación

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

Las lecturas completadas tardaban unos 18,7 segundos. Los flush llegaban a 53,6 segundos. La cola media superaba 128, mientras el throughput útil de lectura era solo de 88 KB/s.

No parecía un almacenamiento simplemente ocupado porque estuviera atendiendo eficientemente un workload exigente. El camino de bloque virtual estaba prácticamente saturado mientras producía muy poco trabajo. Una utilización alta por sí sola no implica un fallo; combinada con latencia extrema, una cola grande, tareas bloqueadas, throughput mínimo y peticiones fallidas es una señal muy distinta.

Dejé de tratar iowait como un diagnóstico

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

Es tentador resumirlo diciendo que la CPU pasaba el 100 % del tiempo esperando al disco. Como intuición ayuda, pero la contabilidad de Linux es más compleja y iowait no mide directamente el disco. Por eso lo traté como un síntoma y busqué señales independientes.

PSI mostró que el I/O estaba deteniendo el trabajo útil

cat /proc/pressure/io

Durante un periodo grave:

some avg10=99.14
full avg10=95.55

En reproducciones posteriores, full se acercó al 98 %. En presión de I/O, some representa tiempo en el que al menos parte del trabajo no inactivo está bloqueado por I/O, mientras que full representa tiempo en el que todas las tareas no inactivas están bloqueadas simultáneamente. Cerca del 100 % ya no hablamos simplemente de un disco ocupado: el workload apenas tiene oportunidades para avanzar.

D-state me hizo dejar de culpar a un solo proceso

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

Un proceso que entra brevemente en D-state no demuestra un problema de almacenamiento. Lo importante era qué procesos estaban bloqueados a la vez. Vi jbd2, systemd-journald, workers de Nginx, procesos de caché de Nginx y otras actividades del sistema de archivos.

Si solo se bloquea el backend, investigo el backend. Si solo se bloquea Nginx, investigo Nginx. Si Nginx, el journal del sistema y el journal de EXT4 dejan de avanzar juntos, me interesa mucho más la dependencia que comparten. En este caso era el sistema de archivos y el storage path situado debajo.

Los stacks del kernel mostraron la siguiente capa

El thread del journal de EXT4 aparecía en rutas como:

wait_on_buffer
jbd2_log_wait_commit
jbd2_journal_commit_transaction

Los workers de Nginx aparecían en rutas normales de lectura:

folio_wait_bit_common
filemap_read
generic_file_read_iter
ext4_file_read_iter
vfs_read
pread64

En un momento el kernel informó:

INFO: task nginx blocked for more than 122 seconds.

systemctl status nginx podía seguir mostrando active (running). Ambas cosas eran ciertas: el proceso existía, pero una tarea de Nginx llevaba más de dos minutos sin poder completar trabajo útil. Un proceso en ejecución y un servicio sano no son lo mismo.

Mi prueba más limpia tardó unos tres milisegundos

Una petición por Nginx falló:

HTTP=000
SSL connection timeout

Después omití Nginx y accedí directamente al backend local:

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

Aproximadamente 3 ms. El estado HTTP exacto no importaba en esta prueba. El backend aceptó la conexión, ejecutó la petición y respondió casi de inmediato. Al mismo tiempo, los workers de Nginx aparecían dentro de rutas de lectura EXT4.

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

Cuanto más profundizaba, menos convincente resultaba una explicación a nivel de aplicación.

El mismo VPS podía colapsar en unos 30 segundos

Antes de una reproducción:

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

Unos 30 segundos después:

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

Más tarde:

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

Al quitar la carga podía recuperarse rápidamente:

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

Por eso los fallos intermitentes de infraestructura son tan difíciles. Diez minutos después alguien puede medir honestamente menos de un milisegundo de latencia en el mismo VPS. No tiene por qué estar equivocado; está observando otro estado.

Una latencia igual a cero puede ser sorprendentemente poco útil

También vi un intervalo de iostat con r_await = 0 mientras el sistema estaba claramente mal: iowait alto, procesos en D-state, I/O pendiente, casi ninguna lectura completada y casi ningún throughput.

Los promedios basados en operaciones completadas pierden valor si prácticamente nada termina dentro de la ventana de muestreo. Un cero no demuestra necesariamente que las lecturas terminaran instantáneamente; puede haber simplemente muy pocas operaciones completadas para describir las que siguen atascadas.

Desde entonces miro juntos latencia, IOPS, throughput, profundidad de cola, I/O en vuelo, D-state, PSI y finalización real de peticiones.

Un incidente acabó convirtiéndose en una investigación de soporte mucho más larga

El primer episodio grave coincidió con un backup programado en la infraestructura original y FDCServers confirmó que estaba en ejecución. Era una explicación razonable inicialmente. Pero luego reproduje el mismo tipo de stall después de que el backup terminara y fuera de su ventana original.

El problema volvió durante varios días. En un incidente el VPS estuvo después indisponible durante 4 horas, 41 minutos y 15 segundos según mis logs. No puedo demostrar que el stall de almacenamiento causara ese estado de la VM; para ello necesitaría datos del host que no tenía.

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

Tras ese interrogante pueden existir virtualización, colas del host, red de almacenamiento, almacenamiento distribuido, medios físicos, schedulers y otras capas invisibles desde el guest. Podía ver dónde se manifestaba el fallo, no su causa física final.

FDCServers escaló el caso internamente y acabó migrando el VPS a otro nodo. Después de la migración volví a capturar un stall grave desde el guest. Eso no demuestra que todos los nodos de FDCServers tuvieran problemas de almacenamiento; solo que, desde mi perspectiva, el problema de mi VPS no había desaparecido.

Por qué sigo sin considerar esto una historia negativa sobre FDCServers

Es fácil convertir un incidente de infraestructura en una sentencia sobre todo un proveedor. No quiero hacer eso.

Para mí hay diferencia entre un servicio cuyo funcionamiento normal es fundamentalmente incompatible con mi workload y un problema intermitente, difícil de reproducir y largo de aislar. Mi experiencia con FDCServers se pareció mucho más al segundo caso.

El VPS había funcionado con normalidad antes del incidente y transportado tráfico real de producción. Soporte investigó y trató de resolver el problema. Finalmente tuve suficientes datos para decidir que no quería seguir dependiendo de ese VPS concreto para producción.

Pedí cancelar el servicio y un reembolso. FDCServers me devolvió el dinero. Eso forma parte de mi valoración global de la experiencia.

No he vuelto a probar su infraestructura actual, así que no puedo decir cómo funciona hoy. Tampoco tengo pruebas de que lo que ocurrió en mi instancia representara al resto de su plataforma. La infraestructura cambia constantemente. No convertiría un incidente difícil en un solo VPS en un juicio permanente sobre todo el proveedor. Tampoco estoy recomendando FDCServers. Solo cuento lo que me ocurrió.

La parte que más disfruté fue Linux

El downtime fue frustrante, pero la investigación fue divertida. Disfruté de verdad encontrando el límite del problema.

No tenía visibilidad suficiente para identificar la causa física final. La respuesta que buscaba era más sencilla: ¿en qué capa deja de completarse el trabajo útil?

El backend respondía en unos tres milisegundos. Nginx aparecía como activo, pero los stacks del kernel lo mostraban esperando dentro de lecturas EXT4. vmstat mostraba procesos bloqueados e I/O wait extremo. PSI mostraba que los stalls de I/O consumían casi todo el workload. iostat mostraba latencia y colas enormes. D-state mostraba procesos no relacionados esperando al mismo tiempo. El kernel llegó incluso a informar de una tarea de Nginx bloqueada durante más de 122 segundos.

Ninguna métrica resolvió el incidente por sí sola. Lo hizo la coincidencia entre todas. Esa es una de las razones por las que me gusta Linux: puedes empezar con algo vago como “mi web a veces no abre” y convertirlo poco a poco en una descripción precisa de la capa donde el trabajo útil deja de progresar.

El flujo de depuración que uso ahora

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

Siempre que puedo, pruebo cada parte del camino de la petición por separado:

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

Ya no pregunto solo por qué el servidor va lento. Pregunto: ¿en qué capa deja de completarse el trabajo útil? Esa pregunta produce experimentos mucho mejores.

Una última regla: recoge evidencias antes de reiniciar

Un reboot puede ser exactamente lo que producción necesita, pero también puede borrar el estado más valioso para el diagnóstico.

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

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

Cuando la disponibilidad y el impacto de negocio lo permiten, capturo primero hora UTC, PSI, vmstat, iostat, D-state, wchan, mensajes del kernel, colas de sockets y tiempos de petición. Después recupero la máquina.

El servidor estaba ejecutándose. El workload no.

Nunca supe qué componente del host causó finalmente el incidente. No puedo afirmar que fallara un SSD concreto, identificar un nodo de almacenamiento específico ni probar qué ocurrió detrás del dispositivo de bloques virtual.

Lo que pude establecer desde Linux fue suficiente:

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

Fue suficiente para separar la aplicación de la capa que fallaba y tomar una decisión operativa. FDCServers reembolsó el VPS, yo seguí adelante y no convierto un incidente difícil en un veredicto permanente sobre el proveedor.

Me quedó una idea más útil: un proceso puede estar running, un servicio active, una VM online y el ping puede funcionar, mientras la máquina casi no completa trabajo útil.

Linux da suficientes evidencias para distinguir ambas situaciones. Solo hay que hacer las preguntas correctas mientras el fallo sigue presente.