CreaRack-SL

Script bash servicios-check — sonda de servicios internos

Descripción

Sonda de vigilancia de servicios internos que UptimeRobot no puede alcanzar (están tras NetBird, solo accesibles desde OPS). Corre en cron cada 5 minutos.

Servicios vigilados (targets):

  1. prod-web (prod): Web interna :8000, con cabecera Host: crearack.com — sano=301/302 (redirección; hasta el 28-08-2026 se pedía por IP desnuda y Django devolvía 400 — contaba como “vivo” pero dejaba un traceback DisallowedHost en el log de PROD en cada tick, ver footgun abajo)
  2. prod-vm (prod): VictoriaMetrics :8428/backend-health — almacén de métricas (hasta el 25-09-2026 era :8428/health; ver footgun B-45 abajo)
  3. prod-alertmanager (prod): Alertmanager de PROD :9093/backend-health — sistema de avisos (hasta el 25-09-2026 la sonda medía por error el Alertmanager de STAGE; ver footgun B-45 abajo)
  4. stage-alertmanager (stage, desde 25-09-2026): Alertmanager de STAGE :9093/backend-health — sistema de avisos del entorno de pruebas
  5. stage-web (stage): Web de STAGE :8000 — entorno de pruebas
  6. stage-mirror (stage): Espejo DR :8090 — copia de emergencia (sano=401)
  7. dokploy-stage (stage): Panel Dokploy STAGE :3000 — gestión despliegues
  8. dokploy-prod (prod): Panel Dokploy PROD :3000 — gestión despliegues
  9. forgejo (ops): Forgejo local :3000 — servidor de código
  10. dca-web (dca): Web de DCA (100.96.6.166, no nombre NetBird) — único ojo sobre ese servidor — sano=301
  11. runner-ci-1 (ops, desde 01-09-2026): Runner 1 del CI (actions.runner.CreaRackSL.crearack-ops-1.service) — sondeo unit://, no HTTP
  12. runner-ci-2 (ops, desde 01-09-2026): Runner 2 del CI (actions.runner.CreaRackSL.crearack-ops-2.service) — sondeo unit://, no HTTP

Lista de targets según el diff de la mega-auditoría B-45 (commit f90cb92, 25-09-2026); no re-verificada aquí la numeración de targets anteriores a ese cambio (p. ej. la estación 24/7 pc-ia-claude, documentada en feature--infra--servicios-check-sonda-internos).

Lógica de chequeo

Timeout / caído: se cuenta como “down” un timeout OR código ≥500.

Definición de caída: 2 ticks consecutivos (10 minutos, 5 min por tick) → se marca como down y avisa a infra@ con email HTML + lista con luces 🟢/🔴.

Detalle técnico: curl sin || echo 000 (bug cazado en simulacro 11-08: duplicaba el código HTTP a “000000”, dejando la sonda ciega). El curl ya imprime %{http_code} de por sí.

Sondeo unit:// (servicio systemd local, 01-09-2026)

Para targets que no son HTTP ni TCP, sino un servicio systemd de la propia máquina OPS: url con prefijo unit://<nombre-unidad>. El chequeo es systemctl is-active <unidad> — active = vivo (código interno unit-ok); cualquier otro estado (failed, inactive…) = caído (código 000, entra por el mismo camino de conteo que un fallo HTTP/TCP). Hoy solo lo usan runner-ci-1 y runner-ci-2.

Operación

Estructura del array TARGETS

Cada entrada: "<name>|<server>|<url>|<label>|<host>"

  • name: ID único (ej: stage-web)
  • server: categoría (prod, stage, ops, dca) — usado para agrupar por origen en el dashboard
  • url: endpoint a chequear (HTTP GET, leyendo solo status)
  • label: nombre humano con contexto
  • host (opcional, desde 28-08-2026): si está presente, se manda como cabecera Host: <host> en el curl (-H "Host: $host"). Sirve para servicios que rechazan la IP desnuda como Host (Django ALLOWED_HOSTS) — hoy solo lo usa prod-web (Host: crearack.com).

