Saltar al contenido principal

Módulo 15 — Monitorización y rendimiento

Introducción

«El servidor va lento» es el ticket más frecuente en cualquier equipo de operaciones. La respuesta típica —abrir top, ver la CPU al 90% y matar el proceso más pesado— es la forma más rápida de no resolver el problema real.

El rendimiento de un sistema es el resultado de cuatro recursos interactuando: CPU, memoria, disco y red. El cuello de botella puede estar en cualquiera de ellos, puede ser latencia o ancho de banda, puede ser del kernel o de la aplicación. Probar comandos al azar sin un modelo mental es ineficiente y a menudo contraproducente.

Este módulo enseña primero la metodología (el modelo mental), luego las herramientas para cada recurso, y termina con la observabilidad continua que permite detectar problemas antes de que los usuarios los reporten.

Los módulos anteriores proveen el contexto: procesos y señales en el Módulo 09, almacenamiento en el Módulo 12, parámetros del kernel en el Módulo 13.

Objetivos de aprendizaje

Al finalizar este módulo, serás capaz de:

  • ✅ Aplicar el método USE para diagnosticar cualquier recurso de forma sistemática
  • ✅ Interpretar correctamente load average, top, vmstat y iostat
  • ✅ Identificar cuellos de botella de CPU, memoria, E/S de disco y red
  • ✅ Trazar syscalls con strace y perfilar con perf record + flame graphs
  • ✅ Usar eBPF/bpftrace para observabilidad a nivel de kernel sin parchear el sistema
  • ✅ Configurar sysstat/atop para históricos post-mortem
  • ✅ Gestionar logs con journald, rsyslog y logrotate
  • ✅ Desplegar Prometheus + node_exporter + Grafana para monitorización continua

15.1 — Metodología de análisis de rendimiento

El método USE

El método USE (Utilization, Saturation, Errors), desarrollado por Brendan Gregg, propone analizar cada recurso del sistema en tres dimensiones antes de sacar conclusiones:

Para cada recurso (CPU, memoria, discos, interfaces de red):

UTILIZACIÓN ¿Qué fracción del tiempo útil está ocupada?
Un recurso al 100% de utilización está saturado.
Ejemplo: CPU a 95% us → cuello de botella en CPU de usuario

SATURACIÓN ¿Hay trabajo que no puede atenderse y queda en cola?
Un recurso puede estar al 70% de utilización pero
tener una cola creciente → problema de latencia.
Ejemplo: load average > número de CPUs → run queue saturada

ERRORES ¿Hay eventos de error? Los errores consumen recursos
y pueden enmascarar el problema real.
Ejemplo: errores de NIC → retransmisiones → CPU y latencia

Aplicación práctica:
1. Listar todos los recursos del sistema
2. Para cada uno: verificar U, S y E con las herramientas adecuadas
3. El primer recurso con indicadores fuera de rango = cuello de botella
4. Investigar la causa raíz (no el síntoma)

Los primeros 60 segundos en un sistema nuevo

Cuando conectas a un servidor con problemas de rendimiento sin contexto previo, estos comandos dan una imagen completa en menos de un minuto:

# 1. uptime: load average y tiempo encendido
uptime
# 11:52:03 up 45 days, 3:12, 2 users, load average: 2.14, 3.02, 2.89
# Los tres números: promedios de los últimos 1, 5 y 15 minutos
# INTERPRETAR: comparar siempre con el número de CPUs (nproc)
# Si load average > nproc → el sistema tiene más trabajo del que puede procesar

# 2. dmesg: errores recientes del kernel
dmesg -T | tail -20
dmesg -T -l err,crit,alert,emerg # Solo errores (módulo 13)

# 3. vmstat 1: panorama completo cada segundo
vmstat 1 5 # 5 muestras, una por segundo
# procs: r=run queue b=bloqueados en E/S
# swap: si=swap in so=swap out (si so>0 continuamente: problema grave)
# cpu: us=user sy=system id=idle wa=iowait st=stolen por hypervisor

# 4. mpstat: CPU por núcleo
mpstat -P ALL 1 3

# 5. free: memoria
free -h

# 6. iostat: estado de los discos
iostat -x 1 3

# 7. ss -s: resumen de sockets
ss -s

# 8. sar -n DEV 1: tráfico de red
sar -n DEV 1 3 # (requiere sysstat instalado)

# Todo-en-uno para un primer vistazo rápido:
sudo apt install glances
glances # CPU, RAM, red, disco en una sola pantalla interactiva

Load average: la interpretación correcta

Load average en Linux incluye:
• Procesos en la run queue (esperando CPU)
• Procesos en estado D (espera de E/S ininterrumpible)

Por tanto, load average alto puede significar:
a) CPU saturada (muchos procesos queriendo CPU)
b) E/S saturada (muchos procesos esperando disco/red)
c) Ambas → desambiguar con vmstat (r vs b) e iostat

ESCALA POR NÚMERO DE CPUs:
• 1 CPU: load 1.0 = saturado load 0.5 = al 50%
• 8 CPUs: load 8.0 = saturado load 2.0 = al 25%
• 32 CPUs: load 10.0 = tranquilo load 32.0 = saturado

TENDENCIA más importante que el valor absoluto:
load(1min) >> load(15min) → problema RECIENTE, empeorando
load(1min) << load(15min) → problema reciente que ya MEJORÓ

15.2 — CPU

top y htop en profundidad

# Líneas de CPU en top (interpretar con método USE):
# %Cpu(s): 2.3 us, 0.5 sy, 0.0 ni, 96.8 id, 0.3 wa, 0.0 hi, 0.1 si, 0.0 st
#
# us (user): tiempo en código de usuario. Alto → la app consume CPU
# sy (system): tiempo en llamadas al sistema. Alto → mucho I/O o syscalls
# ni (nice): tiempo en procesos con prioridad nice modificada
# id (idle): tiempo libre
# wa (iowait): esperando E/S de disco. Alto → cuello de botella en I/O
# hi (hw irq): interrupciones hardware. Alto → problema de dispositivo
# si (sw irq): interrupciones software (red). Alto → saturación de paquetes
# st (stolen): robado por el hypervisor. Alto en VMs → infraestructura sobrevendida

