Reiniciar servicios atascados de forma segura (sin reiniciar)

¿Te fue útil?

Son las 02:13, tu pager hace su pequeña danza enfadada y el servicio que necesitas reiniciar está “stopping…” como si meditara sobre el sentido de la vida. Lo peor: el negocio quiere que “simplemente reinicies la máquina”, porque eso siempre “lo arregla”. Claro. También aumenta tu radio de daño al máximo.

Esta es la forma más segura: diagnosticar por qué el servicio está atascado, decidir si un reinicio es la medida correcta y, si debes aplicar fuerza, hacerlo de forma quirúrgica. Sin pensamiento mágico. Sin reinicios innecesarios. Solo daño controlado.

Principios: qué significa realmente “reinicio seguro”

Un “reinicio seguro” no es “el servicio vuelve a funcionar”. Es “el servicio vuelve a funcionar y no hemos corrompido datos, bloqueado el host ni provocado una falla en cascada”. Eso suena obvio hasta que estás mirando una unidad atascada a las 2 AM y tus manos comienzan a escribir reboot por memoria muscular.

1) No reinicies hasta entender qué está atascado

Cuando un servicio no se detiene, normalmente no es “terquedad” del servicio. Está esperando algo: I/O de disco, un montaje de red, un bloqueo del kernel, una dependencia, un hook de apagado, un proceso hijo que nunca sale, un archivo PID que miente, o systemd esperando una notificación que no llegará.

Si reinicias a ciegas, a menudo conviertes un único proceso atascado en una escena del crimen con múltiples procesos. Especialmente si hay clientes de base de datos, colas de trabajo o rutas de almacenamiento involucradas.

2) Trata el estado como ciudadano de primera clase

Los servicios sin estado perdonan más. Los servicios con estado no. Si el servicio atascado posee datos (base de datos, cola, exportación de sistema de archivos, caché con persistencia), detenerlo violentamente puede causar largos tiempos de recuperación, tormentas de reenvío o corrupción. Tu trabajo es negociar disponibilidad inmediata frente a recuperación e integridad, de forma explícita.

3) Entiende lo que systemd está haciendo, no lo que esperas que haga

Systemd no es “un script init elegante”. Es un supervisor de procesos con grafos de dependencias, watchdogs, cgroups y timeouts. Cuando le pides que detenga algo, usa señales específicas, en un orden específico, a un conjunto específico de procesos. Si se atasca, la razón suele poder descubrirse si miras las propiedades de la unidad, la membresía del cgroup y el journal.

4) Escala la fuerza en pasos y anuncia lo que vas a hacer

Deberías tener una escalera de escalado. Empieza por señales y comprobaciones de corrección suaves. Solo entonces pasa a SIGKILL, terminación por cgroup o resetear estados fallidos. Y cuando lo hagas, comunícalo. Un “reinicio rápido” que descarta trabajo en vuelo rara vez es rápido para los humanos río abajo.

5) Un reinicio no es una solución; es una amputación

Reiniciar funciona porque borra estado, restablece drivers, desatasca recursos del kernel y reinicia todo. También por eso es peligroso: pierdes la oportunidad de diagnosticar, interrumpes servicios no relacionados y puedes provocar largos procesos de fsck / reconstrucción RAID / reproducción de journal.

Una cita para tener a mano, porque refleja la mentalidad que necesitas bajo presión de incidente:

“La esperanza no es una estrategia.” — Vince Lombardi

Sí, viene del deporte. Operar también es un deporte de contacto; el oponente es la entropía.

Guía de diagnóstico rápido (primero/segundo/tercero)

Esta es la versión “tengo cinco minutos antes de que el canal de incidentes se convierta en danza interpretativa”. El objetivo es localizar rápidamente la clase de cuello de botella: lógica del servicio, dependencias, recursos del sistema o kernel/almacenamiento.

Primero: verifica qué está realmente atascado

  1. Revisa el estado de la unidad y los logs recientes: ¿Está “activating”, “deactivating”, “failed” o “running but unhealthy”?
  2. Confirma el PID principal y los procesos del cgroup: ¿Hay procesos hijos que sobrevivieron al proceso principal?
  3. Mira qué está esperando systemd: timeouts, notify, watchdog, hooks de ExecStop o el orden de dependencias.

Segundo: determina si está esperando I/O, bloqueos o red

  1. Busca procesos en D state: el sleep no interrumpible es una gran señal de alarma; matar no ayudará.
  2. Revisa la presión de disco: alta iowait, dispositivo saturado, multipath atascado, sistema de archivos lleno.
  3. Comprueba montajes de red y resolución de nombres: los cuelgues de NFS y los timeouts de DNS hacen que los scripts de parada se congelen.

Tercero: elige la intervención de menor riesgo

  1. Si es un bloqueo de lógica: reinicia de formagraciosa, drena tráfico, rota el proceso.
  2. Si es un bloqueo por dependencia: arregla la dependencia primero (almacenamiento, DNS, montaje) y luego reinicia.
  3. Si es un wedge de kernel/almacenamiento: no repitas reinicios. Decide entre aislar el host, hacer failover o reiniciar con intención.

