StackPractices
intermediate Por Mathias Paulenko

Estrategias de Despliegue: Blue-Green, Canary y Rolling

Guía práctica de estrategias de despliegue: rolling, blue-green, canary, shadow y feature flags. Minimiza el riesgo y el tiempo de rollback al liberar a producción.

Introducción

Una estrategia de despliegue es el proceso controlado de llevar código nuevo a producción minimizando el downtime y el coste de equivocarse. Un mal despliegue puede tumbar tu servicio, corromper datos o degradar la experiencia del usuario durante horas — la estrategia que elijas decide cuánto de ese riesgo absorbes y con qué rapidez puedes revertir.

Esta guía compara las cinco estrategias que realmente usarás en producción — recreate, rolling, blue-green, canary y shadow — más los feature flags como complemento del lado del release. Para cada una: cómo funciona, cuándo encaja y dónde duele. Las guías específicas de blue-green, canary y feature flags cubren cada una en profundidad.

Estrategias de Despliegue Comparadas

EstrategiaDowntimeRiesgoRollbackCoste InfraIdeal Para
RecreateParada totalAltoLento1×Dev/test, no críticos
RollingNingunoMedioMedio~1×Stateless, cambios rutinarios
Blue-GreenNingunoBajoInstantáneo2×Rollback instantáneo crítico
CanaryNingunoMuy bajoRápido~1.1×Riesgo alto, releases graduales
ShadowNingunoMuy bajoN/A2×Validar antes de exponer
Feature FlagsNingunoMínimoInstantáneo1×Deploy ≠ release

Prerequisitos: Lo Que Todo Despliegue Sin Downtime Necesita

Toda estrategia excepto recreate asume unas cuantas cosas ya resueltas. Si falta alguna, arréglala primero — ninguna estrategia te salva:

  • Health checks que realmente fallen. Un endpoint /health que devuelve 200 mientras la app no alcanza la base de datos es peor que no tenerlo — el balanceador sigue enviando tráfico a un zombi. Configura liveness probes (“¿está corriendo?”) y readiness probes (“¿puede recibir tráfico?”) por separado.
  • Apagado ordenado. Las instancias deben drenar las peticiones en curso antes de morir, o los rolling updates cortarán peticiones a la mitad. Consulta la receta de graceful shutdown.
  • Observabilidad antes del deploy. Necesitas dashboards y alertas de tasa de error, percentiles de latencia y tu una o dos métricas de negocio — con valores registrados antes de empezar. Comparar “ahora” contra “antes” es la única señal que vale.
  • Cambios compatibles hacia atrás. Cualquier estrategia donde convivan dos versiones exige que ambas funcionen con los mismos contratos: esquemas de API, formatos de mensaje y esquemas de base de datos.
  • Un pipeline automatizado. Si tu deploy es un ssh manual, empieza por la guía de pipelines CI/CD y vuelve después.

Despliegue Recreate

Apagas la versión vieja por completo y luego arrancas la nueva. Nada convive.

Recreate es el kubectl delete y redespliegue de siempre — simple, barato y con downtime real. Todas las peticiones fallan entre el apagado y el arranque de la nueva versión. También garantiza cero desfase de versiones: solo corre una versión en cada momento, así que jamás te preocupas por código viejo leyendo datos nuevos.

Esa última propiedad hace de recreate la elección correcta para un conjunto sorprendente de servicios: sistemas stateful de instancia única donde dos versiones concurrentes son peligrosas de verdad (un job de migración que no debe correr dos veces, un servicio con líder que no puede partirse en dos), workers por lotes donde un reinicio es invisible, y pares de versiones que no comparten nada compatible. Úsalo en dev, staging y cualquier servicio donde 30 segundos de parada en una ventana programada no cuesten nada. Para servicios de cara al usuario con SLA, es una alarma, no una estrategia.

Rolling Deployment

Reemplaza instancias viejas por nuevas de forma gradual, manteniendo ambas vivas durante la transición:

Fases de un rolling deployment — instancias nuevas reemplazan a las viejas gradualmente mientras ambas sirven tráfico
# Rolling update en Kubernetes — la estrategia por defecto de Deployment
kubectl set image deployment/api api=myapp:v2.4.1
kubectl rollout status deployment/api

# Si sale mal
kubectl rollout undo deployment/api

En Kubernetes, maxSurge controla cuántos pods extra se levantan durante el rollout y maxUnavailable cuántos pueden caer. maxSurge: 1, maxUnavailable: 0 garantiza capacidad completa pero necesita margen para el pod extra; maxUnavailable: 1 es más rápido pero reduce la capacidad temporalmente.

Trade-off: durante el rollout, las versiones vieja y nueva conviven y comparten tráfico. Si v2 rompe un contrato de datos, las instancias v1 pueden fallar al leer datos escritos por v2 — las mismas reglas de compatibilidad que en blue-green, solo que de forma continua en vez de en un único cambio. El rollback tampoco es instantáneo: rollout undo repite el mismo proceso gradual hacia atrás, así que un despliegue ya al 80% tarda casi lo mismo en deshacerse. Para flotas pequeñas son segundos; para un servicio de 200 pods es un número real que conviene conocer antes de necesitarlo.

Blue-Green Deployment

Mantén dos entornos idénticos. Uno está vivo (blue), el otro en reposo (green). Despliega en green, pruébalo y cambia el tráfico en el balanceador — al instante, de forma atómica.

Antes:   Usuarios → [Balanceador] → [Blue: v2.4.0 activo]
                                     [Green: v2.4.0 en reposo]

Después: Usuarios → [Balanceador] → [Blue: v2.4.0 en reposo]
                                     [Green: v2.4.1 activo]
# Esquema Terraform: blue-green detrás de un ALB de AWS
# El cambio de tráfico se hace repesando la regla del listener
resource "aws_lb_listener_rule" "app" {
  action {
    type = "forward"
    forward {
      target_group {
        arn    = aws_lb_target_group.blue.arn
        weight = 100
      }
      target_group {
        arn    = aws_lb_target_group.green.arn
        weight = 0   # subir a 100 en el cambio
      }
    }
  }
}

La característica estrella es la velocidad del rollback: devolver el listener a blue tarda segundos y es un cambio puro de enrutado — sin migración de datos ni redespliegue. Eso convierte a blue-green en la única estrategia donde el “rollback” es genuinamente un no-op para el usuario: la versión vieja ya estaba corriendo y caliente.

El coste es pagar el doble de infraestructura — un setup blue-green sobre una flota de $10k/mes cuesta ~$20k. En clústeres grandes esto domina la decisión; muchos equipos solo levantan green durante la ventana de despliegue y lo tiran después, cambiando coste ocioso por complejidad. Un punto medio: autoescalar green de cero a total como parte del deploy y bajar blue a cero tras el periodo de observación — pagas 2× durante minutos u horas en vez de permanentemente.

Una advertencia más que golpea a los servicios stateful: blue-green funciona a nivel de aplicación, no de datos. Si mantienes dos bases de datos (una por color), las escrituras que entran en blue durante el periodo green se pierden al volver atrás. Mantén una base de datos compartida y gestiona los cambios con expand-contract — o no uses blue-green en ese servicio.

Consideraciones de Base de Datos

Blue-green solo funciona limpio cuando ambas versiones operan sobre la misma base de datos:

Tipo de Cambio¿Compatible con Blue-Green?
Añadir columna (nullable)Sí — el código viejo la ignora
Añadir columna (non-nullable)No — el código viejo no puede insertar sin ella
Renombrar columnaNo — el código viejo referencia el nombre antiguo
Eliminar columnaNo — el código viejo puede seguir leyéndola
Añadir índiceSí — ambas versiones se benefician