Ciclo por pasada

  1. Itera todos los targets.
  2. Ejecuta curl con timeout y captura código HTTP (añadiendo -H "Host: ..." si el target trae 5º campo).
  3. Actualiza estado acumulativo en array asociativo ST[] (name → status).
  4. Si alguno cae por segunda vez seguida → lo añade a array down[].
  5. Si hay caídas: construye email HTML con lista de luces y envía a infra@.
  6. Desde 11-08 tarde: publica la foto completa vía MCP report_service_status (todos los targets, independientemente de estado).

Publicación al workspace (s263, 11-08 tarde)

Nueva función publish_estado():

  • Itera todos los targets.
  • Construye JSON: array con {name, server, label, status, http_code}.
  • Llama a MCP con Bearer token (.token), CF Access headers (.cf-access-id, .cf-access-secret).
  • Timeout: 60s.
  • Si falla: se loguea en $LOG como WARN, pero la sonda sigue (best-effort, no critical path).
  • Si éxito: log OK con recuento de servicios.

Integración: [ "$MODE" != "--dry-run" ] && publish_estado → se ejecuta antes de decidir alertas.

Vigilancia

  • Registro: logs en /var/log/servicios-check.log (con timestamp UTC).
  • Heartbeat: monitoreado por cron-heartbeat con ventana de 1 hora. Si no se ejecuta en 1 h → alerta de infra.
  • Email: solo en transiciones (down→up o up→down), no en cada pasada.

Historias técnicas y footguns

  • curl 000000 bug (11-08): || echo 000 tras curl producía dos códigos. Descubierto en simulacro; arreglado.
  • DisallowedHost en prod-web (28-08-2026, commit 2cff0939): la sonda pedía prod-web por su IP NetBird desnuda (http://100.96.156.31:8000/), sin cabecera Host. Django respondía 400 — contaba como “vivo” para la sonda — pero dejaba un traceback DisallowedHost en el log de PROD cada 5 minutos (cada tick del cron), ensuciando el log real de la app. Fix: 5º campo opcional host en el target (|crearack.com), la sonda manda Host: crearack.com y Django ahora responde 301/302 sin traceback.
  • Sondas ciegas al portero vmauth (25-09-2026, mega-auditoría B-45, commit f90cb92): desde ese día los puertos 8428 y 9093 de NetBird pasan por vmauth ([[decision—20260925—vmauth-auth-netbird-b45]]), que contesta / y /health con 200/401 aunque el servicio de detrás esté caído — la sonda se habría quedado ciega justo cuando más falta hace. Fix: las dos sondas miran /backend-health, que sí llega al servicio real (502 si cae). De paso se corrigió que prod-alertmanager medía por error el Alertmanager de STAGE (100.96.254.204) en vez del de PROD, y se añadió stage-alertmanager como sonda propia de ese servicio.
  • Staleness: el endpoint /api/services marca datos >15 min como rancio. Los LEDs pasan a “sin datos” solos.
  • Red-alert 24h (anterior a s263): sonda anterior era ciega; se mejoró la robustez.
  • Punto ciego de los runners del CI (28-08 → 01-09-2026, commit 3aa6436b): el runner crearack-ops-1 murió por OOM el 28-08 y estuvo cuatro días caído sin que nadie se enterara — el CI seguía en verde porque crearack-ops-2 absorbía todo el trabajo, solo que a mitad de velocidad. Ni UptimeRobot (no ve dentro de la máquina) ni esta sonda (hasta entonces solo HTTP/TCP) lo detectaban. Fix: nuevo tipo de sondeo unit:// (ver arriba) más los dos runners como targets. Simulacro de caída verificado en el servidor real el 01-09: parar el runner → DOWN: runner-ci-1 → correo de aviso (rojo) → restaurar → correo de recuperación (verde). Script instalado en /opt/servicios-check (copia del anterior conservada en .bak-20260901).

Véase también

  • [[entity—functions—tool—report-service-status]]
  • [[entity—migrations—table—service-status]]
  • [[entity—functions—endpoint—api-services]]
  • [[feature—monitoring—led-servicios-onoff]]
  • [[concept—infra—health-checks]]
  • [[decision—20260925—vmauth-auth-netbird-b45]]