Sabes qué pasó a las tres de la madrugada: quince db_timeout con las 200 conexiones agotadas. Lo que no sabes es por qué. Y ahora Marta añade leña: «Varios comerciales dicen que la web va lenta por las mañanas, sobre las nueve. ¿Es cosa nuestra?». «Va lenta» no es un dato, es una sensación; convertirla en un número, encontrar el recurso culpable y demostrar que el cambio ha servido es justamente el trabajo de esta lección. Vas a aprender un método reproducible en lugar de una colección de comandos sueltos, a interpretar de verdad lo que dicen uptime, free o iostat, y a establecer una línea base de srv-tramontana sin la cual ninguna medición significa nada.

Contenido

  1. Monitorizar, diagnosticar y el método USE
  2. CPU: load average, vmstat, mpstat y el steal
  3. Memoria: qué significa «usada», swap y el OOM killer
  4. Disco: iostat -xz, latencia frente a caudal
  5. Red: sar -n DEV, ss -s y las retransmisiones
  6. Herramientas de conjunto y la historia con sar
  7. Métricas continuas y los cuatro golden signals
  8. Procedimiento para «el servidor va lento»
  9. Ajustes al alcance de un administrador
  10. La línea base de srv-tramontana
  11. Caso Tramontana: «la web va lenta por las mañanas»

  1. Monitorizar, diagnosticar y el método USE

Son dos actividades distintas y se confunden constantemente:

  • Monitorizar es saber cómo está el sistema de forma continua y sin que nadie mire: métricas cada quince segundos, guardadas, con alertas. Responde a «¿va bien?» antes de que llame el cliente.
  • Diagnosticar es averiguar por qué va mal en un momento concreto, con herramientas interactivas y una hipótesis que se confirma o se descarta.

Un humano mirando top no es monitorización: es diagnóstico manual, ocasional y sin memoria. Necesitas ambas, y la primera hace posible la segunda, porque una medición sin punto de comparación no dice nada: un load de 3,5 puede ser normal o catastrófico, y solo lo sabes si conoces el de ayer.

El método USE (de Brendan Gregg) da el orden en que se mira. Para cada recurso —CPU, memoria, disco, red— se comprueban tres cosas:

Dimensión Pregunta En CPU En disco
Utilización ¿Qué parte del tiempo está ocupado? %us + %sy %util de iostat
Saturación ¿Cuánto trabajo espera en cola? Columna r de vmstat aqu-sz, await
Errores ¿Hay fallos contabilizados? Excepciones de máquina Errores de E/S en dmesg

Su utilidad práctica es que evita perder tiempo: en cinco minutos recorres los cuatro recursos con tres preguntas cada uno y sales sabiendo cuál es el cuello de botella.

  1. CPU: load average, vmstat, mpstat y el steal

$ uptime ; nproc
 09:14:22 up 6 days,  2:31,  2 users,  load average: 3,42, 2,10, 1,08
2

Los tres números son la media de procesos en estado ejecutable (R) o en espera ininterrumpible (D) durante 1, 5 y 15 minutos. Dos reglas que lo cambian todo. Primera: hay que normalizar por el número de núcleos; con nproc = 2, un load de 3,42 son 1,71 procesos por núcleo y el sistema está saturado, mientras que el mismo 3,42 en una máquina de 16 núcleos sería un 21 % de ocupación, es decir, nada. Segunda: el estado D cuenta, y ese es el matiz que casi nadie conoce, porque en Linux el load incluye los procesos bloqueados esperando disco; por eso puedes ver un load de 8 con la CPU al 5 %.

La tendencia también informa: 3,42 2,10 1,08 es una carga subiendo (el problema empieza ahora); 1,08 2,10 3,42 es una que ya se está resolviendo.

vmstat 1, columna a columna

$ vmstat 1 5
 r  b   swpd   libre   buff  caché   si   so    bi    bo   in   cs us sy id wa st
 4  2      0  198432  91240 1842104    0    0    12    48  412  980 18  4 22 56  0
 5  3      0  196108  91240 1843320    0    0     8 14620  902 2140 21  6  9 64  0
 4  3      0  195884  91240 1843644    0    0     4 15108  918 2205 19  5  8 68  0
