Despliegue zero-downtime en Kubernetes: rolling updates, canary y blue-green en la práctica
Introducción: qué es el despliegue zero-downtime y por qué es crítico
Cada vez que publicas una nueva versión de tu aplicación, surge un riesgo: los usuarios pueden encontrar errores, indisponibilidad del servicio o comportamientos impredecibles. El despliegue zero-downtime es un conjunto de prácticas y enfoques técnicos que permiten actualizar el entorno de producción sin interrumpir el procesamiento de solicitudes.
En el contexto de Kubernetes, el despliegue zero-downtime ha dejado de ser una rareza: es un requisito básico para cualquier producto serio. La plataforma ofrece mecanismos integrados (rolling updates) y también permite implementar estrategias más avanzadas: blue-green y canary. En este artículo analizaremos cada una de ellas con manifiestos y comandos reales.
El público objetivo de este artículo son ingenieros DevOps, SRE y desarrolladores backend que ya trabajan con Kubernetes y desean construir pipelines de entrega confiables.
Rolling Update en Kubernetes: configuración y puntos conflictivos
El rolling update es la estrategia de despliegue predeterminada en Kubernetes. Los nuevos pods se inician gradualmente mientras los antiguos se terminan a medida que los nuevos están listos. Esto permite mantener la disponibilidad del servicio durante toda la actualización.
Parámetros clave: maxSurge y maxUnavailable
El comportamiento del rolling update se controla mediante dos parámetros en la sección strategy del manifiesto Deployment:
- maxSurge — número máximo de pods por encima del número deseado de réplicas que pueden existir simultáneamente. Se puede expresar como número o porcentaje.
- maxUnavailable — número máximo de pods que pueden estar no disponibles durante el proceso de actualización.
Ejemplo de manifiesto Deployment con rolling update configurado:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: production
spec:
replicas: 4
selector:
matchLabels:
app: my-app
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: my-app
version: v2
spec:
containers:
- name: my-app
image: my-registry/my-app:v2
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
failureThreshold: 3
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
Readiness y Liveness probes — un elemento obligatorio
Sin probes correctamente configuradas, el rolling update pierde su sentido. El readiness probe indica a Kubernetes que el pod está listo para recibir tráfico. Mientras la probe no sea satisfactoria, el pod no se incluye en el balanceo. El liveness probe determina si el proceso sigue vivo; si no es así, el pod se reinicia.
Un error típico: la aplicación arranca, pero aún no ha terminado su inicialización (calentamiento de cachés, migraciones). Sin readiness probe, Kubernetes comenzará a enviar tráfico a una instancia no lista. Utiliza también startupProbe para aplicaciones con arranques prolongados.
Comandos kubectl para gestionar el rolling update
# Actualizar la imagen
kubectl set image deployment/my-app my-app=my-registry/my-app:v2 -n production
# Seguir el proceso
kubectl rollout status deployment/my-app -n production
# Ver el historial
kubectl rollout history deployment/my-app -n production
# Revertir a la versión anterior
kubectl rollout undo deployment/my-app -n production
Despliegue Blue-Green: cambio de tráfico mediante Service e Ingress
El despliegue blue-green implica tener dos entornos idénticos: blue (versión actual en producción) y green (nueva versión). El tráfico en todo momento se dirige únicamente a uno de ellos. Tras realizar pruebas exitosas del entorno green, el tráfico se redirige con un solo comando.
Manifiesto del despliegue blue y green
# Blue Deployment (producción actual)
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-blue
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: my-app
slot: blue
template:
metadata:
labels:
app: my-app
slot: blue
version: v1
spec:
containers:
- name: my-app
image: my-registry/my-app:v1
ports:
- containerPort: 8080
---
# Green Deployment (nueva versión)
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-green
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: my-app
slot: green
template:
metadata:
labels:
app: my-app
slot: green
version: v2
spec:
containers:
- name: my-app
image: my-registry/my-app:v2
ports:
- containerPort: 8080
Service y cambio de tráfico
apiVersion: v1
kind: Service
metadata:
name: my-app-svc
namespace: production
spec:
selector:
app: my-app
slot: blue # Cambiar a "green" para redirigir el tráfico
ports:
- protocol: TCP
port: 80
targetPort: 8080
El cambio de tráfico se realiza aplicando un patch al servicio:
kubectl patch service my-app-svc -n production \
-p '{"spec":{"selector":{"slot":"green"}}}'
Al usar Ingress con NGINX Ingress Controller o similares, es posible cambiar el tráfico a nivel del recurso Ingress modificando el servicio backend. Esto proporciona un control adicional sin necesidad de modificar el Service en sí.
Ventaja del blue-green: cambio instantáneo y reversión igualmente instantánea. Desventaja: recursos duplicados durante el período de despliegue.
Despliegue Canary: rollout gradual con reglas de peso
El despliegue canary permite dirigir un pequeño porcentaje del tráfico hacia la nueva versión de la aplicación sin exponer a todos los usuarios al riesgo. Es la estrategia ideal para probar cambios bajo carga real.
Canary con Kubernetes nativo (sin service mesh)
La forma más sencilla es lanzar un canary-deployment con pocas réplicas junto al stable-deployment. Kubernetes balancea el tráfico entre los pods de forma proporcional a su cantidad.
# Stable Deployment: 9 réplicas = ~90% del tráfico
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-stable
namespace: production
spec:
replicas: 9
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
track: stable
spec:
containers:
- name: my-app
image: my-registry/my-app:v1
---
# Canary Deployment: 1 réplica = ~10% del tráfico
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-canary
namespace: production
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
track: canary
spec:
containers:
- name: my-app
image: my-registry/my-app:v2
El Service selecciona pods únicamente por la etiqueta app: my-app, abarcando ambos despliegues.
Canary mediante Ingress con anotaciones de peso (NGINX)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-canary-ingress
namespace: production
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
rules:
- host: my-app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app-canary-svc
port:
number: 80
La anotación canary-weight: "10" dirige el 10% del tráfico al canary. El valor se incrementa gradualmente — 10%, 25%, 50%, 100% — a medida que crece la confianza en la estabilidad de la nueva versión.
Canary con Argo Rollouts
Para una gestión avanzada del despliegue canary, se recomienda usar Argo Rollouts — una extensión de Kubernetes que añade el tipo de recurso Rollout con soporte nativo para canary, blue-green y análisis de métricas.
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: my-app
namespace: production
spec:
replicas: 10
strategy:
canary:
steps:
- setWeight: 10
- pause: {duration: 5m}
- setWeight: 30
- pause: {duration: 10m}
- setWeight: 60
- pause: {duration: 10m}
- setWeight: 100
analysis:
templates:
- templateName: success-rate
startingStep: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: my-registry/my-app:v2
ports:
- containerPort: 8080
Integración de las estrategias de despliegue en el pipeline CI/CD
Las estrategias de despliegue sin interrupciones alcanzan todo su potencial únicamente combinadas con un pipeline CI/CD automatizado. Veamos un flujo típico con GitLab CI como ejemplo:
stages:
- build
- test
- deploy-canary
- promote
- rollback
build:
stage: build
script:
- docker build -t my-registry/my-app:$CI_COMMIT_SHA .
- docker push my-registry/my-app:$CI_COMMIT_SHA
deploy-canary:
stage: deploy-canary
script:
- kubectl set image deployment/my-app-canary
my-app=my-registry/my-app:$CI_COMMIT_SHA -n production
- kubectl rollout status deployment/my-app-canary -n production
environment:
name: production/canary
promote:
stage: promote
when: manual
script:
- kubectl set image deployment/my-app-stable
my-app=my-registry/my-app:$CI_COMMIT_SHA -n production
- kubectl rollout status deployment/my-app-stable -n production
rollback:
stage: rollback
when: manual
script:
- kubectl rollout undo deployment/my-app-stable -n production
- kubectl rollout undo deployment/my-app-canary -n production
Principios clave de integración:
- La etiqueta de la imagen Docker se vincula al commit (
$CI_COMMIT_SHA), lo que garantiza la reproducibilidad. - Cada paso del despliegue se verifica mediante
kubectl rollout status. - El promote (rollout completo) se ejecuta manualmente tras observar las métricas del canary.
- El rollback está disponible como paso independiente del pipeline.
Rollback: automático y manual
Incluso el despliegue más cuidadoso puede salir mal. Kubernetes almacena el historial de revisiones del Deployment, lo que permite revertir rápidamente los cambios.
Rollback manual
# Revertir a la revisión anterior
kubectl rollout undo deployment/my-app -n production
# Revertir a una revisión específica
kubectl rollout undo deployment/my-app --to-revision=3 -n production
# Ver revisiones con detalles
kubectl rollout history deployment/my-app -n production --revision=2
Rollback automático con Argo Rollouts y análisis de métricas
Argo Rollouts admite AnalysisTemplate — una descripción de las condiciones de éxito del despliegue basada en métricas de Prometheus:
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: success-rate
namespace: production
spec:
metrics:
- name: success-rate
interval: 1m
successCondition: result[0] >= 0.95
failureLimit: 3
provider:
prometheus:
address: http://prometheus:9090
query: |
sum(rate(http_requests_total{status!~"5..",app="my-app"}[1m]))
/
sum(rate(http_requests_total{app="my-app"}[1m]))
Si la tasa de éxito cae por debajo del 95% tres veces consecutivas, Argo Rollouts interrumpe automáticamente el despliegue y revierte a la versión anterior.
También es recomendable configurar progressDeadlineSeconds en el Deployment — si el despliegue no finaliza en el tiempo indicado, Kubernetes lo marca como fallido:
spec:
progressDeadlineSeconds: 300
Monitoreo del despliegue: cómo verificar que la actualización fue exitosa
El despliegue zero-downtime es imposible sin observabilidad. Qué debes monitorear:
- Error rate — porcentaje de respuestas 5xx. Un aumento repentino indica un problema.
- Latency (p50, p95, p99) — la degradación de la latencia puede ser señal de una regresión.
- Pod restart count — reinicios frecuentes indican crashes en la aplicación.
- Readiness probe failures — pods que no superan la verificación de disponibilidad.
- Kubernetes events —
kubectl get events -n production --sort-by=.lastTimestamp.
Comandos útiles para monitorear el despliegue en tiempo real
# Estado del despliegue
kubectl rollout status deployment/my-app -n production
# Estado de los pods
kubectl get pods -n production -l app=my-app -w
# Descripción del pod ante problemas
kubectl describe pod <pod-name> -n production
# Logs en tiempo real
kubectl logs -f -l app=my-app -n production --tail=100
# Eventos del namespace
kubectl get events -n production --sort-by=.lastTimestamp
Integra Prometheus + Grafana con dashboards de despliegue. Una buena práctica es anotar automáticamente el dashboard de Grafana en cada despliegue, lo que permite correlacionar visualmente los cambios en las métricas con el momento exacto de la actualización.
Comparación de estrategias de despliegue
Cada estrategia es adecuada para escenarios específicos. A continuación, las características clave:
- Rolling Update: integrado en Kubernetes, mínima sobrecarga de recursos, adecuado para la mayoría de los servicios. Desventaja: durante la actualización coexisten dos versiones simultáneamente, lo que requiere compatibilidad hacia atrás en la API y el esquema de base de datos.
- Blue-Green: cambio y reversión instantáneos, ideal para sistemas críticos. Requiere el doble de recursos durante el despliegue. Más complejo con componentes stateful.
- Canary: la estrategia más segura — el tráfico real prueba la nueva versión en una pequeña fracción de usuarios. Requiere un monitoreo de calidad y herramientas de gestión de pesos (Argo Rollouts, Istio, NGINX). Adecuado para sistemas de alta carga con gran volumen de tráfico.
Recomendaciones para elegir:
- Comienza con Rolling Update — es la opción predeterminada razonable para la mayoría de las aplicaciones.
- Pasa a Blue-Green si necesitas una reversión instantánea garantizada y dispones de recursos suficientes.
- Implementa Canary para servicios de alta carga donde incluso un 1% de errores es crítico y dispones de un sistema de monitoreo maduro.
Conclusión
El despliegue zero-downtime en Kubernetes no es solo una configuración técnica, sino una cultura de ingeniería. El rolling update es el punto de partida adecuado para la mayoría de los equipos. Blue-green aporta confianza en actualizaciones críticas. Canary es la estrategia más madura para sistemas en producción con alta carga.
Cualquiera de estas estrategias funciona de forma confiable únicamente en combinación con readiness/liveness probes correctamente configuradas, un pipeline CI/CD automatizado y un monitoreo completo. Invierte tiempo en configurar estos componentes — te lo agradecerás con noches tranquilas y releases seguros.
El mejor despliegue es aquel que los usuarios no notan.
Tecnologías
Etiquetas
Ruslan Ismailov
Desarrollador Senior Web / Backend. Desarrollador senior web/backend con 9 años de experiencia. Stack: PHP, Laravel, PostgreSQL, Redis, Docker, Kubernetes, REST, microservicios, CI/CD. Más sobre mí →