Pequeña broma #1: Si reinicias un servicio cinco veces y “aleatoriamente” funciona, felicidades: has inventado una prueba de carga para tu suerte.

Datos interesantes y contexto histórico

  • Las señales Unix fueron diseñadas para apagados cooperativos. SIGTERM es una petición, no una orden. Algunos demonios lo tratan con educación; otros lo ignoran; algunos lo interpretan como un desafío personal.
  • “Atascado en D state” es anterior a tu pila de monitorización. El sleep no interrumpible existe desde hace décadas mientras el kernel espera la finalización de I/O; es una pista de que tu problema está por debajo del espacio de usuario.
  • systemd introdujo el seguimiento de procesos basado en cgroup como solución práctica al clásico problema de “el demonio hizo fork y dejó huérfanos” que rondaba los init scripts de SysV.
  • Los archivos PID son un compromiso histórico. Fueron una solución para supervisores que no podían rastrear procesos de forma fiable. Siguen causando incidencias cuando quedan obsoletos.
  • Los timeouts son un hábito operativo relativamente moderno. Los viejos scripts init a menudo esperaban indefinidamente; los gestores de servicios modernos asumen que “para siempre” es inaceptable y escalan o fallan.
  • NFS ha estado congelando apagados desde los años 80. No es malicia; es simplemente que los sistemas distribuidos son sinceros sobre sus modos de fallo.
  • Los modos de “apagado rápido” en bases de datos existen por una razón. PostgreSQL, MySQL y otros distinguen entre “terminar trabajo”, “hacer checkpoint” y “tirar todo”, porque el coste de recuperación tras un crash es real.
  • Los watchdogs se hicieron populares tras suficientes cuelgues silenciosos. Un servicio que está “running” pero no avanza es peor que uno que falla rápido; los supervisores añadieron watchdogs para forzar una decisión.

Tareas prácticas: comandos, salidas y decisiones

A continuación hay tareas prácticas que puedes ejecutar en un host Linux típico usando systemd. Cada tarea incluye: el comando, una salida de ejemplo, qué significa y la decisión que tomas a partir de ello.

Task 1: Confirmar el estado de la unidad y por qué systemd piensa que está atascada

cr0x@server:~$ systemctl status nginx.service --no-pager
● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
     Active: deactivating (stop-sigterm) since Tue 2026-02-05 02:11:27 UTC; 1min 32s ago
       Docs: man:nginx(8)
    Process: 18421 ExecStop=/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid (code=exited, status=0/SUCCESS)
   Main PID: 17102 (nginx)
      Tasks: 5 (limit: 18958)
     Memory: 62.3M
        CPU: 3.114s
     CGroup: /system.slice/nginx.service
             ├─17102 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
             ├─17105 nginx: worker process
             └─17106 nginx: worker process

Feb 05 02:11:27 server systemd[1]: Stopping A high performance web server and a reverse proxy server...
Feb 05 02:11:57 server systemd[1]: nginx.service: State 'stop-sigterm' timed out. Killing.
Feb 05 02:11:57 server systemd[1]: nginx.service: Killing process 17102 (nginx) with signal SIGKILL.

Qué significa: systemd intentó una detención graciosa, alcanzó un timeout y escaló. Sigue desactivándose, lo que sugiere que o el proceso no murió, o los hijos escaparon, o el kernel no lo reaps.

Decisión: Pasa de la “vista de unidad” a la “vista de procesos”. Identifica qué PID(s) siguen existiendo y en qué estado están. No sigas emitiendo reinicios; recopila evidencia.

Task 2: Mostrar el comportamiento exacto de stop de la unidad (timeouts, kill mode)

cr0x@server:~$ systemctl show nginx.service -p TimeoutStopUSec -p KillMode -p KillSignal -p SendSIGKILL -p ExecStop -p ExecStopPost
TimeoutStopUSec=1min 30s
KillMode=control-group
KillSignal=SIGTERM
SendSIGKILL=yes
ExecStop={ path=/sbin/start-stop-daemon ; argv[]=/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=18421 ; code=exited ; status=0 }
ExecStopPost=

Qué significa: systemd matará todo el cgroup si es necesario. El timeout es de 90 segundos. El comando de stop usa un archivo PID; eso es un vector potencial de mentira.

Decisión: Si el archivo PID está obsoleto o es incorrecto, la detención puede estancarse. Verifica el archivo PID y si el cgroup todavía tiene procesos.

Task 3: Inspeccionar la membresía del cgroup (lo que systemd cree que posee)

cr0x@server:~$ systemd-cgls /system.slice/nginx.service
Control group /system.slice/nginx.service:
├─17102 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
├─17105 nginx: worker process
└─17106 nginx: worker process

