Cuando un servidor responde lento, lo primero que suele mirarse es la CPU. Pero un load average alto no siempre significa que falte CPU: puede tratarse de disco lento, de un vecino ruidoso en la virtualización o de un único hilo bloqueado. En esta guía aprenderás a distinguir esos casos en Ubuntu 24.04 con top, ps, pidstat, mpstat y vmstat, a localizar el proceso y el código responsable, y a limitar su consumo mientras lo corriges.

Requisitos previos

  • Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath. Los comandos funcionan igual en Debian 12.
  • Un usuario no root con privilegios sudo.
  • El paquete sysstat, que proporciona pidstat, mpstat, iostat y sar. Lo instalarás en el primer paso.

Paso 1: Instalar sysstat

top y ps vienen con el sistema, pero las herramientas que miden la CPU por proceso e intervalo están en sysstat:

sudo apt update
sudo apt install sysstat

Comprueba que está disponible:

pidstat -V
sysstat version 12.6.1
(C) Sebastien Godard (sysstat <at> orange.fr)

Paso 2: Comparar la carga con el número de núcleos

El load average es el número medio de tareas que están ejecutándose o esperando para ejecutarse (incluidas las que esperan al disco) en los últimos 1, 5 y 15 minutos. Por sí solo no dice nada: hay que compararlo con el número de núcleos.

nproc
uptime
4
 11:02:17 up 3 days,  2:41,  1 user,  load average: 9.12, 7.40, 4.05

Con 4 núcleos, una carga de 9 significa que hay más del doble de trabajo del que la CPU puede atender. Estas son las pautas generales:

Carga (1 min) respecto a núcleosSignificado
Por debajo del número de núcleosHay capacidad libre
Igual al número de núcleosCPU al límite, sin cola
Claramente por encimaHay tareas esperando: el servidor irá lento

Si la carga del minuto es mucho mayor que la de 15 minutos, el problema acaba de empezar; si es al revés, ya está remitiendo.

Paso 3: Averiguar en qué se va el tiempo de CPU

Antes de buscar el proceso culpable, averigua qué tipo de consumo es. mpstat muestra el desglose por núcleo; este ejemplo toma 3 muestras de 2 segundos:

mpstat -P ALL 2 3
Average:     CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
Average:     all   71.30    0.00    9.85    0.40    0.00    0.62    1.10    0.00    0.00   16.73
Average:       0   98.50    0.00    1.50    0.00    0.00    0.00    0.00    0.00    0.00    0.00
Average:       1   62.10    0.00   12.40    0.50    0.00    0.80    1.60    0.00    0.00   22.60
Average:       2   61.80    0.00   13.00    0.60    0.00    0.90    1.40    0.00    0.00   22.30
Average:       3   62.80    0.00   12.50    0.50    0.00    0.80    1.40    0.00    0.00   22.00

Así se interpreta cada columna relevante:

ColumnaQué mideSi es alta
%usrCódigo de aplicacionesBusca el proceso en el paso 4
%sysCódigo del kernel (llamadas al sistema)Muchas llamadas al sistema, red o disco: usa strace en el paso 6
%iowaitCPU ociosa esperando al discoNo es un problema de CPU: revisa el disco con iostat
%softInterrupciones de software, sobre todo redMucho tráfico de red o un posible ataque
%stealTiempo que el hipervisor dio a otras máquinasContención en el host físico

En el ejemplo, el núcleo 0 está al 98 % en %usr mientras los demás tienen margen: es típico de un proceso de un solo hilo que no puede repartir su trabajo, como un script o una instancia de Redis.

Paso 4: Encontrar el proceso con top

Abre top ordenado por CPU:

top -o %CPU

Dentro de top, estas teclas son las más útiles:

  • 1: muestra una línea por núcleo, como mpstat.
  • c: alterna entre el nombre del proceso y la línea de comandos completa.
  • H: muestra hilos en lugar de procesos.
  • u: filtra por usuario (por ejemplo www-data).
  • k: envía una señal a un PID.
  • q: sale.

Si necesitas guardar una instantánea (para un ticket de soporte o un log), usa el modo batch:

top -b -n 1 -o %CPU | head -20

Paso 5: Confirmar con ps y pidstat

ps da un listado fácil de filtrar y guardar:

ps -eo pid,ppid,user,%cpu,%mem,etime,cmd --sort=-%cpu | head -10
    PID    PPID USER     %CPU %MEM     ELAPSED CMD
  21877       1 www-data 96.4  3.2       41:10 php-fpm: pool www
  21880       1 www-data 88.1  3.0       41:09 php-fpm: pool www
   1204       1 mysql    35.7 18.6  3-02:40:11 /usr/sbin/mysqld

Ten en cuenta que el %CPU de ps es la media desde que arrancó el proceso, no el consumo actual. Un proceso que lleva días en marcha y acaba de dispararse puede aparecer con un valor bajo. Para medir el consumo real en un intervalo, usa pidstat. Este ejemplo toma 3 muestras de 5 segundos de los procesos activos:

pidstat -u 5 3
Average:      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
Average:       33     21877   91.20    4.60    0.00    2.10   95.80     -  php-fpm8.3
Average:       33     21880   85.40    3.20    0.00    3.00   88.60     -  php-fpm8.3
Average:      112      1204   30.10    6.40    0.00    0.80   36.50     -  mysqld

La columna %wait indica el tiempo que el proceso estuvo listo para ejecutarse pero esperando CPU. Si es alta en muchos procesos, la máquina se ha quedado corta de núcleos.

Para un proceso con muchos hilos (Java, MySQL, Node.js con workers), ve hilo a hilo con -t. Sustituye PID por el número del proceso:

pidstat -t -p PID 2 5

Y para ver cambios de contexto, útiles cuando %sys es alto:

pidstat -w -p PID 2 5

Un número muy alto de cambios de contexto involuntarios (nvcswch/s) indica que el proceso compite por la CPU; muchos voluntarios (cswch/s) indican que se bloquea continuamente esperando E/S o locks.

Paso 6: Ver la cola de ejecución con vmstat

vmstat resume el sistema en una línea por intervalo:

vmstat 2 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 9  0      0 412304  88120 2310456    0    0     4    36 2210 5120 81 10  8  0  1
 8  0      0 410988  88120 2310520    0    0     0    52 2305 5340 83  9  7  0  1

Mira estas columnas:

  • r: procesos esperando CPU. Si se mantiene por encima del número de núcleos, falta CPU.
  • b: procesos bloqueados esperando E/S. Si es alto, el cuello de botella es el disco.
  • si y so: memoria que entra y sale de swap. Si no son cero de forma constante, el problema real es la memoria y la CPU se consume intercambiando páginas.
  • st: tiempo robado por el hipervisor.

Paso 7: Investigar qué hace el proceso

Una vez localizado el PID, averigua a qué servicio pertenece. systemctl status acepta un PID y muestra la unidad que lo gestiona:

systemctl status PID

Para saber en qué llamadas al sistema pasa el tiempo, strace -c las cuenta durante unos segundos. Pulsa Ctrl+C para ver el resumen:

sudo strace -c -f -p PID
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 62.14    0.412230          12     33210           read
 21.07    0.139781           8     16804      1203 stat

Si el tiempo se va en código de usuario (%usr alto), perf muestra qué funciones consumen la CPU. Instálalo con las herramientas que corresponden a tu kernel:

sudo apt install linux-tools-common linux-tools-$(uname -r)
sudo perf top -p PID

perf top muestra las funciones más calientes en tiempo real. En lenguajes interpretados (PHP, Python, Ruby) verás sobre todo funciones del intérprete; en esos casos es más útil el perfilador propio del lenguaje o los logs de la aplicación.

Algunas causas habituales según el proceso:

  • PHP-FPM: activa el slow log del pool (slowlog y request_slowlog_timeout en /etc/php/8.3/fpm/pool.d/www.conf) para ver qué scripts tardan.
  • MySQL: ejecuta SHOW FULL PROCESSLIST; para ver las consultas en curso y activa el slow query log.
  • Un proceso que no reconoces: puede ser un minero de criptomonedas instalado tras un compromiso. Consulta la guía sobre cómo detectar si tu servidor está bajo ataque.

Paso 8: Limitar el consumo mientras lo corriges

Si no puedes arreglar la causa de inmediato, reduce el impacto sobre el resto del sistema.

Baja la prioridad de un proceso en ejecución. Un valor nice mayor significa menos prioridad (de -20 a 19):

sudo renice -n 10 -p PID

Para un servicio de systemd, es mejor fijar una cuota de CPU. Este ejemplo limita myapp.service al equivalente a medio núcleo:

sudo systemctl set-property myapp.service CPUQuota=50%

El cambio se aplica al momento y persiste tras reiniciar. Compruébalo con:

systemctl show myapp.service -p CPUQuotaPerSecUSec
CPUQuotaPerSecUSec=500ms

Si el proceso está colgado en un bucle, reinicia el servicio con sudo systemctl restart myapp. Usa kill -9 solo como último recurso, porque el proceso no puede cerrar ficheros ni conexiones limpiamente.

Paso 9: Consultar el histórico con sar

Muchas veces el pico ya ha pasado cuando lo investigas. sysstat puede guardar métricas cada 10 minutos. En Ubuntu la recogida está desactivada por defecto; actívala editando el fichero de configuración:

sudo nano /etc/default/sysstat

Cambia la línea ENABLED="false" por:

ENABLED="true"

Activa el servicio:

sudo systemctl enable --now sysstat

A partir de ese momento, puedes consultar la CPU y la carga del día:

sar -u
sar -q

Y los de un día anterior leyendo su fichero en /var/log/sysstat/, donde saDD corresponde al día del mes (por ejemplo sa24):

sar -u -f /var/log/sysstat/sa24

Solución de problemas

  • Load average alto pero CPU ociosa: la carga incluye procesos en estado D (esperando E/S sin interrupción). Lístalos con ps -eo state,pid,cmd | awk '$1=="D"' y revisa el disco con iostat -xz 2 3.
  • %steal por encima del 10 % de forma sostenida: la máquina virtual no recibe toda la CPU que pide porque el host físico está ocupado. No lo puedes arreglar desde dentro del servidor; contacta con el soporte de CubePath o valora un plan con más recursos.
  • ksoftirqd consumiendo CPU: el kernel está procesando muchas interrupciones de red. Comprueba el tráfico con sar -n DEV 1 5; puede ser un pico legítimo o un ataque.
  • perf no encuentra el paquete linux-tools-$(uname -r): estás usando un kernel sin paquete de herramientas disponible. Instala linux-tools-generic o actualiza al kernel más reciente de la distribución.

Conclusión

Has visto cómo pasar de un síntoma genérico ("el servidor va lento") a una causa concreta: comparar la carga con los núcleos, identificar el tipo de consumo con mpstat, localizar el proceso con top y pidstat, e investigarlo con strace y perf. Como siguientes pasos, deja activa la recogida de sar para tener histórico, configura alertas de CPU en tu sistema de monitorización y revisa también la guía de diagnóstico de alto uso de memoria, ya que la falta de memoria suele manifestarse como alto uso de CPU.