Debian 13: host con iowait al 100% se congela — encuentra el proceso/VM ruidoso en 10 minutos

¿Te fue útil?

Tu host Debian 13 se “cuelga”, las sesiones SSH se quedan colgadas, los gráficos de Prometheus parecen un monitor plano, y top muestra CPU al 2%… excepto que el iowait está clavado al 100%. Da la sensación de que la máquina está encendida pero emocionalmente no disponible.

Rara vez es un misterio. Normalmente es un proceso o VM ruidoso que hace algo “perfectamente razonable” en el peor momento posible, más almacenamiento que no puede seguir el ritmo. La tarea no es meditar sobre ello. La tarea es nombrar al culpable rápido, decidir si matarlo, limitarlo o arreglarlo, y recuperar tu host.

El modelo mental: qué significa realmente iowait al 100%

iowait no es “tu disco está ocupado al 100%”. Es “tus CPUs tienen trabajo listo para correr, pero ese trabajo está bloqueado esperando a la finalización de I/O”. Si ves iowait alto con uso de CPU bajo, el kernel te hace un favor al no consumir ciclos. Tus usuarios lo interpretan como “el servidor está muerto”, porque desde su perspectiva, lo está.

En Debian 13, el patrón habitual es:

  • Alguna carga empieza a emitir mucho I/O (las escrituras suelen ser el desencadenante clásico).
  • La profundidad de la cola sube en un dispositivo de bloque.
  • La latencia explota (milisegundos se convierten en segundos).
  • Los hilos se bloquean en estado D (sleep ininterrumpible), y el sistema se siente congelado.

Lo que quieres responder en menos de 10 minutos:

  1. ¿Qué dispositivo? (nvme0n1? md0? dm-crypt? montaje NFS?)
  2. ¿Qué tipo de presión? (latencia? saturación? reintentos? flush/sync?)
  3. ¿Cuál es la fuente? (PID? contenedor? VM/invitado?)
  4. ¿Qué acción? (matar, limitar, mover, arreglar hardware, cambiar configuración)

Verdad seca: “iowait 100%” es un síntoma, no un diagnóstico. El diagnóstico es una cola y una fuente.

Una cita que vale la pena tener en la pared, porque resume todo el trabajo: “La esperanza no es una estrategia.” — Gene Kranz.

Broma #1: iowait es la forma de Linux de decir: “Me encantaría ayudar, pero estoy esperando a que el almacenamiento termine su larga novela.”

Guía rápida de diagnóstico (10 minutos)

Minuto 0–1: confirma que es I/O y no otro tipo de miseria

  • Comprueba carga, iowait y tareas bloqueadas.
  • Si ves muchas tareas en estado D y la carga sube con uso de CPU bajo, estás en territorio de I/O.

Minuto 1–3: identifica el dispositivo que se está ahogando

  • Usa iostat o sar -d para encontrar alto await y alta utilización.
  • Valida con cat /proc/diskstats si las herramientas no están instaladas o están colgadas.

Minuto 3–6: identifica la fuente (PID, cgroup o VM)

  • Usa pidstat -d para encontrar qué PIDs están haciendo I/O.
  • Usa iotop para visibilidad en vivo si funciona (puede no ver writeback en memoria).
  • En hosts de virtualización, mapea hilos de QEMU a invitados específicos vía args del proceso libvirt/QEMU y estadísticas por disco.
  • Usa PSI (/proc/pressure/io) para confirmar la paralización de I/O a nivel de sistema.

Minuto 6–8: decide: matar, limitar o aislar

  • Si es un trabajo de backup, una reconstrucción de índices o una migración: limita su I/O.
  • Si es un proceso desbocado: deténlo ahora y encuentra la causa raíz después.
  • Si es una sola VM: aplica límites de I/O en el hipervisor o en la capa de almacenamiento.

Minuto 8–10: confirma la recuperación y captura evidencias

  • Observa que await y la utilización bajen.
  • Captura journalctl, logs del kernel y una muestra corta de iostat/pidstat para poder solucionarlo permanentemente.

Si no haces nada más, haz esto: encuentra el dispositivo, luego encuentra la fuente. Todo lo demás es decoración.

Tareas prácticas: comandos, salidas y decisiones (12+)

Estos son los movimientos que realmente uso cuando un host está “congelado” pero no muerto. Cada tarea incluye qué significa la salida y qué decisión tomar. Ejecútalas en orden hasta que tengas un nombre (PID o VM) y un dispositivo.

