Metodología de diagnóstico de problemas en Linux

Cuando algo falla en un sistema Linux, la reacción instintiva suele ser ejecutar el primer comando que venga a la mente o, peor, reiniciar directamente. Ambas respuestas pueden funcionar por casualidad, pero reproducirán el problema la semana siguiente porque nunca llegaste a entender qué pasó. Una metodología estructurada no es burocracia: es el mecanismo que convierte un síntoma confuso en una causa raíz localizable.

La idea central es sencilla: los problemas tienen contexto, tienen capas y tienen historia. El contexto responde a qué, cuándo y quién. Las capas indican dónde buscar. La historia revela por qué ocurrió ahora y no antes. Si atacas el síntoma sin cubrir estos tres ejes, estás adivinando.

Esta metodología funciona así por una razón de diseño: Linux expone su estado a través de capas bien delimitadas —hardware, kernel, filesystem y red, servicios, aplicación, configuración—, cada una con sus propias herramientas de observación. Saltar directamente a la capa de aplicación cuando el problema está en el kernel es como revisar el código de una web app cuando el disco está lleno. El diagnóstico de abajo arriba respeta esa arquitectura.

¿Cuándo aplicar esto? Siempre que el problema no sea trivial y evidente. Un servicio que no arranca, una latencia inesperada, un usuario que no puede acceder, un proceso que consume CPU sin razón aparente: todos se benefician del mismo marco. Lo que cambia es hasta qué capa hay que bajar.

Lo que se rompe si lo ignoras es peor que el problema original: arreglas el síntoma, el sistema vuelve a funcionar, y en dos semanas tienes exactamente el mismo incidente porque la causa raíz sigue ahí. Cada recurrencia cuesta más que haberlo investigado bien la primera vez.

Ejemplo: servicio web que falla de forma intermitente

El escenario es real y común: nginx está activo según systemd, pero algunos requests devuelven 502. No todos, no siempre. Veamos el proceso completo.

# ── PASO 1: DELIMITAR ────────────────────────────────────────────────────────

# ¿Desde cuándo? Revisamos eventos recientes del sistema
journalctl --since "2 hours ago" -p err --no-pager

# ¿Qué cambió recientemente? El historial de apt revela instalaciones/upgrades
cat /var/log/apt/history.log | tail -40

# ¿Afecta a todos los usuarios o solo a algunos requests?
# Miramos el log de acceso de nginx antes de tocar nada
tail -100 /var/log/nginx/access.log | grep " 502 "

# ── PASO 2: LEER LOS LOGS PRIMERO ────────────────────────────────────────────

# El primer movimiento siempre: logs del servicio afectado
journalctl -u nginx.service -n 100 --no-pager

# Si nginx hace de proxy, el problema puede estar en el backend (ej. php-fpm)
journalctl -u php8.2-fpm.service -n 100 --no-pager

# ── PASO 3: CAPAS DE ABAJO ARRIBA ────────────────────────────────────────────

# CAPA 1 – Hardware: ¿hay errores de disco o memoria?
journalctl -k -p err --no-pager | tail -20   # mensajes del kernel (-k = dmesg)
df -h                                         # espacio en disco
free -h                                       # memoria disponible

# CAPA 2 – Filesystem/red: ¿el disco tiene inodos libres? ¿hay drops en la red?
df -i /var/lib/php/sessions    # los 502 por falta de inodos son clásicos
ss -s                          # resumen de sockets: cuántos están en TIME_WAIT

# CAPA 3 – Servicio (systemd): estado real del servicio y sus dependencias
systemctl status nginx.service php8.2-fpm.service
# Fíjate en el campo "Active" y en los últimos eventos del journal integrado

# CAPA 4 – Aplicación: ¿el proceso responde? ¿cuántos workers hay?
ps aux | grep php-fpm          # workers activos
# Si todos los workers están ocupados, nginx recibe 502 aunque fpm esté "activo"

# CAPA 5 – Configuración: ¿algo cambió en los archivos de config?
# git diff sobre /etc si tienes etckeeper, o comparación manual
stat /etc/php/8.2/fpm/pool.d/www.conf   # fecha de última modificación

# ── PASO 4: CAPTURAR EL ESTADO ANTES DE CUALQUIER CAMBIO ─────────────────────

# Si vas a reiniciar, guarda el estado primero — después no habrá rastro
journalctl -u php8.2-fpm.service --since "1 hour ago" --no-pager \
  > /tmp/fpm-antes-reinicio.txt

