Alto Load Average: Qué Significa y Cómo Solucionarlo

Introducción

El load average es una de las métricas más comúnmente monitoreadas pero frecuentemente mal interpretadas en la administración de sistemas Linux. Un alto load average puede indicar estrés del sistema, pero interpretar qué constituye "alto" e identificar la causa subyacente requiere entender qué mide realmente el load average y cómo difiere del uso de CPU.

Esta guía completa explica qué significa el load average, cómo interpretarlo correctamente y proporciona enfoques sistemáticos para diagnosticar y resolver situaciones de alta carga. Aprenderás a distinguir entre problemas ligados a CPU, ligados a I/O y problemas de sueño ininterrumpible, permitiéndote identificar rápidamente y corregir la causa raíz de la degradación del rendimiento del sistema.

Entender el load average es crítico para mantener el rendimiento del sistema y prevenir interrupciones. Mientras que el uso de CPU muestra utilización instantánea, el load average revela el estrés del sistema a lo largo del tiempo, convirtiéndolo en una métrica invaluable para la planificación de capacidad y solución de problemas de rendimiento.

Entendiendo el Load Average

¿Qué es el Load Average?

El load average representa el número promedio de procesos en estado ejecutable o sueño ininterrumpible durante un período de tiempo específico. Incluye:

  1. Procesos ejecutables (estado R): Listos para ejecutar, esperando CPU
  2. Sueño ininterrumpible (estado D): Esperando por I/O (disco, red)

Perspectiva clave: El load average mide la demanda de recursos del sistema, no solo el uso de CPU.

Los Tres Números

El load average muestra tres valores:

load average: 1.50, 2.30, 1.80
              ^     ^     ^
              |     |     |
              |     |     +-- Promedio de 15 minutos
              |     +-------- Promedio de 5 minutos
              +-------------- Promedio de 1 minuto

Interpretación:

  • 1 minuto: Tendencia de carga actual
  • 5 minutos: Tendencia de carga reciente
  • 15 minutos: Tendencia de carga a largo plazo

¿Qué es Normal?

El load average es relativo al conteo de CPUs:

# Verificar conteo de CPU
nproc
lscpu | grep "^CPU(s)"

# Ejemplos de sistema de 4 núcleos:
# Load: 1.0 = 25% utilizado
# Load: 2.0 = 50% utilizado
# Load: 4.0 = 100% utilizado
# Load: 8.0 = 200% utilizado (sobrecargado)

Pautas generales:

  • Load < Conteo CPU: Sistema saludable
  • Load = Conteo CPU: Sistema a capacidad
  • Load > Conteo CPU: Sistema sobrecargado

Evaluación Inicial del Load Average

Verificación Rápida de Carga

# Ver load average
uptime

# Carga detallada del sistema
w

# Carga con información del sistema
top -bn1 | head -5

# Carga histórica (si sar está instalado)
sar -q

# Conteo de CPU para comparación
CPUS=$(nproc)
LOAD=$(uptime | awk -F'load average:' '{print $2}' | awk '{print $1}' | cut -d',' -f1)
echo "CPUs: $CPUS, Load: $LOAD"

# Verificación rápida de salud
if (( $(echo "$LOAD > $CPUS" | bc -l) )); then
    echo "System overloaded!"
else
    echo "System load normal"
fi

Entendiendo el Patrón

# Carga creciente (sistema poniéndose más ocupado)
load average: 0.80, 1.50, 2.30

# Carga decreciente (sistema recuperándose)
load average: 2.30, 1.50, 0.80

# Pico de carga (evento reciente)
load average: 5.20, 1.50, 0.80

# Carga alta sostenida (problema continuo)
load average: 8.50, 8.20, 7.90

Paso 1: Determinar Tipo de Carga

Ligado a CPU vs Ligado a I/O

# Verificar uso de CPU
top -bn1 | grep "Cpu(s)"

# Si uso de CPU alto (>70%) Y carga alta
# ENTONCES ligado a CPU

# Si espera I/O (wa) alta (>20%) Y carga alta
# ENTONCES ligado a I/O

# Verificar espera I/O específicamente
mpstat 1 5 | awk '/Average/ {print "I/O Wait:", $6"%"}'

