CreaRack-SL

Entidad: Alertmanager — enrutador y notificador de alertas por email

Resumen

Alertmanager es el componente que recibe alertas de vmalert, las agrupa (para evitar spam), aplica delays anti-falso-positivo, y envía notificaciones al equipo infra vía email (Resend SMTP).

El destinatario es logcrearack@esfericlabs.com (buzón canónico para alertas de runtime SaaS) y el remitente es CreaRack Pro <noreply@crearack.com> (dominio verificado en Resend). Desde el 28-07-2026 el asunto etiqueta el entorno de origen ([CreaRack PROD] / [CreaRack STAGE] / [CreaRack LOCAL]) — antes iba hardcodeado a PROD.

Despliegue

Imagen

prom/alertmanager:v0.27.0

Configuración en compose

Local (compose.observability.yml)

alertmanager:
  image: prom/alertmanager:v0.27.0
  container_name: crearack_alertmanager
  volumes:
    - ./observability/alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
    - ./observability/alertmanager/smtp_password:/etc/alertmanager/smtp_password:ro
    - alertmanager_data:/alertmanager
  command:
    - "--config.file=/etc/alertmanager/alertmanager.yml"
    - "--storage.path=/alertmanager"
  ports:
    - "9093:9093"
  restart: unless-stopped
  networks:
    - crearack_internal

PROD/STAGE (compose.prod.yml — ambos Dokploy usan este archivo)

alertmanager:
  image: prom/alertmanager:v0.27.0
  restart: unless-stopped
  volumes:
    - ./observability/alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
    - ../files/smtp_password:/etc/alertmanager/smtp_password:ro
    - alertmanager_data_prod:/alertmanager
  command:
    - "--config.file=/etc/alertmanager/alertmanager.yml"
    - "--storage.path=/alertmanager"
    - "--web.external-url=${ALERTMANAGER_EXTERNAL_URL:-http://crearack-prod.netbird.cloud:9093}"
  networks:
    - crearack_internal

Nota importante: En PROD, la ruta de smtp_password es ../files/ (sibling del repo clonado en code/), no en el repo mismo. Esto es porque Dokploy no clona archivos gitignored — el secret se proporciona separadamente en cada Dokploy (STAGE y PROD).

