Magia heredada: Nadie sabe cómo funciona, así que no la toques

¿Te fue útil?

Heredas un sistema que paga nóminas, envía pedidos o liquida operaciones. Tiene la disponibilidad de un faro y la legibilidad de una nota de rescate.
Todos lo llaman “estable”. Lo que quieren decir es: nadie se ha atrevido a moverlo en años.

Entonces se estropea un disco, caduca un certificado, desaparece un proveedor o la factura de la nube se duplica. De repente se espera que toques la “magia”.
Esto es cómo hacerlo sin convertir producción en una sesión grupal de terapia.

Qué es realmente la “magia heredada”

“Nadie sabe cómo funciona, así que no la toques” no es una evaluación técnica. Es una cicatriz organizacional. Suele significar:
el sistema es crítico para el negocio, está mal instrumentado, mal documentado y ha acumulado soluciones que funcionan solo bajo
un conjunto de restricciones específicas y mayormente olvidadas.

La parte más peligrosa no es el código antiguo. Son los contratos invisibles: timeouts ajustados a un perfil de latencia de almacenamiento específico,
cron programados alrededor de ventanas de procesamiento por lotes, peculiaridades del esquema en las que depende un trabajo de exportación extraño,
parámetros del kernel fijados por alguien que ahora es instructor de buceo. Los sistemas heredados funcionan sobre suposiciones. Cuando esas suposiciones se pudren, lo “estable” se vuelve “embrujado”.

Hay una cierta mitología alrededor de estos sistemas: el “mago” que lo escribió, el trigger de base de datos que “nunca debe cambiarse”,
el servidor especial que “necesita” cierta NIC. La mitología hace que los equipos sean cautelosos; la cautela es buena. La mitología también hace que los equipos se vuelvan perezosos; la pereza
es como las interrupciones se convierten en tradiciones.

Por qué sobrevive (y por qué falla)

La magia heredada sobrevive porque una vez fue la mejor opción

Muchas pilas “misteriosas” no fueron creadas por gente incompetente. Fueron creadas por personas bajo presión, con herramientas limitadas
y a menudo con restricciones de hardware que exigieron ingenio. Si eres lo bastante joven como para haber conocido siempre SSD baratos y bases de datos gestionadas,
vives en el lujo. Los sistemas antiguos tuvieron que exprimir rendimiento de discos giratorios, RAM escasa, redes lentas
y, a veces, un presupuesto que parecía un error tipográfico.

El sistema sobrevive porque está alineado con el negocio: hace una cosa de forma suficientemente fiable, y el riesgo de cambiarlo parece mayor
que el riesgo de dejarlo. Ese cálculo es racional—hasta que deja de serlo.

La magia heredada falla porque cambia el entorno, no porque “caduque”

Los sistemas no mueren de viejo. Mueren por deriva de interfaces: las librerías cambian comportamiento, los requisitos de TLS se endurecen, problemas de DNS o NTP
se amplifican por comprobaciones estrictas de tiempo, el almacenamiento virtualizado se comporta diferente que los discos locales, y tu nuevo tipo de instancia “más rápido”
trae una topología de CPU completamente distinta. Entonces el sistema que funcionó durante años se desploma en una semana.

La ingeniería de fiabilidad trata principalmente de mantener las suposiciones verdaderas, o de volverlas irrelevantes. Si tratas la magia heredada como un artefacto sagrado,
te garantizas que eventualmente la romperás—justo en el peor momento posible.

Hechos y contexto histórico que puedes usar

  • “Write once, run anywhere” tuvo una sombra: la portabilidad temprana a menudo ocultó suposiciones de rendimiento, especialmente alrededor de la semántica de los sistemas de ficheros y el manejo del tiempo.
  • Los sistemas de ficheros POSIX no se diseñaron para tu enjambre de microservicios: muchas aplicaciones heredadas suponen operaciones locales en disco de baja latencia y fuerte consistencia.
  • NFS se convirtió en el pegamento por defecto en empresas: también normalizó “es lento a veces” como una realidad operativa—muchas apps programaron silenciosamente alrededor de ello.
  • Las caches de controladoras RAID moldearon configuraciones de base de datos: la sintonía antigua de BDs a menudo asumía caché de escritura con batería; quítala y tu sistema “estable” se vuelve una máquina de latencia.
  • Las VMs cambiaron el tiempo: la deriva del reloj y la variabilidad de planificación en entornos virtualizados rompieron software antiguo que asumía tiempo monotónico o temporizadores predecibles.
  • Los ficheros de log solían ser la pila de observabilidad: mucha “magia” depende de scrapear logs o pipelines ad-hoc de grep que nadie admite como críticos en producción.
  • Los scripts init eran lógica operativa: antes de systemd muchas organizaciones incrustaron reglas de secuenciación frágiles en scripts init personalizados.
  • Las barreras de sistema de ficheros y el orden de escritura evolucionaron: opciones como write barriers cambiaron los trade-offs de seguridad/rendimiento, y los consejos antiguos pueden ser activamente dañinos ahora.
  • “Ventanas por lotes” fueron una primitiva de diseño: el procesamiento nocturno moldeó modelos de datos, estrategias de bloqueo y planes de backup; cargas siempre activas los estresan de forma distinta.

