Zero downtime deployment: estrategias para no parar produccion
El coste de “ahora no podemos desplegar”
Hay una frase que mata la velocidad de entrega de cualquier equipo de ingenieria: “esperemos al fin de semana para desplegar.” Lo que realmente dice esa frase es que desplegar es arriesgado, que el equipo no confia en el proceso, y que la ventana de mantenimiento se ha convertido en una muleta.
El coste de no poder desplegar cuando quieras es invisible pero acumulativo. Features que esperan dias. Fixes urgentes que requieren ceremonias de aprobacion. Equipos que agrupan cambios en releases gigantes porque desplegar es caro, lo cual hace cada release mas peligrosa, lo cual refuerza la idea de que desplegar es arriesgado. Un circulo vicioso.
El despliegue sin downtime rompe ese circulo. Si desplegar es seguro, puedes desplegar con frecuencia. Si despliegas con frecuencia, cada despliegue es pequeno. Si cada despliegue es pequeno, el riesgo es bajo. Y si el riesgo es bajo, no necesitas la ventana de mantenimiento.
Las tres estrategias fundamentales
Blue-green
Blue-green es la estrategia mas simple de entender y la mas costosa en infraestructura. Mantienes dos entornos identicos: blue (produccion actual) y green (la nueva version). Despliegas la nueva version en green, la verificas, y cuando estas listo, redireccionas el trafico de blue a green. Si algo falla, rediriges de vuelta a blue. El rollback es instantaneo.
La ventaja es la simplicidad del modelo mental. Siempre hay una version limpia a la que volver. No hay estados intermedios ni mezcla de versiones.
El coste es que necesitas el doble de infraestructura. En cloud, esto es manejable: levantas el entorno green, despliegas, verificas, rediriges, y destruyes el entorno antiguo. Con Kubernetes, un blue-green se implementa con dos Deployments y un Service que apunta a uno u otro. Con Railway o servicios PaaS similares, es aun mas simple: desplegar a un service slot alternativo y hacer el switch a nivel de DNS o load balancer.
El problema real del blue-green no es la infraestructura. Es la base de datos. Si la nueva version requiere cambios de esquema, ambas versiones tienen que funcionar con el mismo esquema. Esto impone una disciplina de migraciones que tratamos mas adelante.
Rolling
Rolling deployment actualiza las instancias de forma incremental. Si tienes 10 pods, actualiza 2, verifica, actualiza 2 mas, y asi sucesivamente. En todo momento hay instancias de la version antigua y la nueva sirviendo trafico simultaneamente.
En Kubernetes, el rolling update es el comportamiento por defecto de un Deployment. Los parametros maxSurge y maxUnavailable controlan cuantas instancias se actualizan simultaneamente. Un maxSurge: 1 y maxUnavailable: 0 garantiza que siempre tienes al menos la capacidad total: se levanta una instancia nueva antes de matar una antigua.
La ventaja es la eficiencia de recursos. No necesitas el doble de infraestructura. El despliegue es gradual y si hay un problema, afecta a una fraccion del trafico.
La complejidad es que durante el despliegue conviven dos versiones. Si la nueva version cambia el formato de un mensaje en una cola, la version antigua no sabra interpretarlo. Si la nueva version cambia una API interna, la version antigua seguira llamando a la anterior. La compatibilidad hacia atras entre versiones consecutivas es obligatoria.
El rollback es mas lento que en blue-green. Hay que volver a desplegar la version anterior de forma incremental. En Kubernetes, kubectl rollout undo automatiza esto, pero tarda lo que tarde un rolling update completo.
Canary
Canary es la estrategia mas sofisticada y la que mejor equilibrio ofrece entre seguridad y velocidad. Despliegas la nueva version a un subconjunto pequeno del trafico (el canary: tipicamente un 5-10%), monitorizas las metricas, y si todo va bien, incrementas gradualmente hasta el 100%.
La diferencia clave con rolling es que en canary tu controlas que porcentaje de trafico recibe la nueva version y puedes mantener esa proporcion el tiempo que necesites. En rolling, el porcentaje avanza automaticamente.
La implementacion requiere un componente de routing inteligente. En Kubernetes, Istio o Linkerd proporcionan traffic splitting basado en pesos. En AWS, ALB con weighted target groups. En Cloudflare, Workers con logica de routing. La herramienta varia, pero el concepto es el mismo: un porcentaje configurable del trafico va al canary.
La monitorizacion durante el canary es critica. Necesitas metricas que permitan comparar el canary contra la baseline en tiempo real: error rates, latencia, metricas de negocio. Si el canary muestra un error rate un 0.5% mayor que la baseline, es una senal de alarma. Herramientas como Prometheus con queries de comparacion o Datadog con dashboards de canary facilitan esta comparacion.
El punto mas importante: automatizar la decision de promover o revertir el canary. Si las metricas estan dentro de los umbrales durante un periodo definido, el canary se promueve automaticamente. Si alguna metrica cruza un umbral, se revierte automaticamente. Argo Rollouts en Kubernetes implementa exactamente este patron con analysis templates.
Migraciones de base de datos sin downtime
Esta es la parte donde la mayoria de equipos tropiezan. Puedes tener el mejor pipeline de despliegue del mundo, pero si tu migracion de base de datos requiere un ALTER TABLE que bloquea la tabla durante 45 segundos, tienes 45 segundos de degradacion de servicio.
La regla fundamental es: nunca hagas un cambio de esquema que sea incompatible con la version actual del codigo. Esto se consigue con el patron expand-contract:
Fase 1: Expand. Anade la nueva columna, tabla, o indice sin eliminar nada. El codigo actual sigue funcionando porque nada de lo que usa ha cambiado. La nueva columna puede tener un valor por defecto o ser nullable.
Fase 2: Migrate. Despliega la nueva version del codigo que escribe en la nueva estructura ademas de la antigua. Si es una columna nueva, el codigo escribe en ambas. Si es un rename, escribe en la columna antigua y en la nueva. Los datos existentes se migran en background (backfill).
Fase 3: Contract. Una vez que la nueva version esta desplegada al 100% y los datos historicos se han migrado, elimina la estructura antigua. Esta fase ocurre en un despliegue separado, posterior.
Tres fases. Tres despliegues. Cada uno compatible hacia atras. Ningun bloqueo.
Para ALTER TABLE en PostgreSQL, la situacion ha mejorado con las versiones recientes. Anadir una columna con un valor por defecto ya no requiere un lock exclusivo desde PostgreSQL 11. Pero crear un indice en una tabla de millones de filas si bloquea lecturas y escrituras a menos que uses CREATE INDEX CONCURRENTLY. MySQL tiene pt-online-schema-change de Percona para migraciones online que crean una tabla shadow, replican cambios, y hacen el swap atomicamente.
Herramientas como Flyway o Liquibase gestionan el versionado de migraciones. Lo que no gestionan es la logica de expand-contract. Eso requiere disciplina del equipo.
Gestion de sesiones
Si tu aplicacion almacena sesiones en memoria del servidor, un rolling deployment mata las sesiones de los usuarios cuyo servidor se recicla. Esto es inaceptable para la mayoria de aplicaciones.
La solucion es externalizar las sesiones. Redis es el estandar de facto. Las sesiones se almacenan en Redis con un TTL apropiado, y cualquier instancia de la aplicacion puede leerlas. Cuando un pod se destruye durante un rolling update, el usuario es redirigido a otro pod que lee su sesion de Redis sin interrupcion.
Para aplicaciones que usan JWT, el problema es menor. Los tokens son autocontenidos y no dependen del estado del servidor. Pero hay que gestionar la rotacion de claves de firma. Si la nueva version usa una clave de firma diferente, los tokens emitidos por la version anterior seran invalidos. La solucion es mantener ambas claves activas durante un periodo de transicion y verificar contra ambas.
Sticky sessions (afinidad de servidor) son una solucion tentadora pero fragil. Funcionan hasta que el servidor al que esta “pegado” el usuario se recicla. En un despliegue sin downtime, eso ocurre por definicion. Las sticky sessions y el zero downtime son fundamentalmente incompatibles.
Rollback: el plan que esperas no necesitar
Todo despliegue necesita un plan de rollback. El plan no es “revertir y rezar.” Es un procedimiento documentado que responde a: que metricas disparan el rollback, quien lo autoriza, como se ejecuta, y cuanto tarda.
En blue-green, el rollback es redirigir trafico al entorno anterior. Segundos.
En rolling, el rollback es un rolling update de vuelta a la version anterior. Minutos.
En canary, el rollback es redirigir el 100% del trafico a la version estable. Segundos, si el routing es configurable en tiempo real.
El caso dificil es cuando el rollback implica revertir una migracion de base de datos. Si seguiste el patron expand-contract, esto no es un problema: la version anterior funciona con el esquema expandido. Si no lo seguiste, estas en territorio peligroso. Revertir un ALTER TABLE que elimino una columna con datos es, en el mejor caso, una restauracion desde backup. En el peor, perdida de datos.
La regla es simple: si la migracion no es reversible, el despliegue no es reversible, y necesitas mas cautela (canary extendido, verificacion manual, feature flags).
Feature flags como multiplicador
Los feature flags desacoplan el despliegue del codigo del lanzamiento de funcionalidad. Puedes desplegar codigo que contiene una nueva feature desactivada por defecto, verificar que el despliegue es estable, y activar la feature gradualmente.
Esto permite un patron poderoso: desplegar primero, activar despues. El despliegue es mecanico y automatizable. La activacion es una decision de producto que puede hacerse sin tocar infraestructura.
Herramientas como LaunchDarkly, Unleash o incluso un simple JSON en Redis proporcionan la infraestructura de feature flags. Lo importante es la disciplina de usarlos para cualquier cambio de comportamiento visible para el usuario y limpiarlos despues (los feature flags eternos son deuda tecnica).
Health checks y readiness probes
Un detalle que parece menor pero tiene impacto directo en la calidad del despliegue: los health checks deben diferenciar entre liveness y readiness.
Liveness responde a “esta vivo el proceso?” Si el liveness check falla, el orquestador mata el pod y crea uno nuevo. Este check debe ser ligero: un endpoint que devuelve 200 si el proceso responde. No debe depender de servicios externos (base de datos, cache, APIs) porque un problema en la base de datos no se resuelve matando pods.
Readiness responde a “puede recibir trafico?” Un pod puede estar vivo pero no listo: esta cargando cache, esperando conexiones a la base de datos, calentando modelos. Mientras el readiness check falle, el Service de Kubernetes no envia trafico a ese pod. Esto es critico durante un rolling deployment: los pods nuevos no reciben trafico hasta que estan genuinamente preparados.
El error clasico es usar el mismo endpoint para ambos checks, o peor, no configurar readiness probes. El resultado es que durante un rolling update, el trafico llega a pods que aun no estan listos, generando errores 503 para un porcentaje de usuarios. Tecnicamente no es downtime. Para el usuario que ve un error, si lo es.
En la practica, configuramos el liveness probe con un delay inicial de 10-15 segundos y un periodo de 10 segundos. El readiness probe con un delay inicial de 5 segundos y un periodo de 5 segundos. Los valores exactos dependen de la aplicacion, pero la clave es que el readiness sea mas frecuente y mas estricto.
Smoke tests post-despliegue
Despues de cada despliegue, antes de declarar exito, un conjunto de smoke tests verifica que las funcionalidades criticas estan operativas. No son tests exhaustivos. Son los 5-10 tests que cubren los flujos de mayor valor: login, busqueda de producto, checkout, procesamiento de pedido.
Los smoke tests deben ejecutarse automaticamente como parte del pipeline. Si fallan, el despliegue se revierte automaticamente. Si pasan, el despliegue se marca como exitoso y se notifica al equipo.
Un smoke test util es rapido (menos de 60 segundos en total), fiable (no tiene flaky tests que generen falsos positivos), y significativo (testa funcionalidad real, no endpoints de health check). Cypress, Playwright o incluso un script de curl que verifica respuestas HTTP cumplen esta funcion.
Poniendolo todo junto
Un pipeline de despliegue maduro combina estos elementos:
- Tests automatizados como gate de entrada. Si los tests fallan, el despliegue no procede.
- Despliegue canary al 5% del trafico con analisis automatizado de metricas.
- Promocion gradual (25%, 50%, 100%) con gates automaticos basados en error rate y latencia.
- Rollback automatico si cualquier metrica cruza los umbrales definidos.
- Feature flags para cambios de comportamiento que requieren activacion controlada.
- Migraciones de BD siempre compatibles hacia atras usando expand-contract.
No necesitas implementar todo desde el dia uno. Empieza con rolling deployment y migraciones compatibles. Anade canary cuando el volumen de trafico justifique la inversion en observabilidad. Anade feature flags cuando la cadencia de despliegue sea diaria o mayor.
El objetivo no es la perfeccion tecnica. Es poder desplegar un martes a las 11 de la manana sin que nadie pierda el sueno. Cuando llegas a ese punto, la velocidad de entrega se multiplica y la calidad mejora porque los cambios son pequenos, incrementales y reversibles. Para profundizar en canary deployments y feature flags, consulta nuestro articulo sobre testing en produccion. Si aun no tienes un pipeline automatizado, empieza por nuestra guia de CI/CD para equipos sin DevOps.
Etiquetas
Sobre el autor
abemon engineering
Equipo de ingenieria
Equipo multidisciplinar de ingenieria, datos e IA con sede en Canarias. Construimos, desplegamos y operamos soluciones de software a medida para empresas de cualquier escala.