Regla: blue-green exige cambios de esquema compatibles hacia atrás. Usa el patrón expand-contract: añade la columna nueva (expand), despliega código que lee y escribe ambas, y elimina la antigua (contract) en una release posterior. La receta de evolución de esquemas desarrolla el patrón completo.

Despliegue Canary

Dirige un porcentaje pequeño del tráfico real a la nueva versión, observa las métricas y sube solo si se comporta:

Paso 1: 1%   → [Canary v2.4.1], 99% → [Estable v2.4.0]
Paso 2: 5%   → [Canary v2.4.1], 95% → [Estable v2.4.0]
Paso 3: 25%  → [Canary v2.4.1], 75% → [Estable v2.4.0]
Paso 4: 100% → [Canary v2.4.1 se vuelve estable]

Lo importante no es el porcentaje — es que una release mala solo daña al 1% de usuarios durante cinco minutos en vez del 100% hasta que lo notas. El análisis del canary es la parte difícil: necesitas umbrales definidos antes de desplegar y automatización que actúe sobre ellos. Mirar Grafana mientras pulsas “siguiente etapa” a mano es un canary solo de nombre.

# Flagger: canary automatizado con análisis de métricas y rollback automático
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: api
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api
  service:
    port: 80
  analysis:
    interval: 30s
    threshold: 5
    maxWeight: 50
    stepWeight: 10
    metrics:
      - name: request-success-rate
        thresholdRange:
          min: 99
      - name: request-duration
        thresholdRange:
          max: 500

Si la tasa de error se dispara o la latencia cruza el umbral, Flagger devuelve el 100% del tráfico a la versión estable automáticamente — sin humanos de por medio. Argo Rollouts ofrece el mismo bucle con su CRD Rollout y AnalysisTemplate (ejemplo completo en la sección de escenario).

No necesitas un service mesh para un canary básico: en Kubernetes puro puedes aproximarlo con dos Deployments y enrutado por proporción de réplicas — tosco (el número de pods es un dial de porcentaje impreciso y se rompe con autoscaling), pero sirve para servicios de poca exigencia. Para enrutado real por porcentaje y cabeceras necesitas un mesh (Istio VirtualService, Linkerd) o un ingress con soporte de traffic split (anotaciones de NGINX ingress, target groups ponderados de AWS ALB).

Dónde Se Divide el Tráfico

Canary, blue-green y shadow necesitan una capa capaz de partir o conmutar el tráfico. Dónde viva esa capa determina lo que cada estrategia puede expresar:

  • Balanceador de carga. Target groups ponderados (AWS ALB, backend services de GCP) o cambios por DNS (registros ponderados de Route53). Soporta porcentajes y cambio instantáneo para blue-green; sin segmentación por usuario, y la caché de DNS hace los cambios lentos — mantén los TTL bajos durante las ventanas de despliegue.
  • Ingress / proxy inverso. Anotaciones canary-* de NGINX ingress, HAProxy, Envoy. Enrutado por porcentaje y cabecera (x-canary: true), barato de operar, sin sidecars. Es el punto dulce para la mayoría de equipos.
  • Service mesh. Istio VirtualService, Linkerd traffic split, Kuma. La opción más expresiva: porcentajes, cabeceras, cookies, reintentos, mirroring para shadow. Su coste también es real: un sidecar por pod y un control plane que mantener.
  • Nivel de aplicación. La app decide internamente — que es exactamente lo que son los feature flags, y por qué combinan tan bien con cualquier estrategia de infraestructura: el balanceador te mueve entre versiones, el flag te mueve entre comportamientos dentro de una versión.

Una progresión práctica que siguen la mayoría de equipos: empezar con rolling (integrado, gratis), añadir enrutado ponderado en el ingress o balanceador para blue-green y canary básico, y recurrir al mesh solo cuando el enrutado por cabeceras, el mirroring de shadow o los splits multi-clúster justifiquen el coste operativo.

Despliegue Shadow (Traffic Mirroring)

Envía a la nueva versión una copia del tráfico de producción mientras los usuarios solo ven respuestas de la versión estable. La instancia shadow procesa peticiones reales en paralelo; sus respuestas se descartan.