Tarea 1 — Ver iowait, carga y tareas bloqueadas de un vistazo

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

Significado: la columna b (procesos bloqueados) es alta, y wa (iowait) está ~98%. Esa es una firma clásica de “el almacenamiento es el cuello de botella”.

Decisión: no debatas. Pasa inmediatamente a latencia por dispositivo (iostat).

Tarea 2 — Encuentra el dispositivo caliente con latencia y utilización

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

Significado: nvme0n1 está en ~100% de utilización con un enorme w_await y una cola masiva (aqu-sz). Ese es tu dispositivo cuello de botella.

Decisión: identifica quién está escribiendo en nvme0n1. También considera si es un pico de latencia (firmware, estrangulamiento térmico) o saturación (carga legítima).

Tarea 3 — Confirma la paralización de I/O a nivel de sistema usando 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

Significado: “full” indica presión de I/O que hace que las tareas queden atascadas porque I/O no puede completarse. Esto no es “unas pocas consultas lentas”, es sistémico.

Decisión: puedes tomar mitigaciones agresivas (limitar/matar), porque todo el host está pagando la factura.

Tarea 4 — Encuentra los PIDs que más I/O generan rápido (y no confíes en tu intuición)

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

Significado: el mayor escritor es qemu-system-x86 PID 21492. Eso normalmente significa “una VM es la vecina ruidosa”, no que QEMU haya decidido la violencia por sí mismo.

Decisión: mapea este proceso QEMU a un nombre de invitado y disco. Luego limita el I/O de esa VM o arregla lo que hace el invitado.

Tarea 5 — Si no es obvio, busca acumulaciones en estado 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

Significado: los hilos en estado D están bloqueados en la capa de bloque (blk_mq_get_tag) o esperando en el planificador de I/O. Eso es consistente con saturación de cola en el dispositivo.

Decisión: no estás ante una fuga de CPU. Necesitas reducir la carga de I/O o aumentar la capacidad de I/O, e identificar la fuente rápidamente.

Tarea 6 — Mapea el PID de QEMU a un nombre 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

Significado: ahora tienes una identidad de VM: acct-db-03, y al menos una ruta de disco. Esto es oro cuando intentas explicar el impacto a humanos.

Decisión: confirma qué disco está más caliente y si el almacenamiento subyacente está en nvme0n1. Si es así, limita esa VM o pausa la carga dentro de ella.

Tarea 7 — Encuentra qué dispositivo de bloque respalda el archivo de disco 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

Significado: las imágenes de VM están en /dev/nvme0n1p2, que está en el NVMe caliente. El mapeo es consistente.

Decisión: la mitigación puede ocurrir en el host: limita I/O a esa VM (cgroup o ajuste libvirt), o en el invitado: detén/reduce el trabajo.

Tarea 8 — Revisa estadísticas de bloque por dominio (libvirt) para encontrar el disco más ruidoso

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

Significado: vdb es el escritor destacado con muchas flushes. Los flushes son un “amplificador de latencia” común porque fuerzan semánticas de orden y finalización.

Decisión: si el invitado hace escrituras sync-intensas (bases de datos, fsync frecuentes, journaling), considera limitar o moverlo a almacenamiento más rápido/menos contendido. También revisa si configuraciones del host (caché de escritura, barreras, dm-crypt) hacen que los flushes sean costosos.

Tarea 9 — Comprueba si el dispositivo te está rechazando con errores o reinicios

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)

Significado: timeouts y errores de I/O no son “carga”. Son un problema de dispositivo o transporte. Bajo condiciones de error, la latencia puede irse por las nubes mientras el rendimiento cae. Tu iowait seguirá pareciendo igual.

Decisión: deja de culpar a las cargas. Cambia a triage de hardware/firmware: comprueba SMART/log NVMe, cableado/backplane, logs del controlador. Considera sacar el dispositivo (RAID/ZFS) si es posible.

Tarea 10 — Salud rápida de NVMe y contadores de errores

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

Significado: la temperatura es alta y hay errores de medio. El estrangulamiento térmico y la recuperación interna de errores pueden causar picos enormes de latencia.

Decisión: trata esto como un incidente de rendimiento y de fiabilidad. Planea reemplazar el dispositivo si los errores continúan. Mejora la refrigeración si la temperatura es consistentemente alta.