Columna Significado Cuándo preocupa
r Procesos listos para ejecutar, esperando CPU Si supera de forma sostenida el nº de núcleos
b Procesos bloqueados en E/S (estado D) Cualquier valor sostenido > 0 apunta al disco
si / so KiB/s entrando y saliendo de swap Cualquier valor sostenido es malo: thrashing
bi / bo Bloques leídos/escritos por segundo Contexto para el disco
us / sy / id % de CPU en usuario, kernel y ocioso sy alto: llamadas al sistema o red
wa % esperando E/S Alto con id bajo: el disco es el cuello
st % robado por el hipervisor (steal) > 5 % sostenido: el problema no está en tu VM

La primera línea de vmstat es la media desde el arranque: ignórala siempre y mira de la segunda en adelante. En el ejemplo, wa en 56–68 % con r en 4–5 y bo disparado dice a gritos que el cuello de botella es el disco, no la CPU.

El st merece un apunte: en una VM es tiempo que el hipervisor le dio a otra máquina, así que un st del 20 % significa que ninguna optimización tuya arreglará nada, porque el problema está en el anfitrión o en el proveedor.

Y el desglose por núcleo y por proceso:

$ mpstat -P ALL 1 1 | tail -3
Media:  CPU  %usr %nice %sys %iowait %steal %idle
Media:    0  19,2   0,0  4,8    58,1    0,0   17,5
Media:    1  17,9   0,0  5,1    54,3    0,0   22,4
$ pidstat -u 1 1 | sort -k8 -rn | head -3
12:31:03  UID  PID  %usr %system  %wait  %CPU  CPU  Command
12:31:03  997 1284  16,00    3,00   1,00 19,00    0  tramontana
12:31:03 1000 8842   4,00    9,00  22,00 13,00    1  respaldo_tramon

mpstat -P ALL revela un caso muy común: la media global parece razonable pero un solo núcleo está al 100 % porque la aplicación no paraleliza. pidstat reparte el consumo por proceso, y su columna %wait —tiempo esperando a que le den CPU— es oro puro para detectar contención.

  1. Memoria: qué significa «usada», swap y el OOM killer

$ free -h
               total       usado       libre  compartido  búfer/caché  disponible
Mem:           3,8Gi       1,1Gi       208Mi        18Mi        2,5Gi       2,4Gi
Inter.:        2,0Gi          0B       2,0Gi

«Libre: 208 Mi» no es un problema. Es exactamente lo contrario: la RAM libre es RAM desaprovechada, y el kernel la usa como caché de página para no volver a leer del disco. Las columnas que importan:

usado es la memoria de procesos sin caché ni buffers; búfer/caché es caché de página y metadatos, que el kernel suelta al instante si hace falta; y disponible es la cifra que hay que mirar: cuánta memoria puede obtener una aplicación nueva sin swapear. Con 2,4 GiB disponibles de 3,8, srv-tramontana va sobrado; la alarma sería disponible cayendo hacia cero, no libre.

$ grep -E 'MemTotal|MemAvailable|Dirty' /proc/meminfo   # el detalle crudo; vmstat -s resume
$ ps -eo pid,user,rss,vsz,comm --sort=-rss | head -3
   PID USER       RSS      VSZ COMMAND
  1284 svc-tram 219848  1284932 tramontana
  1102 postgres  98204   412088 postgres

Tres cifras que se confunden a diario: VSZ es todo el espacio de direcciones reservado —librerías compartidas y memoria pedida pero nunca tocada incluidas—, casi siempre enorme e irrelevante; RSS son las páginas realmente en RAM, útil pero cuenta entera cada librería compartida en cada proceso, así que sumar los RSS da mucho más que la RAM total; y PSS reparte cada página compartida entre quienes la usan, siendo la única que suma bien (se ve con smem -k o en /proc/PID/smaps_rollup).

Swap y thrashing

Tener swap ocupada no es malo por sí solo: el kernel descarga páginas que nadie toca. Lo grave es el trasiego constante, que se ve en si/so de vmstat. Si esas columnas están activas de forma sostenida, el sistema pasa más tiempo moviendo páginas que trabajando: eso es thrashing, y se percibe como una lentitud brutal con la CPU casi ociosa.