Qué significa: Estos son los procesos que systemd señalará al detener. Si ves procesos inesperados, tu servicio puede estar forkeando ayudantes que nunca salen.

Decisión: Si el cgroup está vacío pero systemd dice “stopping”, podrías estar esperando hooks de ExecStop o una llamada de montaje/DNS atascada dentro de ellos.

Task 4: Comprobar si el proceso está en sleep no interrumpible (D state)

cr0x@server:~$ ps -o pid,ppid,stat,wchan:30,cmd -p 17102
  PID  PPID STAT WCHAN                          CMD
17102     1 D    nfs_wait_bit_killable          nginx: master process /usr/sbin/nginx -g daemon on; master_process on;

Qué significa: El proceso está en D state, esperando una función del kernel relacionada con NFS. En D state, las señales no se manejarán hasta que la llamada del kernel retorne. SIGKILL no te salvará.

Decisión: Deja de intentar matarlo. Encuentra el montaje NFS, el problema de red/almacenamiento o planifica un failover. Esto no es un “problema de nginx”.

Task 5: Identificar archivos/sockets abiertos que puedan estar colgando la parada

cr0x@server:~$ sudo lsof -p 17102 | tail -n 8
nginx   17102 root   10u  IPv4  541233      0t0  TCP *:http (LISTEN)
nginx   17102 root   11u  IPv4  541234      0t0  TCP *:https (LISTEN)
nginx   17102 root   12r  REG   0,37  10485760 917514 /var/log/nginx/access.log
nginx   17102 root   13r  REG   0,37   2097152 917515 /var/log/nginx/error.log
nginx   17102 root   14r  REG   0,41    131072  26233 /mnt/shared/certs/bundle.pem
nginx   17102 root   15r  REG   0,41    131072  26234 /mnt/shared/certs/key.pem

Qué significa: El servicio tiene archivos abiertos en /mnt/shared que parecen pertenecer a un sistema de archivos separado (dispositivo 0,41). Si eso es NFS y está unhealthy, el apagado puede bloquearse en operaciones de archivos.

Decisión: Verifica el tipo de montaje y su estado. Si es un montaje remoto y está atascado, arregla eso primero o desvía el tráfico y reubica.

Task 6: Confirmar el tipo de montaje y si es un sistema de archivos de red

cr0x@server:~$ findmnt -T /mnt/shared -o TARGET,SOURCE,FSTYPE,OPTIONS
TARGET      SOURCE               FSTYPE OPTIONS
/mnt/shared nfs01:/exports/shared nfs4   rw,relatime,vers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,sec=sys

Qué significa: Es NFSv4.1 con semántica de montaje hard. Los montajes hard pueden colgar procesos durante problemas de servidor/red. A veces es la elección correcta; nunca es una elección gratuita.

Decisión: Si NFS es inestable, decide entre restaurar NFS, hacer failover del servicio a otro nodo o (como último recurso) reiniciar el cliente para limpiar el I/O atascado—sabiendo que puede volver a ocurrir si NFS sigue caído.

Task 7: Comprobar si el host está bajo presión de memoria o swapping (un “stop” lento puede ser paginación)

cr0x@server:~$ free -h
               total        used        free      shared  buff/cache   available
Mem:            31Gi        29Gi       512Mi       1.2Gi       1.6Gi       745Mi
Swap:            8Gi       6.9Gi       1.1Gi

Qué significa: Poca memoria disponible y swap pesado. Incluso un apagado educado puede arrastrarse si el proceso está siendo paginado dentro/fuera.

Decisión: Antes de reiniciar, considera aliviar la presión (detener cargas no críticas, escalar horizontalmente, añadir memoria o reducir carga). Reiniciar un servicio que trapea memoria puede empeorar las cosas si recarga caches y provoca más swapping.

Task 8: Comprobar la saturación de I/O de disco (el “hang” puede ser latencia de almacenamiento)

