DEV Community

Alex Carter
Alex Carter

Posted on

Kubernetes: un run ID para el correo de CI

Un test de CI que espera un email suele fallar con un mensaje poco util: “timeout esperando el correo”. En Kubernetes, el síntoma puede venir del navegador, del worker, de la cola o de la bandeja de prueba. Si cada capa registra un identificador diferente, el equipo termina repitiendo el job sin saber que evidencia mirar.

Un patrón pequeño ayuda bastante: asignar un run_id al inicio del flujo y llevarlo por el job, el mensaje de prueba, los logs y los artefactos. No arregla una entrega lenta por sí solo, pero convierte una sospecha difusa en una investigación corta. Para un equipo SRE, esa diferencia importa mas que añadir otro reintento.

El síntoma: un test de correo que parece aleatorio

Imagina un despliegue efímero que crea un namespace, registra un usuario y espera un enlace de activación. A veces pasa en 20 segundos; otras veces expira. El reporte muestra que el pod del navegador sigue sano, pero no dice qué bandeja consultó ni desde qué momento empezó a buscar.

Hay varios fallos parecidos que se mezclan:

  • el test leyó un mensaje viejo de otra ejecución;
  • el worker publicó el evento, pero el consumidor todavía no lo procesó;
  • la consulta filtró por asunto y encontró una copia equivocada;
  • el pod murió y su log, que era la única pista, desapareció con él.

En incidentes así, “correo desechable” describe el dato de prueba, no el problema operativo. También aparecen nombres informales como temp mailid o temp org mail en tickets y notas. Es mejor no depender de esas etiquetas: el sistema necesita un identificador estable.

Qué debe identificar un run de CI

El run_id no tiene que ser una plataforma nueva. Puede ser el identificador de la ejecución de CI más un sufijo de intento, por ejemplo ci-1842-attempt-1. Lo importante es que se genere una sola vez y que cada componente lo conserve.

Como mínimo, guardaría estos datos:

  1. run_id y número de intento.
  2. Namespace y nombre del job.
  3. Instante en que se creó el usuario o se disparó el email.
  4. Identificador de la bandeja de prueba, sin imprimir credenciales.
  5. Mensajes observados durante el polling: hora, asunto normalizado y estado.
  6. Motivo final: recibido, expirado, rechazado o cancelado.

No hace falta registrar el contenido completo del mensaje en cada log. Basta con un hash o un artefacto protegido cuando el equipo necesite revisar el cuerpo. Asi se reduce el riesgo de filtrar tokens en los logs y se mantiene una pista útil.

Un contrato pequeño para el job de Kubernetes

El contrato puede viajar como variables de entorno y etiquetas del pod. Por ejemplo, el job de CI puede pasar el run ID al contenedor que ejecuta las pruebas:

env:
  - name: TEST_RUN_ID
    value: "ci-1842-attempt-1"
  - name: EMAIL_WAIT_BUDGET_SECONDS
    value: "90"
Enter fullscreen mode Exit fullscreen mode

El código de prueba debe escribir el mismo valor en cada evento importante:

email.wait.start run_id=ci-1842-attempt-1 since=2026-11-02T11:20:00Z
email.message.seen run_id=ci-1842-attempt-1 age_seconds=18 subject_hash=8f2a
email.wait.done run_id=ci-1842-attempt-1 status=received elapsed_seconds=21
Enter fullscreen mode Exit fullscreen mode

En Kubernetes, añadir test-run-id como label del Job facilita encontrar los pods relacionados, pero no conviene usar datos privados o direcciones completas como labels. Las labels tienen límites de formato y permanecen en más lugares de los que parece. El valor sensible debe quedar en un secreto o en el proveedor de bandejas, nunca en la etiqueta.

Para un run fallido, conserva un artefacto compacto: run.json, los logs del test y una lista de eventos observados. Si el namespace efímero se elimina al terminar, exporta esos archivos antes del cleanup. Ese paso se olvida bastante cuando el pipeline va verde.

Cómo leer la evidencia durante el incidente

Empieza por el reloj, no por el último error. Compara el instante de disparo con el primer mensaje que la prueba pudo observar. Si el mensaje existía antes del run_id, hay contaminación entre ejecuciones. Si apareció después del presupuesto, el problema puede estar en la cola o en la entrega, no en el selector del navegador.

Luego sigue la cadena:

Job de CI → pod de prueba → evento de email → consumidor → mensaje observado

Cada salto debe conservar run_id. Cuando falta en un solo componente, ya tienes un hueco concreto de observabilidad. Un runbook de email que sí escala puede ayudar a convertir esos huecos en acciones conocidas para guardias y revisiones posteriores.

También es útil separar “no llegó” de “llegó pero no coincidió”. El primer caso apunta a entrega, cola o proveedor. El segundo suele señalar un filtro demasiado amplio, un asunto cambiado o una bandeja reutilizada. Un recibo claro para cada email de prueba ofrece una buena forma de pensar esa diferencia a nivel de aplicación.

Checklist para no volver a perseguir fantasmas

  • Genera un run_id antes de crear el usuario.
  • Pasa el valor al Job y a todos los pods que participan.
  • Registra el momento de inicio del polling y su presupuesto.
  • Aísla la bandeja por ejecución o valida su antigüedad.
  • Guarda eventos y logs antes de eliminar el namespace.
  • No imprimas tokens, contraseñas ni el contenido completo del email.
  • Clasifica el fallo como entrega, selección, timeout o infraestructura.

La meta no es observar cada byte. Es poder responder, en una lectura, qué ejecución esperaba qué mensaje y dónde se perdió la señal. Ese nivel de evidencia suele ser suficiente para que el siguiente reintento sea una decisión, no una apuesta.

Preguntas y respuestas

¿Un run ID evita que el email llegue tarde?

No. Hace visible cuánto tardó y qué componentes participaron. Para mejorar la entrega aún necesitas revisar colas, límites y reintentos.

¿Debo guardar el cuerpo completo del correo?

Normalmente no. Guarda metadatos y un artefacto protegido solo cuando sea necesario para depurar. Los enlaces de activación pueden contener secretos.

¿Qué hago si la prueba se reintenta?

Usa un sufijo de intento nuevo y conserva el ID de la ejecución padre. Así puedes distinguir un mensaje tardío del primer intento de uno creado por el segundo.

Top comments (0)