El OOM killer

Cuando no queda memoria ni swap, el kernel elige una víctima y la mata. Saber que ha actuado es fundamental, porque el síntoma que llega es «el servicio se reinició solo»:

$ sudo journalctl -k --since yesterday --grep -i 'out of memory'
ago 17 04:41:07 srv-tramontana kernel: Out of memory: Killed process 1284 (tramontana)
    total-vm:1284932kB, anon-rss:2894120kB, oom_score_adj:0

Elige por oom_score, que premia matar al proceso que más memoria libera, ponderado por oom_score_adj (de −1000 a 1000). Se consulta en /proc/PID/oom_score y se ajusta en caliente con sudo choom -p 1284 -n -500. Aunque en un servicio de systemd lo correcto es declararlo en la unidad (OOMScoreAdjust=-500) y, sobre todo, poner MemoryMax como viste en 05-05: así el OOM actúa dentro del cgroup del servicio y no se lleva por delante la base de datos.

  1. Disco: iostat -xz, latencia frente a caudal

$ iostat -xz 1 2 | tail -4
Device   r/s     rkB/s   w/s     wkB/s   rareq-sz wareq-sz aqu-sz  r_await w_await %util
dm-0     2,00     8,00 148,00 14620,00     4,00    98,78    3,42     1,20   22,85  97,60
sda      1,00     4,00  92,00 15108,00     4,00   164,22    2,98     0,90   31,10  94,20
Columna Qué mide Umbral orientativo
%util % de tiempo con al menos una petición en vuelo > 90 % sostenido: saturado (menos fiable en SSD/NVMe)
r_await / w_await Latencia media por petición, en ms > 10 ms en SSD o > 20 ms en disco mecánico: hay cola
aqu-sz Profundidad media de la cola > 1 sostenido: hay saturación

La distinción crítica es latencia frente a caudal: un disco puede mover 200 MB/s tan tranquilo con una tarea secuencial (caudal alto, latencia baja) y ahogarse con 5 MB/s de escrituras aleatorias pequeñas (caudal ridículo, latencia terrible). Al usuario le duele la latencia, no el caudal.

Y quién está escribiendo:

$ sudo iotop -b -n 1 -o -P | head -3
  PID  PRIO  USER   DISK READ  DISK WRITE  COMMAND
 8842  idle  operad   0.00 B/s   13.94 M/s  respaldo_tramontana.sh
$ sudo lsof -p 8842 | grep -E 'REG.*backups' | head -1
respaldo  8842 operador  4w  REG  253,0  /srv/.../tramontana-2026-08-18.tar.gz.parcial

  1. Red: sar -n DEV, ss -s y las retransmisiones

$ sar -n DEV 1 1 | grep -E 'IFACE|enp0s3'
12:44:01     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s
12:44:02    enp0s3    412,00    398,00    118,42    902,18
$ ss -s | head -2
Total: 214
TCP:   187 (estab 142, closed 21, orphaned 0, timewait 19)
$ ss -tn state established '( dport = :5432 )' | wc -l
200

Ese último comando es un adelanto del caso de hoy: doscientas conexiones establecidas contra el puerto de la base de datos, exactamente el max_conexiones de app.conf.

Para ver el tráfico en vivo, nload (por interfaz) o iftop (por conversación) son inmediatos.

Y el indicador de salud de TCP que hay que conocer, nstat -az | grep TcpRetransSegs: las retransmisiones son segmentos que hubo que reenviar porque no llegó confirmación. Un porcentaje pequeño es normal; una tasa creciente indica pérdida de paquetes —cable, saturación de un enlace o un dispositivo intermedio descartando tráfico—. El diagnóstico completo de red, con mtr y las capas, ya lo tienes de 03-08.

  1. Herramientas de conjunto y la historia con sar

Herramienta Aporta
htop top navegable: árbol con F5, filtros, nice interactivo, barras por núcleo
atop Guarda historia cada 10 min y permite «rebobinar» a la hora del incidente
glances / dstat Panel único con alertas de umbral / series por segundo, ideales para CSV
sysstat / sar La memoria del rendimiento: métricas históricas cada 10 minutos