# Diagnóstico por síntoma:
# wa alto (>20%) → problema de disco (ver §15.4)
# us+sy (>90%) → CPU saturada
# si alto (>5%) → problema de red (muchos paquetes/segundo)
# st alto (>5%) → mala vecindad en el hipervisor (ver módulo 16)

# Atajos dentro de top:
# 1 → mostrar todos los cores individualmente
# M → ordenar por memoria
# P → ordenar por CPU (por defecto)
# c → mostrar la línea de comandos completa
# u → filtrar por usuario
# k → matar un proceso (introduce el PID)
# mpstat: CPU por núcleo (detect. de desequilibrio entre cores)
sudo apt install sysstat
mpstat -P ALL 1 # Todos los núcleos, cada segundo
# Si un core está al 100% y los demás al 5% → app single-threaded: no escala
# Si todos están al 100% → carga masiva o bug de sincronización

# pidstat: CPU por proceso en tiempo real
pidstat 1 # Todos los procesos, actualización cada segundo
pidstat -p 1234 1 # Solo el proceso 1234
pidstat -u -t 1 # Incluir hilos (threads) individuales

# Procesos en estado D (esperando I/O — contribuyen al load average)
ps aux | awk '$8 == "D" {print}'
# Si hay muchos en estado D → saturación de I/O (ver §15.4)

Gobernadores de CPU y frecuencia

# Ver el gobernador de CPU actual
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
# powersave: mínima frecuencia → menor consumo (laptops en batería)
# performance: máxima frecuencia siempre → máximo rendimiento
# schedutil: integrado con el planificador CFS (moderno, recomendado)
# ondemand: sube la frecuencia bajo carga (Linux por defecto históricamente)

# Cambiar el gobernador
sudo apt install linux-tools-$(uname -r) 2>/dev/null || sudo apt install linux-tools-generic
sudo cpupower frequency-set -g performance # Para todos los cores
sudo cpupower frequency-info # Ver estado y frecuencias disponibles

# Ver la frecuencia actual
cat /proc/cpuinfo | grep "cpu MHz"
# turbostat (requiere permisos root y módulo msr)
sudo turbostat --quiet --show Pkg_J,Cor,CPU,Busy%,Bzy_MHz 2>/dev/null

15.3 — Memoria

El mito de la «memoria llena»

Linux usa toda la RAM libre como caché de páginas del disco.
Esta caché acelera el sistema (evita leer el disco si la página ya está en RAM).
La caché se libera AUTOMÁTICAMENTE cuando una aplicación necesita más memoria.

free -h bien interpretado:

total used free shared buff/cache available
Mem: 31Gi 8.2Gi 1.1Gi 456Mi 22Gi 22Gi
Swap: 2Gi 0Bi 2Gi

• "used" (8.2G): memoria en uso real por procesos de usuario
• "buff/cache" (22G): usada por el KERNEL como caché (NO es memoria "perdida")
• "available" (22G): estimación de cuánta puede pedir una app nueva
(libre + caché que se puede liberar)
• "free" (1.1G): literalmente sin usar para nada

ALARMA REAL: "available" cercano a 0 + vmstat muestra "so" (swap out) > 0
NO ES ALARMA: "free" = 100M pero "available" = 20G → el kernel usa la RAM bien
# vmstat: análisis de memoria y swap
vmstat 1

# Columnas de memoria:
# swpd: memoria actualmente en swap (si >0: ha habido presión de memoria)
# free: memoria libre
# buff: buffers del kernel (metadatos de dispositivos de bloque)
# cache: caché de páginas

# Columnas CRÍTICAS de swap (método USE — saturación):
# si (swap in): páginas leídas DESDE swap. Si >0 continuamente: problema grave
# so (swap out): páginas escritas A swap. Si >0 continuamente: problema grave

# /proc/meminfo: información exhaustiva del subsistema de memoria
cat /proc/meminfo | grep -E "MemTotal|MemFree|MemAvailable|Cached:|Dirty:|SwapUsed"

# smem: uso de memoria real por proceso (descuenta la compartida)
sudo apt install smem
smem -s pss -r -n | head -15 # PSS (Proportional Set Size): el más justo
smem -s rss -r -n | head -15 # RSS (Resident Set Size): incluye toda la compartida
# PSS divide la memoria compartida entre los procesos que la usan
# RSS sobreestima el uso cuando hay bibliotecas compartidas

OOM Killer: cuándo y cómo actúa

# OOM (Out Of Memory) Killer: cuando el sistema se queda sin memoria,
# el kernel elige y mata procesos para evitar un kernel panic total.

# Detectar si el OOM Killer ha actuado
dmesg | grep -i "oom\|out of memory\|killed process"
journalctl -k | grep -i "oom\|killed process"

# Ejemplo de mensaje en dmesg:
# Out of memory: Kill process 12345 (java) score 852 or sacrifice child
# Killed process 12345 (java) total-vm:4194304kB, anon-rss:3145728kB

# OOM Score: valor 0-1000; el OOM Killer mata el proceso con score más alto
cat /proc/1234/oom_score # Score actual calculado por el kernel
cat /proc/1234/oom_score_adj # Ajuste manual configurado por el admin (-1000 a 1000)

# Proteger un proceso crítico del OOM Killer
echo -500 | sudo tee /proc/$(pgrep -f postgresql | head -1)/oom_score_adj
# -1000 = inmunidad total (solo para procesos del sistema como init)

