<h2>Introdução: Por que Estratégias de Deploy Importam em Kubernetes</h2>
<p>Quando você trabalha com Kubernetes em produção, simplesmente fazer <code>kubectl apply</code> e rezar para que nada quebre não é uma opção viável. O desafio real está em <strong>atualizar suas aplicações sem interrupção de serviço, minimizando riscos e permitindo rollback rápido caso algo dê errado</strong>. É exatamente aí que estratégias de deploy bem definidas entram em cena.</p>
<p>As três estratégias que vamos explorar neste artigo—Rolling, Blue-Green e Canary—representam diferentes abordagens para resolver este problema, cada uma com seus trade-offs de complexidade, tempo de deploy e segurança. Não existe uma estratégia universalmente superior; a escolha depende do seu contexto, tolerância a risco e infraestrutura disponível.</p>
<h2>Rolling Deployment: O Clássico Gradual</h2>
<h3>O Conceito Fundamental</h3>
<p>Rolling Deployment é a estratégia padrão do Kubernetes. Ela funciona <strong>substituindo gradualmente as instâncias antigas por novas</strong>, mantendo sempre um número mínimo de replicas em execução. Enquanto você termina de escalar as novas versões, as antigas vão sendo drenadas e removidas.</p>
<p>Imagina que você tem 5 replicas em produção. O Kubernetes pode iniciar 1 nova replica com a versão nova, remover 1 antiga, depois repetir até que todas as 5 sejam da nova versão. Durante todo esse processo, o serviço continua respondendo requisições—nenhum downtime.</p>
<h3>Implementação Prática</h3>
<p>A maneira mais simples de fazer Rolling Deployment em Kubernetes é através da estratégia padrão de atualização de Deployment. Vamos criar um exemplo completo:</p>
<pre><code class="language-yaml">apiVersion: apps/v1
kind: Deployment
metadata:
name: app-web
namespace: producao
spec:
replicas: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: app
image: meu-registro.azurecr.io/app-web:v1.0.0
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 3
terminationGracePeriodSeconds: 30</code></pre>
<p>Os parâmetros <code>maxSurge</code> e <code>maxUnavailable</code> são críticos aqui. <code>maxSurge: 1</code> significa que o Kubernetes pode criar 1 pod a mais do que o especificado (6 pods total temporariamente). <code>maxUnavailable: 1</code> permite que no máximo 1 pod esteja indisponível durante o processo. Esses valores controlam a velocidade e o risco do deploy.</p>
<p>Para atualizar a imagem e disparar um novo Rolling Deployment:</p>
<pre><code class="language-bash">kubectl set image deployment/app-web app=meu-registro.azurecr.io/app-web:v1.1.0 \
--namespace=producao \
--record</code></pre>
<p>Você pode monitorar o progresso em tempo real:</p>
<pre><code class="language-bash">kubectl rollout status deployment/app-web -n producao
kubectl rollout history deployment/app-web -n producao</code></pre>
<p>Se algo der errado durante o processo, faça rollback instantâneo:</p>
<pre><code class="language-bash">kubectl rollout undo deployment/app-web -n producao</code></pre>
<h3>Vantagens e Desvantagens</h3>
<p><strong>Vantagens:</strong> Simplicidade, suporte nativo do Kubernetes, não requer infraestrutura adicional, rollback automático se health checks falharem. <strong>Desvantagens:</strong> Tempo de deploy pode ser longo para muitas replicas, período de "versão mista" onde v1 e v2 rodam simultaneamente (pode quebrar se as APIs são incompatíveis), dificuldade em controlar qual percentual de tráfego vai para nova versão.</p>
<h2>Blue-Green Deployment: Dois Mundos Paralelos</h2>
<h3>O Conceito Fundamental</h3>
<p>Blue-Green é uma abordagem onde você mantém <strong>duas versões completas e idênticas de sua aplicação rodando em paralelo</strong>: o ambiente "Blue" (atual) e o "Green" (novo). Todo o tráfego vai para o Blue. Quando você quer fazer deploy, sobe o Green com a nova versão, testa completamente, e então <strong>muda o roteador para direcionar todo o tráfego do Blue para o Green em uma operação atômica</strong>.</p>
<p>O grande benefício? Rollback é trivial: basta voltar o roteador para apontar para o Blue. Se algo der errado, você já tem a versão anterior funcionando perfeitamente. Não há versão mista, não há downtime durante a transição.</p>
<h3>Implementação Prática</h3>
<p>Vou mostrar uma implementação real usando dois Deployments e um Service:</p>
<pre><code class="language-yaml">---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-blue
spec:
replicas: 3
selector:
matchLabels:
app: meu-app
versao: blue
template:
metadata:
labels:
app: meu-app
versao: blue
spec:
containers:
- name: app
image: meu-registro.azurecr.io/app:v1.0.0
ports:
- containerPort: 8080
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-green
spec:
replicas: 3
selector:
matchLabels:
app: meu-app
versao: green
template:
metadata:
labels:
app: meu-app
versao: green
spec:
containers:
- name: app
image: meu-registro.azurecr.io/app:v1.1.0
ports:
- containerPort: 8080
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: app-service
spec:
selector:
app: meu-app
versao: blue # Atualmente apontando para blue
ports:
- protocol: TCP
port: 80
targetPort: 8080</code></pre>
<p>O truque está no selector do Service. Ele aponta para <code>versao: blue</code>, então todo o tráfego vai para o Blue Deployment. Quando você quer fazer o deploy da versão 1.1.0 no Green, você sobe completamente, testa em uma URL privada, e depois muda o selector do Service:</p>
<pre><code class="language-bash"># Verificar que Green está pronto
kubectl get pods -l app=meu-app,versao=green
Quando tudo estiver ok, fazer a troca
kubectl patch service app-service -p '{"spec":{"selector":{"versao":"green"}}}'
Se precisar voltar
kubectl patch service app-service -p '{"spec":{"selector":{"versao":"blue"}}}'</code></pre>
<p>Para automatizar isso um pouco mais, você pode criar um script:</p>
<pre><code class="language-bash">#!/bin/bash
CURRENT_VERSION=$(kubectl get service app-service -o jsonpath='{.spec.selector.versao}')
NEXT_VERSION=$([[ "$CURRENT_VERSION" == "blue" ]] && echo "green" || echo "blue")
echo "Versão atual: $CURRENT_VERSION"
echo "Alternando para: $NEXT_VERSION"
kubectl patch service app-service -p "{\"spec\":{\"selector\":{\"versao\":\"$NEXT_VERSION\"}}}"
echo "Deploy concluído! Pressione Enter para fazer rollback se necessário..."
read -r
kubectl patch service app-service -p "{\"spec\":{\"selector\":{\"versao\":\"$CURRENT_VERSION\"}}}"</code></pre>
<h3>Vantagens e Desvantagens</h3>
<p><strong>Vantagens:</strong> Transição instantânea entre versões, rollback trivial, zero downtime, fácil de testar completamente antes de expor ao público. <strong>Desvantagens:</strong> Requer o dobro de recursos (duas stacks completas rodando), deploy é mais lento porque você precisa aguardar a startup completa do Green, gerenciamento manual de qual versão é qual (pode ficar confuso).</p>
<h2>Canary Deployment: O Experimento Controlado</h2>
<h3>O Conceito Fundamental</h3>
<p>Canary Deployment é inspirado na prática histórica de mineiros levar canários às minas como detector de gás tóxico. A ideia é <strong>fazer deploy da nova versão para um pequeno percentual de usuários reais (5-10%), monitorar métricas de erro e performance, e apenas se tudo estiver bem, aumentar gradualmente até 100%</strong>.</p>
<p>É um meio termo entre Rolling e Blue-Green: você obtém a segurança de testar com tráfego real, mas com risco limitado. Se algo der errado, apenas uma fração de usuários é impactada. Ferramentas como Istio e Flagger tornaram isso prático.</p>
<h3>Implementação com Istio</h3>
<p>Istio é um service mesh que fornece controle fino sobre como o tráfego é roteado entre versões. Vou mostrar uma implementação prática:</p>
<pre><code class="language-yaml">---
apiVersion: v1
kind: Service
metadata:
name: app-canary
spec:
ports:
- port: 8080
name: http
selector:
app: app-canary
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-canary-v1
spec:
replicas: 3
selector:
matchLabels:
app: app-canary
version: v1
template:
metadata:
labels:
app: app-canary
version: v1
spec:
containers:
- name: app
image: meu-registro.azurecr.io/app-canary:v1.0.0
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /health
port: 8080
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
periodSeconds: 5
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-canary-v2
spec:
replicas: 1 # Começando com apenas 1 réplica para canary
selector:
matchLabels:
app: app-canary
version: v2
template:
metadata:
labels:
app: app-canary
version: v2
spec:
containers:
- name: app
image: meu-registro.azurecr.io/app-canary:v2.0.0
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /health
port: 8080
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
periodSeconds: 5
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: app-canary
spec:
hosts:
- app-canary
http:
- match:
- uri:
prefix: "/"
route:
- destination:
host: app-canary
subset: v1
weight: 90
- destination:
host: app-canary
subset: v2
weight: 10 # 10% do tráfego para canary
---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: app-canary
spec:
host: app-canary
trafficPolicy:
connectionPool:
tcp:
maxConnections: 1000
http:
http1MaxPendingRequests: 100
h2UpgradePolicy: UPGRADE
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2</code></pre>
<p>Para aumentar gradualmente o tráfego para v2, você atualiza o VirtualService:</p>
<pre><code class="language-bash"># Fase 1: 10% (já está assim no YAML acima)
kubectl apply -f canary-10.yaml
Monitore por alguns minutos/horas
Verifique métricas no Prometheus/Grafana
Fase 2: 25% se tudo estiver ok
kubectl patch virtualservice app-canary --type merge -p \
'{"spec":{"http":[{"match":[{"uri":{"prefix":"/"}}],"route":[{"destination":{"host":"app-canary","subset":"v1"},"weight":75},{"destination":{"host":"app-canary","subset":"v2"},"weight":25}]}]}}'
Fase 3: 50%
kubectl patch virtualservice app-canary --type merge -p \
'{"spec":{"http":[{"match":[{"uri":{"prefix":"/"}}],"route":[{"destination":{"host":"app-canary","subset":"v1"},"weight":50},{"destination":{"host":"app-canary","subset":"v2"},"weight":50}]}]}}'
Fase 4: 100% (completo)
kubectl patch virtualservice app-canary --type merge -p \
'{"spec":{"http":[{"match":[{"uri":{"prefix":"/"}}],"route":[{"destination":{"host":"app-canary","subset":"v1"},"weight":0},{"destination":{"host":"app-canary","subset":"v2"},"weight":100}]}]}}'</code></pre>
<h3>Automação com Flagger</h3>
<p>Flagger é uma ferramenta que automatiza esse processo. Ela monitora métricas e promove a canary automaticamente:</p>
<pre><code class="language-yaml">apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: app-canary
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: app-canary
progressDeadlineSeconds: 300
service:
port: 8080
analysis:
interval: 1m
threshold: 5
maxWeight: 50
stepWeight: 10
metrics:
- name: request-success-rate
thresholdRange:
min: 99
interval: 1m
- name: request-duration
thresholdRange:
max: 500
interval: 1m</code></pre>
<p>Com este Canary Resource, o Flagger automaticamente:</p>
<ul>
<li>Começa com 10% do tráfego para v2</li>
<li>A cada 1 minuto, verifica as métricas (taxa de sucesso > 99%, latência < 500ms)</li>
<li>Se as métricas estiverem ok, incrementa 10% mais</li>
<li>Se alguma métrica falhar, para o processo e faz rollback</li>
</ul>
<h3>Vantagens e Desvantagens</h3>
<p><strong>Vantagens:</strong> Validação com tráfego real, risco muito reduzido, fácil monitorar diferenças entre versões, pode ser totalmente automatizado. <strong>Desvantagens:</strong> Requer infraestrutura de observabilidade (Prometheus, etc), mais complexo de configurar, "versão mista" por um tempo (compatibilidade de dados/APIs importante).</p>
<h2>Guia de Decisão: Qual Usar?</h2>
<p>A escolha entre essas três estratégias não é arbitrária. Considere estes fatores:</p>
<p><strong>Use Rolling Deployment se:</strong> você tem uma aplicação simples ou monolítica, deploy é frequente (CI/CD agressivo), você confia em seus testes automatizados, e a compatibilidade entre versões é garantida. É a opção mais simples e geralmente a padrão.</p>
<p><strong>Use Blue-Green se:</strong> mudanças de versão são pouco frequentes mas críticas, você precisa de rollback instantâneo, tem budget para rodar duas stacks, ou quer testar completamente antes de expor aos usuários. Ideal para sistemas financeiros ou de saúde.</p>
<p><strong>Use Canary se:</strong> você quer máxima segurança com mínimo downtime, tem infraestrutura de observabilidade madura, deploy frequente com mudanças significativas, ou quer detectar problemas no mundo real antes de afetar todos os usuários.</p>
<p>A realidade é que muitas empresas usam Rolling para deploy automatizado de features, Blue-Green para releases maiores, e Canary com Flagger para feature flags e experimentos A/B.</p>
<h2>Conclusão</h2>
<p>Aprendemos que <strong>a estratégia de deploy não é apenas "como colocar código em produção", mas uma decisão arquitetural que afeta disponibilidade, tempo de deploy, complexidade operacional e capacidade de rollback</strong>. Rolling Deployment oferece simplicidade com trade-offs aceitáveis para a maioria dos casos. Blue-Green fornece a máxima segurança quando você pode investir em infraestrutura duplicada. Canary é a escolha moderna quando você quer experimental evidence de que a nova versão está segura, não apenas hopes and prayers.</p>
<p><strong>O ponto crítico que muitos iniciantes perdem:</strong> independentemente da estratégia escolhida, sua aplicação precisa suportar mudanças. Implementar health checks robustos, liveness e readiness probes corretos, e tentar manter compatibilidade de APIs entre versões consecutivas são pré-requisitos para qualquer deploy seguro. A estratégia escolhida é apenas o orquestrador disso tudo.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/" target="_blank" rel="noopener noreferrer">Kubernetes Documentation - Deployments</a></li>
<li><a href="https://istio.io/latest/docs/tasks/traffic-management/traffic-shifting/canary/" target="_blank" rel="noopener noreferrer">Istio Documentation - Traffic Management</a></li>
<li><a href="https://flagger.app/" target="_blank" rel="noopener noreferrer">Flagger - Progressive Delivery Operator for Kubernetes</a></li>
<li><a href="https://docs.aws.amazon.com/whitepapers/latest/blue-green-deployments/" target="_blank" rel="noopener noreferrer">Blue Green Deployments on AWS</a></li>
<li><a href="https://cloud.google.com/architecture/patterns-for-deploying-kubernetes-applications" target="_blank" rel="noopener noreferrer">Google Cloud - Deployment Strategies</a></li>
</ul>
<p><!-- FIM --></p>