# Desglose detallado de CPU
mpstat -P ALL 1 3

Identificar Estados de Procesos

# Contar procesos en cada estado
ps aux | awk '{print $8}' | sort | uniq -c

# R = Running (ligado a CPU)
# D = Uninterruptible sleep (ligado a I/O)
# S = Sleeping
# Z = Zombie
# T = Stopped

# Encontrar procesos en estado D
ps aux | awk '$8 ~ /D/ {print}'

# Encontrar procesos en estado R
ps aux | awk '$8 ~ /R/ {print}'

# Contar ejecutables vs esperando
echo "Runnable: $(ps aux | awk '$8 ~ /R/' | wc -l)"
echo "I/O Wait: $(ps aux | awk '$8 ~ /D/' | wc -l)"

Paso 2: Análisis de Carga Ligada a CPU

Identificar Consumidores de CPU

# Procesos principales de CPU
ps aux --sort=-%cpu | head -15

# Monitoreo de CPU en tiempo real
top
# Presionar 'P' para ordenar por CPU
# Presionar '1' para ver uso por núcleo

# CPU por proceso
pidstat -u 1 5

# Uso de CPU por comando
ps aux | awk '{cmd[$11]++; cpu[$11]+=$3} END {for(c in cmd) print cmd[c], cpu[c], c}' | sort -rn | head -15

# Encontrar procesos usando 100% CPU
ps aux | awk '$3 > 90 {print}'

# Uso de CPU multi-hilo
top -H
# Muestra hilos en lugar de procesos

Análisis de CPU

# Verificar frecuencia de CPU
lscpu | grep MHz

# Verificación de throttling de CPU
cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq

# Cambios de contexto (alto = contención de CPU)
vmstat 1 5
# Mirar columna 'cs'

# Utilización por CPU
mpstat -P ALL 1 3

# Tiempo de robo de CPU (virtualización)
top -bn1 | grep "Cpu(s)" | awk '{print "Steal:", $16}'
# Steal alto = contención del hipervisor

Paso 3: Análisis de Carga Ligada a I/O

Análisis de I/O de Disco

# Estadísticas de I/O
iostat -x 1 5

# Métricas clave:
# %util aproximándose a 100% = cuello de botella
# await > 20ms = respuestas lentas
# svctm > 10ms = problemas de tiempo de servicio

# I/O por proceso
iotop -o
# Muestra solo procesos haciendo I/O

# I/O por proceso
pidstat -d 1 5

# Encontrar procesos intensivos en I/O
ps aux --sort=-%mem | head -15

