Un pipeline de CI puede terminar en verde y dejar un problema invisible: buzones de prueba, secretos temporales y mensajes de verificación que siguen existiendo mucho después de que acaba el job.
En equipos que ejecutan pruebas de registro, OAuth o recuperación de contraseña dentro de Kubernetes, el fallo suele aparecer días después. El clúster parece sano, pero los datos de email se acumulan, los artefactos pierden contexto y nadie sabe qué ejecución creó cada recurso.
La solución no es borrar todo al final sin mirar. Es dar a cada ejecución una identidad, separar la evidencia mínima de los datos sensibles y hacer la limpieza idempotente.
El síntoma: el pipeline termina, pero el buzón queda vivo
Imaginemos un job que crea un namespace efímero, levanta la aplicación y solicita un generador de cuentas de correo temporal para validar un enlace de activación. La prueba pasa. El runner se cierra. Sin embargo, pueden quedar tres cosas:
- Un buzón asociado a la ejecución.
- Un secreto de Kubernetes con una dirección o token de acceso.
- Un log que dice “email recibido”, pero no indica qué mensaje ni qué versión lo generó.
Cuando llega un fallo intermitente, el equipo reintenta y crea otro conjunto de recursos. Despues de varias horas, la limpieza manual se convierte en parte del incidente.
Qué debe identificar cada ejecución
El primer cambio es usar un run_id que viaje por todo el flujo. No debe depender solo del nombre del branch, porque dos ejecuciones del mismo branch pueden ocurrir al mismo tiempo.
Un identificador útil aparece en:
- La etiqueta del namespace o del recurso temporal.
- El asunto o encabezado técnico del mensaje de prueba.
- El nombre del artefacto de CI.
- Los logs de la aplicación y del consumidor.
Por ejemplo:
metadata:
labels:
ci.run_id: "run-20261107-0522-a81f"
ci.owner: "checkout-tests"
data.class: "synthetic-email"
La etiqueta data.class ayuda a distinguir datos sintéticos de cualquier dato que no debe tocar un proceso automático. Parece pequeño, pero hace la limpieza mucho mas segura.
Separar evidencia de datos temporales
Un error común es guardar el contenido completo del email como artefacto porque “puede servir para depurar”. En realidad, el HTML suele contener tokens, nombres y enlaces que ya no hacen falta cuando la prueba termina.
Conviene conservar una evidencia compacta, por ejemplo:
{
"run_id": "run-20261107-0522-a81f",
"message_id": "msg-7b2c",
"event": "verification_received",
"latency_ms": 1840,
"result": "passed"
}
El cuerpo del mensaje puede eliminarse o redacted en el origen, según la capacidad del sistema de email. Para diagnosticar una demora, message_id, estado y latencia suelen ser mas útiles que guardar todo el contenido.
También ayuda registrar el momento de expiración. Si el buzón debe vivir diez minutos, esa regla debe estar en los metadatos y no solo en la memoria del desarrollador. Un expires_at explícito permite que un reconciliador actúe aunque el runner se corte.
Un patrón sencillo para Kubernetes
Hay dos caminos razonables, y pueden convivir:
Limpieza al terminar el job. El pipeline intenta borrar el namespace, el secreto y el buzón. Es rapido y barato, pero no cubre un runner cancelado, un timeout o un nodo perdido.
Reconciliación periódica. Un proceso busca recursos con data.class=synthetic-email, compara expires_at con la hora actual y elimina los vencidos. Es la red de seguridad que evita depender de un único finally.
El reconciliador debe ser conservador. Primero comprueba que el recurso pertenece al entorno de pruebas, luego confirma que está vencido y finalmente elimina por run_id. Si una ejecución todavía está activa, no debe asumir que su buzón está huérfano solo por ser antiguo.
Para mantener la operación trazable, cada decisión puede producir un evento pequeño: kept, expired, deleted o skipped_active. Esos eventos son mas faciles de buscar que un log narrativo largo. Para trabajos programados, un contrato de salida con estado, motivo y run_id también evita que un agente o un job aguas abajo interprete un borrado incompleto como éxito; este contrato de salida para el job es un buen patrón aplicable.
Limpieza segura y observable
Antes de activar el borrado automático, haría estas comprobaciones:
- Modo de observación: durante un día, el reconciliador solo informa qué eliminaría.
- Permiso limitado: la cuenta de servicio solo puede leer y borrar recursos con las etiquetas esperadas.
- Idempotencia: borrar un buzón ya eliminado debe producir un estado conocido, no una alerta falsa.
- Retención de evidencia: conserva el resumen de la ejecución por un periodo corto y definido.
- Métrica de residuos: mide recursos vencidos, edad máxima y tiempo de limpieza.
- Alerta útil: avisa cuando el volumen o la edad superan un umbral, no por cada recurso individual.
La telemetria debe responder tres preguntas: qué ejecución creó el dato, por que todavía existe y qué acción tomó el sistema. Si no puede responderlas, hay automatización, pero no hay control operacional.
Un caso especialmente delicado es el fallo parcial. La prueba puede pasar, pero la limpieza del buzón fallar. El resultado del job debería separar test_status de cleanup_status; de ese modo, el equipo sabe que la funcionalidad está bien y que queda una tarea de higiene pendiente.
Checklist para SRE
Antes de considerar terminado el diseño, revisa:
- ¿Cada recurso de email tiene
run_id, propietario y expiración? - ¿La evidencia excluye tokens y cuerpos innecesarios?
- ¿Existe un reconciliador para jobs cancelados o nodos perdidos?
- ¿El borrado está restringido a datos sintéticos?
- ¿Se puede explicar cada decisión en los logs?
- ¿Hay una métrica de recursos vencidos y otra de tiempo de limpieza?
- ¿El proceso esta probado con ejecuciones simultáneas?
No hace falta empezar con un operador complejo. Un job pequeño, etiquetas consistentes y una política clara ya reducen mucho el riesgo. Incluso una búsqueda rápida de “tem email” en documentación interna debería llevar a la misma definición de datos sintéticos, no a una colección de scripts distintos.
Preguntas frecuentes
¿Debo borrar el mensaje antes de guardar evidencia?
Guarda primero un resumen sin secretos y elimina el cuerpo tan pronto como termine la aserción necesaria. Si una investigación necesita más detalle, usa una retención explícita y con acceso limitado.
¿Qué pasa si el pipeline se cancela?
El reconciliador debe detectar el expires_at vencido. No dependas únicamente de un paso final del runner, porque ese paso no siempre se ejecuta.
¿Cuándo conviene usar un namespace por ejecución?
Cuando las pruebas cambian configuración o despliegan varios componentes. Para una prueba pequeña puede bastar con etiquetas y nombres únicos, siempre que la limpieza pueda aislar exactamente sus recursos.
La idea central es simple: los datos de email de CI son recursos operativos, no basura anónima. Si tienen identidad, expiración y evidencia mínima, Kubernetes puede ayudar a limpiarlos sin borrar las señales que un equipo SRE necesita para entender el siguiente incidente.
Top comments (0)