Rutas y volúmenes

  • Configuración: ./observability/alertmanager/alertmanager.yml → /etc/alertmanager/alertmanager.yml.
  • Secret (API key Resend): ./observability/alertmanager/smtp_password (local) o ../files/smtp_password (PROD/STAGE).
  • Storage: volumen alertmanager_data o alertmanager_data_prod (persistencia de estado).
  • API web: puerto 9093 — en los servidores se expone SOLO a la VPN NetBird vía proxy socat (scripts/netbird-proxies.sh, relanzar tras cada recreate). El botón “View in Alertmanager” del email usa ALERTMANAGER_EXTERNAL_URL (Dokploy STAGE la define a http://crearack-staging.netbird.cloud:9093; PROD usa el default).

Configuración

alertmanager.yml

Archivo en observability/alertmanager/alertmanager.yml. Estructura:

global:
  resolve_timeout: 5m
  smtp_smarthost: "smtp.resend.com:587"
  smtp_from: "CreaRack Pro <noreply@crearack.com>"
  smtp_hello: "crearack.com"
  smtp_auth_username: "resend"
  smtp_auth_password_file: "/etc/alertmanager/smtp_password"
  smtp_require_tls: true

route:
  receiver: "logcrearack-email"
  group_by: ["alertname"]
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h

receivers:
  - name: "logcrearack-email"
    email_configs:
      - to: "logcrearack@esfericlabs.com"
        send_resolved: true
        headers:
          Subject: '[CreaRack {{ or .CommonLabels.env "prod" | toUpper }}] {{ .Status | toUpper }} · {{ .CommonLabels.alertname }}'

Etiqueta de entorno env (28-07-2026 · PR #348)

El asunto lee la label env que vmalert estampa en cada alerta con --external.label=env=${ALERT_ENV:-prod} (compose.prod.yml):

  • PROD: sin ALERT_ENV en su Dokploy → default prod.
  • STAGE: ALERT_ENV=stage en el env del Dokploy de STAGE.
  • Local: --external.label=env=local fijo en compose.observability.yml.
  • Fallback: or .CommonLabels.env "prod" cubre alertas de un vmalert anterior a la label.

⚠️ Footgun (s95): un cambio en alertmanager.yml NO se aplica con el deploy — el bind del archivo queda apuntando al inode viejo tras el git pull. Hay que docker compose -p <project> -f compose.prod.yml up -d --force-recreate --no-deps alertmanager (y relanzar netbird-proxies.sh, la IP cambia).

Parámetros clave

ParámetroValorPropósito
resolve_timeout5mSi una alerta no se confirma en 5m, se marca resuelta.
smtp_smarthostsmtp.resend.com:587Servidor Resend con STARTTLS.
smtp_auth_usernameresendUsuario PLAIN para Resend SMTP.
smtp_auth_password_file/etc/alertmanager/smtp_passwordRuta al secret (API key).
smtp_require_tlstrueObliga TLS.
group_by["alertname"]Agrupa por nombre de alerta (evita duplicados si múltiples targets activan la misma).
group_wait30sEspera 30s antes de enviar email (permite agregar más alertas).
group_interval5mRecomputa grupos cada 5m.
repeat_interval4hSi una alerta sigue activa, reenvía cada 4h (evita spam).
send_resolvedtrueEnvía notificación también cuando la alerta se resuelve.

Secret: API key de Resend

Cada entorno usa su PROPIA clave send-only (revocación granular, directiva s69):

  • PROD: clave original del Hito A (s94), verificada viva 28-07-2026.
  • STAGE: clave dedicada alertmanager-stage creada el 28-07-2026 — la clave que STAGE tenía montada estaba revocada y el 535 Authentication credentials invalid solo afloró en el primer simulacro de la task #133 (STAGE no había disparado ninguna alerta en 2 meses). Lección: una clave montada ≠ una clave viva; el simulacro E2E es la única verificación real del canal.

Dónde obtener

  1. Login en Resend (https://resend.com).
  2. Crear una API key con permiso Sending access only, una por entorno.
  3. Copiar la clave (formato: re_...).

Almacenamiento

  • Local: copiar a observability/alertmanager/smtp_password (es gitignored).

    echo "re_..." > observability/alertmanager/smtp_password
    chmod 600 observability/alertmanager/smtp_password
  • PROD/STAGE: escribir files/smtp_password en el host de Dokploy (NO en el repo), chmod 644 (el contenedor corre como nobody; 600 de root da permission denied — footgun s95). Tras cambiar la clave: docker restart del alertmanager (el password_file se relee, pero el restart garantiza estado limpio).

Verificación

  • El dominio crearack.com debe estar verificado en Resend (se hizo en s94).
  • Validar una clave sin enviar nada: curl -s -H "Authorization: Bearer $KEY" https://api.resend.com/emails → "restricted to only send emails" = clave VIVA send-only · "API key is invalid" = clave muerta. (Así se diagnosticó la de STAGE el 28-07.)

Comportamiento

Ciclo de notificación

  1. vmalert envía una alerta: {alertname: "CreaRackDown", severity: "critical", env: "stage", ...}.
  2. Alertmanager recibe en POST /api/v1/alerts.
  3. Agrupa: busca alertas activas del mismo nombre (alertname).
  4. Espera 30s (group_wait): agrupa más si llegan.
  5. Envía email:
    • Asunto: [CreaRack STAGE] FIRING · CreaRackDown (el entorno sale de la label env).
    • Body: plantilla de Alertmanager con detalles.
    • De: CreaRack Pro <noreply@crearack.com>.
    • A: logcrearack@esfericlabs.com.
  6. Si sigue activa 4h después, reenvía.
  7. Cuando se resuelve (firing=false), envía otra notificación: [CreaRack STAGE] RESOLVED · CreaRackDown.

Métricas E2E medidas (simulacros 28-07-2026, STAGE)

Web parada → alerta activa en Alertmanager: ~2m15s (scrape + for: 1m + eval 30s). Email FIRING: +30-60s más. El disparo real de PROD del 29-06-2026 cuadró: firing 11:51 → email entregado 11:57.

UI web

En http://crearack-prod.netbird.cloud:9093 (PROD) · http://crearack-staging.netbird.cloud:9093 (STAGE) — solo VPN NetBird:

  • Alertas activas.
  • Grupos agregados.
  • Silenciadores.
  • Historial.

Debugging

# Ver logs (los envíos EXITOSOS no se loguean a nivel info; los fallos sí, como warn/error con retry)
docker logs <alertmanager> -f

# Chequear configuración
docker exec <alertmanager> cat /etc/alertmanager/alertmanager.yml

# Alertas activas (amtool viene en la imagen)
docker exec <alertmanager> amtool alert query --alertmanager.url=http://localhost:9093

Errores comunes

535 5.7.8 Authentication credentials invalid

  • Causa: API key de Resend inválida o revocada (caso STAGE 28-07: montada pero muerta).
  • Solución: validar la clave contra api.resend.com/emails (ver §Verificación); si está muerta, crear una nueva send-only y reescribir files/smtp_password + restart.

starttls: certificate verify failed

  • Causa: certificado SSL de smtp.resend.com no es confiable (raro).
  • Solución: actualizar CA bundles del contenedor.

Email no llega

  • Causa: dominio crearack.com no verificado en Resend, o clave muerta fallando en silencio (solo se ve en los logs como warn de retry).
  • Solución: verificar dominio en Resend dashboard + grep de error|fail|warn en los logs del alertmanager.

smtp_password: No such file or directory / Is a directory

  • Causa: fichero no existe (docker crea un DIRECTORIO vacío al montar el bind) o ruta errónea.
  • Solución: rm -rf el directorio fantasma y crear el archivo con la clave (644).

Integraciones futuras

  • Webhook: añadir receiver type webhook_configs para enviar a Slack/Discord además de email.
  • Templating avanzado: customizar asunto/body por tipo de alerta.

Véase también

  • [[feature—observability—alerting-prod-s95]]
  • [[decision—20260529—plan-hardening-post-master-hito-a]]
  • [[entity—observability—service—vmalert]]
  • [[entity—observability—service—victoria-metrics]]
  • [[runbook—observability—alerting-setup]]