La pregunta casi siempre es la misma: anoche el sitio tardaba veinte segundos en responder y por la mañana todo se había arreglado solo. Ya no queda nada que mirar — top responde por el segundo actual, y lo que se pregunta es por las tres de la madrugada. A continuación, qué significan realmente las cifras de carga, cuáles hay que leer por parejas y por qué una medición aislada engaña más veces de las que ayuda.

Load average: tres números y un malentendido habitual

uptime
cat /proc/loadavg
nproc

Los tres números son promedios de uno, cinco y quince minutos. No se comparan con cero, sino con el número de núcleos que indica nproc. Un valor de 8 en una máquina de ocho núcleos es carga plena pero sana: hay exactamente tanto trabajo como el servidor puede sacar adelante. Un valor de 2 en un VPS de un solo núcleo es una cola el doble de larga que la capacidad, y cada petición espera su turno.

La relación entre los tres números indica la dirección. La cifra de un minuto muy por encima de la de quince significa que la carga está subiendo ahora mismo. Al revés, el pico ya pasó y lo que se ve es su cola.

Y ahora el malentendido que más estropea la lectura de estas cifras. En Linux, el load average no es «uso de CPU». A diferencia de otros sistemas Unix, Linux cuenta en él no solo los procesos que se ejecutan o están listos para ejecutarse, sino también los que están en estado D: sueño ininterrumpible. Es decir, esperando al disco o a un sistema de archivos en red. De ahí el servidor con un load de 12 y una CPU casi ociosa: no se está trabajando, se está haciendo cola.

CPU o disco

Separar esos dos casos es lo primero que conviene hacer:

vmstat 1 5
iostat -x 1 3

En la salida de vmstat importan tres columnas. r es cuántos procesos hacen cola para la CPU, b cuántos están bloqueados esperando entrada/salida y wa la proporción de tiempo que la CPU pasó ociosa esperando al disco. Un wa sostenido por encima del 10–15 % con us y sy modestos significa que el servidor está limitado por el disco: añadir núcleos no sirve de nada, los que hay ya están libres.

Qué procesos están esperando se puede listar con nombre:

ps -eo state,pid,comm,wchan:30 | awk '$1 ~ /^D/'

Un ejemplo de nuestra propia práctica. Con la configuración de fábrica, Suricata escribe una treintena de tipos de evento y no configura rotación alguna: en un servidor en producción eso dio 15 GB de registros en dos días. La carga no se manifestaba como CPU ocupada, sino precisamente como I/O wait: el sistema escribía sin parar y todo lo demás hacía cola detrás. El remedio no es un plan mayor, sino una lista de eventos más corta y una rotación configurada.

El caso contrario, puramente de CPU y bastante menos evidente. Una página web llamaba a apt con el usuario bajo el que corre PHP-FPM. Root mantiene una caché binaria en /var/cache/apt/pkgcache.bin —70 MB en un host con una docena de repositorios— y la mapea en memoria sin coste. Un usuario corriente no puede escribir en ese directorio y reconstruye la caché entera en cada llamada. Medido en una misma máquina: 0,01 segundos de tiempo de CPU como root frente a 4,2 segundos como usuario sin privilegios. El mismo comando, una diferencia de cuatrocientas veces, multiplicada por cada visita a la página.

La conclusión de ambos casos es la misma: la carga se mide, no se adivina. Cronometrar por separado cada comando sospechoso lleva media hora y suele señalar a otro sitio distinto del que apuntaba la sospecha inicial.

Memoria: free no muestra lo que parece

free -h

La columna used por sí sola dice muy poco, y la columna free resulta directamente engañosa: Linux entrega la memoria sin usar a la caché de páginas y se la devuelve a las aplicaciones en cuanto se la piden. La columna que hay que leer es available: cuánto se puede ocupar sin entrar en swap. Un buff/cache grande no es un problema, sino la señal de un sistema que funciona como debe.

En el swap lo que importa no es la cifra actual, sino la forma. Sube y vuelve a cero: hubo un pico breve. Sube una vez y se queda ahí: el pico ya ocurrió, se expulsaron páginas y nadie las devuelve; el servidor parece tranquilo aunque en algún momento se quedó sin memoria. Si el intercambio está ocurriendo ahora mismo lo indican las columnas si y so de vmstat; valores distintos de cero ahí son la forma de lentitud que los usuarios más notan.

Si durante el bajón nocturno un proceso simplemente desapareció, la explicación suele estar aquí:

journalctl -k --since yesterday | grep -i "out of memory"
dmesg -T | grep -i "killed process"

Una línea como Out of memory: Killed process 1234 (mysqld) zanja la cuestión mejor que cualquier gráfico: la base de datos no «se cayó sola», la detuvo el núcleo porque se acabó la memoria.

Quién lo está provocando

ps aux --sort=-%cpu | head -10
ps aux --sort=-%mem | head -10

Una salvedad: el %CPU de ps es un promedio de toda la vida del proceso, así que un pico corto se pierde en esa lista. Para eso hacen falta top o pidstat 1 5, que miden por intervalos.

Cuándo la carga pasa a ser una cuestión de seguridad

Un 100 % de CPU plano durante la noche, con un proceso de nombre insustancial y directorio de trabajo en /tmp o /dev/shm, es la estampa clásica de un minero, no la de un sitio que ha crecido. Un repunte de tráfico entrante junto con más escrituras en los registros es un ataque de contraseñas en marcha. Un volumen de registros que se dispara de golpe suele ser una tormenta de falsos positivos de algún filtro.

Las propias herramientas de protección son una categoría aparte. En uno de nuestros servidores el load se mantenía en torno a tres sin visitante alguno, y en la cabecera de ps por tiempo de CPU acumulado no estaban ni el sitio ni la base de datos, sino CrowdSec, fail2ban, Falco y Suricata. Ni es una avería ni un motivo para apagarlos, pero conviene conocer el precio de la protección en cifras: en un VPS pequeño se nota.

Para qué observar

Todo lo anterior responde a la pregunta «qué está pasando ahora». La pregunta de la mañana —qué pasó a las tres de la madrugada— no la cubren estos comandos: de la noche pasada no hay datos si nadie los registró. Y levantar Prometheus y Grafana para un solo VPS es discutible, porque la pila que observa acaba pesando más que el servidor observado.

Basta con una fila en una base de datos cada cinco minutos y una página que dibuje con ella las últimas 24 horas: load average, uso de CPU e I/O wait por separado, memoria y swap, lecturas y escrituras de disco, ocupación de la partición, inodos, descriptores de archivo, conexiones. Sobre un mismo eje temporal las parejas se leen a simple vista: wa alto con la CPU tranquila, swap que nunca volvió a cero, descriptores acercándose al límite —el futuro error too many open files se ve horas antes de producirse—. Cómo queda todo esto reunido puede verlo en la página de demostración de abajo.