Reglas operativas: cómo tocarla con seguridad

Regla 1: Trata lo “desconocido” como una dependencia, no como una vergüenza

El misterio no es una falla moral. Es un estado del sistema. Tu trabajo es reducir el misterio como reduces la latencia: midiendo,
aislando e iterando. Si la cultura del equipo trata no saber como algo vergonzoso, la gente ocultará la incertidumbre—y tú desplegarás riesgo.

Regla 2: No cambies el comportamiento hasta que puedas observarlo

Antes de refactorizar, actualizar o “limpiar”, necesitas una línea base. Esa línea base no es “parece bien”. Es: distribución de latencias,
tasas de error, puntos de saturación, profundidades de cola y la forma exacta de lo normal.

Una cita que la gente de operaciones redescubre una y otra vez, porque sigue siendo verdad: “No puedes mejorar lo que no mides.” — Peter Drucker (idea parafraseada).
Las mediciones no harán al sistema seguro, pero hacen que el cambio sea probable de probar.

Regla 3: Reduce el radio de impacto antes de mejorar el rendimiento

El trabajo de rendimiento es seductor: ves un pico en una gráfica, quieres arreglarlo. Pero las “mejoras” de rendimiento heredadas suelen depender de órdenes oscuros,
comportamiento de retropresión o sincronización. Primero haz que las fallas sean más pequeñas: cambios canary, feature flags, ventanas de mantenimiento más pequeñas,
conjuntos de datos menores, ámbitos de replicación más reducidos. Luego optimiza.

Regla 4: Prefiere cambios reversibles

Si tu cambio no puede revertirse rápidamente, debe probarse como un implante quirúrgico. En tierra heredada, rara vez tienes ese lujo.
Elige cambios que puedan desactivarse: flags de configuración, cambios de parámetros en tiempo de ejecución, sondas de solo lectura, tráfico en sombra, pipelines paralelos.

Regla 5: Separa “cómo funciona” de “cómo se rompe”

No necesitas entender por completo un sistema heredado para operarlo con seguridad. Necesitas entender:
cómo se ve lo bueno, cómo se ve lo malo y qué palancas cambian los resultados.
Construye runbooks alrededor de modos de falla, no de diagramas de arquitectura.

Broma #1: Los sistemas heredados son como las sartenes de hierro fundido—si las frotas demasiado, se quita el “sazonado” y todos se enfadan.

Regla 6: Documenta la intención, no la trivia

“Set vm.dirty_ratio=12” es trivia. “Mantener la latencia de writeback por debajo de X para que el checkpoint de la BD no se atasque” es intención.
Lo segundo sobrevive a cambios de hardware y actualizaciones de kernel. La intención es lo que permite al siguiente ingeniero tomar una decisión distinta pero correcta.

Regla 7: El almacenamiento suele ser el cómplice silencioso

Como ingeniero de almacenamiento, seré franco: cuando los servicios heredados se comportan mal, el almacenamiento con frecuencia está involucrado—a veces como causa, a veces
como amplificador. Las apps antiguas incrustan suposiciones sobre el coste de fsync, la velocidad de búsqueda de inodos, la atomicidad de rename y el comportamiento del espacio libre.
Pones esa app en un backend de almacenamiento distinto y has cambiado las leyes físicas bajo las que evolucionó.

Regla 8: “No la toques” es un ítem del registro de riesgos, no una estrategia

Si un sistema da tanto miedo que no se puede cambiar, es demasiado arriesgado para depender de él. Ponlo en el registro de riesgos con disparadores concretos:
caducidad de certificados, fin de vida del SO, fin de venta del modelo de disco, renovación de contratos, pérdida de personal clave.
Luego construye un plan que reemplace el miedo por pasos.

Guía rápida de diagnóstico

Cuando el sistema “mágico” se ralentiza, no tienes tiempo para filosofía. Necesitas un bucle de triaje que encuentre el cuello de botella rápidamente,
sin empeorarlo. Este es el orden que suele ganar en producción.

Primero: confirma el impacto y acota el radio de impacto

  • ¿Es latencia visible para el usuario, rendimiento, tasa de errores o corrección de datos?
  • ¿Es un host, una zona de disponibilidad, un shard, un tenant o global?
  • ¿Cambió algo en la última hora/día: despliegue, configuración, kernel, ruta de almacenamiento, ACLs de red, renovación de certificados?

Segundo: decide si estás limitado por CPU, memoria o I/O

  • CPU-bound: cola de ejecución alta, CPU user/system alta, baja espera I/O, latencia estable hasta saturación.
  • Memory-bound: fallos mayores en aumento, actividad de swap, stalls de reclaim, kills por OOM, thrash de caché.
  • I/O-bound: iowait alto, utilización de disco alta, await/tiempos de servicio altos, hilos bloqueados, tormentas de fsync.