# Hacer un proceso candidato prioritario para ser matado
echo 500 | sudo tee /proc/$(pgrep java | head -1)/oom_score_adj

# Configuración permanente vía systemd (módulo 09):
# En la unit .service añadir:
# [Service]
# OOMScoreAdjust=-500

15.4 — Disco y E/S

iostat -x: la herramienta central

sudo apt install sysstat

iostat -x 1 5 # Extended, una muestra por segundo, 5 iteraciones

# Columnas clave (método USE aplicado a disco):
#
# %util: porcentaje de tiempo que el dispositivo está ocupado
# 100% → disco utilizado al máximo (UTILIZACIÓN)
# Pero 100% en NVMe puede no ser problema si la cola fluye bien
#
# avgqu-sz: longitud media de la cola de peticiones pendientes
# > 1 en HDD → saturación (más trabajo del que puede despachar)
# En NVMe puede ser mayor sin ser problema (alta concurrencia interna)
#
# await: latencia media total en milisegundos (espera en cola + tiempo de servicio)
# < 1ms: NVMe excelente
# 1-5ms: SSD SATA bueno
# 5-20ms: HDD normal bajo carga moderada
# > 50ms: disco saturado o con problemas mecánicos
#
# r/s w/s: lecturas/escrituras por segundo (IOPS)
# rkB/s wkB/s: throughput en KB/s

# Ejemplo de disco SATURADO:
# Device r/s w/s await %util avgqu-sz
# sda 0.0 220 185.3 100.0 42.5
# → %util=100%, await=185ms, queue=42 → severamente saturado

iotop: quién escribe

sudo apt install iotop

sudo iotop # Todos los procesos con su I/O
sudo iotop -o # Solo procesos con E/S activa en este momento
sudo iotop -b -n 5 # Batch mode, 5 iteraciones (para scripts/logs)
sudo iotop -p 1234 # Solo el proceso 1234

# Columnas:
# IO: E/S total actual (lectura + escritura)
# PRIO: prioridad de I/O del proceso (be=best-effort, rt=realtime, idle)

Benchmarks de disco: fio

sudo apt install fio

# Test de escritura SECUENCIAL (ancho de banda — backups, logs, video)
fio --name=write-seq --rw=write --bs=1M --size=1G \
--filename=/tmp/fio-test --ioengine=libaio --direct=1

# Test de escritura ALEATORIA 4K (IOPS — bases de datos, VMs)
fio --name=randwrite-4k --rw=randwrite --bs=4k --size=512M \
--filename=/tmp/fio-test --ioengine=libaio --direct=1 --iodepth=32

# Test de lectura ALEATORIA (IOPS de lectura — base de datos caliente)
fio --name=randread-4k --rw=randread --bs=4k --size=512M \
--filename=/tmp/fio-test --ioengine=libaio --direct=1 --iodepth=32

rm -f /tmp/fio-test

# ¿Por qué NO usar dd para benchmarks de disco?
# dd sin oflag=direct mide la velocidad del caché de escritura del kernel,
# NO la velocidad real del hardware de almacenamiento.
# Siempre usar --direct=1 (O_DIRECT) en fio para evitar el caché.

Schedulers de E/S

# Ver el scheduler activo para cada dispositivo
cat /sys/block/sda/queue/scheduler
# [mq-deadline] none kyber bfq → mq-deadline es el activo

# Schedulers disponibles:
# none/noop: sin reordenación — ideal para NVMe y SSDs rápidos
# mq-deadline: buen equilibrio entre latencia y throughput — bueno para HDD y SSD SATA
# bfq: Budget Fair Queueing — latencia baja, ideal para escritorio interactivo
# kyber: latencias controladas — diseñado para SSDs y storage rápido

# Cambiar el scheduler en caliente
echo none | sudo tee /sys/block/nvme0n1/queue/scheduler # Para NVMe
echo mq-deadline | sudo tee /sys/block/sda/queue/scheduler # Para HDD/SSD SATA

# Cambio permanente vía udev (módulo 13):
# /etc/udev/rules.d/60-scheduler.rules:
# ACTION=="add|change", KERNEL=="nvme[0-9]*", ATTR{queue/scheduler}="none"
# ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/scheduler}="mq-deadline"

15.5 — Red (rendimiento)

# ss: estadísticas de sockets (módulo 11 — aquí enfoque rendimiento)
ss -s # Resumen: TCP en ESTABLISHED, LISTEN, TIME_WAIT...
ss -tn state time-wait | wc -l # Número de TIME_WAIT (problema si son miles: backlog)

# nstat: contadores de protocolo del kernel (sustituto moderno de netstat -s)
nstat # Todos los contadores desde el arranque
nstat -z # Incluyendo los que son cero (para completitud)
# Contadores importantes para rendimiento:
# TcpRetransSegs: retransmisiones TCP → pérdida de paquetes o red sobrecargada
# TcpAttemptFails: conexiones TCP fallidas
# IpInDiscards: paquetes IP descartados → colas de red llenas

# ethtool: estadísticas del driver de la NIC (errores de hardware)
sudo ethtool -S eth0 | grep -E "error|drop|miss|overrun"
# rx_errors, tx_errors → errores de la tarjeta de red
# rx_missed_errors → paquetes perdidos por desbordamiento del buffer del driver

# sar -n DEV: estadísticas de red históricas
sar -n DEV 1 5 # Tráfico por interfaz, 5 muestras
sar -n EDEV 1 5 # Errores de red (rxerr, txerr, rxdrop...)
# iftop: tráfico por conexión (como top pero para la red)
sudo apt install iftop
sudo iftop -i eth0 # Monitorizar la interfaz eth0
sudo iftop -n # Sin resolución de DNS (más rápido y legible)

# nethogs: tráfico por PROCESO (quién está generando el tráfico)
sudo apt install nethogs
sudo nethogs eth0