# Uso de disco
df -h
du -sh /* | sort -rh | head -10

Identificar Cuellos de Botella de I/O

# Procesos en estado D (ininterrumpible)
ps aux | awk '$8 ~ /D/ {print}'

# Por qué estos procesos están esperando
for pid in $(ps aux | awk '$8 ~ /D/ {print $2}'); do
    echo "PID: $pid"
    cat /proc/$pid/wchan 2>/dev/null
    cat /proc/$pid/stack 2>/dev/null
done

# Verificar cuelgues de NFS
mount | grep nfs
df -h | grep nfs

# I/O de red
iftop -i eth0
nethogs

# Verificar I/O de swap
vmstat 1 5
# Columnas si/so muestran entrada/salida de swap

Paso 4: Análisis de Recursos del Sistema

Presión de Memoria

# Estado de memoria
free -h

# Uso de swap
swapon --show
vmstat 1 5

# Memoria por proceso
ps aux --sort=-%mem | head -15

# Fallos de página
vmstat -s | grep "page faults"

# Eventos OOM
dmesg | grep -i "out of memory"
grep "killed process" /var/log/kern.log

Carga de Red

# Conexiones de red
ss -s
netstat -an | awk '{print $6}' | sort | uniq -c

# Conteo de conexiones
ss -tan | grep ESTABLISHED | wc -l

# Rendimiento de red
iftop -i eth0

# Ancho de banda por proceso
nethogs eth0

# Errores de red
ip -s link
netstat -i

Paso 5: Análisis de Procesos

Encontrar Procesos Problemáticos

# Procesos ordenados por contribución de carga
ps aux --sort=-%cpu | head -20

# Procesos de larga ejecución
ps -eo pid,user,etime,%cpu,%mem,cmd --sort=-etime | head -20

# Procesos con más hilos
ps -eo pid,nlwp,cmd --sort=-nlwp | head -15

# Encontrar procesos desbocados
ps aux | awk '$3 > 80 || $4 > 80 {print}'

# Árbol de procesos
ps auxf | less
pstree -p | less

Investigación Detallada de Procesos

# Investigar proceso específico
PID=1234

# ¿Qué está haciendo?
strace -p $PID -c

# Llamadas al sistema
strace -p $PID 2>&1 | head -100

# Archivos abiertos
lsof -p $PID

# Conteo de hilos
ps -o nlwp -p $PID

# Afinidad de CPU
taskset -p $PID

# Mapa de memoria
pmap -x $PID

# Traza de pila
cat /proc/$PID/stack

Soluciones y Remediación

Soluciones Ligadas a CPU

Acciones inmediatas:

# Reducir prioridad del proceso
renice +10 PID

# Establecer afinidad de CPU (limitar a núcleos específicos)
taskset -cp 0,1 PID

# Limitar uso de CPU con cpulimit
cpulimit -p PID -l 50  # Limitar al 50%

# Matar proceso intensivo en recursos
kill PID
kill -9 PID  # Forzar terminación

Correcciones de configuración:

# Límites de workers de Apache/Nginx
# Apache - /etc/apache2/mods-available/mpm_prefork.conf
MaxRequestWorkers 150

# Nginx - /etc/nginx/nginx.conf
worker_processes auto;
worker_connections 1024;

# PHP-FPM - /etc/php/fpm/pool.d/www.conf
pm.max_children = 50

# MySQL - /etc/mysql/my.cnf
max_connections = 200

Soluciones Ligadas a I/O

Acciones inmediatas:

# Sincronizar y limpiar caché
sync
echo 3 > /proc/sys/vm/drop_caches

# Ajustar scheduler de I/O
echo deadline > /sys/block/sda/queue/scheduler

# Reducir prioridad de I/O del proceso
ionice -c 3 -p PID  # Clase Idle

# Reducir swappiness
sysctl vm.swappiness=10
echo "vm.swappiness=10" >> /etc/sysctl.conf

Correcciones a largo plazo:

# Optimizar montajes de sistema de archivos
# Agregar 'noatime' a /etc/fstab
/dev/sda1 / ext4 defaults,noatime 0 1

# Aumentar read-ahead
blockdev --setra 8192 /dev/sda

# Optimizar base de datos
# Buffer pool de MySQL
innodb_buffer_pool_size = 4G

# Agregar SSD para base de datos
# Mover MySQL a partición SSD

Optimización de Memoria

# Limpiar caché de memoria
sync && echo 3 > /proc/sys/vm/drop_caches

# Agregar swap
dd if=/dev/zero of=/swapfile bs=1G count=4
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile

# Ajustar swappiness
sysctl vm.swappiness=10

# Establecer en /etc/sysctl.conf
vm.swappiness = 10
vm.vfs_cache_pressure = 50

# Matar consumidores de memoria
kill $(ps aux --sort=-%mem | head -2 | tail -1 | awk '{print $2}')

Monitoreo y Prevención

Script de Monitoreo de Carga

cat > /usr/local/bin/load-monitor.sh << 'EOF'
#!/bin/bash

LOG_FILE="/var/log/load-monitor.log"
ALERT_EMAIL="[email protected]"
CPU_COUNT=$(nproc)
THRESHOLD=$(echo "$CPU_COUNT * 1.5" | bc)

LOAD=$(uptime | awk -F'load average:' '{print $2}' | awk '{print $1}' | cut -d',' -f1)

echo "$(date): Load: $LOAD, CPUs: $CPU_COUNT" >> "$LOG_FILE"

if (( $(echo "$LOAD > $THRESHOLD" | bc -l) )); then
    echo "$(date): High load detected: $LOAD" >> "$LOG_FILE"

    # Capturar estado del sistema
    echo "=== Top Processes ===" >> "$LOG_FILE"
    ps aux --sort=-%cpu | head -15 >> "$LOG_FILE"

    echo "=== I/O Stats ===" >> "$LOG_FILE"
    iostat -x >> "$LOG_FILE"

    echo "=== Memory ===" >> "$LOG_FILE"
    free -h >> "$LOG_FILE"

    # Enviar alerta
    echo "High load: $LOAD on $(hostname)" | \
        mail -s "Load Alert: $LOAD" "$ALERT_EMAIL"
fi
EOF

chmod +x /usr/local/bin/load-monitor.sh
echo "*/5 * * * * /usr/local/bin/load-monitor.sh" | crontab -