Tercero: verifica saturación y encolamiento en la capa más baja

  • Almacenamiento: profundidad de cola del dispositivo, latencia, errores, failover multipath, filesystem lleno, salud de pool ZFS, resync de md RAID.
  • Red: retransmisiones, drops, desajuste MTU, problemas de duplex, latencia DNS, picos en handshakes TLS.
  • Kernel: advertencias en dmesg, tareas colgadas, soft lockups, timeouts de la capa de bloques.

Cuarto: identifica el proceso cuello de botella y su dependencia

  • ¿Qué PID está consumiendo CPU, bloqueado en I/O o reteniendo locks?
  • ¿En qué archivos, sockets o discos está esperando?
  • ¿La contención es interna (locks) o externa (almacenamiento/red)?

Quinto: aplica la mitigación más pequeña y segura

  • Limita la tasa de jobs ruidosos (cron, batch, compactaciones).
  • Falla las lecturas a otro lado, drena tráfico o desplaza cargas.
  • Aumenta margen temporalmente (capacidad, caché o tunning de colas) solo si entiendes los efectos secundarios.

Tareas prácticas con comandos, qué significa la salida y qué decisión tomar

Estos son los movimientos básicos cuando te enfrentas a un servicio misterioso en Linux con almacenamiento en juego.
Cada tarea incluye un comando, salida de ejemplo, qué significa y la decisión que tomas a partir de ello.
Ejecútalas primero en modo solo lectura. La curiosidad está bien; los reinicios sorpresa no.

Task 1: Find what changed recently (package and service state)

cr0x@server:~$ journalctl --since "6 hours ago" -p warning..alert | tail -n 20
Jan 12 10:14:03 app01 kernel: nvme nvme0: I/O 123 QID 4 timeout, aborting
Jan 12 10:14:03 app01 kernel: blk_update_request: I/O error, dev nvme0n1, sector 987654 op 0x1:(WRITE) flags 0x0 phys_seg 16 prio class 0
Jan 12 10:18:11 app01 systemd[1]: myapp.service: Main process exited, code=killed, status=9/KILL

Significado: timeouts de I/O del kernel más errores, luego un servicio fue terminado. Esto no es “la app está lenta.” Es “el almacenamiento está en llamas.”

Decisión: deja de tunear la aplicación y empieza a proteger datos: revisa la salud del dispositivo, multipath, controladora e integridad del filesystem. Considera drenar tráfico.

Task 2: Get a one-screen health view (CPU, memory, load, iowait)

cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 2  0      0  81232  41200 621000    0    0   120   210  950 1400 12  6 78  4  0
 4  1      0  50440  39800 612400    0    0   110   980 1100 2100 10  7 55 28  0
 6  3      0  49600  39000 610800    0    0    90  1600 1200 2600  8  6 40 46  0

Significado: el aumento de b (bloqueados) y wa (iowait) indica hilos atascados esperando I/O.

Decisión: pivota a comprobaciones de disco y filesystem; no “añadas CPU” a la ligera. La CPU no es el cuello de botella todavía.

Task 3: Identify which disks are slow and how (latency vs utilization)

cr0x@server:~$ iostat -xz 1 3
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           9.21    0.00    6.14   31.55    0.00   53.10

Device            r/s     w/s   rkB/s   wkB/s  rrqm/s  wrqm/s  %util  await  svctm
nvme0n1         220.0  310.0  9024.0 12800.0     0.0     0.0   99.5  18.2   0.9
nvme1n1          10.0   12.0   400.0   520.0     0.0     0.0    3.1   1.2   0.2

Significado: %util cerca del 100% con await alto significa que el dispositivo está saturado y las peticiones se encolan.

Decisión: encuentra qué está golpeando nvme0n1 y reduce su tasa de escritura, mueve datos calientes o añade capacidad/IOPS. Si esto debía estar “replicado,” verifica que realmente lo esté.

Task 4: Find which process is generating I/O

cr0x@server:~$ sudo iotop -o -b -n 3
Total DISK READ: 8.12 M/s | Total DISK WRITE: 45.33 M/s
  PID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN  IO>    COMMAND
 7321 be/4   myapp     1.23 M/s  22.10 M/s   0.00 %  6.12 % myapp --worker
 9012 be/4   postgres  0.00 B/s  18.44 M/s   0.00 %  5.90 % postgres: checkpointer

Significado: la app más Postgres checkpointer son los grandes escritores. Esa combinación a menudo significa que las páginas sucias se están volcando en ráfagas.

Decisión: considera suavizar las escrituras (tunear checkpoints BD) y verifica si la latencia de almacenamiento cambió recientemente. No mates el checkpointer a ciegas; está controlando el daño.

Task 5: Check filesystem fullness and inode exhaustion (the dumb outage)

cr0x@server:~$ df -hT
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/nvme0n1p2 ext4  200G  196G  2.1G  99% /
/dev/nvme1n1p1 ext4  500G  120G  356G  26% /data

Significado: el filesystem raíz está al 99%. Espera comportamientos extraños: fallos al crear ficheros temporales, logs que no se escriben, servicios que se caen de maneras no obvias.