cr0x@server:~$ iostat -xz 1 3
Linux 6.1.0 (server) 	02/05/2026 	_x86_64_	(8 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          12.21    0.00    4.05   38.44    0.00   45.30

Device            r/s     rkB/s   rrqm/s  %rrqm r_await rareq-sz     w/s     wkB/s   wrqm/s  %wrqm w_await wareq-sz  aqu-sz  %util
nvme0n1         120.0   4800.0     0.0    0.0  120.22    40.0    95.0   9200.0     2.0    2.1  210.45    96.8   35.20  99.8

Qué significa: Utilización del dispositivo casi 100%, tiempos de espera enormes y alto iowait. Tu “stop atascado” puede ser víctima de un disco saturado.

Decisión: No reinicies servicios en una cola de I/O en llamas. Identifica los consumidores principales de I/O, alivia la presión o haz failover. Reiniciar suele aumentar I/O (reproducción de logs, calentamiento de caché, reindexado, etc.).

Task 9: Encontrar qué procesos están golpeando el disco

cr0x@server:~$ sudo iotop -oPa -n 1
Total DISK READ: 15.20 M/s | Total DISK WRITE: 42.10 M/s
  PID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN      IO    COMMAND
22340 be/4 postgres   1.20 M/s   18.30 M/s  12.00 %  85.00 % postgres: checkpointer
22410 be/4 postgres   0.00 B/s   10.20 M/s   0.00 %  60.00 % postgres: wal writer
30112 be/4 root       0.00 B/s    9.80 M/s   0.00 %  55.00 % rsync -a /var/log/ /mnt/backup/

Qué significa: Tareas en segundo plano (escritores de base de datos, rsync de backups) dominan la escritura. Tu servicio atascado puede ser daño colateral.

Decisión: Pausa o limita I/O no esencial (trabajos de backup, batch). Si la base de datos está haciendo recuperación necesaria, déjala terminar antes de tocar servicios dependientes.

Task 10: Revisar el journal por hooks de stop/start que estén bloqueando

cr0x@server:~$ journalctl -u nginx.service -n 50 --no-pager
Feb 05 02:10:55 server systemd[1]: Stopping A high performance web server and a reverse proxy server...
Feb 05 02:10:55 server start-stop-daemon[18421]: stopping nginx (pid 17102)...
Feb 05 02:11:25 server start-stop-daemon[18421]: waiting for nginx to die...
Feb 05 02:11:55 server start-stop-daemon[18421]: waiting for nginx to die...
Feb 05 02:11:57 server systemd[1]: nginx.service: State 'stop-sigterm' timed out. Killing.

Qué significa: El helper de stop está esperando. Eso no es evidencia de un bug de la aplicación; es evidencia de que el proceso no puede salir o no puede ser reaped.

Decisión: Correlaciona con el estado del proceso (ps) y las esperas del kernel (wchan). Si está en D state, arregla la I/O subyacente.

Task 11: Verificar la salud de DNS (sí, DNS también puede colgar un shutdown)

cr0x@server:~$ resolvectl status
Global
         Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
  resolv.conf mode: stub

Link 2 (ens192)
    Current Scopes: DNS
         Protocols: +DefaultRoute
Current DNS Server: 10.10.0.53
       DNS Servers: 10.10.0.53 10.10.0.54
        DNS Domain: corp.internal

Qué significa: Tienes systemd-resolved en modo stub. Si el resolvedor está caído o lento, servicios que hacen reverse lookups durante el shutdown (logging, auth, métricas) pueden estancarse.

Decisión: Si ves delays de resolución en los logs, prueba consultas y arregla la alcanzabilidad del resolvedor antes de reiniciar servicios dependientes.

Task 12: Ver dependencias fallidas y problemas de orden

cr0x@server:~$ systemctl list-dependencies --reverse postgresql.service
postgresql.service
● ├─app-api.service
● ├─worker.service
● └─reporting.service

Qué significa: Estos servicios dependen de PostgreSQL. Reiniciar PostgreSQL hará efecto en cascada—a menos que primero detengas/draines a los dependientes intencionalmente.

Decisión: Decide si debes reiniciar la dependencia (Postgres) o el dependiente (app-api). A menudo el síntoma atascado vive en el dependiente; la causa está en la dependencia. Maneja el orden intencionalmente.

Task 13: Comprobar un archivo PID obsoleto que bloquea start/stop

cr0x@server:~$ sudo cat /run/nginx.pid
99999
cr0x@server:~$ ps -p 99999 -o pid,cmd
  PID CMD

Qué significa: El archivo PID apunta a un proceso inexistente. Algunos scripts de stop esperarán o fallarán; algunos scripts de start se negarán a arrancar pensando que el demonio sigue vivo.

Decisión: Si confirmaste que nginx no está corriendo, elimina el archivo PID obsoleto y arranca limpio. Si está corriendo pero el PID cambió, arregla la unidad para evitar fragilidad por archivos PID (prefiere el seguimiento por cgroup de systemd / Type=notify cuando aplique).

Task 14: Intentar un reload seguro en lugar de restart (cuando sea posible)

cr0x@server:~$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
cr0x@server:~$ sudo systemctl reload nginx.service

Qué significa: Un reload mantiene el proceso master y aplica la configuración, a menudo evitando caídas de conexión. No siempre está soportado, pero cuando lo está, es la palanca más suave.

Decisión: Si el problema es de configuración, prefiere reload. Si el proceso está wedged, reload no ayudará; no finjas que sí.

Task 15: Resetear la vista “failed” de systemd antes de reintentar (para ver errores nuevos)

cr0x@server:~$ systemctl is-failed worker.service
failed
cr0x@server:~$ sudo systemctl reset-failed worker.service

Qué significa: Resetear failed limpia el latch para que la monitorización y los humanos vean el siguiente fallo claramente, no el fantasma de ayer.

Decisión: Haz esto antes de tu siguiente intento de start durante un incidente, de otro modo perseguirás síntomas obsoletos.

Task 16: Cuando systemd está atascado, revisa jobs pendientes y transacciones bloqueadas

cr0x@server:~$ systemctl list-jobs
JOB  UNIT             TYPE  STATE
421  nginx.service     stop  running
422  app-api.service   stop  waiting

Qué significa: app-api está esperando porque la detención de nginx todavía se está ejecutando. Las transacciones de systemd serializan ciertas operaciones; una unidad atascada puede retrasar otras.

Decisión: Arregla/limpia la unidad atascada o aísla operando sobre unidades no bloqueadas por esa transacción. Evita lanzar una docena de jobs nuevos hacia el atasco.

Escalera de escalado: de cortés a firme (sin pánico)

Cuando algo está atascado, los humanos alcanzan botones grandes y rojos. Resiste. Usa una escalera de escalado, porque quieres la acción menos destructiva que restaure el servicio mientras preservas evidencia.

Nivel 0: Decide si un reinicio es la herramienta correcta

Reiniciar es apropiado cuando:

  • El proceso está vivo pero se comporta mal (deadlock, fuga de memoria, pool de threads atascado), y tienes redundancia o un plan de drenaje.
  • Un cambio de configuración lo requiere (y no está disponible el reload).
  • Una dependencia se arregló y necesitas un handshake limpio (p. ej., reconectar a la base de datos, volver a montar almacenamiento).

Reiniciar es mala idea cuando:

  • El proceso está en D state (espera de I/O). Matar no funcionará; los intentos repetidos solo desperdician tiempo y añaden confusión.
  • El host está bajo presión extrema de memoria o I/O. Reiniciar añade carga.
  • Puedes hacer failover a un nodo sano más rápido de lo que puedes “arreglar” este.

Nivel 1: Stop/start gracioso con drenado de tráfico

Si estás tras un load balancer, drena primero. No “reinicies en sitio” y esperes que los clientes sean pacientes.

cr0x@server:~$ sudo systemctl stop app-api.service

Decisión: Si el stop completa rápido y limpio, arráncalo. Si se cuelga, pasa a inspección en lugar de repetir el comando.

Nivel 2: Usa reload o rota workers si está soportado

Para algunos demonios (nginx, haproxy, algunos agentes de logging), reload es suficiente. Para otros, a veces puedes “rotar” workers (p. ej., enviar una señal para generar nuevos workers y retirar los antiguos). Esto mantiene sockets abiertos y reduce el dolor de los clientes.

Nivel 3: Confirma qué bloquea el apagado (bloqueos, I/O, dependencias)

Aquí usas journalctl, lsof, ps wchan y herramientas de recursos. El objetivo: identificar si esperas en almacenamiento, red o lógica de aplicación.

Nivel 4: Terminación dirigida de los procesos correctos

Si el proceso está en estado interrumpible y simplemente ignora SIGTERM, puedes escalar con intención. Hazlo en el cgroup del servicio, no buscando PIDs al azar.

cr0x@server:~$ sudo systemctl kill -s SIGTERM nginx.service
cr0x@server:~$ sudo systemctl kill -s SIGKILL nginx.service

Qué significa: Le pides a systemd que envíe la señal a todos los procesos en el cgroup de la unidad. Esto es más limpio que rastrear PIDs y respeta los límites del servicio.

Decisión: Solo usa SIGKILL cuando hayas decidido que la pérdida de estado es aceptable y el proceso no está en D state. Si está en D state, SIGKILL no aterrizará.

Nivel 5: Si systemd está atascado, limpia cgroups con cuidado

A veces una unidad “se fue” pero el cgroup está desordenado, o un proceso escapó. Puedes inspeccionar y actuar, pero mantén las manos firmes.

cr0x@server:~$ systemctl show nginx.service -p ControlGroup
ControlGroup=/system.slice/nginx.service
cr0x@server:~$ cat /sys/fs/cgroup/system.slice/nginx.service/cgroup.procs
17102
17105
17106

Decisión: Si esos PIDs son realmente propiedad de ese servicio, puedes detener la unidad y matar vía systemd. Si los PIDs están en D state, para aquí y arregla I/O; matar por cgroup no ayudará.

Nivel 6: Último recurso—reiniciar con intención, no con frustración

Si el kernel está wedged en almacenamiento (dispositivo colgado, NFS muerto, ruta multipath rota) y no puedes recuperar desde userspace, el reinicio puede ser la elección operativa correcta. Pero hazlo como ingeniero:

  • Haz failover del tráfico primero.
  • Captura evidencia: dmesg, journal, muestras iostat, estado de montajes.
  • Comunica impacto esperado y pasos de recuperación.

Pequeña broma #2: Un reboot es como apagar y encender otra vez—salvo que el “otra vez” incluye explicarlo a tu change board.

Tres mini-historias corporativas desde el campo

Incidente causado por una suposición errónea: “Reiniciar el servicio no afectará los datos”

Una empresa mediana ejecutaba una API de pagos respaldada por una cola y una base de datos relacional. Los nodos de la API eran “stateless”, o eso decía todo el mundo. Un deploy introdujo un bug sutil: los threads de trabajo se acumulaban esperando una conexión DB que nunca volvía. La latencia subió; la tasa de errores siguió.

El on-call vio threads atascados e hizo el movimiento estándar: reiniciar el servicio API en un nodo. Volvió. Durante unos tres minutos. Luego volvió a quedarse colgado. Así que reiniciaron más nodos. Rápido. El load balancer, obediente, desplazó tráfico a los nodos restantes, que se saturaron de inmediato.

La suposición que los golpeó: “API es stateless”. En realidad, cada nodo también ejecutaba un agente local que almacenaba en disco peticiones en vuelo para reintento durante fallos transitorios. Durante la tormenta de reinicios, la cola de reintentos del agente corrió, reenvió peticiones antiguas y las claves de idempotencia de la DB no se aplicaron de forma consistente entre caminos de código.

No perdieron dinero—por poco. Perdieron un fin de semana en conciliaciones y postmortems. La solución no fue “no reiniciar”. La solución fue modelar el estado del sistema honestamente: dónde vive, cómo se reintenta y qué componentes deben drenarse antes del reinicio. El runbook ahora incluye “deshabilitar agente de reintentos local” y “verificar enforcement de idempotencia” antes de cualquier reinicio masivo.

La lección práctica: no puedes reiniciar de forma segura algo que no has inventariado. “Stateless” no es una vibra; es una decisión arquitectónica que puedes demostrar.

Optimización que salió mal: “Tiempos de espera más cortos harán que los reinicios sean más rápidos”

Otra organización tenía una flota de hosts Linux ejecutando un forwarder de logs y un agente de métricas. El equipo afinó las unidades systemd para reducir el tiempo de apagado durante mantenimiento rolling: TimeoutStopSec=10 en todas partes. Parecía nítido. Parecía eficiente. También fue incorrecto.

Un día, un fallo de almacenamiento remoto causó retrasos intermitentes al escribir buffers de logs. El forwarder necesitaba ~20–30 segundos para vaciar con seguridad. Pero con un timeout de 10 segundos, systemd escaló a SIGKILL regularmente. El resultado no fue downtime inmediato; fue peor: logs de auditoría descartados silenciosamente y lotes medio entregados que rompieron el parsing descendente.

El incidente fue difícil de detectar porque el “reinicio tuvo éxito”. Dashboards verdes. Estado de unidades feliz. Mientras tanto, compliance notó huecos y el equipo consumidor notó patrones “duplicados-ish” en eventos.

El rollback fue restaurar timeouts sensatos y añadir métricas explícitas de flush. También aprendieron a diferenciar servicios: un proxy web puede matarse más rápido que una tubería de datos con buffer. La “optimización” uniforme es cómo cometes errores uniformes a escala.

La lección práctica: los timeouts forman parte de tu historia de integridad de datos. Hazlos específicos por servicio y probados con pruebas de fallo, no por estética.

Práctica aburrida pero correcta que salvó el día: “Drenar, verificar, luego reiniciar un nodo”

Una compañía SaaS ejecutaba un cluster de nodos de aplicación tras un load balancer, más una capa de almacenamiento separada. Un nodo empezó a fallar health checks intermitentemente. El proceso de la app no estaba muerto; estaba “vivo pero atascado”, respondiendo lentamente y fallando deadlines del watchdog.

El on-call siguió el runbook aburrido. Primero drenó el nodo del balancer y confirmó que el tráfico cayó casi a cero. Luego capturó evidencia local: una ventana de cinco minutos de iostat, el systemctl status actual y las últimas 200 líneas del journal de la unidad.

Solo entonces reiniciaron el servicio en ese único nodo. Aún así se colgó al detenerse. En lugar de escalar a ciegas, revisaron estados de procesos y encontraron esperas en D state vinculadas a una ruta iSCSI obsoleta. Desconectaron la sesión de almacenamiento del nodo (planificado), lo marcaron fuera de rotación y dejaron que el cluster siguiera funcionando.

Más tarde, en horario laboral, arreglaron la configuración de multipath y añadieron alertas para flaps de rutas. Sin reboot heroico. Sin reinicios masivos. El impacto al cliente fue mínimo porque trataron “un nodo malo” como un problema de aislamiento, no como un drama de flota.

La lección práctica: drenar y canary de un solo nodo parecen lentos, pero evitan la clásica experiencia de “convertí un problema en doce”.

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

1) Síntoma: systemctl stop cuelga para siempre (o hasta timeout)