# iperf3: medir el ancho de banda disponible entre dos puntos
# En el servidor (receptor):
iperf3 -s
# En el cliente:
iperf3 -c IP-SERVIDOR # Test TCP básico
iperf3 -c IP-SERVIDOR -u -b 100M # Test UDP a 100 Mbps
iperf3 -c IP-SERVIDOR -P 4 # 4 flujos paralelos
# El resultado establece la "línea base" de la capacidad del enlace

# Diagnóstico de retransmisiones (señal de pérdida de paquetes)
ss -ti state established | grep retrans # Contador de retransmisiones por socket
ping -c 20 destino | tail -2 # Latencia y pérdida de paquetes
mtr --report destino # Traceroute continuo con estadísticas

15.6 — Trazado y perfilado de procesos

strace — Trazar llamadas al sistema

# strace: intercepta todas las llamadas al sistema de un proceso
# ÚTIL PARA: entender qué hace un proceso que va lento o no funciona
# COSTE: puede ralentizar el proceso hasta 10× (solo en diagnóstico, nunca en prod continuo)

# Trazar un proceso nuevo
strace ls /tmp
strace -o /tmp/strace.txt ls /tmp # Guardar en archivo

# Adjuntarse a un proceso ya en ejecución
strace -p 1234

# Las opciones más valiosas:
strace -c programa # Resumen estadístico: % de tiempo por syscall
strace -T programa # Tiempo empleado en cada llamada individual
strace -e trace=file programa # Solo syscalls de archivos (open, read, write, stat...)
strace -e trace=network programa # Solo syscalls de red (connect, send, recv...)
strace -f programa # Seguir procesos hijo (fork/exec)
strace -ff -o /tmp/out programa # Un archivo por PID (útil para procesos multi-proceso)

# Caso de uso: ¿por qué tarda tanto este comando?
strace -c -T find /var/log -name "*.log" 2>&1 | head -20
# Los syscalls con mayor tiempo acumulado son el cuello de botella

# Encontrar a qué servidor DNS consulta una aplicación
strace -e trace=network -f curl https://ejemplo.com 2>&1 | grep connect

lsof — Archivos y sockets abiertos

# lsof: list open files (en Linux, todo es un archivo: sockets, pipes, dispositivos...)
sudo lsof | wc -l # Total de file descriptors abiertos en el sistema

# Por proceso
sudo lsof -p 1234 # Todos los archivos del proceso 1234
sudo lsof -p 1234 | grep REG # Solo archivos regulares
sudo lsof -p 1234 | grep "IPv4\|IPv6" # Solo sockets de red

# Por archivo o directorio
sudo lsof /var/log/nginx/access.log # ¿Qué procesos tienen abierto este archivo?
sudo lsof +D /var/www # ¿Qué procesos usan algún archivo de /var/www?

# Sockets de red
sudo lsof -i # Todos los sockets de red
sudo lsof -i :80 # Solo los que usan el puerto 80
sudo lsof -i TCP:443 # TCP en el puerto 443
sudo lsof -i -sTCP:LISTEN # Solo sockets en escucha

# CASO CLÁSICO: recuperar un archivo de log borrado accidentalmente
# Si el proceso sigue escribiendo en el archivo borrado, los datos aún están en disco
# hasta que el proceso cierre el file descriptor
sudo lsof -p $(pgrep nginx) | grep deleted # Archivos borrados pero aún abiertos
# FD column muestra el número: 7u, 8w, etc.
sudo cp /proc/$(pgrep nginx | head -1)/fd/7 /tmp/log-recuperado.txt

perf — Perfilado de rendimiento a nivel de CPU

sudo apt install linux-perf

# perf top: profiler en tiempo real (como top pero a nivel de función del kernel/app)
sudo perf top # Todo el sistema
sudo perf top -p 1234 # Solo el proceso 1234
# Muestra: % de ciclos de CPU por función → identifica hot paths

# perf record + report: captura para análisis posterior
sudo perf record -g -p 1234 sleep 10 # Capturar 10s con call graph
sudo perf record -g -a sleep 10 # Todo el sistema
sudo perf report # Análisis interactivo (TUI)
sudo perf report --stdio | head -40 # Salida de texto plana

# perf stat: estadísticas de contadores de hardware
sudo perf stat -p 1234 sleep 5
# Muestra: ciclos, instrucciones, cache-misses, branch-misses...
# IPC (instructions per cycle) < 1 → stalls por memoria o E/S
# cache-misses / cache-refs alto → código con mala localidad de datos

# Flame graphs: la forma más intuitiva de ver perf
git clone --depth=1 https://github.com/brendangregg/FlameGraph /opt/flamegraph
sudo perf record -F 99 -g -p 1234 sleep 30
sudo perf script | /opt/flamegraph/stackcollapse-perf.pl | \
/opt/flamegraph/flamegraph.pl > flamegraph.svg
# Abrir flamegraph.svg en el navegador
# El ANCHO de cada barra = % del tiempo total en esa función/pila de llamadas

eBPF y bpftrace — La nueva generación de observabilidad

# eBPF (extended Berkeley Packet Filter): permite ejecutar programas verificados
# DENTRO del kernel SIN parchearlo ni reiniciarlo.
# Overhead mínimo: ideal para producción donde strace es demasiado costoso.
# Usado por Netflix, Facebook, Google, Cloudflare para observabilidad en producción.

sudo apt install bpftrace

# Trazar qué archivos abre cualquier proceso (en tiempo real)
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat {
printf("%s %s\n", comm, str(args->filename));
}'

# Distribución de latencia de la syscall read() (histograma)
sudo bpftrace -e '
tracepoint:syscalls:sys_enter_read { @start[tid] = nsecs; }
tracepoint:syscalls:sys_exit_read /@start[tid]/ {
@us = hist((nsecs - @start[tid]) / 1000);
delete(@start[tid]);
}'