Decisión: libera espacio inmediatamente (rotar logs, eliminar artefactos antiguos), luego arregla la ruta de crecimiento. Si necesitas tiempo, redirige escrituras a /data temporalmente.

Task 6: Validate inode availability (small files can kill you)

cr0x@server:~$ df -i
Filesystem      Inodes  IUsed   IFree IUse% Mounted on
/dev/nvme0n1p2 1310720 1310100     620  100% /
/dev/nvme1n1p1 3276800  210000 3066800    7% /data

Significado: uso de inodos al 100%: puedes tener “espacio libre” y aun así no poder crear ficheros. Los sistemas batch heredados aman crear ficheros diminutos.

Decisión: encuentra el directorio con la explosión (normalmente logs, spool, temp, cache). Limpia y añade salvaguardas (logrotate, políticas de retención).

Task 7: Identify mount options and risky behaviors (barriers, noatime, etc.)

cr0x@server:~$ findmnt -no TARGET,SOURCE,FSTYPE,OPTIONS /
/ /dev/nvme0n1p2 ext4 rw,relatime,errors=remount-ro

Significado: relatime está bien; errors=remount-ro significa que el filesystem cambiará a solo lectura ante errores, lo que puede parecer que “la app se congeló.”

Decisión: si viste errores de I/O, planifica el cambio a solo lectura y asegúrate de que los servicios lo manejen. En sistemas heredados, a menudo no lo hacen.

Task 8: Check kernel for hung tasks and block layer pain

cr0x@server:~$ dmesg -T | tail -n 30
[Sun Jan 12 10:14:03 2026] nvme nvme0: I/O 123 QID 4 timeout, aborting
[Sun Jan 12 10:14:09 2026] INFO: task myapp:7321 blocked for more than 120 seconds.
[Sun Jan 12 10:14:09 2026]       Tainted: G        W  OE     5.15.0-91-generic #101-Ubuntu
[Sun Jan 12 10:14:09 2026] Call Trace:
[Sun Jan 12 10:14:09 2026]  __schedule+0x2c5/0x6f0
[Sun Jan 12 10:14:09 2026]  io_schedule+0x46/0x70

Significado: tareas bloqueadas en la planificación de I/O: síntoma clásico de stalls del almacenamiento.

Decisión: escala a la capa de almacenamiento: revisa logs SMART/NVMe del dispositivo, controladora, multipath, SAN y cualquier cambio de firmware reciente.

Task 9: Map hot files to devices (which mount is the culprit)

cr0x@server:~$ sudo lsof -p 7321 | head -n 10
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
myapp   7321 myapp  cwd    DIR  259,2     4096  131073 /
myapp   7321 myapp  10w   REG  259,2  1048576  262200 /var/log/myapp/worker.log
myapp   7321 myapp  11w   REG  259,2  5242880  262199 /var/lib/myapp/spool/events.dat

Significado: el proceso escribe bajo /var en el disco raíz (DEVICE 259,2), no en /data. Eso puede ser accidental y catastrófico.

Decisión: reubica spool/logs a un volumen de datos adecuado, idealmente con un symlink o bind mount para un cambio mínimo. Primero, verifica que nada dependa del comportamiento de la ruta antigua.

Task 10: Check service restart loops and dependency ordering

cr0x@server:~$ systemctl status myapp.service --no-pager
● myapp.service - MyApp Worker
     Loaded: loaded (/etc/systemd/system/myapp.service; enabled; vendor preset: enabled)
     Active: activating (auto-restart) (Result: exit-code) since Sun 2026-01-12 10:18:12 UTC; 6s ago
    Process: 9441 ExecStart=/usr/local/bin/myapp --worker (code=exited, status=1/FAILURE)
   Main PID: 9441 (code=exited, status=1/FAILURE)
        CPU: 210ms

Jan 12 10:18:12 app01 myapp[9441]: FATAL: cannot create /var/lib/myapp/spool/lock: No space left on device

Significado: ya no es un misterio: falla por falta de espacio y systemd está provocando reinicios continuos.

Decisión: detén la tormenta de reinicios si empeora el I/O (temporal systemctl stop), libera espacio y luego reinicia. Añade monitorización/alertas para disco/inodos para evitar repeticiones.

Task 11: Inspect cron/system timers (the invisible workload)

cr0x@server:~$ systemctl list-timers --all --no-pager | head -n 12
NEXT                        LEFT     LAST                        PASSED   UNIT                         ACTIVATES
Sun 2026-01-12 10:30:00 UTC 7min left Sun 2026-01-12 10:00:00 UTC 23min ago logrotate.timer              logrotate.service
Sun 2026-01-12 11:00:00 UTC 37min left Sun 2026-01-12 10:00:03 UTC 23min ago fstrim.timer                fstrim.service
Sun 2026-01-12 12:00:00 UTC 1h 37min Sun 2026-01-12 00:00:00 UTC 10h ago    apt-daily-upgrade.timer      apt-daily-upgrade.service

Significado: fstrim en almacenamiento thin-provisioned o muy usado puede causar picos de latencia; logrotate puede causar ráfagas de I/O por rename/compress.

Decisión: si los picos correlacionan, reprograma los timers a ventanas de bajo tráfico o ajústalos (p. ej., frecuencia de trim). Valida con métricas antes de cambiar.

Task 12: Network sanity for “storage” that is actually remote

cr0x@server:~$ ip -s link show dev eth0 | sed -n '1,12p'
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:ab:cd:ef brd ff:ff:ff:ff:ff:ff
    RX:  bytes packets errors dropped  missed   mcast
    987654321  1234567      0    1241       0       0
    TX:  bytes packets errors dropped carrier collsns
    123456789  2345678      0       0       0       0

Significado: drops en RX: pueden causar problemas en NFS/iSCSI que se perciben como “lenteza de disco” en la capa de la app.

Decisión: investiga colas de NIC, driver/firmware, MTU y congestión río arriba. Si el almacenamiento es remoto, trata la red como parte del disco.

Task 13: Confirm what storage you’re actually on (LVM/MD/DM)

cr0x@server:~$ lsblk -o NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT
NAME          TYPE  SIZE FSTYPE MOUNTPOINT
nvme0n1       disk  238G
├─nvme0n1p1   part    1G vfat   /boot/efi
└─nvme0n1p2   part  200G ext4   /
nvme1n1       disk  477G
└─nvme1n1p1   part  477G ext4   /data

Significado: no se muestra RAID/LVM. Si alguien te dijo “está espejado,” puede que recuerde otra era.

Decisión: verifica las afirmaciones de redundancia. Si no existe redundancia, planifica mitigación de riesgo: backups, replicación o RAID/espejado real.

Task 14: Measure latency distribution to separate “slow” from “spiky”

cr0x@server:~$ sudo fio --name=latcheck --filename=/data/fio.test --size=1G --direct=1 --rw=randread --bs=4k --iodepth=32 --runtime=30 --time_based --group_reporting
latcheck: (g=0): rw=randread, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=32
fio-3.28
Run status group 0 (all jobs):
   READ: bw=85.2MiB/s (89.3MB/s), 85.2MiB/s-85.2MiB/s (89.3MB/s-89.3MB/s), io=2556MiB (2681MB), run=30001-30001msec
    slat (nsec): min=800, max=21000, avg=3200.4, stdev=900.1
    clat (usec): min=180, max=12000, avg=340.2, stdev=410.7
     lat (usec): min=190, max=12010, avg=344.0, stdev=411.0
    clat percentiles (usec):
     |  1.00th=[  210],  5.00th=[  230], 10.00th=[  245], 50.00th=[  290],
     | 90.00th=[  520], 95.00th=[  780], 99.00th=[ 2100], 99.90th=[ 7200]

Significado: la latencia mediana está bien, la latencia en cola es fea. Los sistemas heredados a menudo mueren por el percentil 99.9.

Decisión: investiga stalls periódicos (GC, compactación, backups, trim, snapshots, fallos de controladora). Optimiza para las colas, no solo para las medias.

Task 15: Validate backup capability without trusting the story

cr0x@server:~$ sudo -n test -r /var/backups/myapp/latest.sql.gz && echo "backup readable" || echo "backup missing"
backup readable

Significado: la existencia y legibilidad son el mínimo indispensable. Muchas afirmaciones de “tenemos backups” acaban aquí.

Decisión: programa una prueba de restauración en un host de pruebas. Si no puedes restaurar, no tienes backups; tienes arrepentimiento comprimido.

Task 16: Check for silent data corruption signals (ZFS example)

cr0x@server:~$ sudo zpool status -v
  pool: tank
 state: DEGRADED
status: One or more devices has experienced an error resulting in data corruption.
action: Restore the file in question if possible.  Otherwise restore the entire pool from backup.
  scan: scrub repaired 0B in 00:12:44 with 2 errors on Sun Jan 12 09:40:11 2026
config:

        NAME        STATE     READ WRITE CKSUM
        tank        DEGRADED     0     0     0
          mirror-0  DEGRADED     0     0     0
            sda     ONLINE       0     0     2
            sdb     ONLINE       0     0     0

errors: Permanent errors have been detected in the following files:
        /data/myapp/index/segment-00017

Significado: los checksums detectaron corrupción. Eso no es ZFS dramatizando; es ZFS haciendo su trabajo y diciéndote una verdad incómoda.

Decisión: restaura los datos afectados desde una réplica/backup conocido bueno, reemplaza hardware sospechoso y programa scrubs periódicos. Si tu app heredada no tolera corrupción de ficheros, trata esto como un sev-1.

Tres microhistorias del mundo corporativo

1) Incidente causado por una suposición errónea: “¿Es redundante, verdad?”

Una empresa mediana ejecutaba una canalización de facturación en un único host Linux. Todos creían que la base de datos vivía en “un RAID espejado”
porque, históricamente, así había sido. El creador original se había ido, y el equipo actual vio dos discos en el chasis y asumió seguridad.
Nadie había comprobado en años, porque comprobarlo parecía tentar a la suerte.