Causa raíz: El proceso está en D state esperando I/O de almacenamiento/red (NFS, iSCSI, disco muerto), o un ExecStop script está bloqueado por DNS/montaje.

Solución: Confirma con ps ... wchan y findmnt. Restaura la dependencia (almacenamiento/red/DNS) o haz failover; no ataques con señales en spam.

2) Síntoma: reiniciar “funciona” pero el servicio queda lento inmediatamente

Causa raíz: Presión de recursos subyacente (saturación I/O, swapping de memoria) más caches fríos tras reinicio.

Solución: Revisa iostat, free y los mayores consumidores de I/O. Reduce carga o arregla el cuello de botella primero; reinicia al final.

3) Síntoma: systemd dice “active (running)” pero el servicio está muerto/no responde

Causa raíz: Tipo de unidad incorrecto o señalización de readiness errónea (p. ej., Type=forking con un archivo PID obsoleto), o falta de gating por health check.

Solución: Valida el seguimiento de PID; prefiere Type=notify cuando sea posible. Añade watchdogs/checks de salud o un chequeo de socket y alerta sobre ellos.

4) Síntoma: systemctl start falla con “Address already in use”

Causa raíz: Proceso viejo aún escuchando (cgroup escapado, huérfano), o confusión con unidades socket-activadas.