# BCC tools: scripts bpftrace/eBPF listos para usar
sudo apt install bpfcc-tools
sudo opensnoop-bpfcc # Qué archivos abre cada proceso
sudo execsnoop-bpfcc # Qué procesos se lanzan (fork+exec)
sudo biolatency-bpfcc -D # Distribución de latencia de I/O de bloque
sudo tcpconnect-bpfcc # Conexiones TCP salientes en tiempo real
sudo tcpaccept-bpfcc # Conexiones TCP entrantes aceptadas
sudo runqlat-bpfcc # Distribución de latencia en la run queue de CPU
sudo cachetop-bpfcc # Uso del page cache por proceso

15.7 — Recolección histórica

sysstat / sar

sudo apt install sysstat

# Activar la recolección automática (cada 10 minutos, cron o timer)
sudo systemctl enable --now sysstat

# sar: analizar los datos históricos
sar # CPU del día actual
sar -u 1 5 # CPU en tiempo real, 5 muestras
sar -r # Memoria del día actual
sar -b # E/S de disco del día actual
sar -n DEV # Tráfico de red del día actual
sar -q # Load average y run queue

# Datos de días anteriores (archivos en /var/log/sysstat/saDD)
sar -u -f /var/log/sysstat/sa$(date -d yesterday +%d)
sar -r -f /var/log/sysstat/sa15 # El día 15 del mes actual

# CASO DE USO REAL: "el servidor fue lento ayer a las 14:00"
# Con sar puedes reconstruir exactamente qué pasó:
FILE=/var/log/sysstat/sa$(date -d yesterday +%d)
echo "=== CPU ayer 14:00 ===" && sar -u -f $FILE | grep "^14:"
echo "=== MEMORIA ===" && sar -r -f $FILE | grep "^14:"
echo "=== DISCO ===" && sar -b -f $FILE | grep "^14:"
echo "=== RED ===" && sar -n DEV -f $FILE | grep "^14:"

atop — Registro continuo y detallado

sudo apt install atop

# atop registra TODO el sistema cada vez (por defecto cada 10 minutos)
# en archivos diarios en /var/log/atop/
sudo systemctl enable --now atop

# Ver el estado actual (interfaz interactiva)
atop # Vista general
# Teclas dentro de atop:
# d → detalle de disco
# n → detalle de red
# m → detalle de memoria
# 1 → vista de CPUs individuales

# Reproducir el pasado (ANÁLISIS POST-MORTEM)
atop -r /var/log/atop/atop_$(date -d yesterday +%Y%m%d)
# Dentro de atop: 'b'=inicio del día, 't'=avanzar 10min, 'T'=retroceder
# Permite ver exactamente qué hacía el sistema en el momento del incidente:
# qué procesos estaban activos, qué consumían, qué servicios respondían

15.8 — Gestión de logs

Revisión de journald y rsyslog

# journald: el diario binario de systemd (módulo 09)
journalctl -n 100 # Las últimas 100 líneas
journalctl -f # Seguimiento en tiempo real
journalctl -u nginx -n 50 # Logs de una unidad específica
journalctl --since "2024-01-15 10:00" --until "2024-01-15 11:00"
journalctl -p err -b # Solo errores del arranque actual
journalctl -p err --since today # Errores de hoy

# Tamaño del journal y limpieza
journalctl --disk-usage
sudo journalctl --vacuum-size=500M # Reducir a 500 MB
sudo journalctl --vacuum-time=30d # Eliminar entradas de más de 30 días

# Configuración en /etc/systemd/journald.conf:
# Storage=persistent → guardar en disco (default: auto)
# SystemMaxUse=500M → tamaño máximo total del journal en disco
# SystemKeepFree=200M → espacio mínimo a dejar libre
# MaxRetentionSec=1month → retención máxima

# rsyslog: el daemon de logs clásico
# Escribe a /var/log/syslog, /var/log/auth.log, /var/log/kern.log
# Recibe de journald via imjournal o socket /dev/log
# Severidades RFC 5424: emerg alert crit err warning notice info debug
# Facilities: kern user mail daemon auth syslog cron local0-local7

logrotate — Que los logs no llenen el disco

# logrotate: rotación automática de logs
# Configuración: /etc/logrotate.conf (global) + /etc/logrotate.d/* (por aplicación)

cat /etc/logrotate.d/nginx # Ver la configuración de nginx como ejemplo