Tarea 11 — Distinguir “dispositivo saturado” vs “dispositivo inactivo pero lento”

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

Significado: esto es saturación (util ~100%, cola grande). Si la util fuese baja pero await alta, sospecharías de paradas intermitentes, problemas de firmware, o capas superiores (dm-crypt, resync de RAID, NFS) en lugar de carga pura.

Decisión: si es saturación, reduce carga o añade capacidad (otro disco, más discos, mejor NVMe, separar cargas ruidosas). Si son paradas, encuentra la capa que las está provocando.

Tarea 12 — Encuentra qué archivos están siendo machacados (cuando sea necesario)

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

Significado: confirma qué archivos de imagen están en juego. No te dirá qué bloques son calientes, pero ancla la culpa a una ruta concreta.

Decisión: si necesitas reubicar: ahora sabes qué mover. Si necesitas snapshot: sabes qué snapshotear. Si necesitas explicar impacto: tienes un archivo y un nombre de VM.

Tarea 13 — Identifica writeback mayor y presión de páginas sucias

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

Significado: Dirty/Writeback elevados sugieren que el kernel está empujando escrituras en buffer al disco y puede estar atascado detrás de I/O lento. Si la memoria sucia se dispara y el disco no da abasto, todo se vuelve “lento de maneras raras”.

Decisión: considera si una carga está generando escrituras sostenidas que exceden el rendimiento del dispositivo, y si throttling (ionice, cgroup io.max) es la mitigación adecuada.

Tarea 14 — Revisa el dolor a nivel de sistema de ficheros (EXT4/XFS) y comportamiento del 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

Significado: advertencias del sistema de ficheros más errores de I/O son eventos “detener el mundo”. Incluso sin errores, hilos de journal (como jbd2) que aparecen en pidstat son una pista de que las escrituras fuerzan actualizaciones de metadata y commits.

Decisión: si existen errores, trátalo como fallo de almacenamiento. Si no, y tienes muchos flushes/commits, céntrate en el patrón de carga (fsync frecuentes, escrituras pequeñas aleatorias) y la configuración de almacenamiento (caché de escritura, barreras, modo de caché en virtualización).

Tarea 15 — Cuando necesites evidencia dura: muestrea I/O de bloque con 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]

Significado: puedes atribuir I/O en la capa de bloque a un PID (aquí, QEMU). Observa la diferencia temporal entre emisión y completado; esa es tu latencia en bruto.

Decisión: si necesitas convencer a alguien de que “la VM realmente lo hizo”, esto es evidencia de grado forense. También útil cuando múltiples PIDs están implicados y las herramientas de espacio de usuario difieren.

Tarea 16 — Mitigación rápida: limitar un cgroup ruidoso (systemd) con 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

Significado: acabas de aplicar límites de ancho de banda I/O en runtime al scope systemd de esa VM (el nombre varía; confirma el nombre exacto en tu host). Esto reduce la capacidad de la VM para dejar al host sin recursos.

Decisión: usa esto cuando necesites que el host sea estable más que una VM rápida. Luego programa una solución permanente: aislar cargas, ajustar parámetros de la BD o mover la VM a almacenamiento dedicado.

Broma #2: Limitar una VM ruidosa es como volver a colocar la rueda de un carro de compras. No lo hará elegante, pero al menos deja de chillar.

Hechos e historia que te ayudan a depurar más rápido

Conocer algunos hechos de “por qué es así” evita que persigas fantasmas. Aquí hay algunos que realmente importan cuando miras gráficos de iowait a las 03:00.

  1. iowait de Linux es un estado de contabilidad de CPU, no una métrica de disco. Puede ser alto incluso cuando el disco no está totalmente utilizado, especialmente con paradas intermitentes o colas profundas del kernel.
  2. El antiguo campo svctm de iostat se volvió poco fiable con dispositivos modernos y multi-queue. La gente todavía lo cita como evangelio. No lo hagas.
  3. blk-mq (capa de bloque multi-cola) cambió la intuición de “una cola por dispositivo”. NVMe y el almacenamiento moderno pueden tener muchas colas; el comportamiento de saturación se ve diferente que con SATA antiguo.
  4. CFQ murió, BFQ llegó, y mq-deadline se convirtió en una opción por defecto para muchos SSD. La elección del planificador puede cambiar la latencia final bajo cargas mixtas.
  5. PSI (Pressure Stall Information) es relativamente nuevo comparado con las herramientas clásicas. Te dice “qué tan paralizado está” el sistema, no solo “qué tan ocupado está”, lo que está más cerca del dolor del usuario.
  6. El writeback en caché puede ocultar el inicio de un incidente. Puedes almacenar escrituras en RAM hasta que ya no puedas, y entonces pagas la deuda con intereses: grandes tormentas de flush.
  7. La virtualización añade una pila extra de planificadores. El invitado cree que hace un fsync “razonable”; el host lo convierte en flush reales; el almacenamiento subyacente decide que es hora de garbage collection.
  8. El estrangulamiento térmico de NVMe es un modo real de fallo en producción. Escrituras secuenciales altas pueden empujar las unidades a throttling, causando aumentos dramáticos de latencia sin errores obvios al principio.
  9. Resync de RAID y scrubs pueden crear una denegación de servicio de I/O perfectamente legítima. No se necesita un bug. Solo sincronización de tiempos.

Si es una VM: mapear el I/O del host al invitado ruidoso

En un host de virtualización, el error más común es tratar a QEMU como el culpable. QEMU es el mensajero. Tu pregunta real: ¿qué carga del invitado está saturando qué dispositivo del host, y es un trabajo por lotes esperado o una trampa accidental?

Cómo se ve una “VM ruidosa” en el host

  • pidstat -d muestra uno o pocos PIDs qemu-system-* dominando las escrituras.
  • iostat muestra un dispositivo con una cola enorme (aqu-sz) y alto await.
  • La media de carga sube, pero la CPU está mayormente idle excepto iowait.
  • Otras VMs se quejan: “el disco está lento” o “la base de datos está atascada” aunque no estén haciendo mucho.

Cómo conectar hilos de QEMU a un dominio limpiamente

Si usas libvirt, puedes mapear por:

  • la línea de comando del proceso QEMU (-name guest=...)
  • virsh domblkstat por dispositivo de disco
  • unidades scope systemd bajo machine.slice (los nombres varían)

Una vez tengas el nombre del invitado, tienes opciones:

  • Mitigación dentro del invitado: pausa el backup, reduce concurrencia, ajusta checkpoints de la BD, detén una reconstrucción de índice, limita compactación.
  • Mitigación en el host: aplica límites de I/O usando cgroup v2 (io.max / propiedades systemd), o ajusta la afinación I/O de libvirt (si ya la tienes configurada).
  • Solución estructural: mueve esa VM a almacenamiento dedicado, separa disco OS de datos, o separa “trabajos por lotes” de VMs “sensibles a latencia”.

Cuando la VM no está haciendo “tanto”, pero aún así mata el host

Aquí es donde los flushes, escrituras sync y patrones intensivos en metadata importan. Una VM que hace un número moderado de transacciones con fsync puede crear una presión enorme si la pila subyacente convierte cada fsync en flushes costosos a través de dm-crypt, RAID y una unidad haciendo garbage collection interno.

Observa:

  • altas flush_operations en virsh domblkstat
  • cambios de configuración de base de datos que aumentan garantías de durabilidad
  • cambios en opciones de montaje del sistema de ficheros del invitado
  • cambios en el modo de caché del host (p.ej., cache=none vs writeback)

Triage de la capa de almacenamiento: NVMe, RAID, dm-crypt, ZFS, red

Después de identificar el proceso/VM ruidoso, aún necesitas decidir si la solución real es “dejar de hacer eso” o “la pila de almacenamiento está enferma”. La forma más rápida es reconocer modos de fallo comunes por su telemetría.

NVMe: rápido hasta que no lo es

Los incidentes NVMe a menudo caen en dos cubetas: saturación (carga legítima) y picos de latencia (hardware/firmware/térmico). La saturación es aburrida. Los picos de latencia son dramáticos.

Qué comprobar:

  • Temperatura y errores vía nvme smart-log
  • Logs del kernel por timeouts/reinicios
  • Util vs await: si util es baja pero await alto, sospecha paradas en lugar de limitación de throughput

mdadm RAID: el resync es un incidente programado si lo permites

El RAID por software puede comportarse bien durante meses, luego una reconstrucción o scrub empieza y de repente tu “host estable” se convierte en una lección sobre recursos compartidos. El I/O de reconstrucción compite con todo.

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>

Significado: resync está activo. Eso puede generar iowait incluso cuando la carga de aplicación no ha cambiado.

