Ir para o conteúdo
Infra

Estratégias de Deploy: Blue-Green, Canary e Rolling Updates na Prática

Marcos Soares
Atualizado em 
13 minutos de leitura
Ilustracao 3D de tres estruturas de vidro translucido representando estrategias de deploy blue-green canary e rolling
Ouça este artigo
0:00Estratégias de Deploy: Blue-Green, Canary e Rolling Updates na Prática--:--

Conteúdo técnico toda semana

Receba artigos sobre arquitetura, padrões de projeto e engenharia de software. Direto no seu e-mail, sem enrolação.

Sem spam. Cancele a qualquer momento com 1 clique.

Neste artigo

Um deploy que derruba 100% dos usuários por 30 segundos custa diferente de um que derruba 1% por 5 minutos. A diferença entre essas duas situações não é sorte: é estratégia de deploy.

Blue-Green, Canary e Rolling Updates resolvem o mesmo problema (colocar código novo em produção sem destruir a experiência do usuário), mas cada uma faz trade-offs diferentes entre complexidade de infra, velocidade de rollback e custo de recursos. Escolher errado não significa que o deploy vai falhar. Significa que você vai gastar infra demais, ou descobrir bugs tarde demais, ou levar minutos para reverter o que poderia levar segundos.

Como cada estratégia funciona por baixo

Antes de comparar, a mecânica precisa estar clara.

Blue-Green mantém dois ambientes idênticos (blue e green). Apenas um recebe tráfego de produção. O deploy acontece no ambiente ocioso. Quando os health checks passam, o roteador (load balancer, DNS, ingress) troca o ponteiro de tráfego de blue para green. Rollback é trocar o ponteiro de volta.

Canary direciona uma fração pequena do tráfego (1%, 5%, 10%) para a versão nova. Métricas de erro, latência e saturação são observadas. Se estiverem saudáveis, o percentual sobe progressivamente até 100%. Se degradarem, o tráfego volta para a versão anterior.

Rolling Update substitui instâncias da versão antiga pela nova em lotes. Se você tem 10 pods, atualiza 2 por vez. Durante o processo, versões antiga e nova coexistem. O Kubernetes usa essa estratégia como padrão.

Text
Blue-Green:
  [LB] ──► [Blue v1] ✓ tráfego ativo
           [Green v2]   ocioso, recebendo deploy
  (troca)
  [LB] ──► [Green v2] ✓ tráfego ativo
           [Blue v1]   ocioso, pronto para rollback
 
Canary:
  [LB] ──► 95% [v1]
       ──►  5% [v2]  ← observando métricas
  (progressão)
  [LB] ──► 50% [v1]
       ──► 50% [v2]
  (fim)
  [LB] ──► 100% [v2]
 
Rolling Update:
  [pod1-v1] [pod2-v1] [pod3-v1] [pod4-v1]
  [pod1-v2] [pod2-v2] [pod3-v1] [pod4-v1]  ← 2 atualizados
  [pod1-v2] [pod2-v2] [pod3-v2] [pod4-v2]  ← todos atualizados

Tabela comparativa: critérios que importam na decisão

CritérioBlue-GreenCanaryRolling Update
Velocidade de rollbackSegundos (troca de ponteiro)Segundos (redireciona tráfego)Minutos (precisa recriar pods antigos)
Custo de infraAlto (ambiente duplicado 100%)Médio (poucos pods extras)Baixo (reutiliza os mesmos recursos)
Risco de exposição a bugsTudo ou nada: 0% ou 100%Gradual: 1% → 5% → 25% → 100%Parcial: depende do tamanho do lote
Complexidade de configuraçãoMédia (dois ambientes + roteamento)Alta (observabilidade + regras de peso)Baixa (padrão do Kubernetes)
Coexistência de versõesNão (uma versão recebe tráfego)Sim (duas versões simultâneas)Sim (duas versões simultâneas)
Compatibilidade de bancoPrecisa de migrations compatíveis com ambasPrecisa de migrations compatíveis com ambasPrecisa de migrations compatíveis com ambas
Melhor paraAplicações com rollback crítico (fintech, saúde)Validação gradual com métricas (SaaS, e-commerce)Workloads stateless com baixo risco (APIs internas)

Rolling Update com Kubernetes: a configuração padrão que você já usa

O Kubernetes aplica Rolling Update por padrão. Mas o padrão sem ajuste é perigoso: maxUnavailable: 25% e maxSurge: 25% significam que, em um deployment com 4 réplicas, 1 pod fica indisponível e 1 pod extra é criado simultaneamente. Em serviços com poucos pods, isso pode derrubar 25% da capacidade durante o deploy.

YAML
# deployment-rolling.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-pedidos
  namespace: production
spec:
  replicas: 6
  strategy:
    type: RollingUpdate
    rollingUpdate:
      # maxUnavailable 0 garante que nenhum pod sai antes do novo estar Ready
      maxUnavailable: 0
      # maxSurge 2 cria no máximo 2 pods extras durante o rollout
      maxSurge: 2
  selector:
    matchLabels:
      app: api-pedidos
  template:
    metadata:
      labels:
        app: api-pedidos
        version: v2.3.1
    spec:
      containers:
        - name: api-pedidos
          image: registry.example.com/api-pedidos:2.3.1
          ports:
            - containerPort: 3000
          # readinessProbe evita que tráfego chegue antes do app estar pronto
          readinessProbe:
            httpGet:
              path: /health/ready
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 10
            failureThreshold: 3
          # livenessProbe reinicia pods travados, mas com threshold alto
          # para não matar pods em GC pause
          livenessProbe:
            httpGet:
              path: /health/live
              port: 3000
            initialDelaySeconds: 15
            periodSeconds: 20
            failureThreshold: 5
          resources:
            requests:
              cpu: "250m"
              memory: "256Mi"
            limits:
              cpu: "500m"
              memory: "512Mi"

O maxUnavailable: 0 é a configuração que faz diferença real. Sem ele, o Kubernetes derruba pods antigos antes dos novos estarem prontos, e o serviço perde capacidade durante o deploy.

Para monitorar o progresso:

Bash
# Acompanha o rollout em tempo real
kubectl rollout status deployment/api-pedidos -n production --timeout=300s
 
# Se algo der errado, reverte para a revisão anterior
kubectl rollout undo deployment/api-pedidos -n production
 
# Verifica o histórico de revisões (útil para saber qual versão reverter)
kubectl rollout history deployment/api-pedidos -n production

Blue-Green com Kubernetes Services

Blue-Green no Kubernetes não exige ferramentas extras. Dois Deployments (blue e green) e um Service que alterna o selector entre eles.

YAML
# deployment-blue.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-pedidos-blue
  namespace: production
spec:
  replicas: 4
  selector:
    matchLabels:
      app: api-pedidos
      slot: blue
  template:
    metadata:
      labels:
        app: api-pedidos
        slot: blue
        version: v2.3.0
    spec:
      containers:
        - name: api-pedidos
          image: registry.example.com/api-pedidos:2.3.0
          ports:
            - containerPort: 3000
YAML
# deployment-green.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-pedidos-green
  namespace: production
spec:
  replicas: 4
  selector:
    matchLabels:
      app: api-pedidos
      slot: green
  template:
    metadata:
      labels:
        app: api-pedidos
        slot: green
        version: v2.3.1
    spec:
      containers:
        - name: api-pedidos
          image: registry.example.com/api-pedidos:2.3.1
          ports:
            - containerPort: 3000
YAML
# service-production.yaml
apiVersion: v1
kind: Service
metadata:
  name: api-pedidos
  namespace: production
spec:
  selector:
    app: api-pedidos
    # Troca entre blue e green para alternar o tráfego
    slot: blue
  ports:
    - port: 80
      targetPort: 3000

A troca acontece com um único comando:

Bash
# Alterna tráfego de blue para green editando o selector do Service
kubectl patch service api-pedidos -n production \
  -p '{"spec":{"selector":{"slot":"green"}}}'
 
# Rollback: volta para blue
kubectl patch service api-pedidos -n production \
  -p '{"spec":{"selector":{"slot":"blue"}}}'

O rollback leva o tempo de propagação do kube-proxy: normalmente menos de 2 segundos. Essa é a maior vantagem do Blue-Green. O custo é manter o dobro de pods rodando durante (e entre) os deploys. Se sua aplicação usa 8 pods com 512Mi cada, são 4Gi extras de memória alocados permanentemente.

Canary com Nginx Ingress e pesos de tráfego

Canary requer um mecanismo de split de tráfego. O Nginx Ingress Controller suporta isso nativamente via annotations, sem precisar de service mesh.

YAML
# ingress-stable.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-pedidos-stable
  namespace: production
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: api-pedidos-stable
                port:
                  number: 80
YAML
# ingress-canary.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-pedidos-canary
  namespace: production
  annotations:
    kubernetes.io/ingress.class: nginx
    # nginx.ingress.kubernetes.io/canary ativa o modo canary
    nginx.ingress.kubernetes.io/canary: "true"
    # Peso define o percentual de tráfego para o canary (5 = 5%)
    nginx.ingress.kubernetes.io/canary-weight: "5"
spec:
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: api-pedidos-canary
                port:
                  number: 80

A progressão do canary acontece alterando o canary-weight:

Bash
# Sobe para 25% após validar métricas
kubectl annotate ingress api-pedidos-canary -n production \
  nginx.ingress.kubernetes.io/canary-weight="25" --overwrite
 
# Sobe para 50%
kubectl annotate ingress api-pedidos-canary -n production \
  nginx.ingress.kubernetes.io/canary-weight="50" --overwrite
 
# Promoção completa: remove o canary e atualiza o stable
kubectl annotate ingress api-pedidos-canary -n production \
  nginx.ingress.kubernetes.io/canary-weight="100" --overwrite

Sem observabilidade, Canary é Rolling Update com passos extras. A progressão precisa ser guiada por métricas reais. Se você não tem dashboards de taxa de erro e latência p99 por versão, Canary não vai te dar mais segurança que Rolling Update. Ferramentas como Argo Rollouts automatizam essa progressão com análise de métricas do Prometheus, mas a configuração manual com Nginx Ingress funciona para times que preferem controle explícito.

O que NÃO fazer

Anti-pattern 1: Blue-Green sem health check antes da troca

Bash
# ERRADO: troca o tráfego imediatamente após o deploy
kubectl apply -f deployment-green.yaml
kubectl patch service api-pedidos -n production \
  -p '{"spec":{"selector":{"slot":"green"}}}'

O deploy do green pode levar 30 segundos para os pods ficarem Ready. Se você troca o Service antes disso, o tráfego vai para pods que ainda estão inicializando. A versão correta:

Bash
# CORRETO: espera todos os pods do green ficarem Ready antes de trocar
kubectl apply -f deployment-green.yaml
 
# rollout status bloqueia até que todos os pods passem no readinessProbe
kubectl rollout status deployment/api-pedidos-green -n production --timeout=120s
 
# Só troca o tráfego depois da confirmação
kubectl patch service api-pedidos -n production \
  -p '{"spec":{"selector":{"slot":"green"}}}'

Anti-pattern 2: Rolling Update com maxUnavailable alto e sem readinessProbe

Sem readinessProbe, o Kubernetes considera o pod Ready assim que o container inicia. Se a aplicação leva 10 segundos para carregar configurações, conectar ao banco e aquecer caches, o tráfego chega antes do app estar pronto. Combinado com maxUnavailable: 25%, o resultado é uma janela onde 25% das requisições falham.

Anti-pattern 3: Canary sem métricas por versão

Se o seu sistema de monitoramento agrega métricas de todas as versões juntas, você não consegue distinguir se o aumento de erros vem do canary (5% do tráfego) ou do stable (95%). Labels de versão nos seus logs e métricas são pré-requisito, não luxo.

Quando usar cada uma: matriz de decisão

Use Rolling Update quando:

  • O serviço é stateless e tolera coexistência de versões
  • O time não tem infraestrutura de observabilidade por versão
  • O custo de manter pods extras é relevante (clusters pequenos)
  • O risco do deploy é baixo (mudanças incrementais, boa cobertura de testes)

Use Blue-Green quando:

  • Rollback em segundos é requisito de negócio (pagamentos, saúde)
  • O orçamento de infra comporta o dobro de recursos
  • As mudanças são grandes e você quer testar o ambiente completo antes de expor tráfego
  • A aplicação não tolera coexistência de versões (breaking changes no protocolo)

Use Canary quando:

  • Você tem observabilidade com métricas segmentadas por versão
  • O volume de tráfego é suficiente para gerar dados estatísticos no canary (5% de 100 req/min são 5 req/min, pouco para detectar regressão de latência)
  • A mudança tem risco médio-alto e você quer validar em produção com blast radius controlado
  • O time tem maturidade para definir critérios de promoção e rollback automatizados

Se o seu serviço recebe menos de 1000 requisições por minuto, Canary com 5% significa 50 requisições por minuto na versão nova. Dependendo da variância natural da sua latência, você precisa de 10 a 15 minutos nesse percentual para ter dados confiáveis. Considere isso no tempo total de deploy.

Migrations de banco: o problema que todas compartilham

As três estratégias sofrem do mesmo problema: durante o deploy, duas versões da aplicação acessam o mesmo banco. Se a versão nova espera uma coluna que não existe, ou a versão antiga não sabe lidar com uma coluna removida, o deploy quebra independente da estratégia.

A solução é a mesma para as três: migrations compatíveis com ambas as versões. Adicione colunas antes do deploy, remova colunas depois. Renomeie em dois passos (cria nova, copia dados, remove antiga). Se você usa Prisma, o post sobre migrations seguras em produção detalha esse fluxo.

Essa restrição de compatibilidade é mais severa no Blue-Green, porque as duas versões podem coexistir por horas (enquanto o ambiente antigo fica de standby). No Rolling Update, a coexistência dura minutos. No Canary, pode durar horas se a progressão for lenta.

Integração com o pipeline de CI/CD

Nenhuma dessas estratégias funciona isolada. Elas são o último estágio de um pipeline que começa com testes, passa por build de imagem e termina no deploy. Se você está containerizando com Docker, a imagem precisa ser imutável e tagueada com a versão exata (nunca latest em produção). Se o projeto é um monorepo com Turborepo, o pipeline precisa identificar quais serviços mudaram para fazer deploy seletivo.

Feature flags complementam Canary de forma poderosa: o deploy sobe o código novo para 100% dos pods, mas a funcionalidade nova fica desligada até ser ativada para um percentual de usuários. Isso separa deploy de release, que é uma distinção que reduz risco drasticamente. Se você quer implementar isso sem depender de SaaS, o post sobre feature flags com TypeScript cobre o tema.

Para APIs que precisam manter compatibilidade durante deploys graduais, padrões como versionamento de API e validação de payload ajudam a evitar quebras. O post sobre API Gateway patterns discute roteamento por versão, que se conecta diretamente com Canary baseado em header.

FAQ

Posso combinar Blue-Green com Canary? Sim, e isso é comum em organizações maiores. O fluxo: deploy no ambiente green, direciona 5% do tráfego via Canary para o green, progride até 100%, e o blue vira o ambiente de standby. Argo Rollouts suporta esse modelo nativamente com a estratégia blueGreen combinada com analysis.

Rolling Update funciona com aplicações stateful? Funciona, mas com cuidados extras. StatefulSets no Kubernetes fazem rolling update respeitando a ordem dos pods (do último para o primeiro). Se a aplicação depende de eleição de líder ou replicação, o podManagementPolicy e o updateStrategy.partition precisam ser configurados para evitar split-brain.

Qual o mínimo de infraestrutura para começar com Canary? Nginx Ingress Controller com annotations de canary. Não precisa de service mesh. Mas precisa de métricas por versão: no mínimo, taxa de erro HTTP 5xx e latência p95 segmentadas por label de versão no Prometheus ou equivalente. Sem isso, você está fazendo Canary no escuro.

Blue-Green dobra meu custo de infra permanentemente? Depende de como você opera. Se o ambiente ocioso fica ligado 24/7 esperando o próximo deploy, sim. Se você escala o ambiente ocioso para zero réplicas entre deploys e sobe sob demanda antes do deploy, o custo extra é proporcional ao tempo de deploy. Em clusters com autoscaler, essa abordagem é viável.

Qual estratégia é melhor para deploy de frontend (SPA, Next.js)? Blue-Green funciona bem para frontends porque a troca é atômica: o CDN ou o deploy na edge aponta para o novo bundle de uma vez. Rolling Update em frontend não faz sentido porque não existem "pods" de frontend rodando. Canary em frontend é possível via split de tráfego no CDN, mas a granularidade é limitada e o cache do navegador complica a segmentação.

Posição técnica

Rolling Update é o ponto de partida correto para a maioria dos serviços. Não porque é o melhor, mas porque é o que o Kubernetes já faz, exige zero infraestrutura extra e, com maxUnavailable: 0 e readinessProbe bem configurado, oferece deploys sem downtime. Migrar para Canary só faz sentido quando você já tem observabilidade por versão funcionando. Migrar para Blue-Green só faz sentido quando o custo de um rollback lento é maior que o custo de manter infraestrutura duplicada. Escolha a estratégia pelo problema que você precisa resolver, não pela que parece mais sofisticada no diagrama de arquitetura.

Marcos Soares

Escrito por

Marcos Soares

Fullstack Developer · CEO da Agência Poti

Fullstack Developer e CEO da Agência Poti. Mais de 20 anos construindo arquiteturas cloud-native com React, Next.js e sistemas distribuídos. Parceiro comercial do estúdio iellou design. Fundador do Vivo de Código.

Comentários

Participe da discussão

Seja o primeiro a comentar!

Continue Aprofundando

Conteúdo técnico toda semana

Receba artigos sobre arquitetura, padrões de projeto e engenharia de software. Direto no seu e-mail, sem enrolação.

Sem spam. Cancele a qualquer momento com 1 clique.