# Crear configuración para una aplicación propia:
sudo tee /etc/logrotate.d/mi-app << 'EOF'
/var/log/mi-app/*.log {
daily # Rotar diariamente
rotate 30 # Conservar 30 archivos rotados (30 días)
compress # Comprimir con gzip los archivos rotados
delaycompress # No comprimir el más reciente (el proceso puede escribir aún)
missingok # No fallar si el archivo no existe
notifempty # No rotar si el archivo está vacío
create 0640 mi-usuario adm # Crear el nuevo archivo con estos permisos
sharedscripts # Ejecutar postrotate una vez para todos los *.log
postrotate
# Señalizar al proceso para que reabra el archivo de log
systemctl kill -s HUP mi-app.service 2>/dev/null || true
endscript
}
EOF

# Probar sin hacer cambios reales
sudo logrotate -d /etc/logrotate.d/mi-app # Debug: qué haría
sudo logrotate -f /etc/logrotate.d/mi-app # Forzar rotación ahora (para testing)

Centralización de logs

# Opción 1: rsyslog remoto (protocolo syslog clásico, simple y robusto)
# En el SERVIDOR receptor:
sudo tee /etc/rsyslog.d/10-server.conf << 'EOF'
module(load="imtcp")
input(type="imtcp" port="514")
EOF

# En los CLIENTES emisores:
sudo tee /etc/rsyslog.d/50-remote.conf << 'EOF'
# Enviar todos los logs al servidor de logs vía TCP (más fiable que UDP)
*.* action(type="omfwd" target="log-servidor.interno" port="514" protocol="tcp")
EOF

sudo systemctl restart rsyslog

# Opción 2: Loki + Promtail (stack Grafana, más moderno)
# Loki no indexa el contenido de los logs, solo las etiquetas (labels)
# → Mucho más económico en almacenamiento y memoria que Elasticsearch
# Promtail: agente ligero que lee los logs (journald, archivos) y los envía a Loki
# Grafana puede consultar Loki con LogQL y mostrar logs junto a métricas Prometheus

15.9 — Observabilidad moderna: Prometheus y Grafana

Los tres pilares de la observabilidad

MÉTRICAS: valores numéricos a lo largo del tiempo
"La CPU estuvo al 95% durante 3 minutos a las 14:23"
→ Prometheus, VictoriaMetrics, InfluxDB, Grafana

LOGS: eventos textuales con timestamp y contexto
"nginx: 500 Internal Server Error en /api/users"
→ journald, Loki, Elasticsearch, Graylog

TRAZAS: recorrido completo de una petición a través del sistema
"La petición /api tardó 2s: 50ms en nginx, 1.9s en la DB"
→ Jaeger, Zipkin, OpenTelemetry

Para un servidor Linux standalone: métricas + logs es suficiente.
Para microservicios y Kubernetes: los tres pilares son necesarios.

Prometheus + node_exporter

# node_exporter: exporta métricas del sistema Linux en formato Prometheus
# Expone: CPU, memoria, disco, red, filesystem, temperatura, carga del sistema...

# Instalación de node_exporter
wget https://github.com/prometheus/node_exporter/releases/download/v1.8.0/\
node_exporter-1.8.0.linux-amd64.tar.gz
tar xzf node_exporter-*.tar.gz
sudo mv node_exporter-*/node_exporter /usr/local/bin/

# Servicio systemd para node_exporter
sudo tee /etc/systemd/system/node_exporter.service << 'EOF'
[Unit]
Description=Prometheus Node Exporter
After=network.target

[Service]
Type=simple
User=nobody
ExecStart=/usr/local/bin/node_exporter \
--collector.systemd \
--collector.processes
Restart=always

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now node_exporter

# Verificar que node_exporter sirve métricas
curl -s http://localhost:9100/metrics | grep "node_cpu_seconds" | head -5
# Instalación de Prometheus
wget https://github.com/prometheus/prometheus/releases/download/v2.52.0/\
prometheus-2.52.0.linux-amd64.tar.gz
tar xzf prometheus-*.tar.gz
sudo mv prometheus-*/prometheus /usr/local/bin/
sudo mv prometheus-*/promtool /usr/local/bin/
sudo mkdir -p /etc/prometheus /var/lib/prometheus

# Configuración de Prometheus
sudo tee /etc/prometheus/prometheus.yml << 'EOF'
global:
scrape_interval: 15s # Recoger métricas cada 15 segundos
evaluation_interval: 15s # Evaluar reglas de alerta cada 15 segundos

scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']

- job_name: 'node'
static_configs:
- targets: ['localhost:9100']
labels:
hostname: 'servidor-principal'

# Múltiples servidores:
# - job_name: 'fleet'
# static_configs:
# - targets: ['192.168.1.10:9100', '192.168.1.11:9100']
EOF