El punto que más veces se pasa por alto: la foto de ahora no sirve sin la de ayer. Si Marta se queja a las 09:10 de algo ocurrido a las 09:00, top ya no dice nada; sar sí:

sudo apt install sysstat
sudo sed -i.bak-$(date +%F) 's/^ENABLED="false"/ENABLED="true"/' /etc/default/sysstat
sudo systemctl enable --now sysstat sysstat-collect.timer
sar -u -s 08:50:00 -e 09:30:00   # CPU en esa franja de HOY; -q da el load histórico
sar -r -f /var/log/sysstat/sa17  # memoria del día 17
sar -b -s 04:00:00 -e 05:00:00   # E/S durante la copia nocturna

Activar sysstat cuesta un minuto y es lo que convierte «creo que iba lento» en «a las 09:04 el %iowait subió al 61 %».

  1. Métricas continuas y los cuatro golden signals

Para más de un servidor, o para alertar sin que nadie mire, el estándar de hoy es Prometheus (base de datos temporal que consulta a los objetivos), node_exporter (expone las métricas del sistema en /metrics), Grafana (paneles) y Alertmanager (avisos). No lo montamos aquí: es materia del Módulo 8. Lo que sí debes llevarte es qué medir, los cuatro golden signals:

Señal Qué mide En Tramontana
Latencia Duración de una petición en percentiles (p50, p95, p99), separando las que fallan ms de acceso.log
Tráfico / Errores Peticiones por segundo / tasa de fallos Líneas por minuto y los 23 códigos de error del log
Saturación Cuán lleno está el recurso más limitado conexiones_activas / max_conexiones

Y una regla sobre alertas: una alerta que suena a diario deja de leerse. Alerta sobre síntomas que le duelen al usuario (latencia p95, tasa de errores), no sobre cada pico de CPU. Es la misma disciplina del «silencio si todo va bien» de tus scripts.

  1. Procedimiento para «el servidor va lento»

Este es el procedimiento numerado que puedes aplicar tal cual, en este orden, sin saltarte pasos:

  1. Traduce la queja a un número medible. ¿Qué operación, a qué hora, cuánto tarda ahora y cuánto antes? Sin esto no puedes saber si has arreglado algo.
  2. Foto de 60 segundos. uptime, vmstat 1 5, free -h, iostat -xz 1 3, ss -s, systemctl --failed. Con seis comandos sabes si el problema es CPU, memoria, disco o red.
  3. Compara con la línea base. ¿Es anormal, o siempre ha sido así y lo que ha cambiado es la expectativa?
  4. Identifica el recurso con USE y elige uno, descartando los demás explícitamente.
  5. Localiza al culpable dentro de ese recurso con pidstat, iotop, ss o top -H: nombre y PID.
  6. Formula una hipótesis falsable. «La copia de las 04:20 satura el disco y por eso las consultas caducan» se puede confirmar o descartar; «va lento» no.
  7. Revisa qué cambió: journalctl --since, /var/log/apt/history.log, git log de tus scripts, el HISTORIAL. El 80 % de los problemas nuevos vienen de un cambio reciente.
  8. Mide antes de tocar nada y guarda las cifras en un fichero con fecha.
  9. Cambia UNA sola cosa y documenta qué y por qué.
  10. Mide después, en las mismas condiciones, y compara. Si no mejora, revierte antes de probar otra cosa.
  11. Anota el resultado en el HISTORIAL, incluso —sobre todo— si el cambio no sirvió.

Los pasos 8, 9 y 10 son innegociables. Cambiar tres parámetros a la vez y ver que mejora no te enseña nada: no sabes cuál sirvió, y arrastrarás dos ajustes inútiles para siempre.

  1. Ajustes al alcance de un administrador

Los parámetros del kernel vía sysctl son materia de 07-03 y el perfilado con perf y eBPF es de 07-02. Lo que sí está en tus manos hoy:

Límites de ficheros abiertos. Un servidor con muchas conexiones agota el límite por proceso y rechaza peticiones con Too many open files:

$ ulimit -n ; cat /proc/1284/limits | grep 'open files'
1024
Max open files            1024                 4096                 files

En un servicio de systemd no se toca /etc/security/limits.conf —que solo aplica a sesiones de login vía PAM—, sino la unidad:

[Service]
LimitNOFILE=65535

Límites de recursos del servicio: MemoryMax y CPUQuota de 05-05, que además de proteger sirven para medir, porque systemd-cgtop muestra si el servicio llega al techo. Y noatime en fstab (05-04), que evita una escritura por cada lectura y se nota en un servidor con muchos ficheros pequeños.

Perfilado somero de hilos: top -H -p 1284 desglosa el consumo hilo a hilo cuando un proceso multihilo va cargado y no sabes qué parte. perf top daría el desglose por función, pero eso es 07-02.

Los parámetros de la propia aplicación, que suelen rendir más que cualquier ajuste del sistema: en Tramontana, max_conexiones y timeout_consulta de /etc/tramontana/app.conf.

  1. La línea base de srv-tramontana

Una línea base es una foto del comportamiento normal, tomada cuando todo va bien, para comparar cuando no. Se guarda junto a la de referencia del Módulo 1:

#!/usr/bin/env bash
# /home/operador/scripts/linea_base.sh — foto de rendimiento con fecha
set -euo pipefail
destino="/srv/tramontana/backups/lineabase-$(date +%F-%H%M).txt"
{
    echo "== $(date --iso-8601=seconds) $(hostname) =="
    uptime; nproc; free -h
    vmstat 1 5 | tail -4
    iostat -xz 1 2 | tail -6
    ss -s; df -h /; df -i / | tail -1
} > "$destino"

Valores normales medidos en srv-tramontana un martes a las 11:00, con la aplicación en marcha:

Métrica Valor normal Umbral de atención
Load average (2 vCPU) 0,15 – 0,40 > 2,0 sostenido
CPU id / wa 85 – 95 % / 0 – 3 % < 40 % / > 20 %
Memoria disponible 2,3 – 2,5 GiB < 400 MiB
si/so de swap 0 Cualquier valor sostenido
w_await en dm-0 0,8 – 2,5 ms > 20 ms
Conexiones a 5432 / disco / 30 – 60 / 30 % > 150 / > 85 %

Esa tabla, y no la intuición, es lo que convierte una medición en un diagnóstico.

  1. Caso Tramontana: «la web va lenta por las mañanas»

Paso 1 — Traducir la queja. Preguntamos y medimos: la búsqueda de disponibilidad, que normalmente responde en 180 ms, tarda entre 4 y 9 segundos entre las 08:50 y las 09:15. Ya es un número.

Paso 2-3 — Foto y comparación con la línea base. Como el incidente es recurrente, medimos a las 09:00 del día siguiente:

$ uptime
 09:04:11 up 6 days, 22:20,  2 users,  load average: 3,42, 2,10, 1,08
$ vmstat 1 3 | tail -1
 5  3      0  195884  91240 1843644    0    0     4 15108  918 2205 19  5  8 68  0

Load 3,42 con 2 vCPU (línea base: 0,15–0,40) y wa al 68 % (base: 0–3 %) con id al 8 %. No falta CPU: falta disco. Memoria y swap, normales. Recurso identificado: E/S.

Paso 4-5 — El culpable.

$ iostat -xz 1 2 | tail -2
Device   r/s   rkB/s    w/s    wkB/s  aqu-sz  w_await  %util
dm-0    2,00    8,00 148,00 14620,00    3,42    22,85   97,60
$ sudo iotop -b -n 1 -o | tail -1
 8842  idle operador   13.94 M/s  respaldo_tramontana.sh
$ journalctl -u tramontana-respaldo.service --since "2 days ago" -o short-iso | grep -E 'inicio|completada'
2026-08-17T04:20:06+0200 respaldo[8811]: inicio de copia (version=3.2.1)
2026-08-17T06:02:44+0200 respaldo[8811]: copia completada en 6158s
2026-08-18T04:20:07+0200 respaldo[8842]: inicio de copia (version=3.2.1)

