Blue-green deployments no Kubernetes: estratégias sem downtime

1. Fundamentos do Blue-green deployment

O conceito de blue-green deployment é uma estratégia de implantação que mantém dois ambientes de produção idênticos, chamados de "blue" e "green". Em qualquer momento, apenas um deles está recebendo tráfego real de produção. Quando uma nova versão precisa ser implantada, ela é deployada no ambiente inativo, e após validação, o tráfego é instantaneamente redirecionado para ele.

Os objetivos principais são:
- Zero downtime: a troca de tráfego é instantânea, sem janelas de indisponibilidade
- Rollback imediato: basta reverter o tráfego para o ambiente anterior
- Isolamento de versões: cada versão roda em seu próprio conjunto de pods, sem interferência

Diferentemente do Rolling update (que substitui pods gradualmente) e do Canary release (que envia uma fração do tráfego para a nova versão), o blue-green oferece uma troca binária e completa. É ideal para cenários onde a consistência da experiência do usuário é crítica.

2. Arquitetura no Kubernetes: Services, Ingress e Labels

No Kubernetes, a implementação blue-green se apoia em três pilares:

Service com seletor de labels: o Service é configurado para apontar para um conjunto de pods através de labels. Alternar entre blue e green significa simplesmente mudar o selector do Service.

Ingress e Gateway API: para roteamento mais sofisticado, o Ingress pode direcionar tráfego para diferentes Services baseados em pesos ou cabeçalhos HTTP.

Labels e annotations: usamos labels como version: blue e version: green para identificar cada deployment. Annotations podem armazenar metadados como hash da imagem ou data do deploy.

3. Implementação prática com Deployments e Services

Vamos criar dois Deployments e um Service que alterna entre eles. Primeiro, o Deployment blue (versão atual):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-blue
  labels:
    app: minha-app
    version: blue
spec:
  replicas: 3
  selector:
    matchLabels:
      app: minha-app
      version: blue
  template:
    metadata:
      labels:
        app: minha-app
        version: blue
    spec:
      containers:
      - name: app
        image: minha-app:1.0.0
        ports:
        - containerPort: 8080
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 15
          periodSeconds: 20

Agora o Deployment green (nova versão):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-green
  labels:
    app: minha-app
    version: green
spec:
  replicas: 3
  selector:
    matchLabels:
      app: minha-app
      version: green
  template:
    metadata:
      labels:
        app: minha-app
        version: green
    spec:
      containers:
      - name: app
        image: minha-app:2.0.0
        ports:
        - containerPort: 8080
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 15
          periodSeconds: 20

E o Service que inicialmente aponta para o blue:

apiVersion: v1
kind: Service
metadata:
  name: minha-app-service
spec:
  selector:
    app: minha-app
    version: blue
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080

4. Estratégias de troca de tráfego (switch)

Troca manual com kubectl patch:

kubectl patch service minha-app-service -p '{"spec":{"selector":{"version":"green"}}}'

Este comando altera o seletor do Service instantaneamente. Todos os novos requests passam a ser roteados para os pods green.

Troca automatizada com script e validação:

#!/bin/bash
# Valida se o deployment green está saudável
kubectl rollout status deployment/app-green --timeout=120s
if [ $? -eq 0 ]; then
  # Troca o tráfego
  kubectl patch service minha-app-service -p '{"spec":{"selector":{"version":"green"}}}'
  echo "Tráfego redirecionado para green"
else
  echo "Deployment green não está saudável. Abortando."
  exit 1
fi

Uso de probes: o readinessProbe garante que apenas pods prontos recebam tráfego. Durante a troca, o Kubernetes só considera pods com status Ready. Isso evita que requests sejam enviados para pods que ainda estão inicializando.

5. Blue-green com Ingress Controller (NGINX, Traefik)

Com o Ingress NGINX, podemos usar annotations para canary releases:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minha-app-ingress
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "0"
spec:
  rules:
  - host: app.exemplo.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: minha-app-service-green
            port:
              number: 80

Para alternar totalmente, removemos a annotation canary do ingress green e a adicionamos no ingress blue, invertendo os pesos.

Roteamento por cabeçalhos HTTP para testes controlados:

nginx.ingress.kubernetes.io/canary-by-header: "X-Canary"
nginx.ingress.kubernetes.io/canary-by-header-value: "green-test"

Isso permite que apenas requests com o header X-Canary: green-test sejam roteados para o ambiente green.

6. Rollback e gerenciamento de estado

Rollback imediato é tão simples quanto reverter o seletor:

kubectl patch service minha-app-service -p '{"spec":{"selector":{"version":"blue"}}}'

Para aplicações stateful (bancos, filas), o blue-green exige cuidado extra:
- Use bancos de dados separados para cada ambiente, com replicação assíncrona
- Ou utilize volumes compartilhados com locks distribuídos (ex: ZooKeeper, etcd)
- Ferramentas como Helm facilitam o gerenciamento: cada release helm pode representar um ambiente

ArgoCD pode gerenciar blue-green automaticamente com syncPolicy e automated:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: minha-app
spec:
  destination:
    namespace: production
  source:
    repoURL: https://git.exemplo.com/repo
    path: k8s/blue-green
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

7. Monitoramento e validação pós-deploy

Métricas chave com Prometheus + Grafana:
- Latência (p50, p95, p99)
- Taxa de erro HTTP (5xx, 4xx)
- CPU e memória dos pods
- Número de pods Ready vs Total

Testes automatizados com k6 durante a troca:

import http from 'k6/http';
import { check, sleep } from 'k6';

export default function () {
  const res = http.get('http://app.exemplo.com/api/health');
  check(res, {
    'status é 200': (r) => r.status === 200,
    'tempo de resposta < 500ms': (r) => r.timings.duration < 500,
  });
  sleep(1);
}

Alertas para rollback automático: integre Prometheus Alertmanager com um webhook que executa kubectl patch para reverter o tráfego se a taxa de erro exceder 1% nos primeiros 5 minutos após o deploy.

8. Integração com temas vizinhos da série

Compliance as code: use OPA/Gatekeeper para garantir que todo deployment blue-green tenha probes configuradas:

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredProbes
metadata:
  name: require-readiness-probe
spec:
  match:
    kinds:
      - apiGroups: ["apps"]
        kinds: ["Deployment"]
    namespaces: ["production"]
  parameters:
    probes: ["readinessProbe", "livenessProbe"]

Chaos engineering: LitmusChaos pode injetar falhas durante a troca para testar a resiliência:

apiVersion: litmuschaos.io/v1alpha1
kind: ChaosEngine
metadata:
  name: pod-delete-during-switch
spec:
  appinfo:
    appns: production
    applabel: "app=minha-app"
    appkind: deployment
  experiments:
  - name: pod-delete
    spec:
      components:
        env:
        - name: TOTAL_CHAOS_DURATION
          value: "60"
        - name: CHAOS_INTERVAL
          value: "10"

Canary releases: o blue-green é pré-requisito natural para canary. Primeiro estabelecemos o ambiente green, depois direcionamos gradualmente o tráfego usando pesos no Ingress.

Feature flags: com LaunchDarkly, podemos ativar funcionalidades específicas no ambiente green sem afetar o blue:

if (ldclient.variation("nova-funcionalidade", user, false)) {
  // Código da nova funcionalidade
}

Isso permite testar funcionalidades em produção com usuários reais, mesmo antes do switch completo.


Referências