Línea Base de Rendimiento

cat > /usr/local/bin/performance-baseline.sh << 'EOF'
#!/bin/bash

BASELINE_DIR="/var/log/performance-baseline"
mkdir -p "$BASELINE_DIR"
DATE=$(date +%Y%m%d-%H%M%S)

# Capturar línea base
uptime > "$BASELINE_DIR/uptime-$DATE.txt"
ps aux --sort=-%cpu | head -50 > "$BASELINE_DIR/processes-$DATE.txt"
iostat -x > "$BASELINE_DIR/iostat-$DATE.txt"
free -h > "$BASELINE_DIR/memory-$DATE.txt"
mpstat -P ALL > "$BASELINE_DIR/cpu-$DATE.txt"

echo "Baseline captured: $DATE"
EOF

chmod +x /usr/local/bin/performance-baseline.sh
echo "0 */6 * * * /usr/local/bin/performance-baseline.sh" | crontab -

Planificación de Capacidad

# Rastrear carga a lo largo del tiempo con sar
sar -q 1 86400 > daily-load.txt

# Carga promedio del día
sar -q | awk '/Average/ {print "Avg Load:", $4, $5, $6}'

# Tiempos de carga pico
sar -q | sort -k5 -rn | head -10

# Análisis de tendencia
cat > /tmp/load-trend.sh << 'EOF'
#!/bin/bash
for i in {30..1}; do
    DATE=$(date -d "$i days ago" +%d)
    AVG=$(sar -q -f /var/log/sysstat/sa$DATE 2>/dev/null | \
        awk '/Average/ {print $4}')
    echo "$(date -d "$i days ago" +%Y-%m-%d): $AVG"
done
EOF

chmod +x /tmp/load-trend.sh
/tmp/load-trend.sh

Diagnósticos Avanzados

Usando perf

# Análisis de rendimiento a nivel de sistema
perf top

# Registrar eventos
perf record -a -g -- sleep 30

# Analizar grabación
perf report

# Gráfico de llama de CPU
perf record -F 99 -a -g -- sleep 60
perf script | ./FlameGraph/stackcollapse-perf.pl | \
    ./FlameGraph/flamegraph.pl > load-analysis.svg

Análisis del Kernel

# Verificar mensajes del kernel
dmesg | tail -100

# Parámetros del kernel
sysctl -a | grep -E "threads-max|pid_max"

# Límites de procesos
cat /proc/sys/kernel/threads-max
cat /proc/sys/kernel/pid_max

# Conteo actual de procesos
ps aux | wc -l

Conclusión

El load average es una métrica crítica para entender la salud del sistema, pero debe interpretarse en contexto con el conteo de CPU y otras métricas. Conclusiones clave:

  1. Comparar con conteo de CPU: Carga relativa a núcleos, no absoluta
  2. Verificar tendencia: Carga creciente vs decreciente vs estable
  3. Identificar tipo: Ligada a CPU vs ligada a I/O
  4. Encontrar causa raíz: Procesos principales, I/O de disco o memoria
  5. Monitorear continuamente: Rastrear líneas base y tendencias
  6. Planificar capacidad: Usar datos históricos para planificación de recursos
  7. Actuar apropiadamente: Diferentes causas necesitan diferentes soluciones

Entender el load average permite una planificación proactiva de capacidad y una resolución rápida de problemas de rendimiento. El monitoreo regular, el seguimiento de líneas base y estas técnicas de diagnóstico garantizan un rendimiento óptimo del sistema y previenen interrupciones relacionadas con la carga.