Setup completo para capturar automáticamente qué está causando %wa alto en un servidor Linux, con evidencia forense de cada pico.
Requisitos previos
bash
apt install -y sysstat iotop iptables-persistent
sysstat trae iostat, vmstat. iotop para I/O por proceso.
Paso 1: Script del monitor principal
Crea /usr/local/bin/wa_monitor.sh:
bash
#!/bin/bash
# Monitor de %iowait — captura evidencia cuando supera el umbral
# Uso: wa_monitor.sh [umbral_wa] [intervalo_seg]
THRESHOLD=${1:-15}
INTERVAL=${2:-5}
LOG_DIR="/var/log/wa_monitor"
COOLDOWN=60
mkdir -p "$LOG_DIR"
LAST_CAPTURE=0
echo "[$(date '+%F %T')] Monitor iniciado. Umbral=%wa>${THRESHOLD}, intervalo=${INTERVAL}s"
while true; do
WA=$(top -bn2 -d1 | grep -E '^%Cpu' | tail -1 | awk -F'wa' '{print $1}' | awk '{print $NF}' | tr ',' '.')
WA_INT=$(printf "%.0f" "$WA" 2>/dev/null || echo 0)
NOW=$(date +%s)
if [ "$WA_INT" -ge "$THRESHOLD" ]; then
if [ $((NOW - LAST_CAPTURE)) -lt $COOLDOWN ]; then
sleep "$INTERVAL"
continue
fi
TS=$(date '+%Y%m%d_%H%M%S')
OUT="$LOG_DIR/pico_${TS}_wa${WA_INT}.log"
LAST_CAPTURE=$NOW
{
echo "===== PICO DE IOWAIT ====="
echo "Timestamp: $(date '+%F %T')"
echo "%wa detectado: ${WA}%"
echo "Uptime: $(uptime)"
echo
echo "===== TOP -bn1 (procesos activos) ====="
top -bn1 | head -30
echo
echo "===== PROCESOS EN ESTADO D (esperando I/O) ====="
ps -eo pid,stat,user,command | awk '$2 ~ /D/'
echo
echo "===== IOSTAT -xz 2 3 (uso de disco) ====="
iostat -xz 2 3
echo
echo "===== IOTOP -boPn3 (top de I/O) ====="
iotop -boPqqqn3 2>/dev/null | head -40
echo
echo "===== VMSTAT 2 5 ====="
vmstat 2 5
echo
echo "===== WAZUH / NESSUS STATUS ====="
ps -ef | grep -E 'wazuh-syscheck|nessus' | grep -v grep
echo
echo "===== TOP 10 PROCESOS POR MEMORIA ====="
ps -eo pid,user,%cpu,%mem,rss,command --sort=-%mem | head -11
} > "$OUT" 2>&1
# Dispara el snapshot de contexto en paralelo (opcional)
[ -x /usr/local/bin/ws_snapshot.sh ] && /usr/local/bin/ws_snapshot.sh "ws_snapshot" > /dev/null 2>&1 &
echo "[$(date '+%F %T')] ⚠ Pico capturado (wa=${WA}%) → $OUT"
fi
sleep "$INTERVAL"
done
bash
chmod +x /usr/local/bin/wa_monitor.sh
Qué captura: en cada pico, un archivo pico_YYYYMMDD_HHMMSS_waN.log con top, procesos bloqueados en I/O, iostat, iotop, vmstat, estado de agentes de seguridad y top procesos por RAM.
Paso 2: Script de snapshot complementario (contexto de aplicación)
Crea /usr/local/bin/ws_snapshot.sh. Este es un ejemplo para VS Code Remote — adaptalo al software que estés investigando (procesos Java, contenedores Docker, workers de una app, etc.):
bash
#!/bin/bash
# Snapshot de contexto de aplicación al momento del pico
# Uso: ws_snapshot.sh [prefijo_archivo]
PREFIX="${1:-workspaces}"
OUT="/var/log/wa_monitor/${PREFIX}_$(date +%Y%m%d_%H%M%S).log"
{
echo "===== Snapshot $(date '+%F %T') ====="
echo
echo "--- JDT Language Servers activos (adaptar patrón según tu caso) ---"
ps -eo pid,rss,user,command | grep 'jdt_ws' | grep -v grep | while read pid rss user rest; do
ws=$(echo "$rest" | grep -oP 'workspaceStorage/\K[a-f0-9-]+' | head -1)
gb=$(awk "BEGIN {printf \"%.2f\", $rss/1024/1024}")
printf "PID %-7s | %5s GB | user=%s | ws=%s\n" "$pid" "$gb" "$user" "$ws"
done
echo
echo "--- Total RAM por procesos de interés ---"
ps -eo rss,command | grep 'jdt_ws' | grep -v grep | \
awk '{sum+=$1} END {printf " %.2f GB\n", sum/1024/1024}'
echo
echo "--- Estado de RAM y swap ---"
free -h
echo
echo "--- Top 15 procesos por RAM residente ---"
ps -eo pid,user,%mem,rss,command --sort=-rss | head -16
} > "$OUT" 2>&1
echo "$OUT"
bash
chmod +x /usr/local/bin/ws_snapshot.sh
Personalización: cambiá el patrón grep 'jdt_ws' por lo que quieras trackear. Ejemplos:
- Contenedores Docker:
grep 'docker' | grep -v 'grep'+ agregardocker stats --no-stream - Java apps:
grep 'java' | grep -v grep - Procesos de un usuario:
ps -u <usuario> - PostgreSQL workers:
grep 'postgres'
Paso 3: Servicio systemd (persistencia entre reboots)
Crea /etc/systemd/system/wa-monitor.service:
ini
[Unit]
Description=I/O Wait Monitor
After=multi-user.target
[Service]
Type=simple
ExecStart=/usr/local/bin/wa_monitor.sh 15 5
Restart=on-failure
StandardOutput=append:/var/log/wa_monitor/monitor.log
StandardError=append:/var/log/wa_monitor/monitor.log
[Install]
WantedBy=multi-user.target
Los dos números en ExecStart son umbral %wa y intervalo de muestreo en segundos. Ajustá según sensibilidad deseada (típico: 15 5 para detección temprana, 25 5 si hay ruido de fondo de scans de seguridad).
bash
mkdir -p /var/log/wa_monitor
systemctl daemon-reload
systemctl enable --now wa-monitor
systemctl status wa-monitor
Paso 4: Cron de limpieza automática
bash
crontab -e
Agregar:
0 3 * * * find /var/log/wa_monitor -name 'pico_*.log' -mtime +7 -delete
5 3 * * * find /var/log/wa_monitor -name 'ws_snapshot_*.log' -mtime +7 -delete
Corre a las 3 AM, borra archivos de más de 7 días. Ajustá +7 según cuánto historial quieras conservar.
Comandos útiles para analizar
bash
# Listar picos capturados, más recientes primero
ls -lth /var/log/wa_monitor/pico_*.log | head
# Ver el pico más reciente
sudo cat "$(ls -t /var/log/wa_monitor/pico_*.log | head -1)"
# Ver un pico específico y su snapshot correspondiente (por timestamp)
TS=20260921_1019
sudo cat /var/log/wa_monitor/pico_${TS}*.log
sudo cat /var/log/wa_monitor/ws_snapshot_${TS}*.log
# Ordenar picos por valor de wa (los peores primero)
ls /var/log/wa_monitor/pico_*.log | sort -t'wa' -k2 -n -r | head
# Ver heartbeat del monitor
tail -f /var/log/wa_monitor/monitor.log
# Espacio consumido
du -sh /var/log/wa_monitor/
Glosario de programas que consumen recursos
Sospechosos frecuentes cuando ves %wa alto o carga inexplicable en un servidor Linux:
Kernel / sistema
kswapd0— daemon de swap del kernel. Si aparece al 100% de CPU, es swap thrashing: RAM llena, kernel paginando entre disco y memoria. La causa raíz es siempre falta de RAM.kworker/*— threads de trabajo del kernel. Actividad alta suele indicar drivers de disco/red trabajando duro.jbd2/dm-0-8— journal de ext4. Si genera mucho I/O, hay muchas escrituras en el filesystem.
Agentes de seguridad (muy comunes en empresas)
pmd— Palo Alto Cortex XDR (endpoint protection). Usafanotifypara monitorear cada archivo tocado. Puede consumir 100%+ CPU cuando otro proceso está haciendo I/O intensivo.wazuh-syscheckd— Wazuh File Integrity Monitoring. Recorre el filesystem periódicamente calculando hashes.wazuh-modulesd:syscollector— inventario de paquetes, procesos, puertos, hardware. Corre por defecto cada 1h.wazuh-agentd,wazuh-logcollector,wazuh-modulesd— otros componentes de Wazuh.nessus-service,nessus-agent-module,nessusd— agente de Tenable Nessus para escaneo de vulnerabilidades.sandboxd(cortexus) — análisis de archivos con YARA de Cortex XDR.fail2ban-server— puede generar I/O leyendo logs cuando hay muchos intentos.
Desarrollo (VS Code Remote / Java)
java ... jdt_ws— JDT Language Server de Red Hat (extensión Java para VS Code). Indexa proyectos. Configurado con-Xmx5Gpor defecto — un solo workspace puede pedir 5 GB.node ... vscode-server— servidor de VS Code Remote. Usabootstrap-forkpara extensionHost, fileWatcher, ptyHost.java ... oracle.dbtools— extensión Oracle SQL Developer, arranca un JDK propio por conexión.node .../.vscode-server/con--type=extensionHost— cada extensión que corre en su propio process host.
Contenedores / servicios
dockerd,containerd— daemons de Docker.docker ps,docker eventsgeneran lecturas.snapd— daemon de snap packages. Refresca automáticamente snaps de vez en cuando (I/O importante).
Sistema operativo / escritorio
systemd-journald— recolector de logs de systemd. Si está en estado D, hay presión sobre/var/log/journal/.tracker-miner-fs-3— indexador de archivos de GNOME. En servidores no tiene sentido, se puede desactivar.gnome-shell,gsd-*— shell y daemons de GNOME. No deberían correr en un server sin GUI.vmtoolsd— VMware Tools. Se comunica con el hypervisor, actividad ligera pero constante.
Backups / replicación
rsync,borg,restic— herramientas de backup, cuando corren generan I/O masivo.vgauth, procesos de snapshot — cuando el hypervisor toma un snapshot de la VM, el I/O del guest sufre.
Interpretación rápida del pico_*.log
Cuando abrás un pico, mirá en este orden:
%Cpu(s)line entop: ¿qué proporción esus,sy,wa?waalto +syalto = kernel bloqueado en I/Owaalto +usbajo = procesos esperando disco, no CPU
kswapd0al 100% en top → falta RAM, revisáfree -hen el snapshot- Procesos en estado D: son los que están esperando I/O. El culpable directo.
iostat: mirá%utildel dispositivosda(o el que corresponda) yw_await/r_await. Latencia > 20ms = disco saturado.iotop: quién está leyendo/escribiendo. Ojo con los?difque a veces significanwaiting for I/O.- Swap usage en
free -h: siInter usadoestá cerca del 100% → swap thrashing.
Combinación clásica del thrashing por RAM: kswapd0 al 100% + swap casi lleno + %wa > 50% + iotop mostrando lecturas de swap.
Combinación clásica del scan de seguridad: pmd al 100% + procesos wazuh/nessus activos + %wa moderado (15-25%) + duración corta (< 2 min).
Combinación clásica del backup del hypervisor: %wa alto, pero ningún proceso interno explica el I/O — hay que mirar métricas del datastore en vCenter.