DEV Community

Alex Carter
Alex Carter

Posted on

Kubernetes: un SLO para el correo de CI

En un pipeline de CI, el correo de verificación suele tratarse como una dependencia menor. Un pod crea un usuario, espera el mensaje y sigue con la prueba. Cuando varias ejecuciones comparten buzón, sin embargo, un resultado verde puede esconder un problema: una prueba leyó el mensaje de otra ejecución.

En una guardia de SRE, la pregunta no es solo “¿llegó el email?”. Es “¿llegó el email correcto, dentro del tiempo esperado y con evidencia que podamos revisar?”. Tratar el correo temporal como un servicio con SLO ayuda a responderla sin aumentar timeouts a ciegas.

El síntoma: el pod está verde, pero el email no llegó

Un fallo típico muestra verification email not found. El equipo sube el timeout y reintenta. A veces la prueba pasa porque encuentra un mensaje viejo; otras, dos pods consumen el mismo correo y el resultado depende del orden de ejecución. El sistema parece estable, pero la señal esta contaminada.

Para diagnosticarlo, separa tres eventos:

  • Solicitud: la aplicación pidió enviar el mensaje y devolvió un identificador.
  • Entrega: el colector observó un mensaje con ese identificador.
  • Consumo: la prueba validó el enlace o código y cerró la ejecución.

Si solo guardas el último evento, no sabes si el problema fue de la aplicación, de la entrega o del lector. Un tempail mail en un log tampoco explica qué ejecución era propietaria del buzón.

Convierte la entrega en un SLO medible

Un SLO pequeño es mejor que una promesa vaga. Por ejemplo: “el 99% de los emails de CI debe ser observable en 45 segundos desde la solicitud, sin aceptar mensajes de otra ejecución”. La cifra depende de tu proveedor y región; lo importante es definir una ventana y una condición de corrección.

Mide al menos estas duraciones:

  1. send_requested_at a provider_accepted_at.
  2. provider_accepted_at a message_observed_at.
  3. message_observed_at a assertion_completed_at.

El segundo intervalo suele recibir toda la atención, pero el primero puede descubrir colas internas y el tercero puede señalar un test lento. No mezcles los tres en una única métrica, porque el remedio para cada uno es distinto.

Un presupuesto de errores también evita discusiones durante el incidente. Si CI tiene 100 ejecuciones y dos mensajes tardan más de la ventana, no conviertas cada retraso en un incidente de plataforma. Registra el consumo y decide por adelantado cuándo se pausa un despliegue.

Da una identidad única a cada ejecución

Cada job debería reservar una identidad que sobreviva a los reintentos del pod. Un esquema simple puede ser:

ci-{repository}-{pull_request}-{run_id}@dominio-de-prueba
Enter fullscreen mode Exit fullscreen mode

El run_id debe venir del orquestador, no de la hora local del contenedor. La prueba debe buscar por ese identificador y por una ventana temporal posterior a send_requested_at. Buscar “el último email” es cómodo, pero es una fuente frecuente de falsos positivos.

La identidad también debe aparecer en los logs estructurados y en los artefactos del job. Si usas un servicio de temp mail so, conserva la referencia de la bandeja como dato de prueba y no como secreto compartido entre todos los namespaces.

El TTL de la bandeja debe cubrir la prueba y la investigación corta, no toda la vida del cluster. Al expirar, el sistema debería devolver un estado claro como expired, no un timeout genérico. Asi el runbook puede distinguir una limpieza normal de una entrega tardía.

Qué evidencias debe guardar el job

Una ejecución útil deja un recibo pequeño, incluso cuando falla. Incluye:

  • run_id, commit y namespace;
  • identificador del mensaje esperado;
  • timestamps de solicitud, observación y consumo;
  • resultado del filtro: coincidencia, mensaje antiguo o mensaje de otra ejecución;
  • latencia total y motivo de expiración;
  • hash o metadato mínimo del mensaje, sin volcar tokens ni contenido sensible.

Para jobs que llaman a agentes o tareas programadas, conviene definir también un contrato de salida. Estos contratos de salida para trabajos programados hacen que un reintento pueda decir qué observó y qué no observó. Si el flujo tiene polling, separa su límite del SLO de entrega; un polling mas largo no arregla un selector incorrecto.

La latencia merece su propio presupuesto y no debe ocultarse en un “sleep 10”. Un presupuesto de latencia para emails ayuda a repartir el tiempo entre proveedor, cola y aserción. Tambien facilita comparar namespaces o tipos de runner sin culpar al componente equivocado.

Checklist para el próximo incidente

Antes de cambiar el timeout, revisa:

  • ¿El run_id aparece en la solicitud y en el mensaje recibido?
  • ¿La búsqueda empezó después de enviar el mensaje?
  • ¿El buzón pertenece a una sola ejecución y namespace?
  • ¿Se guardaron las tres duraciones por separado?
  • ¿El error distingue not_delivered, wrong_run y expired?
  • ¿El TTL permite una investigación razonable?
  • ¿Se borraron el contenido y los tokens de los artefactos públicos?

Si alguna respuesta es no, la prioridad es mejorar la señal. Reintentar puede dar alivio rapido, pero también puede gastar el presupuesto de errores sin hacer el sistema más confiable. Un pequeño contrato, aplicado de forma consistente, suele ahorrar más tiempo que otro minuto de espera.

Preguntas frecuentes

¿Un SLO de 45 segundos sirve para todos los clusters?

No. Empieza midiendo una semana de ejecuciones representativas y define la ventana con margen para la variación normal. Revisa el valor cuando cambien proveedor, región o runners.

¿Puedo compartir un buzón para ahorrar recursos?

Solo si cada mensaje tiene una identidad fuerte y el lector aplica cursor, ventana temporal y run_id. En la práctica, un buzón por ejecución suele ser más sencillo de razonar y limpiar.

¿Qué hago con una palabra mal escrita en datos antiguos?

Trátala como dato de migración, no como selector. Por ejemplo, tem email puede conservarse para localizar un fixture viejo, pero las nuevas pruebas deben usar nombres estructurados y validables.

El objetivo no es que ningún email tarde jamás. Es que Kubernetes y SRE puedan explicar qué ocurrió, cuánto tardó y si el resultado pertenece realmente a esa ejecución.

Top comments (0)