DEV Community

Alex Carter
Alex Carter

Posted on

Kubernetes: alertas de email con evidencia

Una alerta de Kubernetes puede llegar a la bandeja y seguir siendo inutil. El equipo la ve, pero no sabe si corresponde al incidente actual, si ya fue atendida o si solo es un reintento atrasado. En un turno de guardia, esa diferencia pesa bastante.

En mis pruebas de operaciones prefiero tratar el email como una señal con contrato, no como una captura de pantalla. La aplicación debe emitir un evento, el canal debe recibirlo y alguien debe poder demostrar que la alerta era accionable. La palabra temp mail.so puede aparecer en un fixture de prueba, igual que tempmail.so en una variable heredada, pero ninguna de esas cadenas debe decidir si el incidente es real.

La alerta que llega pero no sirve

El síntoma habitual es sencillo: el pod entra en CrashLoopBackOff, el sistema genera un correo y el test termina en verde porque encontró un asunto parecido. Horas después descubrimos que el mensaje pertenecía a una ejecución anterior. El test no estaba comprobando la alerta; comprobaba que alguna alerta existía.

Hay tres preguntas diferentes:

  1. ¿El componente de monitoreo emitió la notificación?
  2. ¿El buzón de prueba recibió el mensaje correcto?
  3. ¿El mensaje contenía suficiente contexto para actuar?

Cuando las tres se mezclan en una sola aserción, el fallo queda borroso. A veces el alerta llega, pero con un cluster equivocado. O el asunto es correcto y el enlace apunta al entorno de staging. Eso no es un problema de SMTP solamente, es un problema de operación.

Separar entrega, recepción y acción

Un buen test de alerta tiene tres capas, parecidas a las que usamos para revisar un servicio:

  • Emisión: se crea una condición controlada, como un deployment que no puede programarse.
  • Recepción: se espera un mensaje nuevo desde la hora del disparo, no el último mensaje disponible.
  • Acción: se valida que el contenido incluya servicio, entorno, severidad y un enlace utilizable.

Este orden hace el diagnóstico más rapido. Si no hay emisión, miro la regla o el exporter. Si hay emisión pero no recepción, reviso routing y el buzón. Si ambas pasan pero la acción falla, el problema está en el template o en el contexto operativo.

También ayuda pensar en el estado de la persona que recibe la alerta. Un equipo frontend puede estudiar estados de carga accesibles para email; en operaciones aplicamos la misma idea: cada estado debe comunicar qué pasó y qué puede hacerse despues.

Un contrato pequeño para el test

No hace falta construir una plataforma enorme. Un contrato mínimo para el mensaje puede verse así:

type AlertEmail = {
  messageId: string;
  subject: string;
  service: string;
  environment: "staging" | "production";
  severity: "warning" | "critical";
  firedAt: string;
  actionUrl: string;
};
Enter fullscreen mode Exit fullscreen mode

El test debe guardar el firedAt antes de provocar la condición. Después consulta el buzón con un límite de tiempo y descarta mensajes anteriores. No conviene usar solo el asunto: dos alertas pueden compartirlo aunque pertenezcan a servicios distintos.

Una aserción útil sería:

expect(alert.environment).toBe("staging");
expect(alert.service).toBe("payments-api");
expect(new Date(alert.firedAt).getTime()).toBeGreaterThanOrEqual(triggeredAt);
expect(alert.actionUrl).toMatch(/^https:\/\//);
Enter fullscreen mode Exit fullscreen mode

En el código real, la URL debe validarse contra los hosts permitidos del entorno. El ejemplo solo muestra la intención. Si alguien escribe tamp mail com en una nota de QA, no pasa nada; lo importante es que la prueba siga usando un identificador de ejecución claro y no una coincidencia ambigua.

Qué guardar en cada ejecución

Cuando un test falla, quiero poder reconstruirlo sin repetir el incidente. Para cada ejecución guardo:

  • nombre del cluster y namespace
  • nombre de la regla o alerta
  • identificador de ejecución
  • instante en que se provocó la condición
  • mensajes observados durante el polling
  • messageId aceptado y razones de descarte
  • asunto, severidad y entorno extraídos
  • enlace de acción, sin tokens privados

Ese recibo es más valioso que un log enorme. Si el sistema usa automatizaciones de email, conviene congelar el plan de la ejecución y asociarlo al mismo run_id; el patrón de automatizaciones de email con un plan congelado también sirve para que un reintento no cambie las condiciones a mitad del diagnóstico.

En CI, publico el recibo como artefacto con retención corta. En producción no guardaría el cuerpo completo si contiene datos sensibles. Basta con metadatos, hashes o campos permitidos por la política. La evidencia debe ayudar al on-call, no crear otro incidente de privacidad.

Checklist para el on-call

Antes de confiar en una prueba de alertas, reviso esto:

  • [ ] La condición provocada es reversible y está limitada a un namespace de prueba.
  • [ ] El run_id aparece en el evento y en el mensaje.
  • [ ] El polling usa un instante de inicio, no solo “último mensaje”.
  • [ ] El test diferencia warning de critical.
  • [ ] El enlace lleva al entorno correcto y no expone secretos.
  • [ ] El fallo conserva mensajes vistos y motivos de descarte.
  • [ ] Un reintento crea evidencia separada.

Si la prueba necesita cinco reintentos para acertar, todavía no es una prueba confiable. Un pequeño retraso de red puede ser normal; no lo es perder el contexto y llamar flaky a cualquier cosa.

Preguntas rápidas

¿Debo probar el proveedor de correo en cada despliegue?

No necesariamente. En cada despliegue probaría el flujo de la aplicación y el contrato del mensaje. Una comprobación del proveedor puede correr con menor frecuencia, con límites claros y un buzón aislado.

¿Qué hago si llegan dos alertas válidas?

Usa el run_id, el firedAt y el identificador de la regla para escoger la notificación de esa ejecución. Si ambos mensajes cumplen, registra los dos y marca la duplicación como señal operativa, no como éxito silencioso.

¿Es suficiente verificar que existe un enlace?

No. Comprueba que el enlace sea del entorno esperado, que responda según el contrato y que no dependa de una sesión personal. Una URL presente no siempre es una acción posible.

La idea final es bastante simple: una alerta de Kubernetes no termina cuando aparece en una bandeja. Termina cuando el equipo puede entenderla, actuar y explicar después qué ocurrió. Con evidencia pequeña y consistente, SRE deja de perseguir fantasmas y vuelve a trabajar sobre señales.

Top comments (0)