Migraron el host a un nuevo rack durante una limpieza del centro de datos. Tras el reinicio, el sistema arrancó, funcionó un día y luego
empezó a arrojar errores de filesystem bajo carga de escritura. Los logs del kernel mostraron timeouts de I/O. El on-call hizo lo razonable:
“fallar” al segundo disco. No hubo failover. Solo había un segundo disco, formateado para otra cosa, silenciosamente sin usar.

La recuperación se volvió arqueología. El último backup conocido bueno era más antiguo de lo que la dirección estaba cómoda admitiendo, porque los jobs de backup habían estado
“verdes” pero en realidad estaban respaldando un directorio vacío tras un cambio de ruta. El incidente no fue causado por un disco que murió; los discos mueren todo el tiempo.
Fue causado por una suposición que sobrevivió más que su autor.

La solución no fue heroica. Fue aburrida: inventario con lsblk, verificar redundancia, restaurar backups a un entorno de pruebas mensualmente,
y añadir una alerta para “backup produjo salida sospechosamente pequeña.” El nuevo runbook incluyó una sola línea: “Si alguien dice ‘espejado’, muestra la salida del comando.”

2) Optimización que salió mal: “Encendimos lo más rápido”

Otra organización tenía un servicio Java heredado que escribía registros pequeños en disco y confiaba en fsync para durabilidad. Corría en SSD locales
y se comportaba predeciblemente. Una iniciativa de rendimiento lo movió a una plataforma de almacenamiento en red compartida que ofrecía mejor utilización
y snapshots centralizados. La migración cumplió el KPI: el throughput promedio mejoró y el equipo de plataforma declaró victoria.

Dos semanas después, el sistema empezó a experimentar picos de latencia periódicos. No lentitud constante—picos. Cada pocas horas, las escrituras se atascaban,
las colas de solicitudes crecían y los servicios upstream hacían timeout. Los ingenieros persiguieron GC, pools de threads y flags de configuración al azar.
Alguien “optimizó” más aumentando la concurrencia de la aplicación para “ocultar la latencia.” Eso aumentó el tamaño de las ráfagas de escritura, lo que empeoró la latencia de cola de la
plataforma de almacenamiento, provocando backlogs aún mayores. Un bucle de realimentación limpio—pero equivocado.

La raíz fue un desajuste: el modelo de durabilidad de la app asumía fsync barato; la plataforma de almacenamiento implementó durabilidad con semánticas diferentes y trabajo de fondo periódico.
La app no estaba equivocada. La plataforma no estaba equivocada. El emparejamiento estaba mal.

Se estabilizaron limitando la concurrencia, moviendo los journals calientes de vuelta a NVMe local y dejando los datos masivos en el backend compartido.
Luego midieron p99.9 como métrica de primera clase, no como nota al pie postmortem. La lección de la “optimización” quedó: si no mides colas, mides tu optimismo.

3) Práctica aburrida pero correcta que salvó el día: “El runbook lo hizo”

Un equipo de servicios financieros tenía un procesador batch añejo más viejo que algunos ingenieros. Nunca lo reescribieron porque funcionaba.
Pero el equipo lo trató como un servicio de producción de primera clase de todas formas: tenía dashboards, una prueba de restauración semanal y un runbook que describía
modos de fallo con comprobaciones simples y baselines conocidos.

Una noche, el tiempo de procesamiento se duplicó. Sin errores. Solo más lento. El on-call siguió el runbook: comprobar utilización de disco, iowait,
NTP, concurrencia de jobs, eventos recientes de timers. En diez minutos encontraron al culpable: un job de logrotate comprimiendo un
log muy grande en el mismo volumen que el dataset de entrada, causando contención de I/O periódica. El sistema no estaba roto; competía por recursos.

La solución fue tan emocionante como una hoja de cálculo: mover logs a un volumen separado y limitar la CPU de compresión. También añadieron una alerta:
“archivo de log supera tamaño de retención” y “compresión solapa ventana de batch.” La siguiente ejecución fue normal.

Lo interesante es lo que no pasó: no hubo tuning en pánico, no reinicios aleatorios, no “quizá el SAN está embrujado.” La práctica aburrida funcionó porque
convirtió desconocidos en comprobaciones. En producción, el aburrimiento es una característica.

Broma #2: Si tu monitorización solo verifica promedios, felicitaciones—has construido un sistema que siempre está sano en retrospectiva.

Errores comunes: síntomas → causa raíz → solución

1) “Está lento después de la migración” → suposiciones ocultas de fsync → mueve journals o cambia estrategia de durabilidad

Síntomas: p99 picos de latencia de escritura, hilos bloqueados, timeouts de la aplicación, especialmente durante ráfagas.

Causa raíz: la carga se movió de SSD local a almacenamiento en red/NFS/iSCSI con semánticas de flush y latencia en cola diferentes.

Solución: mantén WAL/journals sensibles a latencia en NVMe local; benchmark con fio; mide p99.9; limita concurrencia y añade retropresión.

2) “Caídas aleatorias” → disco lleno o inodos agotados → aplicar retención y alertas

Síntomas: servicios salen con errores raros, fallan ficheros lock, logs se detienen, actualizaciones de paquetes fallan.