w_await de 22,85 ms frente a los 0,8–2,5 de la base, %util al 97,6 %, y quien escribe es la copia de seguridad, que en teoría se lanza a las 04:20: la del día 17 tardó 1 h 42 min y la del 18 aún no había terminado a las 09:04. El conjunto de datos ha crecido y la copia se solapa con la hora punta.

Paso 6 — Hipótesis falsable. «La copia de seguridad satura la E/S del disco hasta pasadas las 09:00; eso alarga las consultas a la base de datos, que llegan al timeout_consulta de 30 s y agotan las 200 conexiones de max_conexiones, que es lo que provoca los db_timeout de los logs.»

Comprobación de la segunda mitad:

$ ss -tn state established '( dport = :5432 )' | wc -l   # 200: el techo exacto
$ journalctl -u tramontana.service --since "09:00" -p err --no-pager | wc -l
11

Doscientas conexiones exactas —el techo— y once errores en cuatro minutos. Hipótesis confirmada: el mismo mecanismo que causó los db_timeout de las 03:00 de la lección anterior.

Paso 7-9 — Qué cambió, medir y cambiar UNA cosa. El HISTORIAL lo dice: el volumen de copias se migró el 18 y el conjunto de datos ha crecido. La causa raíz es la duración de la copia, no las conexiones. El cambio mínimo y reversible:

### /etc/systemd/system/tramontana-respaldo.service.d/override.conf
[Service]
# La copia debe terminar antes de la hora punta (08:45); si no, se aborta y avisa.
RuntimeMaxSec=3h
IOWeight=10
$ sudo systemctl edit tramontana-respaldo.timer   # OnCalendar=*-*-* 02:30:00
$ sudo systemctl daemon-reload && systemctl list-timers tramontana-respaldo.timer | tail -1
Wed 2026-08-19 02:30:00 CEST 14h left  tramontana-respaldo.timer

Paso 10 — Medir después, a la misma hora y con los mismos comandos.

Métrica a las 09:04 Antes Después
Load average / CPU wa 3,42 / 68 % 0,38 / 2 %
w_await dm-0 22,85 ms 1,40 ms
Conexiones a 5432 200 (techo) 47
Errores db_timeout (09:00–09:15) 11 0
Latencia de búsqueda 4–9 s 190 ms

Paso 11 — Documentar. Y el informe para Marta, con la estructura de siempre: «La lentitud de las mañanas la causaba la copia de seguridad, que había crecido hasta tardar más de cuatro horas y seguía escribiendo en hora punta; al saturar el disco, las consultas caducaban y se agotaban las 200 conexiones a la base de datos. Hemos adelantado la copia a las 02:30 y le hemos puesto un límite de 3 horas y menor prioridad de E/S. Medido a las 09:04: la búsqueda vuelve a 190 ms y no hay errores. Qué protege esto: que la copia interfiera con el horario laboral. Qué no protege: si el conjunto de datos sigue creciendo, volveremos a chocar; hay que pasar a copias incrementales, y eso lo abordamos ahora mismo. Tampoco resuelve que 200 conexiones sea un límite ajustado si crece el número de usuarios.»

sudo tee -a /opt/tramontana/HISTORIAL >/dev/null <<'FIN'
2026-08-19  Rendimiento matinal (operador)
  - Causa: copia de 4h+ saturando E/S (w_await 22,85 ms, %util 97,6) pasadas las 09:00
  - Efecto: consultas a 30s de timeout -> 200/200 conexiones -> db_timeout
  - Cambio: timer a 02:30, RuntimeMaxSec=3h, IOWeight=10 (un solo cambio, medido)
  - Resultado 09:04: load 0,38 / wa 2% / w_await 1,40ms / 47 conexiones / 0 errores / 190 ms
  - Pendiente: copias incrementales (la copia completa seguirá creciendo)
FIN