Decisión: limita la velocidad de reconstrucción si está perjudicando producción, o programa ventanas de resync. No “optimices” dejando que la reconstrucción se ejecute sin control en discos compartidos.

dm-crypt: el cifrado no es gratis y las semánticas de flush importan

dm-crypt añade coste de CPU y puede afectar la latencia bajo cargas intensivas en sync. Generalmente está bien en CPUs modernas, pero puede empeorar la latencia final en ciertos patrones.

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

Significado: tu sistema de ficheros caliente está encima de dm-crypt. Eso no es automáticamente malo. Es una pista de que cargas intensivas en flush pueden pagar sobrecoste adicional.

Decisión: si las tormentas de flush te matan, prueba con cargas realistas y considera separar cargas sync-intensas en dispositivos dedicados o revisar caché/ajustes de I/O. No desactives el cifrado en pánico a menos que disfrutes reuniones de cumplimiento.

ZFS: el golpe en la cara en forma de “txg sync”

ZFS puede dar excelente rendimiento y también enseñarte sobre amplificación de escritura. Un patrón común de iowait son paradas periódicas durante txg sync cuando el pool está sobrecargado o mal ajustado para la carga.

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

Significado: escrituras intensas; si la latencia es mala, revisa patrones de write sync y uso de SLOG, recordsize, compresión y si el pool está casi lleno.

Decisión: si una BD de VM hace escrituras sync, considera un dispositivo SLOG separado (con protección contra pérdida de energía) o ajusta el comportamiento de la aplicación. No “arregles” esto apagando sync a menos que aceptes pérdida de datos.

Almacenamiento en red (NFS/iSCSI): cuando el “disco” es en realidad un problema de red

NFS puede producir iowait que parece saturación de disco local. El host espera finalizaciones de I/O, pero esas dependencias dependen de latencia de red, carga del servidor o retransmisiones.

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

Significado: las retransmisiones no son cero y están subiendo; existen commits; un montaje “hard” significa que las tareas pueden colgar hasta que el servidor responda.

Decisión: comprueba errores de red, carga del servidor NFS y considera aislar VMs sensibles a latencia lejos de almacenamiento basado en red. Si tu host “se congela” por NFS, trátalo como una falla de dependencia, no como un problema de disco local.

Tres mini-historias corporativas desde el terreno

1) Incidente causado por una suposición equivocada: “iowait significa que el disco está al máximo”

Un equipo de plataforma tenía un clúster de virtualización basado en Debian que ocasionalmente entraba en “todo el mundo va lento”. El on-call vio iowait al 90–100%, miró un dashboard que mostraba un throughput moderado y asumió que el array de almacenamiento estaba bien porque “ni siquiera está empujando ancho de banda”. Así que se centraron en CPU y memoria. Ajustaron swappiness del kernel. Reiniciaron algunos servicios. Incluso culparon a un despliegue reciente porque eso es lo que la gente hace bajo estrés.

Mientras tanto, el clúster seguía congelándose a ráfagas: sesiones SSH bloqueadas, VMs con timeout, y luego recuperándose mágicamente. Parecía un problema externo y transitorio. El equipo abrió un ticket con el proveedor de almacenamiento. El proveedor pidió números de latencia. Nadie los tenía en la alerta.

La causa real fue la latencia tail por paradas intermitentes en un dispositivo NVMe debido a estrangulamiento térmico. La utilización y el throughput no eran el problema; lo era el tiempo de finalización. Durante las paradas, se acumularon colas, las tareas se bloquearon, iowait subió y luego la cola se drenó. Los gráficos de ancho de banda permanecían “bien” porque los MB/s medios no capturaban las pausas de segundos de duración.

Cuando miraron iostat -xz y los logs SMART de NVMe, quedó claro: temperaturas altas, logs de error en aumento, await en cientos de ms. La solución fue poco glamurosa: mejorar el flujo de aire en el chasis, actualizar firmware y reemplazar la unidad peor. También añadieron alertas sobre latencia y PSI, no solo throughput. La próxima vez que iowait subió, la alerta decía “IO full pressure is high; nvme0 await 280ms.” La discusión terminó antes de empezar.

2) Optimización que salió mal: “Hagamos backups más rápidos”