Solución: Encuentra listeners con ss -ltnp, confirma membresía de cgroup, mata el proceso correcto vía systemd o detén la unit socket si aplica.

5) Síntoma: el servicio sigue reiniciando en bucle

Causa raíz: Restart=always más una falla persistente (config mala, dependencia faltante, permisos). El bucle puede crear carga y tormentas de logs.

Solución: Detén la unidad, inspecciona logs, arregla el error real y luego arranca. Considera añadir backoff o StartLimitIntervalSec / StartLimitBurst.

6) Síntoma: stop tiene éxito pero start se queda en “activating”

Causa raíz: El chequeo de readiness nunca completa (esperando notify), o espera en una dependencia que está “up” pero inutilizable (DNS responde, pero lento; montaje existe, pero obsoleto).

Solución: Revisa systemctl status + propiedades de la unidad (Type=, notify), valida salud de dependencias con comandos directos (consulta DNS, prueba de lectura/escritura en montaje).

7) Síntoma: matar el PID no hace nada

Causa raíz: El PID es incorrecto (archivo PID obsoleto), o el proceso está atascado en sleep no interrumpible.

Solución: Verifica el PID con systemctl status y systemd-cgls. Si está en D state, trátalo como una falla de I/O subyacente.

8) Síntoma: “Unit is masked” o “Refusing to operate on alias name” durante un incidente