Causa raíz: filesystem al 95–100% o agotamiento de inodos; apps heredadas crean muchos ficheros diminutos.

Solución: alertas con df -hT y df -i; logrotate que realmente se ejecute; mover spools fuera del root; establecer cuotas donde sea posible.

3) “La CPU está alta, así que debe ser cómputo” → la CPU está alta por iowait o compresión → separa señal del ruido

Síntomas: media de carga alta, usuarios se quejan, pero añadir CPU no ayuda.

Causa raíz: hilos bloqueados inflan la carga; ciclos CPU gastados en kernel o compresión; iowait escondido bajo la carga.

Solución: usa vmstat e iostat -xz; examina tareas bloqueadas en dmesg; mueve jobs de compresión fuera de ventanas pico.

4) “Reiniciamos y empeoró” → orden de dependencias + recuperación con estado → detener tormentas de reinicio

Síntomas: el servicio sigue reiniciando, discos thrash, logs inundan, la recuperación se alarga.

Causa raíz: bucles de restart de systemd más tareas de warm-up largas (reindex, rebuild de cache) más almacenamiento saturado.

Solución: detén temporalmente el servicio; confirma margen de disco; aumenta backoff de restart; añade dependencias explícitas y checks de readiness.

5) “Backups están verdes” → ruta incorrecta o dataset vacío → probar restauraciones

Síntomas: backups “exitosos”, pero las restauraciones fallan o restauran datos vacíos.

Causa raíz: ruta movida, permisos cambiados o job de backup captura el mount equivocado; la monitorización solo comprueba código de salida.

Solución: simulacros periódicos de restauración; valida rangos de tamaño de backup; guarda manifiestos de backup; alerta sobre salidas inesperadamente pequeñas.

6) “El almacenamiento dice sano” → latencia de cola y micro-stalls → observa percentiles y profundidad de cola

Síntomas: el throughput global parece bien; los usuarios ven congelamientos; las gráficas muestran acantilados periódicos.

Causa raíz: los checks de salud de la plataforma se centran en promedios; tareas de fondo (scrubs, trims, snapshots) crean picos en la cola.

Solución: instrumenta p95/p99/p99.9 de latencia; monitoriza await del dispositivo, profundidad de cola; reprograma tareas de fondo.

7) “Tuneamos el kernel como un blog” → consejos antiguos en kernels nuevos → revierte a la base y prueba un cambio a la vez

Síntomas: latencia impredecible, problemas de reclaim de memoria, stalls extraños tras “sysctl tuning.”

Causa raíz: sysctls cargo-culted que entran en conflicto con kernels modernos o comportamiento de cgroups.

Solución: registra cambios de sysctl; revierte a los valores por defecto de la distro; introduce cambios con una hipótesis y plan de medición.

8) “Solo falla en un nodo” → divergencia de hardware/firmware → aplica homogeneidad y captura versiones

Síntomas: un host muestra más errores, latencia distinta o reinicios extraños.

Causa raíz: firmware NVMe distinto, estado de caché de la controladora RAID, driver de NIC, ajustes de BIOS.

Solución: inventario de firmware y módulos del kernel; estandariza; aisla el nodo; reemplaza componentes sospechosos.

Listas de verificación / plan paso a paso

Checklist A: “Tocar la magia” sin romperla

  1. Define el límite de seguridad: qué datos nunca deben perderse, qué downtime es aceptable, qué significa revertir en minutos.
  2. Captura una línea base: CPU, memoria, percentiles de latencia de disco, tasas de error, profundidades de cola. Guarda capturas si hace falta; guarda algo.
  3. Inventario la realidad: discos, montajes, RAID/LVM/ZFS, rutas de red, timers, cron y backups. “Creemos” no cuenta.
  4. Escribe la primera página del runbook: cómo distinguir sano de enfermo, y los cinco comandos que lo prueban.
  5. Elige primero un cambio reversible: logging, métricas, sondas de solo lectura o mover escrituras no críticas.
  6. Haz un cambio: uno. No tres “ya que estamos.”
  7. Mide de nuevo: si no ves mejora o degradación, no controlaste el experimento.
  8. Documenta la intención: por qué existe el cambio, qué métrica afecta y cómo revertirlo.

Checklist B: Pasos de seguridad específicos de almacenamiento (porque el almacenamiento es donde las carreras van a morir)

  1. Comprueba espacio libre e inodos en todos los mounts relevantes; aplica objetivos de margen mínimo.
  2. Valida afirmaciones de redundancia con comandos, no con folklore.
  3. Confirma expectativas de durabilidad de escritura (frecuencia de fsync, modo de journaling, settings WAL de BD).
  4. Inspecciona logs del kernel por timeouts de dispositivo, resets y remounts de filesystem.
  5. Prueba latencia de cola con un benchmark controlado fuera de pico, no durante un incidente.
  6. Ejecuta un test de restauración antes de cualquier migración riesgosa.