Un grupo de operaciones quería ventanas de backup más cortas para imágenes de VM. Pasaron de un método rsync más lento a uno nuevo que usaba múltiples streams en paralelo y tamaños de bloque agresivos. En papel fue excelente: backups más rápidos, menos tiempo en ventana, más “moderno”. Lo desplegaron en todos los hypervisors un viernes, porque por supuesto.

El primer fin de semana, el clúster entró en un estado raro: los backups terminaron rápido, pero durante la ventana, las VMs de cara al cliente se volvieron no responsivas. Servicios sensibles a la latencia reportaron timeouts. Los hypervisors mostraron iowait al tope. Algunas guests fueron marcadas “paused” por sus watchdogs porque los discos virtuales dejaron de responder a tiempo.

La “optimización” fue efectivamente una denegación de servicio distribuida contra su propio almacenamiento. Los streams paralelos saturaron la profundidad de cola del dispositivo subyacente y empujaron al array/controlador a un comportamiento peor para cargas mixtas. Los backups terminaron rápido porque el clúster sacrificó todo lo demás para hacerlos rápidos.

La solución fue tratar los backups como carga en background, no como una carrera. Implementaron throttling I/O para los jobs de backup y aplicaron un límite de concurrencia por host. Los backups volvieron a tardar más, pero el negocio dejó de perder fines de semana. También separaron “backups de imagen” de “VMs DB en caliente” en diferentes niveles de almacenamiento. La lección fue directa: acelerar un trabajo tomando latencia prestada de todo lo demás no es optimización, es redistribución del dolor.

3) La práctica aburrida pero correcta que salvó el día: guardarraíles I/O por host

Otra compañía ejecutaba una flota de virtualización Debian con una regla estricta: cada scope de VM tenía guardarraíles de I/O definidos. Nada dramático, solo valores por defecto sensatos en cgroup v2: un cap de ancho de banda y un cap de IOPS, afinados por clase de carga. Al principio hubo ojos rodando. Los desarrolladores querían “sin límites”. La dirección quería “máxima utilización”. Los SREs querían “sin sorpresas”. Los SREs ganaron, en silencio.

Una tarde, un equipo lanzó una migración de datos dentro de una VM. Empezó a hacer una transformación de escritura intensiva con fsync frecuentes. En la mayoría de entornos, aquí el host se vuelve un ladrillo y todos aprenden vocabulario nuevo. Aquí, la VM de migración se ralentizó porque llegó a su límite. Otras VMs permanecieron normales. El host se mantuvo responsable. Nadie abrió un pager.

La migración terminó más tarde de lo esperado, así que el desarrollador preguntó qué ocurrió. El SRE mostró las estadísticas del cgroup y explicó el throttling I/O. Explicaron que la alternativa fue que todo el hypervisor entrara en iowait hell y llevarse servicios no relacionados abajo.

No ocurrió nada heroico. Ese es el punto. La práctica aburrida fueron los guardarraíles, y convirtió un incidente potencial en un ingeniero ligeramente molesto y una migración más lenta. Este tipo de victoria solo la aprecias después de quemarte.

Errores comunes: síntomas → causa raíz → solución

Aquí es donde la mayoría de equipos pierde horas. Los patrones se repiten, y las soluciones también.

1) “iowait está al 100%, así que el disco está al máximo”

Síntoma: iowait alto, throughput moderado, sistema “se congela” a ráfagas.
Causa raíz: picos de latencia (estrangulamiento térmico, fallo de firmware, reinicios de path) en lugar de saturación sostenida de ancho de banda.
Solución: mira await, aqu-sz, PSI, logs del kernel; revisa temperatura/errores NVMe; actualiza firmware y refrigeración; reemplaza medios que fallen.

2) “iotop no muestra nada escribiendo, así que no puede ser I/O”

Síntoma: iowait alto, iostat muestra escrituras, iotop parece tranquilo.
Causa raíz: escrituras en buffer siendo vaciadas por hilos del kernel (writeback), o I/O atribuido a hilos diferentes de los que esperas (QEMU, jbd2, kswapd).
Solución: usa pidstat -d, revisa /proc/meminfo Dirty/Writeback, e inspecciona hilos del kernel en estado D.

3) “La media de carga está alta, así que es problema de CPU”

Síntoma: la carga sube, CPU idle alta, iowait alto.
Causa raíz: la carga incluye tareas bloqueadas en sleep ininterrumpible por I/O; no es una métrica solo de CPU.
Solución: correlaciona la carga con tareas bloqueadas (columna b de vmstat) y estado D (ps). Trátalo como I/O.