# Reglas de alerta
sudo mkdir -p /etc/prometheus/rules
sudo tee /etc/prometheus/rules/alertas.yml << 'EOF'
groups:
- name: sistema
rules:
- alert: CPUAltaUtilizacion
expr: 100 - (avg by(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 90
for: 5m
labels:
severity: warning
annotations:
summary: "CPU alta en {{ $labels.instance }}"
description: "CPU al {{ $value | humanize }}% durante más de 5 minutos"

- alert: MemoriaCritica
expr: node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes < 0.10
for: 2m
labels:
severity: critical
annotations:
summary: "Memoria crítica en {{ $labels.instance }}"
description: "Solo queda el {{ $value | humanizePercentage }} de memoria disponible"

- alert: DiscoLleno
expr: (node_filesystem_size_bytes - node_filesystem_avail_bytes) /
node_filesystem_size_bytes > 0.90
for: 5m
labels:
severity: critical
annotations:
summary: "Disco al {{ $value | humanizePercentage }} en {{ $labels.mountpoint }}"
EOF

# Añadir rule_files a prometheus.yml:
# rule_files:
# - "/etc/prometheus/rules/*.yml"

# Servicio systemd para Prometheus
sudo tee /etc/systemd/system/prometheus.service << 'EOF'
[Unit]
Description=Prometheus
After=network.target

[Service]
Type=simple
User=nobody
ExecStart=/usr/local/bin/prometheus \
--config.file=/etc/prometheus/prometheus.yml \
--storage.tsdb.path=/var/lib/prometheus \
--storage.tsdb.retention.time=30d \
--web.listen-address=0.0.0.0:9090
Restart=always

[Install]
WantedBy=multi-user.target
EOF

sudo chown -R nobody /etc/prometheus /var/lib/prometheus
sudo systemctl daemon-reload
sudo systemctl enable --now prometheus
# UI: http://IP-SERVIDOR:9090

PromQL: consultas fundamentales

# CPU utilizada (todos los modos menos idle), promedio 5 minutos
100 - (avg by(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

# Memoria disponible en GB
node_memory_MemAvailable_bytes / 1073741824

# Porcentaje de disco utilizado
(node_filesystem_size_bytes - node_filesystem_avail_bytes) /
node_filesystem_size_bytes * 100

# Tráfico de red entrante en Mbps (interfaz eth0)
rate(node_network_receive_bytes_total{device="eth0"}[5m]) * 8 / 1048576

# Load average
node_load1 # Último minuto
node_load5 # Últimos 5 minutos
node_load15 # Últimos 15 minutos

# Número de procesos en estado D (esperando I/O — métrica de saturación de disco)
node_procs_blocked

Grafana

# Instalar Grafana
sudo apt install -y apt-transport-https software-properties-common wget
wget -q -O - https://packages.grafana.com/gpg.key | sudo apt-key add -
echo "deb https://packages.grafana.com/oss/deb stable main" | \
sudo tee /etc/apt/sources.list.d/grafana.list
sudo apt update && sudo apt install grafana
sudo systemctl enable --now grafana-server
# UI: http://IP-SERVIDOR:3000 (admin/admin por defecto — cambiar inmediatamente)

# Configurar la fuente de datos (vía UI):
# Configuration → Data Sources → Add → Prometheus
# URL: http://localhost:9090 → Save & Test

# Dashboard preconstruido para node_exporter:
# Dashboards → Import → ID: 1860 (Node Exporter Full)
# Cubre: CPU detallado por core, memoria, swap, I/O de disco, red, temperatura

# Alertas en Grafana (Alerting → Alert rules):
# Condition: avg(cpu_usage) > 90 during 5m
# Contact point: email, Slack, PagerDuty, webhook...

Anexos

A. Resumen del método USE por recurso

RecursoUtilizaciónSaturaciónErrores
CPUtop (%us+%sy)vmstat r > nprocperf stat (stalls)
Memoriafree (used/total)vmstat si/so > 0dmesg (OOM killed)
Discoiostat %utiliostat avgqu > 1smartctl -H
Red (NIC)sar -n DEV txkBss -s backlogethtool -S errors

B. Interrelaciones con otros módulos

◀ Módulo 09 — Procesos y systemd
│ load average, estados de proceso (R, D, S), señales, cgroups para límites

◀ Módulo 12 — Almacenamiento avanzado
│ El tipo de dispositivo (NVMe/SSD/HDD/RAID) determina los valores
│ normales de iostat await y avgqu-sz

◀ Módulo 13 — Arranque y kernel
│ sysctl: vm.dirty_ratio, vm.swappiness afectan el comportamiento de I/O
│ Gobernadores de CPU (cpupower) afectan los benchmarks

◀ Módulo 14 — Seguridad
│ Prometheus expone un endpoint HTTP: proteger con ufw/nftables
│ Los logs de auditd se integran en la estrategia de observabilidad

▶ Módulo 16 — Virtualización y contenedores
│ cAdvisor: node_exporter equivalente para contenedores Docker
│ Kubernetes expone métricas vía kube-state-metrics y metrics-server

▶ Módulo 17 — Linux como servidor
│ node_exporter + Prometheus para monitorizar servidores web y de BD
│ Alertas de disco lleno y memoria para gestión proactiva de SLAs

Referencias y Bibliografía

  1. Brendan Gregg — Systems Performance, 2ª ed.
    Addison-Wesley, 2020. La referencia definitiva sobre rendimiento en Linux/Unix.

  2. Brendan Gregg — BPF Performance Tools
    Addison-Wesley, 2019. eBPF, bpftrace y BCC tools en profundidad.

  3. Brendan Gregg — The USE Method
    http://www.brendangregg.com/usemethod.html

  4. Brendan Gregg — Linux Performance Analysis in 60 seconds
    http://www.brendangregg.com/Articles/Netflix_Linux_Perf_Analysis_60s.pdf

  5. Brendan Gregg — Flame Graphs
    http://www.brendangregg.com/flamegraphs.html

  6. Prometheus documentation
    https://prometheus.io/docs/

  7. PromQL documentation
    https://prometheus.io/docs/prometheus/latest/querying/basics/

  8. Grafana documentation
    https://grafana.com/docs/grafana/

  9. node_exporter — GitHub
    https://github.com/prometheus/node_exporter

  10. sysstat / sar documentation
    https://github.com/sysstat/sysstat

  11. atop documentation
    https://www.atoptool.nl/

  12. fio — Flexible I/O Tester
    https://fio.readthedocs.io/

  13. strace manual — man7.org
    https://man7.org/linux/man-pages/man1/strace.1.html

  14. perf documentation — kernel.org
    https://perf.wiki.kernel.org/

  15. bpftrace documentation — GitHub
    https://github.com/bpftrace/bpftrace

  16. eBPF — What is eBPF?
    https://ebpf.io/what-is-ebpf/

  17. Linux Observability with BPF — David Calavera, Lorenzo Fontana
    O'Reilly, 2019.

  18. logrotate man page
    https://linux.die.net/man/8/logrotate

  19. Practical Monitoring — Mike Julian
    O'Reilly, 2017.

  20. Unix and Linux System Administration Handbook — Nemeth et al.
    Pearson, 2017. Capítulo 28: Performance Analysis.


Preguntas de autoevaluación

  1. Explica el método USE. ¿Cuáles son sus tres dimensiones? ¿Por qué es superior a "probar comandos al azar"?
  2. Un sistema tiene load average: 8.5, 6.2, 4.1 con 4 CPUs. ¿Qué indica? ¿Cómo distinguirías si el problema es de CPU o de E/S?
  3. free -h muestra "free: 200M" pero "available: 8G". ¿Hay problema de memoria? Explica.
  4. ¿Qué indica un wa (iowait) alto en top? ¿Qué herramienta usarías a continuación?
  5. En iostat -x, ¿qué diferencia hay entre %util y avgqu-sz? ¿Cuál es más indicativo de saturación?
  6. Explica la diferencia entre fio --direct=1 y dd para benchmarks de disco. ¿Por qué dd puede engañar?
  7. ¿Para qué sirve strace? ¿Por qué no se usa en producción de forma continua? ¿Qué herramienta lo sustituye con menor overhead?
  8. Describe cómo recuperar el contenido de un archivo de log borrado accidentalmente cuando el proceso que lo escribe sigue activo.
  9. ¿Qué es un flame graph? ¿Qué representa el ancho de cada barra? ¿Cómo se genera con perf?
  10. ¿Qué ventaja tiene eBPF/bpftrace sobre strace para observabilidad en producción?
  11. ¿Para qué sirve atop -r y cuándo es más útil que sar?
  12. Escribe una consulta PromQL que calcule el porcentaje de CPU en uso (todos los modos menos idle), con una ventana de 5 minutos.

Laboratorios prácticos

Lab 15.1 — Los primeros 60 segundos

# Secuencia de diagnóstico inicial completa
echo "=== uptime ===" && uptime
echo "=== dmesg errors ===" && dmesg -T -l err 2>/dev/null | tail -5
echo "=== vmstat ===" && vmstat 1 3
echo "=== free ===" && free -h
echo "=== ss summary ===" && ss -s
echo "=== iostat ===" && iostat -x 1 2 2>/dev/null || echo "instala sysstat"

Lab 15.2 — Identificar un proceso que consume CPU

# 1. Generar carga artificial en background
sudo apt install stress-ng -y 2>/dev/null
stress-ng --cpu 1 --timeout 20s &
STRESS_PID=$!

# 2. Identificar con pidstat
pidstat 1 5 | grep stress

# 3. Ver las syscalls que hace
strace -c -p $STRESS_PID sleep 3 2>&1 | head -20

# 4. Limpiar
wait $STRESS_PID 2>/dev/null

Lab 15.3 — Analizar memoria con /proc y smem

# 1. Estado actual de la memoria
free -h
cat /proc/meminfo | grep -E "MemTotal|MemAvailable|Cached:|Dirty:|SwapUsed"

# 2. Top 10 procesos por uso de memoria
ps aux --sort=-%mem | head -12

# 3. OOM score de procesos clave
for proc in sshd systemd bash; do
pid=$(pgrep $proc | head -1)
[ -n "$pid" ] && echo "$proc (PID $pid): OOM score $(cat /proc/$pid/oom_score)"
done

Lab 15.4 — lsof: inspeccionar archivos y sockets abiertos

# 1. Total de FDs abiertos en el sistema
sudo lsof 2>/dev/null | wc -l

# 2. Archivos del proceso sshd
sudo lsof -p $(pgrep sshd | head -1) 2>/dev/null | head -15

# 3. Qué proceso tiene /var/log/syslog abierto
sudo lsof /var/log/syslog 2>/dev/null

# 4. Todos los sockets TCP en escucha
sudo lsof -i -sTCP:LISTEN 2>/dev/null | head -10

Lab 15.5 — Configurar logrotate

# 1. Crear un log de prueba
sudo mkdir -p /var/log/lab-logrotate
sudo bash -c 'seq 100 | xargs -I{} echo "$(date) Línea {}" > /var/log/lab-logrotate/app.log'

# 2. Configuración de logrotate
sudo tee /etc/logrotate.d/lab-logrotate << 'EOF'
/var/log/lab-logrotate/*.log {
daily
rotate 7
compress
missingok
notifempty
create 0640 root adm
}
EOF

# 3. Probar en modo debug
sudo logrotate -d /etc/logrotate.d/lab-logrotate

# 4. Forzar rotación y verificar
sudo logrotate -f /etc/logrotate.d/lab-logrotate
ls -lh /var/log/lab-logrotate/

# 5. Limpiar
sudo rm -rf /var/log/lab-logrotate
sudo rm /etc/logrotate.d/lab-logrotate

Lab 15.6 — Prometheus: explorar métricas vía API

# Si node_exporter está instalado:
if curl -s http://localhost:9100/metrics > /dev/null 2>&1; then
echo "=== Memoria disponible ==="
curl -s http://localhost:9100/metrics | grep "^node_memory_MemAvailable"
echo "=== CPU total seconds ==="
curl -s http://localhost:9100/metrics | grep "^node_cpu_seconds_total" | head -5
echo "=== Filesystem disponible ==="
curl -s http://localhost:9100/metrics | grep "^node_filesystem_avail" | head -3
else
echo "node_exporter no está corriendo en el puerto 9100"
echo "Para instalarlo: ver §15.9 de este módulo"
fi

Resumen del módulo

Metodología USE: Utilización, Saturación, Errores — el marco que sustituye "probar comandos al azar"
Los primeros 60s: uptime, dmesg -T, vmstat 1, mpstat, top, iostat -x, free -h, ss -s
CPU: top (us/sy/wa/st), mpstat por núcleo, pidstat, procesos en estado D, gobernadores
Memoria: free -h bien interpretado, vmstat (si/so), smem PSS, OOM killer y oom_score_adj
Disco: iostat -x (await/avgqu-sz/%util), iotop, fio con O_DIRECT, schedulers de E/S
Red: ss -s, nstat, ethtool -S, iftop, nethogs, iperf3, retransmisiones TCP
Trazado: strace -c/-T/-e, lsof (recuperar archivos borrados), perf top/record, flame graphs
eBPF: bpftrace, BCC tools (opensnoop, biolatency, execsnoop, runqlat, cachetop)
Histórico: sar con sysstat, atop con replay post-mortem
Logs: journald, rsyslog, logrotate, centralización con syslog remoto o Loki
Observabilidad: Prometheus + node_exporter (scrape 15s), PromQL, Grafana + alertas, tres pilares

Próximo paso: Módulo 16 — Virtualización y contenedores. Con el sistema monitorizado y entendido, el siguiente desafío es multiplexar la infraestructura: VMs con KVM, contenedores con Docker/Podman y orquestación con Kubernetes.


Última actualización: 2024-06
Versión: 1.0
Estado: ✅ Listo para enseñanza