Shadow es la única estrategia con impacto cero al usuario y la mejor respuesta a “¿funciona de verdad bajo carga real?” — nuevos motores de búsqueda, motores de precios o una reescritura de la que dudas. Normalmente es un paso previo al canary: valida en shadow un día y luego empieza el canary.

Los inconvenientes: cuesta una segunda copia de la infraestructura de producción, jamás debe escribir en estado compartido (un shadow que cobra dos veces a los clientes es un evento de carrera — dirige las escrituras a un stub o a un esquema aparte), y el mirroring de tráfico requiere soporte del mesh o del balanceador — VirtualService mirroring de Istio, Envoy, o un setup de traffic mirroring en el upstream. Ten en cuenta también que el tráfico shadow duplica la carga de lectura sobre la base de datos: dimensiona para ello o reproduce el tráfico a ritmo reducido en servicios de muchas lecturas.

Feature Flags (Desacoplar Deploy de Release)

Despliega el código a producción pero mantenlo oculto tras un flag. Actívalo para usuarios o porcentajes concretos cuando estés listo — de forma independiente a cualquier despliegue.

# Feature flag estilo LaunchDarkly
if client.variation("new-checkout-flow", user_context, False):
    return new_checkout.handle(request)
return old_checkout.handle(request)
Usa Feature Flags ParaNO Uses Feature Flags Para
Nuevas capacidades de UIParches de seguridad (no deberían ser apagables)
Tests A/BParches de bugs críticos
Liberaciones graduales de funcionalidadCódigo de migración de datos
Kill switches de funciones arriesgadasUn estilo de vida de ramas eternas

Los flags complementan, no sustituyen, a las estrategias de despliegue. Un combo habitual en producción: canary en el deploy para la seguridad de infraestructura y flag sobre la nueva función para control del lado de producto — el rollback de la función es un clic de toggle, sin redespliegue.

La higiene de flags es una disciplina propia. Dale a cada flag un dueño y una fecha de caducidad al crearlo; el flag que “quitamos el próximo sprint” sigue ahí dos años después, sin documentar y con otros tres flags anidados dentro. Un inventario de flags que crece para siempre convierte el código en una matriz combinatoria de tests que nadie puede razonar — cada flag que queda atrás es una rama que solo se ejercita en uno de sus estados.

Cómo Elegir una Estrategia

Flujo de decisión para elegir estrategia de despliegue según tolerancia a downtime, velocidad de rollback y riesgo del cambio

Las preguntas, en orden:

  1. ¿Puedes tolerar downtime? Si sí, recreate — no sobreingenierices.
  2. ¿Necesitas rollback instantáneo? Blue-green. Paga el 2× de infra o acepta un rollback más lento.
  3. ¿El cambio es arriesgado? Refactors, lógica nueva, cualquier cosa que toque rutas críticas (pagos, auth) → canary.
  4. ¿El comportamiento bajo carga real no está probado? Shadow antes del canary.
  5. En cualquier otro caso, rolling — es el default correcto para cambios stateless rutinarios.

Y por tipo de cambio, en concreto:

CambioEstrategiaDuración
Corrección de typoRolling2 min
Actualización de dependenciasRolling5 min
Nueva funcionalidad de UIBlue-green10 min
Refactor de lógicaCanary 5→20→50→10030 min
Cambio de esquema de BDExpand-contractMulti-deploy
Migración de infraShadow + canary1-2 horas

Las estrategias también se componen. Los equipos maduros no eligen una — las apilan: un rolling update lleva el código, los feature flags ocultan el comportamiento nuevo, y el encendido gradual del flag actúa como canary funcional. La pregunta deja de ser “qué estrategia” y pasa a ser “qué capa carga cada tipo de riesgo” — el riesgo de infraestructura va a la estrategia de despliegue, el de producto al flag.

Estrategia de Rollback

Cada estrategia de arriba es en realidad una estrategia de rollback disfrazada de despliegue — la estrategia determina con qué rapidez deshaces, no si las cosas saldrán mal. Unas reglas que valen para todas:

  • El rollback debe ser automático. Un humano haciendo SSH a las 3 AM para revertir es poco fiable por definición. Flagger y Argo Rollouts revierten al romperse una métrica; kubectl rollout undo debe ser un comando, no un procedimiento.
  • Practícalo. Un rollback que nunca has ejecutado es una hipótesis. Ejecútalo cada trimestre en staging — idealmente, rompe un deploy a propósito para probar que el bucle funciona.
  • No puedes des-escribir una escritura de base de datos. El código vuelve atrás; las escrituras no. Si v2 escribió filas que v1 no sabe leer, revertir el código no las recupera. Esta es la razón real de existir de expand-contract: cada estado intermedio del esquema es legible por ambas versiones.
  • Mantén disponible la versión anterior. Blue-green conserva todo el entorno viejo caliente; canary mantiene a la estable sirviendo tráfico. No desmontes la versión previa hasta que la nueva sobreviva un ciclo completo de tráfico.
  • Define “éxito” antes de desplegar. Escribe los criterios de aborto (tasa de error, latencia, métricas de negocio) en el pipeline — decidir qué cuenta como fallo durante un incidente garantiza elegir la respuesta equivocada.

Métricas a Vigilar Durante el Despliegue

MétricaUmbral CanaryAcción al Brecharse
Tasa de error< 0.1% sobre baselineRollback del canary
Latencia p99< baseline + 20%Rollback del canary
ThroughputSin caída > 10%Rollback del canary
Métrica de negocio (registros, checkouts)Sin caídaRollback del canary

Los baselines importan más que los números. Si tu tasa de error ya corre al 0.05%, un umbral del 0.1% es demasiado estricto; fíjalo en relación a las últimas 24h de tráfico sano. Vigila también el denominador: un canary al 1% puede mostrar “0 errores” mientras el modo de fallo real solo se dispara con patrones que la muestra del canary no incluía — jobs nocturnos por lotes, clientes móviles, un tenant concreto.

Un detalle operativo más: los despliegues mueven métricas por motivos ajenos al código nuevo. Los reinicios de pods vacían cachés calientes (la latencia salta), los IDs de instancia nuevos confunden dashboards por instancia, y maxUnavailable baja la capacidad brevemente (el throughput se hunde). Anota los despliegues en tus dashboards o perseguirás fantasmas en cada rollout.

Lo Que Funciona

  • Automatiza el rollback — un humano pulsando un botón a las 3 AM no es fiable. Ver pipelines CI/CD.
  • Usa tráfico sintético — golpea el canary con tests automatizados antes que los usuarios reales.
  • Despliegues pequeños — los cambios chicos se depuran más fácil y se revierten más rápido.
  • Un cambio a la vez — no combines un deploy con una migración de base de datos y un cambio de configuración.
  • Prueba el rollback — un rollback que nunca has practicado es una apuesta.

Errores Comunes y Solución de Problemas

  • Desplegar el viernes por la tarde — pasarás el fin de semana depurando.
  • Sin rollback automático — las reversiones manuales son más lentas y propensas a errores, sobre todo bajo presión.
  • Combinar varios cambios en un deploy — un deploy más una migración de esquema más un cambio de config, y cuando se rompe no sabes cuál de los tres causó el problema.
  • Ignorar las métricas del canary porque “los tests pasaron” — el tráfico de producción es la única prueba real.
  • Olvidar la compatibilidad de base de datos en blue-green — el código viejo y el nuevo deben convivir durante el cambio.
  • El despliegue se revierte una y otra vez — revisa health checks, límites de recursos y startup probes; un readiness probe que falla es la causa habitual de reinicios en cadena.
  • El contenedor muere al arrancar — verifica variables de entorno, secrets y config montada; lee las primeras 50 líneas de logs antes de escalar réplicas.

Escenario: Pipeline de Despliegue para E-commerce

Un servicio de pagos en Kubernetes usando Argo Rollouts (el CRD Rollout del proyecto Argo Rollouts — distinto del Application de Argo CD):

# Argo Rollouts: despliegue canary con análisis automatizado
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: payment-service
spec:
  replicas: 10
  strategy:
    canary:
      steps:
        - setWeight: 5        # 5% del tráfico a v2
        - pause: { duration: 5m }
        - setWeight: 20
        - pause: { duration: 10m }
        - setWeight: 50
        - analysis:           # análisis automático
            templates:
            - templateName: success-rate
            args:
            - name: service-name
              value: payment-service
        - setWeight: 100
  selector:
    matchLabels: { app: payment-service }
  template:
    spec:
      containers:
        - name: payment
          image: registry.example.com/payment:v2.1.0
          resources:
            requests: { cpu: 200m, memory: 256Mi }
            limits: { cpu: 500m, memory: 512Mi }
---
# AnalysisTemplate: criterios de éxito
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: success-rate
spec:
  args:
    - name: service-name
  metrics:
    - name: success-rate
      interval: 1m
      successCondition: result[0] >= 0.99
      failureLimit: 3
      provider:
        prometheus:
          address: http://prometheus:9090
          query: |
            sum(rate(http_requests_total{
              service="{{args.service-name}}",
              status!~"5.."}[2m]))
            / sum(rate(http_requests_total{
              service="{{args.service-name}}"}[2m]))

Aborto automático: tasa de error por encima del 1% durante 2 minutos, p99 por encima de 2× el baseline, o el análisis fallando 3 veces → el rollout revierte el peso de tráfico. Lecciones de correr esto en producción: el canary con análisis automático le gana al manual siempre; define los criterios de éxito antes de desplegar; expand-contract para cambios de esquema no es opcional.

Lectura Adicional

Preguntas frecuentes

¿Debería cada deploy usar canary?

No. Los cambios de bajo riesgo (actualizaciones de dependencias, typos) pueden usar rolling. Reserva el canary para funcionalidad de cara al usuario, refactors arriesgados y cambios que tocan rutas críticas como pagos o autenticación — la sobrecarga del análisis no es gratis.

¿Blue-green o canary — cuál es más seguro?

Canary es más seguro frente a bugs desconocidos (el radio de explosión queda limitado al porcentaje que enrutas), mientras que blue-green revierte más rápido una vez detectas el problema (segundos contra minutos). Para cambios de verdad arriesgados, shadow primero y canary después. En servicios donde domina la velocidad de rollback — pasarelas de pago, auth — gana blue-green.

¿Cuánto debe durar un canary?

Hasta tener confianza estadística. En servicios de mucho tráfico pueden bastar 15-30 minutos. En los de poco tráfico, horas o un ciclo de negocio completo. Usa error budgets y SLOs para definir "terminado".

¿Y si hay que cambiar el esquema de la base de datos?

Usa el patrón expand-contract. Paso 1: despliega el cambio de esquema (añade columna nueva, conserva la vieja). Paso 2: despliega código que escribe en ambas. Paso 3: rellena datos (backfill). Paso 4: despliega código que solo lee la nueva. Paso 5: elimina la columna vieja. Son 4-5 despliegues y te dan downtime cero.

¿Cómo manejo los despliegues multi-región?

Despliega secuencialmente, no a la vez: primero us-east, luego eu-west y después ap-southeast, con una pausa entre regiones para pillar problemas regionales. Un balanceador global (Route53, Cloudflare) gestiona los health checks, y si una región falla las demás ya están en la versión nueva.

¿Cuál es la opción sin downtime más barata?

Rolling. No necesita infraestructura extra — solo health checks y apagado ordenado. Blue-green dobla tu factura, canary añade complejidad de tooling, y shadow también la dobla. Si 30 segundos de parada son asumibles, recreate es la opción barata honesta.