4) “Es la base de datos” (dicho sin evidencia)

Síntoma: consultas de la base de datos fallan durante el incidente; la gente culpa a la BD.
Causa raíz: la BD es víctima de saturación de cola de disco subyacente causada por otra carga (backup, migración, rotación de logs) o por una VM vecina compartida.
Solución: prueba la fuente con pidstat y estadísticas por VM; aisla almacenamiento de la BD; implementa límites I/O para jobs por lotes no relacionados con la BD.

5) “Lo arreglaremos cambiando el planificador I/O”

Síntoma: picos aleatorios de latencia bajo carga mixta; el equipo debate planificadores.
Causa raíz: la elección de planificador puede ayudar la latencia final, pero no arreglará un disco que falla, una tormenta de resync o una VM saturando el dispositivo.
Solución: primero identifica dispositivo y culpable; luego prueba cambios de planificador con cargas realistas y plan de reversión.

6) “Podemos pausar la VM y todo se recuperará”

Síntoma: pausar/matar al mayor escritor no restaura responsividad de inmediato.
Causa raíz: la cola aún drena; commits del journal; resync de RAID; writeback; o recuperación de errores del sistema de ficheros.
Solución: espera a que las colas se drenen mientras monitorizas iostat; revisa si hay resync/scrub en curso; confirma logs del kernel por errores.

7) “Desactivar sync está bien, es más rápido”

Síntoma: el rendimiento mejora al desactivar sync en ajustes de almacenamiento/aplicación.
Causa raíz: cambiaste durabilidad por velocidad; la consistencia ante caídas puede desaparecer.
Solución: usa hardware apropiado (NVMe con PLP, SLOG), ajusta la carga, o acepta I/O más lento. No introduzcas cambios de durabilidad en producción sin una decisión explícita.

Listas de verificación / plan paso a paso

Lista A: ejercicio de 10 minutos “nombrar al culpable”

  1. Ejecuta vmstat 1 5: confirma wa alto y muchas tareas bloqueadas b.
  2. Ejecuta iostat -xz 1 3: identifica el dispositivo caliente por await, aqu-sz y %util.
  3. Comprueba /proc/pressure/io: confirma impacto en el sistema (alto “full”).
  4. Ejecuta pidstat -d 1 5: identifica PIDs top en lectura/escritura.
  5. Si el PID top es QEMU: mapea al nombre de VM vía línea de comando ps y/o herramientas libvirt.
  6. Valida el mapeo de almacenamiento: df en la ruta de imagen, y/o lsblk para la pila (dm-crypt, LVM).
  7. Mitiga:
    • Detén o limita la carga.
    • Aplica límites I/O de cgroup para el scope.
    • Pausa la VM solo si es necesario, y prepárate para tiempo de drenaje de la cola.
  8. Confirma recuperación: iostat await y util se normalizan; PSI full baja.
  9. Captura evidencias: muestras iostat/pidstat, logs del kernel, estadísticas de VM.

Lista B: árbol de decisión “¿hardware o carga?”

  1. ¿Los logs del kernel muestran timeouts/reinicios/errores de I/O? Trátalo primero como problema de hardware/ruta.
  2. ¿Util del dispositivo ~100% con cola alta? Probable saturación; encuentra al escritor top y limita/aisla.
  3. ¿Util baja pero await alta? Busca paradas: estrangulamiento térmico, problemas de firmware, eventos RAID, fallos de almacenamiento en red.
  4. ¿Los flushes son enormes? Busca aplicaciones sync-intensas, barreras, overhead de dm-crypt, comportamiento sync de ZFS.
  5. ¿Solo una VM afectada? Problema específico del invitado; aun así verifica si starvea a otros vía cola compartida.
  6. ¿Todo afectado incluyendo servicios no relacionados? Cuello de botella a nivel host; implementa guardarraíles e aísla cargas críticas.

Lista C: prevención permanente (qué implementar tras el incidente)

  • Añade monitorización de: await por dispositivo, profundidad de cola, PSI I/O “full” y temperatura NVMe.
  • Establece defaults sensatos de cgroup v2 I/O para VMs y jobs por lotes.
  • Separa almacenamiento para cargas “críticas de latencia” vs “escritura masiva”.
  • Programa resync/scrub de RAID y ZFS fuera de horas punta; limita si es necesario.
  • Documenta qué cargas pueden hacer I/O intensivo y cuándo.