systemctl status php8.2-fpm.service >> /tmp/fpm-antes-reinicio.txt

# Captura de conexiones activas al puerto 9000 (socket fpm en modo TCP)
ss -tnp sport = :9000 >> /tmp/fpm-antes-reinicio.txt

# ── PASO 5: UN CAMBIO A LA VEZ ───────────────────────────────────────────────

# Hipótesis: workers de fpm agotados. Verificamos el límite actual
grep '^pm.max_children' /etc/php/8.2/fpm/pool.d/www.conf
# Si el valor es bajo (ej. 5) y hay carga real, aumentamos SOLO este parámetro

# Antes de editar, hacemos copia con fecha para poder revertir
cp /etc/php/8.2/fpm/pool.d/www.conf \
   /etc/php/8.2/fpm/pool.d/www.conf.bak-$(date +%Y%m%d-%H%M)

# Editamos y recargamos (reload, no restart — no interrumpe requests en curso)
# pm.max_children = 20   ← valor nuevo
systemctl reload php8.2-fpm.service

# ── PASO 6: VERIFICAR QUE EL CAMBIO TUVO EFECTO ──────────────────────────────

# Comprobamos que fpm aceptó la nueva configuración sin errores
journalctl -u php8.2-fpm.service -n 20 --no-pager

# Observamos si los 502 desaparecen en el log de nginx en tiempo real
tail -f /var/log/nginx/access.log | grep --line-buffered " 502 "
# Ctrl+C después de 2-3 minutos si no aparecen más errores

# ── PASO 7: DOCUMENTAR ───────────────────────────────────────────────────────

# Mínimo viable: fecha, síntoma, causa raíz, cambio aplicado, cómo verificarlo
cat >> /var/log/incidencias-manuales.log << 'EOF'
2024-11-15 16:30 | 502 intermitentes en nginx
  Causa raíz: pm.max_children=5 insuficiente con la carga actual (picos de 18 workers)
  Cambio:     pm.max_children=20 en /etc/php/8.2/fpm/pool.d/www.conf
  Verificado: ausencia de 502 en access.log durante 30 min post-cambio
  Pendiente:  revisar por qué aumentó la carga (posible crawler, ver access.log)
EOF

Lo que está pasando en cada decisión

journalctl -u nginx.service -n 100 --no-pager antes que cualquier otra cosa. No systemctl restart, no editar configuración. Los logs son la única fuente de verdad sobre lo que ocurrió antes de que llegaras. --no-pager es importante en sesiones remotas o cuando vas a redirigir la salida; sin él, journalctl abre un paginador interactivo que rompe scripts y tuberías.

journalctl -k -p err revisa el journal del kernel, equivalente a filtrar dmesg por severidad. Los 502 por disco lleno, por OOM killer o por errores de hardware aparecen aquí, no en el log de la aplicación. Saltarte esta capa y ir directo a la configuración de nginx es el error más común.

df -i /var/lib/php/sessions merece atención especial. Un disco puede tener espacio libre en bytes pero tener los inodos agotados, lo que impide crear nuevos ficheros. df -h no te lo muestra; necesitas df -i. PHP-FPM crea un fichero de sesión por usuario activo; en sitios con muchas sesiones y limpieza infrecuente, esto falla silenciosamente y genera 502 que parecen problemas de red.

Capturar el estado antes de reiniciar es la parte que más se salta bajo presión. Si reinicias php-fpm sin guardar nada, pierdes el conteo exacto de workers saturados, los mensajes de error del momento del fallo y el estado de las conexiones activas. El fichero en /tmp con la fecha es suficiente; no necesitas nada elaborado.

systemctl reload en lugar de restart para php-fpm recarga la configuración sin matar los workers que están procesando requests en ese momento. Un restart en producción interrumpe conexiones activas y puede causar exactamente los 502 que estás intentando eliminar. La distinción entre reload y restart existe precisamente para este escenario.

El bloque de documentación no es opcional ni burocrático. La línea Pendiente: es la más importante: identifica que el aumento de carga es un síntoma de algo más, probablemente un crawler o un cambio de tráfico. Si solo subes pm.max_children y cierras el ticket, en un mes lo bajarán de nuevo “para liberar memoria” y el problema volverá. La causa raíz aquí no es el valor de configuración: es el cambio de patrón de carga que lo hizo insuficiente.

100

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio