Conectas por SSH a una máquina Ubuntu 24.04 que está “en llamas”: el load average está en dos dígitos, los servicios fallan por tiempo de espera y el canal de incidentes se llena de capturas de pantalla de top que muestran… básicamente ningún uso de CPU. Los gráficos no parecen una tormenta de CPU. Aun así, el sistema claramente no está bien.
Bienvenido a la trampa del iowait: la máquina no está “ocupada computando”, está ocupada esperando. Y Linux gustosamente mostrará esa espera como “no usa CPU” mientras tus usuarios lo experimentan como “todo está roto”.
Qué significa realmente alta carga + baja CPU
El load average en Linux no es “uso de CPU”. Es un recuento de tareas que están o bien:
- En ejecución (en CPU, estado
R) - En sueño ininterrumpible (normalmente espera de I/O, estado
D)
Cuando el almacenamiento se vuelve lento, tus hilos se acumulan en estado D. No consumen ciclos de CPU, por eso el uso de CPU parece bajo. Pero siguen contribuyendo al load average porque están listos para hacer trabajo… tan pronto como el kernel pueda completar su I/O.
Ese desajuste explica por qué ves:
- Load average alto
- Bajo % de CPU en user/system
- Iowait elevado (a veces), además de latencia de disco alta
- Muchos hilos bloqueados (estado D) y timeouts por todas partes
Una cosa más: “iowait” no es un recurso que puedas “consumir”. Es un síntoma. Indica que las CPU están inactivas porque las tareas esperan I/O. No luches contra el síntoma; encuentra la espera.
Broma #1: Si tu load average está alto y la CPU baja, felicidades—has construido una sala de espera muy cara.
Hechos e historia que te importarán durante un incidente
- El load average precede a Linux. Vino de sistemas Unix en los años 70; el concepto asumía que “ejecutable” significaba “quiere CPU”, mucho antes de las pilas de almacenamiento modernas y los sistemas distribuidos.
- Linux cuenta el sueño ininterrumpible en la carga. Por eso los bloqueos de I/O inflan el load average aunque las CPU no estén saturadas.
- “iowait” es atribución de tiempo inactivo por CPU. La CPU no está ocupada; el planificador dice: “ejecutaría trabajo, pero está bloqueado por I/O”.
- NVMe hizo visible la latencia, no irrelevante. Los dispositivos más rápidos reducen la latencia media, pero la latencia cola (p95/p99) aún arruina colas y carga cuando los dispositivos o el firmware fallan.
- El writeback puede convertir lecturas en problema. Los umbrales de páginas sucias y la limitación de escritura pueden bloquear trabajo no relacionado cuando el kernel decide que debe vaciar.
- El almacenamiento en bloque en la nube suele tener comportamiento por ráfagas. Puedes “quedarte sin rendimiento” aun teniendo capacidad, y se ve exactamente como iowait.
- Los sistemas de archivos intercambian consistencia por rendimiento de distinta forma. Los modos de journaling de ext4, el comportamiento de asignación de XFS y los grupos de transacciones de ZFS crean firmas de bloqueo distintas.
- Los controladores RAID aún mienten. Algunas cachés reconocen escrituras temprano y luego bloquean los flush bajo problemas de batería/capacitor o cambios de política de writeback.
- Linux ha mejorado en observabilidad. Herramientas como
iostat -x,pidstat -dy trazas basadas en BPF hacen más difícil que los problemas de almacenamiento se escondan detrás de “la CPU parece bien”.
Guía rápida de diagnóstico (primero/segundo/tercero)
Esta es la lista corta que ejecutas cuando el pager suena fuerte y tu cerebro hace esa divertida cosa de olvidar cómo funcionan las computadoras.
Primero: confirma que no es realmente CPU
- Revisa load, desglose de CPU y cola de ejecución vs tareas bloqueadas.
- Busca procesos en estado D y alto iowait.
Segundo: confirma latencia y encolamiento en el almacenamiento
- ¿Está el dispositivo ocupado (
%util)? - ¿La latencia es alta (
await,r_await,w_await)? - ¿La cola es profunda (
aqu-sz)?
Tercero: identifica cuál carga de trabajo y qué capa
- ¿Qué PIDs hacen I/O? ¿Qué sistemas de archivos? ¿Qué puntos de montaje?
- ¿Es disco local, mdraid, LVM, dm-crypt, ZFS, NFS, iSCSI o un volumen en la nube?
- ¿Es limitación de writeback, contención del journal o un problema en el dispositivo subyacente?
Una vez que hayas hecho esos tres, puedes dejar de debatir y empezar a arreglar.
Confirmar iowait y trabajo bloqueado: comandos que zanjan debates
A continuación hay tareas prácticas que puedes ejecutar en Ubuntu 24.04. Cada una incluye: un comando, salida realista, qué significa y la decisión que tomas a partir de ello.
Tarea 1: Toma una foto de la escena del crimen (load + desglose de CPU)
cr0x@server:~$ uptime
14:22:08 up 36 days, 6:11, 2 users, load average: 18.42, 17.90, 16.77
Significado: El load es muy alto para la mayoría de servidores. No dice por qué.
Decisión: Comprueba inmediatamente si la carga es cola de ejecución (CPU) o tareas bloqueadas (I/O).
Tarea 2: Usa top como un adulto (mira wa, no solo %CPU)
cr0x@server:~$ top -b -n 1 | head -n 15
top - 14:22:13 up 36 days, 6:11, 2 users, load average: 18.42, 17.90, 16.77
Tasks: 612 total, 9 running, 168 sleeping, 0 stopped, 435 zombie
%Cpu(s): 3.1 us, 1.2 sy, 0.0 ni, 63.9 id, 31.6 wa, 0.0 hi, 0.2 si, 0.0 st
MiB Mem : 64221.1 total, 1234.8 free, 40211.4 used, 22774.9 buff/cache
MiB Swap: 8192.0 total, 8100.0 free, 92.0 used. 18920.2 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
18412 postgres 20 0 4267120 2.1g 12536 D 1.3 3.4 12:48.21 postgres
19344 www-data 20 0 621432 31420 8920 D 0.7 0.0 0:38.02 php-fpm
2228 root 20 0 0 0 0 I 0.3 0.0 9:12.33 kworker/u16:2
Significado: wa en ~30% es un cartel: las CPU esperan I/O. Fíjate también en procesos en estado D.
Decisión: Cambia de “¿qué usa la CPU?” a “¿qué está bloqueado por I/O?”.
Tarea 3: Mide cola de ejecución vs tareas bloqueadas (vmstat)
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
7 38 94208 126412 81200 23110240 0 0 812 9440 3221 6110 4 1 63 32 0
5 41 94208 125980 81200 23109988 0 0 640 10320 3199 5902 3 1 62 34 0
8 44 94208 124992 81200 23109012 0 0 712 11088 3340 6408 3 1 60 36 0
6 39 94208 125220 81200 23108211 0 0 540 9720 3101 6055 3 1 64 32 0
7 45 94208 125104 81200 23107010 0 0 690 10840 3368 6530 4 1 61 34 0
Significado: La columna b (bloqueados) es enorme comparada con r (ejecutables). Esto es presión clásica de I/O.
Decisión: Pasa a métricas de latencia y colas a nivel de dispositivo.
Tarea 4: Confirma saturación y latencia del disco (iostat -x)
cr0x@server:~$ iostat -x 1 3
Linux 6.8.0-41-generic (server) 12/28/2025 _x86_64_ (16 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
3.2 0.0 1.3 31.4 0.0 64.1
Device r/s w/s rKB/s wKB/s rrqm/s wrqm/s r_await w_await aqu-sz rareq-sz wareq-sz svctm %util
nvme0n1 22.0 340.0 1800.0 93200.0 0.1 12.0 9.80 144.20 52.10 81.8 274.1 2.1 99.2
dm-0 21.8 338.9 1792.0 93140.0 0.0 0.0 10.10 146.90 52.30 82.2 274.9 0.0 0.0
Significado: %util ~99% indica que el dispositivo está saturado o constantemente ocupado. w_await ~144ms es doloroso para cualquier cosa remotamente transaccional. aqu-sz ~52 significa una cola profunda: las solicitudes se están acumulando.
Decisión: Identifica quién está escribiendo y por qué; decide si limitar la carga, moverla o arreglar la ruta de almacenamiento.
Tarea 5: Encuentra qué procesos hacen I/O (pidstat -d)
cr0x@server:~$ pidstat -d 1 5
Linux 6.8.0-41-generic (server) 12/28/2025 _x86_64_ (16 CPU)
14:22:41 UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
14:22:42 999 18412 0.00 18240.00 2400.00 11234 postgres
14:22:42 33 19344 0.00 1220.00 180.00 1320 php-fpm
14:22:42 0 1451 0.00 9800.00 0.00 2100 systemd-journald
14:22:42 UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
14:22:43 999 18412 0.00 19010.00 2600.00 11620 postgres
14:22:43 0 1451 0.00 9400.00 0.00 2050 systemd-journald
Significado: postgres y journald son escritores importantes, y iodelay es alto (tiempo esperando I/O). kB_ccwr/s sugiere writeback/escrituras en caché que luego deben vaciarse.
Decisión: Investiga patrones de la base de datos y del registro; considera limitar la tasa de logs, cambiar destino de logs, ajustar checkpoints de BD o mover el WAL.
Tarea 6: Confirma muchos hilos atascados en estado D (ps)
cr0x@server:~$ ps -eo state,pid,comm,wchan:32 --sort=state | head -n 20
D 18412 postgres io_schedule
D 19344 php-fpm io_schedule
D 20488 nginx io_schedule
D 20501 nginx io_schedule
D 20512 nginx io_schedule
D 22110 rsyslogd io_schedule
R 9123 top -
R 9130 ps -
S 1451 systemd-journald ep_poll
S 1680 sshd do_select
Significado: Varios procesos de aplicación bloqueados en io_schedule. Esperan la finalización de I/O.
Decisión: Trata esto como un incidente en la ruta de almacenamiento salvo que se demuestre lo contrario.
Tarea 7: Mapea I/O a sistemas de archivos y opciones de montaje (findmnt)
cr0x@server:~$ findmnt -D
SOURCE FSTYPE OPTIONS TARGET
/dev/dm-0 ext4 rw,relatime,errors=remount-ro /
/dev/nvme0n1p2 ext4 rw,relatime /var/lib/postgresql
tmpfs tmpfs rw,nosuid,nodev /run
tmpfs tmpfs rw,nosuid,nodev /dev/shm
Significado: Los datos de Postgres están en /dev/nvme0n1p2, root está en dm-0. Si iostat muestra que nvme0n1 está saturado, coincide.
Decisión: Concéntrate en el dispositivo NVMe y en el patrón de escritura de la base de datos primero.
Tarea 8: Comprueba presión a nivel de sistema de archivos (df, inodos y síntomas del journal)
cr0x@server:~$ df -hT /var/lib/postgresql
Filesystem Type Size Used Avail Use% Mounted on
/dev/nvme0n1p2 ext4 1.8T 1.6T 120G 94% /var/lib/postgresql
Significado: 94% lleno no es condena instantánea, pero es un riesgo conocido: la asignación se complica, la fragmentación sube y algunos sistemas de archivos van peor cerca del límite.
Decisión: Planifica liberar espacio o ampliar el sistema de archivos. Mientras tanto, sigue investigando—esto por sí solo no explica un await de 150ms, pero contribuye.
Tarea 9: Revisa logs del kernel por resets/timeouts de disco
cr0x@server:~$ sudo journalctl -k -n 30 --no-pager
Dec 28 14:18:02 server kernel: nvme nvme0: I/O 672 QID 4 timeout, completion polled
Dec 28 14:18:02 server kernel: nvme nvme0: Abort status: 0x371
Dec 28 14:18:03 server kernel: nvme nvme0: resetting controller
Dec 28 14:18:06 server kernel: nvme nvme0: controller reset complete
Dec 28 14:18:11 server kernel: EXT4-fs (nvme0n1p2): Delayed block allocation failed for inode 262401 at logical offset 918234
Dec 28 14:18:12 server kernel: blk_update_request: I/O error, dev nvme0n1, sector 1819238480 op 0x1:(WRITE) flags 0x0 phys_seg 1 prio class 0
Significado: Resets de controlador y errores de I/O. Eso no es “la app está lenta.” Es “el almacenamiento está enfermo.” Además, fallos de asignación de ext4 sugieren presión y/o errores subyacentes.
Decisión: Escala inmediatamente a salud del hardware/volumen en la nube; empieza mitigación (failover, modo solo lectura, reducir carga de escritura) mientras investigas.
Tarea 10: Comprueba señales SMART/NVMe
cr0x@server:~$ sudo nvme smart-log /dev/nvme0 | head -n 20
Smart Log for NVME device:nvme0 namespace-id:ffffffff
critical_warning : 0x00
temperature : 63 C
available_spare : 100%
available_spare_threshold : 10%
percentage_used : 89%
data_units_read : 12,349,221
data_units_written : 81,442,109
host_read_commands : 902,112,441
host_write_commands : 5,110,992,120
controller_busy_time : 145,221
power_cycles : 33
power_on_hours : 14,880
unsafe_shutdowns : 7
media_errors : 18
num_err_log_entries : 18
Significado: percentage_used en 89% y media_errors no nulo es una fuerte pista de que la unidad está envejeciendo o fallando. No es concluyente por sí sola, pero combinada con timeouts/resets es contundente.
Decisión: Reemplaza/evacua el dispositivo (o mueve la carga) en lugar de tunear alrededor de una unidad que está muriendo.
Tarea 11: Verifica si la presión de memoria está provocando tormentas de writeback
cr0x@server:~$ cat /proc/meminfo | egrep 'Dirty|Writeback|MemFree|MemAvailable|Buffers|Cached'
MemFree: 126412 kB
MemAvailable: 19374132 kB
Buffers: 83192 kB
Cached: 22640480 kB
Dirty: 1562040 kB
Writeback: 81220 kB
Significado: Datos sucios ~1.5GB; no es una locura en una máquina de 64GB. MemAvailable está sano. Entonces es menos probable que “la memoria fuerce un writeback brutal”, más probable que “la latencia del dispositivo es alta”.
Decisión: No ajustes precipitadamente sysctl de writeback aún. Arregla la ruta de almacenamiento primero.
Tarea 12: Busca evidencia directa de encolamiento en la capa de bloques
cr0x@server:~$ cat /sys/block/nvme0n1/queue/nr_requests
1023
Significado: La cola del dispositivo permite muchas solicitudes pendientes. Eso puede ser bueno para rendimiento, malo para latencia si el dispositivo ya está sobrecargado (la teoría de colas no es una disciplina basada en sensaciones).
Decisión: Si este es un servicio sensible a latencia, considera reducir la concurrencia en la aplicación o en la capa de base de datos primero; no modifiques aleatoriamente colas del kernel sin medir impacto.
Tarea 13: Comprueba el planificador de I/O y la pila multipath/dm rápidamente
cr0x@server:~$ cat /sys/block/nvme0n1/queue/scheduler
[none] mq-deadline kyber bfq
Significado: NVMe comúnmente usa none (aka sin planificador tradicional) con multiqueue. Está bien. Si ves una pila rara (dm-crypt, dm-thin, multipath), tenla en cuenta en tu modelo mental.
Decisión: Si esto es almacenamiento en la nube/virtualizado, busca los límites de la capa de disco virtual; cambiar planificadores no arreglará la desincronización de créditos de ráfaga o la limitación del backend.
Tarea 14: Correlaciona con sar para ver si es crónico o agudo
cr0x@server:~$ sar -u 1 3
Linux 6.8.0-41-generic (server) 12/28/2025 _x86_64_ (16 CPU)
14:23:41 CPU %user %nice %system %iowait %steal %idle
14:23:42 all 3.05 0.00 1.22 32.10 0.00 63.63
14:23:43 all 2.88 0.00 1.11 30.84 0.00 65.17
14:23:44 all 3.18 0.00 1.30 31.66 0.00 63.86
Average: all 3.04 0.00 1.21 31.53 0.00 64.22
Significado: iowait es consistentemente alto en esta ventana. Si tienes datos históricos de sar, puedes comparar con lo “normal” e identificar cuándo empezó.
Decisión: Si empezó tras un cambio (deploy, actualización de kernel, migración de almacenamiento), revierte o aisla. Si es gradual, sospecha capacidad y desgaste.
Tarea 15: Si es almacenamiento en red, revisa lo obvio (tipo de montaje y estadísticas RPC)
cr0x@server:~$ mount | egrep 'type nfs|type cifs' || true
cr0x@server:~$ nfsstat -c 1 2
Client rpc stats:
calls retrans authrefrsh
12433 118 0
Client nfs v4:
null read write commit open
0 812 9340 220 14
Significado: Retransmisiones indican red o retraso en el servidor remoto. Para cargas con NFS, iowait puede ser “disco” pero en realidad es “red + disco remoto + colas del servidor”.
Decisión: Si retrans aumenta, trátalo como un incidente de almacenamiento distribuido: revisa pérdidas de red, carga del servidor NFS y latencia del backend de almacenamiento.
Tarea 16: Confirma si systemd/journald está amplificando el dolor
cr0x@server:~$ sudo journalctl --disk-usage
Archived and active journals take up 6.7G in the file system.
Significado: Journales grandes más servicios muy verbosos pueden generar escrituras sostenidas. No suele bastar para saturar un NVMe solo, pero durante un incidente de almacenamiento puede mantener el problema vivo.
Decisión: Durante la mitigación, reduce la verbosidad de logs o envía logs a otro destino. No borres logs a ciegas en un incidente salvo que hayas acordado el tradeoff.
Encontrar el cuello de botella: disco, sistema de archivos, controlador, almacenamiento en red o kernel
Paso 1: No confíes en una sola métrica—correla con tres
Para afirmar con confianza que la carga es por iowait, quieres un triángulo de evidencias:
- Evidencia del planificador: load average alto + muchos procesos en estado D (
ps,vmstat) - Atribución de CPU: iowait significativa (
top,sar -u) - Evidencia del dispositivo: latencia alta + encolamiento (
iostat -x)
Si los tres coinciden, deja de discutir con los gráficos. Empieza a arreglar la ruta de almacenamiento.
Paso 2: Decide si tratas con rendimiento o latencia
Esto importa porque la solución es diferente:
- Problema de latencia: awaits altos, cola alta, %util puede ser alto. Las apps hacen timeouts; las BD se entristecen; las peticiones web se acumulan.
- Techo de throughput: awaits moderados pero hay I/O sostenido enorme. Los usuarios ven trabajos por lotes más lentos, no necesariamente timeouts.
En el incidente de “alta carga, baja CPU”, suele ser latencia y encolamiento.
Paso 3: Averigua si son lecturas, escrituras o flushes
iostat -x separa r_await vs w_await. Te importa porque:
- Picos en write await suelen correlacionar con commits de journal, patrones WAL/fsync, política de cache del controlador, o limitación de volúmenes en la nube.
- Picos en read await sugieren fallos de caché, amplificación de I/O aleatorio, o un dispositivo que no mantiene el working set.
Paso 4: Identifica la capa que añade dolor
En Ubuntu, la pila de I/O puede incluir: filesystem → LVM → dm-crypt → mdraid → dispositivo, además de almacenamiento en red opcional. Cada capa puede multiplicar la latencia bajo carga.
Formas rápidas de mapearlo:
lsblk -fmuestra el mapeo de device-mapper.dmsetup ls --treelo hace explícito.multipath -ll(si se usa) muestra salud de caminos.
cr0x@server:~$ lsblk -o NAME,TYPE,FSTYPE,SIZE,MOUNTPOINT
NAME TYPE FSTYPE SIZE MOUNTPOINT
nvme0n1 disk 1.8T
├─nvme0n1p1 part vfat 512M /boot/efi
├─nvme0n1p2 part ext4 1.6T /var/lib/postgresql
└─nvme0n1p3 part crypto 200G
└─dm-0 crypt ext4 200G /
Significado: Root está cifrado vía dm-crypt; los datos de BD están en ext4 plano en la partición 2.
Decisión: Si el volumen de BD es el dispositivo caliente, no te distraigas optimizando dm-crypt en root.
Paso 5: Entiende el modo de fallo “estado D no muere”
Cuando los hilos quedan atascados en sueño ininterrumpible, no siempre puedes matarlos. El kernel espera la finalización de I/O; las señales no ayudan. Si el dispositivo o la ruta subyacente está bloqueada, los procesos pueden permanecer atascados hasta que el dispositivo se recupere—o hasta que reinicies.
Por eso “simplemente reinicia el servicio” a veces no hace nada. Los procesos antiguos siguen bloqueados, los nuevos también se bloquean, y ahora añadiste inestabilidad.
Broma #2: Enviar SIGKILL a un proceso en estado D es como gritarle a una barra de progreso—satisfactorio emocionalmente, inútil operacionalmente.
Paso 6: Usa una cita para guiar tu comportamiento en incidentes
Werner Vogels (idea parafraseada): “Todo falla, todo el tiempo—diseña y opera como si la falla fuera normal”.
Los incidentes de iowait parecen misteriosos porque “no son CPU”. Pero son normales: discos se bloquean, redes laten, controladores se resetean y volúmenes en la nube limitan. Construye detección y runbooks en consecuencia.
Tres micro-historias corporativas (las que recuerdas)
Micro-historia 1: El incidente causado por una suposición errónea
Tenían un servicio de pagos en Ubuntu, gráficos de CPU sanos y un pico repentino en load average que disparó las alarmas de “escalado”. El on-call supuso que era un pico de tráfico y añadió dos instancias más. La carga siguió alta. Las peticiones seguían haciendo timeout.
Luego hicieron lo clásico: buscar en logs de la app, encontraron “database connection timeout” y declararon que la base de datos “estaba lenta”. Reiniciaron la BD. No ayudó. Peor: el reinicio tardó mucho porque el proceso de la base de datos estaba atascado en estado D esperando disco, y los hooks de apagado colgaron.
Finalmente alguien ejecutó iostat -x y vio awaits de escritura en cientos de milisegundos y %util al máximo. El almacenamiento era un volumen en la nube. No estaba sin espacio; se había quedado sin IOPS garantizados por un cambio de configuración semanas antes, más un pico en logging intensivo de auditoría que consumió el crédito de ráfagas.
La suposición errónea fue sutil: “alta carga significa presión de cómputo”. Lo trataron como CPU y escalado. Pero iowait no escala así cuando cada instancia golpea el mismo límite de almacenamiento (backend compartido) o el mismo patrón de carga (amplificación de escritura).
Solución: revertir el cambio de logging, reducir la frecuencia de escrituras y mover temporalmente WAL/logs a un nivel de volumen más rápido. Solo entonces el sistema mejoró. El postmortem fue franco: tenían alarmas para CPU pero ninguna para latencia de disco. El pager gritó, los dashboards susurraron.
Micro-historia 2: La optimización que salió mal
Un equipo de plataforma quería despliegues más rápidos. Movieron capas de imágenes de contenedor y caches de builds a un montaje NFS compartido para “desduplicar almacenamiento” y “simplificar backups”. Era un diagrama limpio. Todos aplaudieron. Luego llegó el primer gran día de lanzamientos.
Los nodos mostraron alta carga, pero top no mostraba uso de CPU. Pods fallaban checks de readiness. Los ingenieros persiguieron scheduling de Kubernetes, DNS y cuotas de “vecinos ruidosos” de CPU. Mientras tanto, el backend del servidor NFS estaba haciendo escrituras síncronas para operaciones ricas en metadata y era bombardeado por miles de operaciones de archivos pequeños.
La optimización salió mal porque concentró I/O aleatorio de metadata. La ruta de almacenamiento en red amplificó la latencia cola: cachés de cliente, colas del servidor, disco backend y retransmisiones ocasionales cuando la red se saturó. Cada capa “estaba bien” en promedio. Juntas fueron una fábrica de latencia.
La recuperación no fue elegante: mover los caches de builds a NVMe local en cada nodo y tratar el montaje NFS compartido como repositorio de artefactos, no como área temporal. Los “ahorros” en capacidad de almacenamiento se pagaron con tiempo de incidente, pérdida de confianza y mucho escalado de CPU inútil.
Micro-historia 3: La práctica aburrida pero correcta que salvó el día
Una empresa ejecutaba un pipeline de analítica de clientes en unos pocos hosts Ubuntu grandes. Nada glamoroso: jobs por lotes, una base de datos y una cola de mensajes. El equipo SRE tenía un hábito poco sexy: registraban semanalmente latencias base para cada dispositivo de almacenamiento—resúmenes de iostat -x, picos de await y una nota de cómo era lo “normal”.
Una tarde, el load average subió y el equipo de aplicación reportó “la CPU parece bien”. El on-call abrió el documento base y vio de inmediato la discrepancia: el w_await normal era de un dígito de milisegundos; ahora era de tres cifras. No hubo debate. No hubo “quizá sea el código”. Era almacenamiento hasta que se demuestre lo contrario.
Revisaron logs del kernel y encontraron resets ocasionales del controlador. No suficientes para tumbar el sistema, pero suficientes para destrozar la latencia. Como tenían una línea base, no perdieron una hora probando “esto es anormal”. Ya lo sabían.
La mitigación fue igualmente aburrida: fallover de la BD a un standby en hardware distinto, drenar workers por lotes y programar una ventana de mantenimiento para reemplazar el dispositivo sospechoso. El informe del incidente no fue emocionante. Ese es el punto. Las buenas operaciones suelen parecer alguien negándose a sorprenderse dos veces por la misma clase de fallo.
Errores comunes: síntoma → causa raíz → solución
Aquí es donde los equipos pierden tiempo. El truco es tratar los síntomas como pistas, no como diagnósticos.
1) Síntoma: Load average alto, CPU baja, muchos timeouts
Causa raíz: hilos bloqueados en estado D por latencia o errores de almacenamiento.
Solución: Confirma con vmstat/ps/iostat -x. Mitiga reduciendo carga de escritura, moviendo rutas calientes (WAL/logs) a almacenamiento más rápido o haciendo failover. Investiga salud del dispositivo y logs del kernel.
2) Síntoma: iowait es bajo, pero la carga es alta y las apps cuelgan
Causa raíz: la carga incluye estado D; el porcentaje de iowait puede ser engañoso en sistemas con múltiples núcleos o cuando las tareas bloqueadas no se muestrean como esperas.
Solución: Cuenta tareas en estado D directamente (ps) y revisa métricas de la capa de bloques (iostat -x). No te bases solo en iowait.
3) Síntoma: %util al 100% pero el throughput no es alto
Causa raíz: el dispositivo está ocupado realizando I/O lento (alta latencia), a menudo escrituras aleatorias, workloads fsync-heavy, o reintentos por errores.
Solución: Identifica el patrón de I/O (iostat -x, pidstat -d). Reduce frecuencia de fsync donde sea seguro, mueve WAL, o arregla límites de hardware/volumen en la nube.
4) Síntoma: Todo empeora después de “tunear” writeback del kernel
Causa raíz: aumentar ratios de sucios eleva la ráfaga; las tormentas de flush se vuelven más grandes y más dañinas cuando el dispositivo no puede seguir.
Solución: Revierte a valores por defecto salvo que tengas una razón medida. Prefiere suavizar a nivel de aplicación (batching, rate limits) sobre apuestas en el kernel.
5) Síntoma: Reiniciar servicios no ayuda; el apagado cuelga
Causa raíz: procesos en estado D esperando finalización de I/O; no morirán hasta que vuelva el I/O.
Solución: Arregla el bloqueo de almacenamiento subyacente o reinicia como último recurso tras asegurar la integridad de datos. Si es una BD, prefiere failover en lugar de reiniciar el primario.
6) Síntoma: Solo un punto de montaje es lento, otros bien
Causa raíz: un volumen/dispositivo específico está saturado o con errores; no es “todo el host”.
Solución: Mapea montajes a dispositivos (findmnt, lsblk). Mueve esa carga, ajusta cuotas o repara/reemplaza ese dispositivo.
7) Síntoma: Lentitud por ráfagas en intervalos predecibles
Causa raíz: checkpoints, commits de journal, txg syncs de ZFS, rotación de logs o snapshots periódicos de backup.
Solución: Alinea horarios, reduce concurrencia, escalona jobs, afina checkpoints de BD cuidadosamente e aisla logs/WAL de los datos cuando sea posible.
8) Síntoma: Alto iowait en una VM, pero los discos “parecen bien” en el invitado
Causa raíz: contención en el host, throttling o efectos de vecino ruidoso; las métricas dentro del invitado muestran síntomas, pero no la causa real.
Solución: Revisa métricas del hipervisor/nube y límites del volumen. Las herramientas en el invitado muestran el síntoma; la causa puede estar fuera de la VM.
Listas de verificación / plan paso a paso
Checklist A: Triage de 10 minutos (haz esto antes de cambiar nada)
- Registra
uptime, capturatopy ejecutavmstat 1 5. - Ejecuta
iostat -x 1 3y guarda la salida. - Lista procesos top en I/O con
pidstat -d 1 5. - Cuenta tareas en estado D:
ps -eo state | grep -c '^D'. - Revisa logs del kernel por errores de almacenamiento:
journalctl -k -n 100. - Mapea montajes a dispositivos:
findmnt -Dylsblk. - Comprueba espacio libre e inodos en montajes calientes:
df -hT,df -i.
Checklist B: Opciones de mitigación (elige la menos riesgosa que funcione)
- Reduce la carga de escritura rápido: disminuye logging de depuración, pausa jobs por lotes, reduce ingesta.
- Protege la base de datos: si WAL/checkpoints son el problema, mueve WAL a un dispositivo más rápido si hay, o haz failover.
- Aísla procesos ruidosos: detén o limita el mayor ofensor de I/O (con cuidado—no mates lo único que mantiene consistencia de datos).
- Mueve tráfico: drena el nodo de los balanceadores; redirige a réplicas sanas.
- Último recurso: reinicia solo tras confirmar que no empeorarás la recuperación (especialmente en errores de almacenamiento).
Checklist C: Flujo de trabajo para causa raíz (después de estabilizar)
- Determina si la latencia es de lectura o escritura (
iostat -x). - Correlaciona con despliegues/cambios (
journalctl, el registro de cambios). - Confirma salud de hardware/volúmenes (SMART NVMe, logs RAID, eventos del proveedor cloud).
- Revisa comportamiento del sistema de archivos cerca del lleno, modo de journal y opciones de montaje.
- Evalúa cambios en el patrón de I/O de la app (nuevo logging, índices, vacuum, trabajos de compactación).
- Añade alertas: await por disco, profundidad de cola, conteo de estado D, llenado del FS y tasa de errores del kernel—no solo CPU.
Preguntas frecuentes
1) ¿Por qué sube el load average si la CPU está inactiva?
Porque la carga en Linux incluye tareas en sueño ininterrumpible (D), típicamente esperando I/O. No están usando CPU, pero “no progresan”, así que cuentan en la carga.
2) ¿Es siempre malo un iowait alto?
No. Un iowait alto durante un trabajo conocido de lectura/escritura masiva puede ser aceptable si los servicios sensibles están aislados. Es malo cuando se correlaciona con timeouts visibles por usuarios y aumento de tareas en estado D.
3) ¿Por qué a veces iowait es bajo aunque el almacenamiento sea el problema?
iowait es una atribución de CPU inactiva, no una medición directa de latencia de disco. En sistemas multi-core, unos pocos hilos bloqueados quizás no cambien mucho el porcentaje global. Además el muestreo y las herramientas pueden ocultar picos cortos. Cuenta tareas en estado D y mide latencia del dispositivo directamente.
4) ¿Cuál es el comando más rápido para demostrar que es almacenamiento?
iostat -x 1 3. Si ves await alto, aqu-sz elevado y %util alto en un dispositivo relevante, tienes evidencia real. Combínalo con procesos en estado D para una prueba contundente.
5) ¿Significa %util alto que el disco está al máximo de throughput?
No necesariamente. Puede significar que el dispositivo está ocupado atendiendo operaciones lentas (alta latencia) o manejando reintentos/errores. El throughput puede ser mediocre mientras %util está al máximo.
6) ¿Cómo sé si es el disco o el sistema de archivos/journal?
Si el dispositivo bruto muestra alta latencia, empieza por ahí. Si la latencia del dispositivo bruto está bien pero un montaje específico es lento, revisa comportamiento a nivel de sistema de archivos: contención del journal, problemas cerca del lleno, opciones de montaje y patrones fsync de la app.
7) ¿Por qué los procesos en estado D ignoran kill -9?
Porque están atrapados en una espera ininterrumpible del kernel, típicamente esperando la finalización de I/O. La señal queda pendiente, pero el proceso no puede gestionarla hasta que retorne la llamada del kernel.
8) Si es una VM en la nube, ¿qué debo sospechar primero?
Límites de rendimiento del volumen, agotamiento de créditos de ráfaga, efectos de vecino ruidoso y contención a nivel de host. Las herramientas dentro de la VM muestran el síntoma; la causa puede estar fuera de la VM.
9) ¿Puede el swapping causar exactamente este patrón?
Sí. El swap intenso genera presión de I/O y tareas bloqueadas. Pero normalmente verás actividad de swap en vmstat (si/so) y MemAvailable reducido. No adivines—mide.
10) ¿Qué alertas debería añadir para no sorprenderme de nuevo?
Como mínimo: await por dispositivo (lectura/escritura), profundidad de cola (aqu-sz), conteo de tareas en estado D, llenado del sistema de archivos y tasa de errores del kernel/almacenamiento. Solo CPU es engañosa en esta historia.
Conclusión: siguientes pasos que realmente mueven la aguja
Cuando Ubuntu 24.04 muestra alta carga pero “nada usa CPU”, trátalo como un problema de trabajo bloqueado hasta que se demuestre lo contrario. Confirma con conteos de estado D, vmstat y iostat -x. Una vez que veas latencia elevada y encolamiento, deja de trastear la CPU y empieza a trabajar la ruta de almacenamiento: salud del dispositivo, resets de controlador, límites de volúmenes, presión del sistema de archivos y los procesos específicos que generan escrituras.
Pasos prácticos:
- Incluye la guía rápida de diagnóstico en tu runbook de on-call tal cual.
- Añade alertas sobre latencia de disco y tareas bloqueadas, no solo CPU y carga.
- Normaliza “normal” para
awaity profundidad de cola en tus hosts críticos. - Separa logs/WAL sensibles a latencia de los datos masivos cuando sea posible.
- Si los logs del kernel muestran timeouts/resets: deja de tunear y empieza a migrar/reemplazar almacenamiento. No puedes sysctl-izar la física.