Preguntas frecuentes

1) ¿iowait al 100% siempre es un problema de disco?

No. Es un problema de finalización de I/O. El disco puede ser local SSD, RAID, pila dm-crypt, almacenamiento en red, o incluso un controlador que se queda intermitentemente. Siempre identifica el dispositivo y revisa la latencia.

2) ¿Por qué el host “se congela” aunque la CPU esté mayormente idle?

Porque los hilos que necesitas están bloqueados en sleep ininterrumpible esperando I/O. Tener CPU idle no ayuda cuando el kernel espera en la capa de bloque a que se completen las solicitudes.

3) ¿Cuál es la forma más rápida de encontrar el PID culpable?

pidstat -d 1 es la herramienta rápida y fiable para tasas de I/O por PID. iotop es útil, pero puede perder writeback y a veces omite información.

4) ¿Cómo encuentro qué VM lo está causando en un host KVM/libvirt?

Usa pidstat para identificar el PID de QEMU, luego inspecciona la línea de comando de QEMU por -name guest=..., y confirma con virsh domblkstat para ver qué disco escribe.

5) iostat muestra await alto pero %util bajo. ¿Cómo es posible?

Porque el dispositivo puede estar teniendo paradas o completando I/O a ráfagas, o el cuello de botella está upstream (almacenamiento en red, reinicios de controlador, recuperación de errores). Util baja con alta latencia es bandera roja de paradas intermitentes, no “demasiada carga”.

6) ¿Debería cambiar el planificador I/O para arreglar los cuelgues por iowait?

No como primera medida. Los ajustes de planificador pueden ayudar la latencia final bajo cargas mixtas, pero no arreglarán hardware que falla, tormentas de resync o una VM saturando el dispositivo. Diagnostica primero y luego prueba cambios con rollback.

7) ¿Cuál es la mitigación inmediata más segura cuando una VM es ruidosa?

Limítala vía límites I/O de cgroup o ajuste I/O de libvirt para que el host siga siendo responsivo. Matar la VM también funciona, pero es más arriesgado si no sabes qué estaba haciendo (bases de datos, escrituras en vuelo).

8) ¿Puede la caché en RAM ocultar problemas de I/O?

Sí. Las escrituras en buffer pueden hacer que todo parezca bien hasta que el writeback comienza y el disco no da abasto. Entonces llega una tormenta de flush y todo el host paga la factura.

9) ¿Por qué los flushes/fsync causan desaceleraciones tan dramáticas?

Porque fuerzan orden y semánticas de durabilidad. Si la pila debajo (RAID, dm-crypt, firmware SSD) hace que un flush sea caro, la carga se vuelve dependiente de la latencia rápidamente, incluso con throughput moderado.

10) ¿Cómo sé si debo reemplazar la unidad?

Si los logs del kernel muestran timeouts/reinicios, o los logs SMART de NVMe muestran errores de medio crecientes y entradas de log de errores, trátalo como un problema de fiabilidad. Incidentes de rendimiento a menudo se convierten en incidentes de pérdida de datos si se ignoran.

Conclusión: pasos prácticos siguientes

Cuando Debian 13 alcanza iowait al 100% y el host “se congela”, no lo trates como un misterio sin resolver. Trátalo como un problema de identificación con cronómetro. Nombra el dispositivo. Nombra la fuente. Mitiga. Captura evidencia. Luego arregla la estructura que permitió que una carga monopolizara el I/O compartido.

Haz lo siguiente:

  1. Añade alertas sobre await por dispositivo y PSI I/O “full”, no solo throughput.
  2. Implementa guardarraíles I/O para VMs y jobs por lotes (cgroup v2) para que una invitada no pueda bloquear un host.
  3. Separa cargas de escritura masiva de servicios sensibles a latencia en la capa de almacenamiento.
  4. Audita logs del kernel y contadores de salud NVMe regularmente; reemplaza unidades sospechosas antes de que te enseñen humildad.

El mejor incidente de iowait es aquel donde no pasa nada emocionante porque limitaste al vecino ruidoso meses atrás. Apunta a lo aburrido.

← Anterior
VPN más lenta de lo esperado: diagnostica CPU, criptografía y MSS como si importara
Siguiente →
Confusión con AllowedIPs de WireGuard: por qué el tráfico no va a donde esperas (y cómo arreglarlo)

Deja un comentario