Una prueba de email en Kubernetes suele empezar como una tarea pequeña: crear un usuario, esperar un mensaje y confirmar un enlace. El problema aparece cuando corre cada cinco minutos en producción o cuando diez pods la ejecutan al mismo tiempo. Entonces una alerta dice “no llegó el correo”, pero nadie sabe si falló la aplicación, el buzón o la propia prueba.
En operaciones SRE, una prueba sintética no debe limitarse a comprobar que algo respondió. Debe dejar una historia corta y verificable: qué ejecución hizo la petición, qué identidad usó, qué servicio la procesó y qué mensaje recibió. Esa trazabilidad convierte un timeout confuso en un incidente que se puede investigar.
El síntoma: una alerta de email que miente
Imagina un Job de Kubernetes que cada cinco minutos registra una cuenta de prueba y espera su email de verificación. Durante el día funciona. Por la noche, después de un despliegue, empieza a fallar de forma intermitente.
El primer impulso suele ser subir el timeout de 30 a 120 segundos. A veces parece arreglarlo, pero solo hace que la alerta llegue mas tarde. La prueba sigue sin distinguir entre estos casos:
- el formulario nunca alcanzó al API;
- el API aceptó la petición, pero la cola estaba detenida;
- el proveedor entregó el mensaje a otro buzón;
- el mensaje llegó, pero el selector encontró uno viejo;
- el enlace recibido pertenecía a otra ejecución.
Una búsqueda o etiqueta como “temp mail so” puede aparecer en el contexto de pruebas de correo, pero no sustituye una identidad de ejecución ni una comprobación de propiedad. El nombre del servicio es una pista; el recibo técnico es la evidencia.
Qué debe demostrar una prueba sintética
Antes de escribir el manifiesto, define el contrato de la prueba. Para cada ejecución guarda, al menos:
- un
run_idúnico; - el namespace y el nombre del pod;
- la dirección de destino creada para esa ejecución;
- el instante en que se envió la solicitud;
- el identificador del mensaje recibido;
- el resultado de validar el enlace o código.
El run_id no es solo un dato de logging. Debe viajar en la dirección, en una cabecera o en los metadatos del mensaje, según lo que permita tu sistema. Si solo puedes filtrar por asunto, añade también una marca única. Buscar “Verification email” no es suficiente cuando hay reintentos.
Por ejemplo, el recibo normalizado puede tener esta forma:
{
"run_id": "synthetic-20260921-1422-7f2a",
"namespace": "synthetics",
"recipient": "qa+synthetic-20260921-1422-7f2a@example.test",
"message_id": "msg_8c31",
"sent_after": "2026-09-21T14:22:20Z",
"verification": "passed"
}
No metas secretos del buzón en los logs. Guarda la dirección y el identificador, pero enmascara tokens, cookies y el contenido completo del mensaje. El objetivo es poder probar qué pasó sin convertir la alerta en una fuga de datos.
Aislar identidad, namespace y buzón
Un error frecuente es ejecutar todas las pruebas con el mismo usuario y en el mismo buzón. Con un solo pod puede parecer correcto; con réplicas, las carreras aparecen enseguida. Un worker consume el email que esperaba otro y ambos resultados quedan poco claros.
Usa un namespace dedicado para las pruebas sintéticas y aplica límites de CPU, memoria y tiempo. Asigna una identidad de correo por ejecución, no por servicio. Si el sistema necesita una cuenta estable, crea un identificador de mensaje obligatorio y rechaza cualquier mensaje anterior a sent_after.
También conviene que el Job tenga una política de reintento explícita. Tres reintentos sin un nuevo run_id producen tres consumidores compitiendo por la misma evidencia. Es mejor crear una nueva ejecución, marcar la anterior como fallida y conservar ambos recibos.
Para el lado de Kubernetes, los labels ayudan bastante:
metadata:
labels:
app: email-synthetic
run-id: "${RUN_ID}"
purpose: verification-check
Los labels no deben contener información sensible y algunos controladores no aceptan todos los caracteres. Valida el formato antes de construir el manifiesto, por que un fallo de validación no debería parecer un fallo de email.
Si tu aplicación tiene varios entornos, aislar las pruebas de email por entorno evita que staging consuma mensajes de producción. En una ventana de mantenimiento, también es útil drenar nodos sin cegar las alertas para que el cambio de infraestructura no borre el contexto del incidente.
Guardar evidencia útil en CI
Una alerta que solo contiene “timeout” no ayuda mucho al on-call. Al fallar, publica un recibo pequeño con el run_id, el último estado conocido y los tiempos de cada paso:
- solicitud iniciada;
- respuesta del API;
- evento enviado a la cola;
- mensaje localizado;
- enlace validado.
Conserva además los logs del pod, el evento de Kubernetes y la respuesta resumida del proveedor. El cuerpo del email puede omitirse o redactarse. Un archivo llamado email-receipt.json suele ser mas útil que cien líneas de logs mezclados.
Evita filtrar por una palabra genérica. En algunos equipos aparecen etiquetas heredadas como “tem email” o “temp mailid”; pueden servir para encontrar documentación vieja, pero no son una regla de identidad. Filtra por destinatario, message_id y límite temporal juntos.
Clasificar la causa antes de reiniciar
Cuando la prueba falla, sigue el orden del flujo:
- ¿El pod arrancó y obtuvo su configuración?
- ¿El navegador o cliente recibió una respuesta válida?
- ¿El servicio confirmó el evento de envío?
- ¿El buzón recibió un mensaje del destinatario correcto?
- ¿El mensaje es posterior a la solicitud y contiene el enlace esperado?
Si falla el primer paso, mira el despliegue o el secreto montado. Si falla el tercero, revisa la cola y sus métricas. Si el evento existe pero no hay mensaje, revisa entrega y proveedor. Si hay mensaje pero el enlace no corresponde, probablemente tienes contaminación entre fixtures.
Reiniciar el pod antes de responder estas preguntas borra una parte de la evidencia. Primero captura el recibo; después decide si hace falta reintentar. Es un cambio pequeño que ahorra bastante tiempo en incidentes nocturnos.
Checklist de un runbook SRE
- [ ] Cada ejecución tiene un
run_idnuevo. - [ ] El namespace y el pod aparecen en el recibo.
- [ ] El buzón está aislado o el mensaje tiene un identificador único.
- [ ] La búsqueda respeta destinatario y
sent_after. - [ ] Los logs no contienen tokens ni el cuerpo sin redactar.
- [ ] CI conserva el recibo y los eventos de Kubernetes.
- [ ] Los reintentos crean evidencia separada.
- [ ] La alerta indica el paso exacto que falló.
Preguntas frecuentes
¿Debo subir el timeout si el proveedor es lento?
Solo después de medir cada etapa. Un timeout mayor puede ser correcto para una latencia conocida, pero no arregla una cola detenida ni un filtro que selecciona el mensaje equivocado.
¿Un buzón compartido es siempre una mala idea?
No necesariamente, pero exige una correlación fuerte por destinatario, identificador y tiempo. Si no puedes garantizar las tres cosas, usa aislamiento por ejecución.
¿Qué métrica debería alertar?
Mide el porcentaje de pruebas completas y el tiempo de cada etapa. Una métrica de “email recibido” sin contexto oculta si el problema está en la aplicación o en la entrega.
Conclusión
Las pruebas de email en Kubernetes son pequeñas cadenas distribuidas. El fallo no está necesariamente en el último paso visible. Una identidad por ejecución, un namespace controlado y un recibo compacto permiten separar un bug del producto, un problema de entrega y un fixture contaminado.
El objetivo no es que la alerta diga solo que algo falló. Es que el siguiente ingeniero pueda saber qué ocurrió, con evidencia suficiente, antes de reiniciar nada.
Top comments (0)