Skip to main content

Core2 Watchdog (auto-recovery de contenedores)

Detecta y recupera contenedores Core2 caídos. Diseñado para producción (sd1).

Tambien escucha reinicios de redis-central. Si Chatwoot sigue ejecutando procesos iniciados antes que Redis, reinicia Rails y Sidekiq para renovar las conexiones Redis y la suscripcion ActionCable Pub/Sub.

Por qué existe

Incidente mosquitto (jul-ago 2026): tras docker system prune -af + reinicio del daemon, mosquitto no volvió a arrancar. El watchdog instalado en sd1 tenía un bug (COMPOSE="docker compose" + "$COMPOSE" → rc=127) y nunca recuperó nada. Este watchdog corregido detecta por nombre de contenedor (no por compose ps por proyecto) y recupera con run.sh up.

Cómo detecta

  • Para cada proyecto con run.sh bajo el repo root (maxdepth 5), invoca bash <run.sh> containers — subcomando añadido a project_runtime.sh que resuelve los nombres esperados usando la MISMA invocación compose del runtime (env files, overrides, modo dev). Esto evita los falsos positivos cuando el PROJECT_NAME del run.sh no coincide con la etiqueta real del contenedor (p.ej. mcp_servers/mercadolibre, platform/portal).
  • Estado por proyecto:
    • UP — todos los contenedores esperados están corriendo.
    • DOWN — al menos uno no está. → se recupera.
    • SKIPENABLED=false o SKIP_BULK_START=true (p.ej. voxbox). Se ignora.
    • EMPTY / UNKNOWN — sin servicios resolubles (proyectos no-compose). Se ignora.

Cómo recupera

CORE2_COMPOSE_NO_BUILD=true bash <run.sh> up (sin rebuild). El runtime aplica pre_up hooks, override de imágenes y env files como un up normal.

Uso (local / CLI)

./infra watchdog list                 # proyectos + estado
./infra watchdog check [proj...] # detecta, no recupera
./infra watchdog run [proj...] # detecta y recupera (self-heal)
./infra test watchdog # test aislado de detección y recuperación

Sin argumentos, run procesa todos los proyectos habilitados. Con argumentos, filtra por key del proyecto o por nombre de directorio.

Instalación en servidor (sd1)

sudo bash deployment/scripts/health/install-watchdog.sh --root /opt/core2infra

Instala:

  • /usr/local/sbin/core2-watchdog.sh (script)
  • /usr/local/sbin/core2-redis-chatwoot-watcher.sh (listener de eventos Docker)
  • /etc/systemd/system/core2-watchdog.service (oneshot)
  • /etc/systemd/system/core2-watchdog.timer (cada 2 min, OnBootSec=90s)
  • /etc/systemd/system/core2-redis-chatwoot-watcher.service (listener continuo)
  • Deshabilita cualquier timer anterior y hace un check inicial sin recuperar.
  • Deja ambos servicios deshabilitados por seguridad. Tras revisar el preflight:
sudo systemctl enable --now core2-watchdog.timer core2-redis-chatwoot-watcher.service

También puede instalarse y habilitarse explícitamente en un paso:

sudo bash deployment/scripts/health/install-watchdog.sh --root /opt/core2infra --enable

Desinstalar:

sudo systemctl disable --now core2-watchdog.timer
sudo systemctl stop core2-watchdog.service
sudo systemctl disable --now core2-redis-chatwoot-watcher.service
sudo rm -f /etc/systemd/system/core2-watchdog.{service,timer}
sudo rm -f /etc/systemd/system/core2-redis-chatwoot-watcher.service
sudo rm -f /usr/local/sbin/core2-{watchdog,redis-chatwoot-watcher}.sh
sudo systemctl daemon-reload

Verificación en producción

# El watchdog detecta y reporta:
sudo CORE2_WATCHDOG_ROOT=/opt/core2infra /usr/local/sbin/core2-watchdog.sh check

# Simular caída y comprobar auto-recovery en la siguiente pasada (≤2 min):
docker stop mosquitto
# ... esperar el timer ...
docker ps | grep mosquitto # debe estar Up

# Caso contenedor BORRADO (el del incidente):
docker rm -f mosquitto
# ... el watchdog lo recrea con `run.sh up` ...

Dependencia Redis -> Chatwoot

core2-redis-chatwoot-watcher.service ejecuta una reconciliacion al arrancar y despues escucha eventos start de Docker con replay desde el inicio de esa reconciliacion. Esto cubre reinicios normales, la carrera al suscribirse y eventos perdidos mientras el listener estaba apagado. Si se reinicia el daemon, el stream termina y systemd vuelve a conectar el listener automaticamente.

Cuando redis-central arranca:

  1. Compara su StartedAt con chatwoot-rails-1 y chatwoot-sidekiq-1.
  2. Si Chatwoot es posterior a Redis, no hace nada.
  3. Si Chatwoot es anterior, espera que Redis este healthy.
  4. Reinicia solo Rails y Sidekiq y espera que Rails acepte TCP en el puerto 3000.

El listener usa flock, por lo que un evento duplicado no puede iniciar dos recuperaciones simultaneas. Comandos manuales:

sudo /usr/local/sbin/core2-redis-chatwoot-watcher.sh check
sudo /usr/local/sbin/core2-redis-chatwoot-watcher.sh reconcile
sudo journalctl -u core2-redis-chatwoot-watcher.service -f

Archivos

  • deployment/scripts/health/core2-watchdog.sh — script standalone
  • deployment/scripts/health/redis-chatwoot-watcher.sh — dependencia Redis/Chatwoot
  • deployment/scripts/health/install-watchdog.sh — instalador systemd
  • deployment/systemd/core2-watchdog.{service,timer} — unidades
  • deployment/systemd/core2-redis-chatwoot-watcher.service — listener de eventos
  • deployment/scripts/infra/watchdog.sh — comando ./infra watchdog
  • deployment/scripts/utils/project_runtime.sh — subcomando containers

Opt-outs (proyectos que no deben arrancarse solos)

Editar project.conf del proyecto:

ENABLED=false          # ignora por completo (p.ej. airflow, sites obsoletos)
SKIP_BULK_START=true # no arrancar automáticamente (p.ej. voxbox)

Revisar antes de activar en sd1: processing/airflow y sites/desarrolloselectronicos no tienen opt-out y actualmente están DOWN; el watchdog los arrancaría salvo que se marquen.