Checklist C: Respuesta a incidentes en sistemas heredados

  1. Estabiliza: detén el bucle de reinicios, limita jobs batch, reduce tráfico si es necesario.
  2. Identifica la capa del cuello de botella: CPU vs memoria vs almacenamiento vs red.
  3. Confirma el síntoma con dos fuentes: kernel + logs de app, o iostat + métricas de latencia.
  4. Aplica la mitigación más pequeña: mueve un camino caliente, reprograma un timer, añade 1 GB de margen, no un rediseño.
  5. Registra lo que funcionó: comandos, salidas y marcas de tiempo. Estás escribiendo el runbook del mañana bajo presión.
  6. Post-incidente: convierte una suposición en una comprobación, y una comprobación en una alerta.

FAQ

1) ¿“No la toques” alguna vez es correcto?

Temporalmente, sí—durante ventanas de ingresos pico o cuando no tienes rollback. Permanentemente, no. Si no se puede cambiar, debe aislarse,
medirse y ponerse en camino hacia reemplazo o contención.

2) ¿Cómo empiezo a documentar un sistema que no entiendo?

Empieza por el comportamiento: entradas, salidas, SLOs y modos de fallo. Luego dependencias: montajes de almacenamiento, bases de datos, endpoints de red, cron/timers
y backup/restore. Los diagramas de arquitectura son agradables; la verdad operativa es mejor.

3) ¿Cuál es la primera métrica para añadir a un servicio heredado?

Percentiles de latencia para la operación crítica, más tasa de error. Si hay almacenamiento involucrado, añade latencia y utilización del dispositivo.
Los promedios te mentirán con cara seria.

4) ¿Por qué los sistemas heredados odian el almacenamiento compartido?

A menudo asumen semánticas de disco local: baja jitter, fsync predecible y modos de fallo simples. El almacenamiento compartido introduce encolamiento,
interferencia multi-tenant y mecanismos de durabilidad distintos. A veces sigue funcionando—simplemente no por defecto.

5) ¿Deberíamos reescribirlo?

No como primera respuesta. Primero, hazlo observable y seguro de operar. Las reescrituras se justifican cuando el coste de reducir riesgo supera
el coste de reemplazo, o cuando las dependencias (SO, runtime, proveedor) son genuinamente insostenibles.

6) ¿Cómo convenzo a la dirección de que “estable” es riesgoso?

Lleva disparadores concretos: fechas de fin de vida del SO, caducidad de certificados, puntos únicos de fallo, falta de pruebas de restauración y latencia de cola medida.
El riesgo que se puede graficar se financia más rápido que el miedo.

7) ¿Cuál es un primer cambio “tacto” seguro?

Añadir observabilidad de solo lectura: dashboards, logs con IDs de correlación, alertas de disco/inodos y una prueba de restauración de backup.
Estos cambian tu visibilidad, no el comportamiento en producción.

8) ¿Cómo trato con “el mago” que guarda el sistema?

Respeta su experiencia y luego extráela en artefactos: runbooks, salidas base y procedimientos de prueba. Si el conocimiento permanece en una sola cabeza,
el sistema ya está en estado degradado—solo que aún no has tenido el outage.

9) ¿Y si el sistema es demasiado frágil para probar?

Entonces tu primer proyecto es crear un entorno de staging o una réplica de solo lectura en sombra. Si de verdad no puedes, reduce el riesgo limitando
cambios a toggles de configuración reversibles y controles operativos.

10) ¿Cómo evito tunings cargo-cult?

Escribe una hipótesis para cada cambio: “Esto reduce la ráfaga de checkpoints y baja la latencia p99 de escritura.” Mide antes/después. Ten un plan de reversión.
Si no puedes explicar el modo de fallo que puedas introducir, no lo despliegues.

Conclusión: próximos pasos que puedes hacer

La magia heredada es simplemente ingeniería no documentada más tiempo. Trátala como producción, no como folklore. La meta no es ser intrépido.
La meta es ser metódico lo suficiente para que el miedo deje de ser tu principal mecanismo de control.

Haz esto a continuación, en orden:

  1. Baselina y observa: captura snapshots de iostat/vmstat bajo carga normal; añade percentiles de latencia a dashboards.
  2. Inventario la realidad del almacenamiento: montajes, redundancia, espacio libre/inodos, logs de error del kernel. Escríbelo.
  3. Prueba backups restaurando: una ejecución en seco a un host de pruebas. Ponlo en calendario.
  4. Reduce el radio de impacto: separa logs/spools del root, añade límites a jobs batch, implementa canaries para cambios.
  5. Convierte suposiciones en comprobaciones: si alguien dice “está espejado,” automatiza la verificación; si alguien dice “los backups están bien,” prueba la restauración mensualmente.

Toca el sistema, pero tócalo con guantes: mediciones, reversibilidad y un plan que valore la corrección aburrida por encima de las heroicidades excitantes.
Así la herencia deja de ser magia y pasa a ser solo otro servicio que ejecutas con confianza.

← Anterior
Instantánea de Proxmox atascada: limpieza segura de restos en LVM-thin
Siguiente →
Replicación de Proxmox fallida: por qué se rompe y cómo recuperar

Deja un comentario