Causa raíz: Alguien enmascaró un servicio para evitar que subiera, o estás apuntando a un alias/symlink en lugar de la unidad real.

Solución: Confirma con systemctl status y systemctl cat. Desenmascara intencionalmente, documenta por qué se enmascaró y arranca la unidad canónica.

Listas de verificación / plan paso a paso

Checklist A: Reinicio seguro de un servicio típico sin estado (web/API)

  1. Confirma el alcance del impacto: ¿Este nodo es redundante? Si no, pausa y planea una ventana de mantenimiento o failover.
  2. Drena tráfico: saca el nodo del load balancer; confirma que las conexiones activas caen.
  3. Captura evidencia: systemctl status, últimas 100 líneas de journalctl -u y una instantánea de ps de los PIDs del servicio.
  4. Prueba acción graciosa: reload si está soportado; de lo contrario stop con systemd.
  5. Si stop cuelga: comprueba D state, montajes, presión de I/O. Arregla la causa antes de forzar.
  6. Arranca y verifica: unidad activa, puerto en escucha, endpoint de salud OK, tasa de errores estable.
  7. Devuelve tráfico gradualmente: vuelve a añadir el nodo; vigila latencia y saturación.

Checklist B: Reinicio seguro de un servicio con estado (base de datos/cola)

  1. Confirma estado de replicación / HA: conoce primario vs réplica, quórum y reglas de failover.
  2. Silencia escrituras si hace falta: pausa consumidores, detén jobs batch o activa modo mantenimiento.
  3. Revisa espacio en disco y salud de I/O primero: espacio bajo o latencia alta convierten un “reinicio” en una “maratón de recuperación”.
  4. Usa comandos nativos de admin cuando existan: p. ej., shutdown rápido de base de datos vs kill duro, para evitar larga recuperación por crash.
  5. Detén vía systemd y monitorea: vigila logs por mensajes de checkpoint/flush.
  6. Si cuelga: no uses SIGKILL reflexivamente. Identifica si está haciendo checkpoint, esperando fsync o atascado en almacenamiento subyacente.
  7. Después de arrancar: verifica señales de consistencia (replicación catch-up, WAL replay completo) y solo entonces re-habilita escritores/consumidores.

Checklist C: Cuando sospechas que almacenamiento/red es la verdadera culpable

  1. Busca procesos en D state: si los hay, probablemente estás por debajo del espacio de usuario.
  2. Revisa montajes y sistemas remotos: NFS, iSCSI, SMB—confirma salud, latencia y logs de error.
  3. Revisa mensajes del kernel: timeouts, flaps de enlace, resets SCSI, errores NVMe.
  4. Aísla el host: drena tráfico y deja de empeorarlo.
  5. Recupera la dependencia o haz failover: restaura rutas, reinicia servicios de montaje o mueve la carga.
  6. Reinicia solo con plan: tras capturar evidencia y con failover confirmado.

