Hay pocos momentos más tensos en la vida de un equipo de ingeniería que el deploy a producción. Alguien ejecuta el comando, todos miran el canal de Slack, y durante unos segundos nadie respira. ¿Funcionó? ¿Se cayó algo? ¿Los usuarios lo notaron?
Si tu equipo siente esa tensión cada vez que despliega, hay un problema. Los deploys no deberían dar miedo. En un setup de Kubernetes bien configurado, podés desplegar una nueva versión de tu aplicación sin que ningún usuario note que algo cambió. Cero errores 502. Cero conexiones cortadas. Cero downtime.
Kubernetes no te da zero downtime por defecto. Te da las herramientas para lograrlo. Configurarlas correctamente es tu responsabilidad.
🔍Cómo funciona un deploy en Kubernetes por dentro
Cuando ejecutás un helm upgrade o un kubectl apply, el Rolling Update sigue una coreografía precisa:
SIGTERM y tiene un período de gracia para terminar lo que estaba haciendo.Dos parámetros controlan cómo se hace esa transición:
maxUnavailable: 0 + maxSurge: 1 — nunca bajés un pod viejo hasta que el nuevo esté listo, y podés crear pods extras temporalmente para lograrlo. Más lento y usa más recursos durante el deploy, pero garantiza que la capacidad nunca baje del 100%.
❤️No tener readiness probes (o tenerlos mal)
Este es el error más común y el más impactante. El readiness probe le dice a Kubernetes: “este pod está listo para recibir tráfico”. Sin él, Kubernetes asume que el pod está listo en el momento que el container arranca — no cuando tu aplicación realmente está lista.
La mayoría de las aplicaciones necesitan unos segundos (a veces más) para levantar — cargar configuración, conectarse a la base de datos, calentar caches. Si Kubernetes empieza a enviar tráfico antes de que todo esto termine, los primeros usuarios que caigan en el pod nuevo van a recibir errores 503, conexiones rechazadas o timeouts.
Configurá un readiness probe apuntando a un endpoint que valide que la aplicación genuinamente está lista — incluyendo conexiones a dependencias críticas como la base de datos. No solo que el proceso está corriendo.
Si tu app tarda significativamente en arrancar (Spring Boot, modelos de ML, caches grandes), sumá un startupProbe para que Kubernetes no la marque como muerta mientras levanta.
🛑No manejar el SIGTERM correctamente
Cuando Kubernetes decide terminar un pod viejo, le envía SIGTERM. Este es un aviso: “terminá lo que estás haciendo, tenés X segundos antes del SIGKILL”. El problema es que muchas aplicaciones no lo manejan y simplemente mueren, cortando cualquier request en vuelo.
El usuario que tenía un request en proceso recibe un error. Si era un request de pago, una operación de escritura o una transacción, las consecuencias pueden ser graves. Cada deploy con SIGTERM sin manejar es una ventana de potencial corrupción de datos.
Para zero downtime real, tu aplicación necesita implementar graceful shutdown:
- Escuchar la señal
SIGTERMy dejar de aceptar conexiones nuevas. - Terminar los requests que ya están en vuelo.
- Cerrar conexiones a bases de datos y servicios externos de forma limpia.
- Recién entonces, terminar el proceso.
Ajustá terminationGracePeriodSeconds para darle a tu app tiempo suficiente. El default de 30 segundos puede no ser suficiente para algunas aplicaciones con transacciones largas o cierres de conexiones complejos.
⏱️La ventana de muerte entre SIGTERM y desregistración
Este es el gotcha más sutil y el que menos equipos conocen. Cuando Kubernetes decide terminar un pod, dos cosas pasan al mismo tiempo pero de forma asincrónica:
SIGTERM para que empiece a apagarse.Estas dos acciones no están sincronizadas. Tu app puede recibir SIGTERM y empezar a cerrarse, pero el Service todavía no sabe que ese pod está saliendo. Durante esos milisegundos — a veces segundos — el Service sigue enviando requests nuevos a un pod que ya está muriendo.
Los errores son intermitentes y casi imposibles de reproducir en desarrollo porque dependen del timing exacto de la propagación de red.
Agregá un preStop hook con un sleep de 3 a 10 segundos. Este delay le da tiempo al sistema de networking de Kubernetes para propagar el cambio antes de que tu aplicación empiece a cerrarse. Es contraintuitivo — estás retrasando el shutdown a propósito — pero es necesario para evitar requests perdidos.
🔢No tener suficientes réplicas
Si tu servicio corre con 1 o 2 réplicas y hacés un Rolling Update, la matemática no da. Con una sola réplica, Kubernetes necesita matar el pod viejo para crear el nuevo. Durante ese intercambio, hay un momento donde potencialmente ningún pod está listo.
Con 2 réplicas y maxUnavailable: 1, durante el deploy tenés un solo pod sirviendo toda la carga mientras el otro se reemplaza. Si ese pod no puede manejar toda la carga solo, tus usuarios van a experimentar degradación o timeouts.
Al menos 3 réplicas en cualquier servicio que reciba tráfico de usuarios. Esto le da a Kubernetes suficiente margen para rotar pods sin perder capacidad total. Complementá con un Pod Disruption Budget (PDB) para evitar que Kubernetes mate demasiados pods al mismo tiempo durante operaciones de mantenimiento.
🚀El pipeline de CI/CD que no espera el rollout
Un error frecuente en pipelines: asumir que el deploy terminó cuando helm upgrade retorna éxito. Helm retorna éxito cuando el manifiesto se aplica, no cuando los pods están listos.
Si tu pipeline reporta “deploy exitoso” en el momento que Helm termina, puede estar mintiendo. Los pods nuevos todavía están levantando, los readiness probes todavía están fallando, y la versión vieja todavía sirve tráfico. Si algo sale mal, no te vas a enterar hasta que los usuarios empiecen a reportar errores.
- Ejecutá
kubectl rollout statusdespués del deploy — espera activamente a que el Rolling Update complete y todos los pods nuevos estén en estado Ready. - Configurá rollback automático si el rollout no completa en un tiempo razonable o si los readiness probes fallan repetidamente.
- Definí un timeout explícito para el rollout — si no completa en X minutos, algo salió mal y hay que actuar.
🗺️Rolling Update no es la única estrategia
El Rolling Update es la estrategia por defecto, pero no la única. Dependiendo del caso podés considerar:
| Configuración | Qué hace | Capa |
|---|---|---|
| En tu Deployment de Kubernetes | ||
| maxUnavailable: 0 | Nunca baja pods viejos antes de tener uno nuevo listo | K8s |
| maxSurge: 1+ | Permite pods extras temporales durante el deploy | K8s |
| réplicas ≥ 3 | Margen suficiente para rotar pods sin perder capacidad | Crítico |
| readinessProbe | Tráfico solo cuando la app está genuinamente lista | Crítico |
| livenessProbe | Detecta pods vivos pero atascados (deadlock, leak) | K8s |
| startupProbe | Tiempo extra para apps con startup lento | K8s |
| preStop sleep 3–10s | Tiempo para propagar la desregistración del Service | Crítico |
| terminationGracePeriod | Tiempo suficiente para el graceful shutdown completo | K8s |
| PodDisruptionBudget | Protege contra bajas masivas en mantenimiento de nodos | K8s |
| En tu aplicación | ||
| Manejo de SIGTERM | Para de aceptar conexiones, termina requests en vuelo | Crítico |
| Health endpoint | Devuelve 200 solo cuando la app puede servir correctamente | App |
| En tu pipeline de CI/CD | ||
| rollout status wait | Espera a que todos los pods nuevos estén en Ready | CI/CD |
| Rollback automático | Revierte si el rollout falla o los probes fallan repetidamente | CI/CD |
Zero downtime debería ser el default, no un logro
Desde SleakOps, los workloads se despliegan con todas estas configuraciones aplicadas por defecto — Rolling Update, probes, graceful shutdown, preStop hook. No necesitás descubrir cada gotcha por tu cuenta.
Conocé SleakOps →