Errores Comunes y Consejos

  • Leer el load average sin dividir por nproc. Un 3,42 no significa nada hasta saber cuántos núcleos hay.
  • Asustarse por «poca memoria libre». La cifra que importa es disponible; la caché es memoria bien empleada.
  • Fiarse de la primera línea de vmstat o iostat. Es la media desde el arranque, no el momento actual.
  • Mirar %util en un SSD como si fuera un disco mecánico. Con colas paralelas, un NVMe puede marcar 100 % y no estar saturado. Mira await y aqu-sz.
  • Diagnosticar sin línea base. Sin saber qué es normal, cualquier número parece alarmante o tranquilizador según el ánimo. Y no cambies varias cosas a la vez: no sabrás cuál funcionó, y arrastrarás ajustes inútiles durante años.
  • No tener historia. Instala sysstat el primer día: cuando alguien pregunte por las 09:00, ya será tarde para activarlo.
  • Confundir un síntoma con la causa. Las 200 conexiones eran el síntoma; la copia era la causa. Subir max_conexiones habría escondido el problema.
  • Consejo: guarda cada medición en un fichero con fecha, junto al HISTORIAL. Dentro de seis meses, esa carpeta valdrá más que tu memoria.

Ejercicios

  1. Leer una foto. Un servidor con 4 vCPU muestra load average: 7,80, 7,20, 6,90, vmstat con us=6 sy=3 id=4 wa=87 st=0, r=1, b=6, y free -h con 5,1 GiB disponibles de 8. ¿Cuál es el recurso saturado, cuál no lo es, y cuáles son los dos comandos siguientes?
  2. Un servicio que muere de madrugada. tramontana.service aparece reiniciado cada noche sobre las 03:40 sin que nadie lo toque. Da los comandos para confirmar si fue el OOM killer y, si lo fue, dos formas de evitar que se lleve por delante la base de datos.
  3. Demostrar una mejora. Diseña el protocolo exacto —comandos, momentos y criterio de éxito— para demostrar que activar noatime en /srv/tramontana/backups acorta la copia nocturna, de forma que el resultado convenza a alguien escéptico.

Soluciones

1. El recurso saturado es el disco. La prueba: wa=87 % con id=4 % significa que la CPU está parada esperando E/S, y b=6 son seis procesos bloqueados en estado D, que además son los que inflan el load hasta 7,80 pese a que solo hay r=1 esperando CPU. Normalizado, 7,80 entre 4 núcleos serían 1,95, pero ese número es engañoso aquí precisamente porque lo componen procesos en D, no en R. Lo que no está saturado: la CPU (us+sy = 9 %) ni la memoria (5,1 GiB disponibles de 8, sin swap en movimiento). Los dos comandos siguientes:

iostat -xz 1 3          # qué dispositivo, con qué latencia (w_await) y qué cola (aqu-sz)
sudo iotop -b -n 1 -o   # qué proceso concreto está haciendo esa E/S

2.

$ sudo journalctl -k --since "03:00" --until "04:00" --grep -i 'out of memory'
ago 17 03:41:12 srv-tramontana kernel: Out of memory: Killed process 1284 (tramontana)
$ systemctl show tramontana.service -p NRestarts     # NRestarts=6
$ journalctl -u tramontana.service --since "03:00" | grep 'Main process'
ago 17 03:41:12 systemd[1]: tramontana.service: Main process exited, code=killed, status=9/KILL

status=9/KILL sin que nadie ejecutara un kill es la firma del OOM killer, y el mensaje del kernel lo confirma. Nótese la coincidencia horaria con la copia nocturna: la caché de página que genera la copia presiona la memoria disponible.

Dos formas de proteger a la base de datos:

# (a) En tramontana.service: el OOM actúa DENTRO del cgroup del servicio
[Service]
MemoryMax=512M
Restart=on-failure
# (b) En postgresql.service: hacerla víctima improbable
[Service]
OOMScoreAdjust=-800

La primera es la buena, porque acota el problema en su origen: cuando Tramontana se pase de memoria morirá Tramontana, y systemd la levantará, sin que el kernel elija víctima en toda la máquina. La segunda es un complemento defensivo: sesga la elección, pero no impide que la máquina se quede sin memoria.

3. El protocolo, que es el método de la sección 8 aplicado:

# 1. Medir el estado actual, tres noches seguidas para tener variabilidad
journalctl -u tramontana-respaldo.service --since "3 days ago" -o cat | grep 'completada en'
# -> 6158s, 5904s, 6021s  (mediana 6021 s); sar -b -f /var/log/sysstat/sa17 para la E/S

# 2. Cambiar UNA sola cosa, con copia previa y verificación
sudo cp -a /etc/fstab /etc/fstab.bak-$(date +%F)
sudo sed -i 's|\(/srv/tramontana/backups.*defaults\)|\1,noatime|' /etc/fstab
sudo diff -u /etc/fstab.bak-$(date +%F) /etc/fstab
sudo mount -o remount /srv/tramontana/backups
findmnt -no OPTIONS /srv/tramontana/backups    # comprobar que noatime está activo

# 3. Medir otras tres noches, mismos comandos y mismo horario
journalctl -u tramontana-respaldo.service --since "3 days ago" -o cat | grep 'completada en'

Criterio de éxito, fijado antes de medir: reducción de la mediana de duración superior al 10 % sin aumento de errores en journalctl -u tramontana-respaldo.service -p err. Tres noches a cada lado porque una sola medición no distingue una mejora de una noche floja. Y si no se cumple el criterio, se revierte con el .bak y se anota en el HISTORIAL que no sirvió: un experimento negativo documentado ahorra que alguien lo repita dentro de un año.

Conclusión

Ya no dependes de que alguien «note» que el servidor va lento. Distingues monitorizar de diagnosticar y aplicas el método USE —utilización, saturación, errores— a los cuatro recursos. Lees el load average normalizado por nproc sabiendo que incluye los procesos en estado D, e interpretas vmstat 1 columna a columna: r y b, si/so, us/sy/id/wa y el st que delata al hipervisor. Repartes el consumo con mpstat -P ALL y pidstat.

En memoria sabes que «libre» es una cifra engañosa y que la buena es disponible, distingues VSZ, RSS y PSS, reconoces el thrashing en si/so, y sabes probar que actuó el OOM killer con journalctl -k y cómo evitar que se lleve al vecino con MemoryMax y OOMScoreAdjust. En disco manejas iostat -xz con %util, await y aqu-sz, tienes clara la diferencia entre latencia y caudal, y localizas al culpable con iotop y lsof. En red usas sar -n DEV, ss -s y las retransmisiones. Conoces htop, atop, glances y dstat, y —lo más importante— has activado sysstat para tener la historia sin la cual la foto de hoy no significa nada. Sabes qué son los cuatro golden signals y por qué una alerta que suena a diario deja de leerse.

Tienes un procedimiento de once pasos para «el servidor va lento», con las tres reglas innegociables: medir antes, cambiar una sola cosa, medir después. Tienes una línea base de srv-tramontana con umbrales escritos. Y has resuelto el caso: la lentitud matinal era la copia de seguridad saturando la E/S hasta pasadas las 09:00, provocando los db_timeout y el agotamiento de las 200 conexiones; adelantarla y limitarla devolvió la búsqueda de 4–9 s a 190 ms, con las cifras de antes y después escritas en el HISTORIAL.

Pero el informe para Marta terminaba con un cabo suelto que no puede esperar: la copia completa seguirá creciendo, y con ella el problema. Y hay algo mucho peor que nadie ha comprobado todavía: nadie ha restaurado nunca esa copia. En Respaldo y Restauración, la última lección del módulo, verás por qué no existen las copias de seguridad sino solo las restauraciones probadas: fijarás RPO y RTO para Tramontana, aplicarás la regla 3-2-1 y la retención por generaciones, resolverás la consistencia con snapshots LVM de 05-04, usarás tar --listed-incremental, rsync --link-dest y una herramienta moderna con deduplicación y cifrado, y escribirás el procedimiento de restauración paso a paso para los tres escenarios que pueden ocurrirte de verdad.

Curso de Linux: De Principiante a Administrador de Sistemas

Módulo 1: Introducción a Linux

Módulo 2: Comandos Básicos de Linux

Módulo 3: Habilidades Avanzadas en la Línea de Comandos

Módulo 4: Scripting en Shell

Módulo 5: Administración del Sistema

Módulo 6: Redes y Seguridad

Módulo 7: Temas Avanzados

Módulo 8: Proyectos Prácticos

© Copyright 2026. Todos los derechos reservados