Preguntas frecuentes

1) ¿Por qué no simplemente reiniciar? Es más rápido.

A veces es más rápido para ti, no para el sistema. Reiniciar resetea todo, puede provocar largas recuperaciones (fsck, resync RAID, reproducción de base de datos) y destruye evidencia. Reinicia cuando hayas identificado un wedge a nivel kernel o ya hayas hecho failover y quieras una pizarra limpia.

2) ¿Qué significa “D state” y por qué me importa?

D state es sleep no interrumpible: el proceso está esperando en el kernel, típicamente por I/O. Las señales no se manejarán hasta que la llamada del kernel termine. Si un proceso está atascado en D state, matarlo no funcionará; necesitas arreglar la ruta de I/O subyacente o reiniciar el host.

3) ¿Debería usar killall o pkill durante incidentes?

Normalmente no. Son instrumentos contundentes y causan daño colateral. Prefiere systemctl kill para una unidad específica para golpear el cgroup correcto. Si debes usar pkill, acótalo bien y verifica con ps y systemd-cgls.

4) ¿Por qué systemd dice que mató el proceso, pero aún está ahí?

Si el proceso está en D state, puede permanecer porque no puede completar la operación del kernel y salir. Otra posibilidad: estás mirando un PID distinto (archivo PID obsoleto) o un proceso hijo fuera del cgroup de la unidad.

5) ¿Bajar TimeoutStopSec es buena idea para evitar cuelgues?

No como política general. Para pipelines con buffer y servicios stateful, timeouts cortos convierten shutdowns gráciles en pérdida de datos. Establece timeouts por servicio, basados en tiempos de apagado medidos bajo carga y degradación.

6) ¿Cuándo es aceptable SIGKILL?

Cuando has decidido que preservar estado es menos importante que restaurar disponibilidad, y entiendes el coste de recuperación. También es aceptable para servicios realmente sin estado donde el proceso está siendo grosero. No es una solución para cuelgues en D state.

7) ¿Cuál es la diferencia entre restart y try-restart?

restart arranca el servicio incluso si no está corriendo. try-restart reinicia solo si ya está en ejecución. En producción, try-restart es más seguro cuando no quieres arrancar accidentalmente algo que fue detenido intencionalmente.

8) ¿Cómo evito fallas en cascada al reiniciar dependencias?

Conoce el grafo de dependencias. Drena o detén dependientes antes de reiniciar una dependencia central (base de datos, cola, DNS, almacenamiento). Trae de vuelta las dependencias primero, luego los dependientes en pequeños lotes mientras vigilas tasas de error y saturación.

9) systemd muestra “activating” para siempre. ¿Cuál es el culpable habitual?

Señalización de readiness. El servicio puede estar esperando un evento notify, un archivo PID o un hook post-start que llama a la red. Inspecciona el tipo de unidad y ExecStartPost; revisa logs y timeouts.

10) ¿Cómo conservo evidencia si debo intervenir rápidamente?

Captura la evidencia mínima viable antes de aplicar fuerza: estado de la unidad, últimas ~200 líneas del journal, snapshot del estado de procesos y una muestra rápida de I/O. Eso suele ser suficiente para diagnosticar la mayoría de causas raíz de “stop atascado” después.

Próximos pasos que puedes hacer mañana

Los reinicios seguros no son cuestión de cautela; son cuestión de precisión. Cuando un servicio está atascado, tu primer trabajo es clasificar: ¿es un bloqueo de la aplicación, de la dependencia o del kernel/almacenamiento? Solo una de esas se resuelve con “reiniciar más fuerte”.

Haz lo siguiente:

  1. Escribe una escalera de escalado para tus 5 servicios principales: reload vs restart, pasos de drenado y cuándo se permite SIGKILL.
  2. Establece una línea base de tiempo de apagado bajo carga normal y bajo degradación leve, y ajusta TimeoutStopSec en consecuencia.
  3. Añade una alerta de D-state: cuenta procesos en D state y pagea cuando suba. Es una alerta temprana de que tu almacenamiento/red te está mintiendo.
  4. Inventaría el estado: identifica qué servicios “sin estado” realmente persisten, almacenan en buffer o reintentan localmente.
  5. Practica en un nodo en un entorno tipo staging: fuerza un montaje atascado, observa cómo se comporta stop y actualiza el runbook con lo aprendido.

El objetivo no es nunca reiniciar. El objetivo es reiniciar solo cuando lo decides a conciencia—y saber exactamente qué estás comprando con ese radio de daño.

← Anterior
Pantalla negra tras iniciar sesión: solución sin reinstalar Windows
Siguiente →
iommu=pt: El modo oculto de rendimiento para virtualización en Linux (cuándo usarlo)

Deja un comentario