Deploy sem susto: progressive delivery com canário e rollback automático
Por que "mergeou, subiu" não basta em escala: canário, análise automática por métrica e error budget, e rollback sem humano no meio da noite.
"Mergeou, subiu" é um ótimo lema — até o dia em que sobe um bug que só aparece com tráfego real, às sextas às 18h, e derruba o serviço para 100% dos usuários de uma vez. Deploy que troca todas as réplicas ao mesmo tempo transforma qualquer erro em incidente total. Não porque o time é ruim, mas porque a estratégia de deploy não deixa espaço para o erro ser pequeno.
Progressive delivery é a ideia de que a exposição de uma versão nova deve crescer gradualmente e ser revertida automaticamente quando as métricas piorarem — sem depender de alguém acordado para apertar o botão. É a diferença entre um blast radius de 100% e um de 5%.
Por que deploy "tudo de uma vez" não serve em escala
Testes pegam muita coisa, mas não pegam tudo. O que escapa costuma ser justamente o que só se manifesta em produção: uma consulta que degrada com o volume real de dados, um caminho de código que só dispara com um padrão específico de tráfego, uma dependência que se comporta diferente sob carga. Com deploy atômico, quando esse problema aparece, ele já pegou todo mundo.
Canário inverte a lógica: você sobe a versão nova para uma fatia pequena do tráfego, observa, e só avança se os números continuarem sãos. Se piorarem, você recua antes de a maioria dos usuários ser afetada.
A anatomia de um canário que funciona
Um canário decente tem três partes, e as três precisam existir:
- Divisão de tráfego progressiva. Começa em 5%, passa para 25%, 50%, 100% — com pausas entre os degraus. Um service mesh ou um Ingress com pesos resolve o roteamento.
- Análise automática por métrica. Em cada degrau, o sistema compara canário contra baseline (a versão estável). Taxa de erro, latência P95/P99, e métricas de negócio quando fizer sentido. Se o canário está pior, ele reprova.
- Rollback automático. Reprovou, volta. Sem humano no meio. Este é o ponto inteiro — o valor do canário some se depender de alguém interpretar um gráfico às 3h da manhã.
# Exemplo com Argo Rollouts: os degraus e a análise por métrica.
strategy:
canary:
steps:
- setWeight: 5
- pause: { duration: 5m }
- analysis:
templates:
- templateName: taxa-de-erro-e-latencia
- setWeight: 25
- pause: { duration: 10m }
- setWeight: 50
- pause: { duration: 10m }
- setWeight: 100O AnalysisTemplate é o coração da coisa. Ele consulta o Prometheus (ou seu backend de métricas) e falha o rollout se um limiar for cruzado:
metrics:
- name: taxa-de-erro
interval: 1m
successCondition: result < 0.01 # menos de 1% de erro
failureLimit: 2 # duas medições ruins reprovam
provider:
prometheus:
address: http://prometheus.monitoring:9090
query: |
sum(rate(http_requests_total{app="pagamentos",status=~"5.."}[2m]))
/
sum(rate(http_requests_total{app="pagamentos"}[2m]))Canário e error budget andam juntos
Aqui entra a parte de SRE que muita gente pula. A decisão de reprovar um canário não deveria ser um número mágico chutado — ela deveria estar amarrada ao seu error budget. Se o SLO do serviço é 99,9% e o canário está consumindo orçamento de erro rápido demais para sustentar esse alvo, reprova. Isso transforma "achei que estava ruim" em "está fora do contrato que definimos", o que é uma conversa muito mais defensável.
Amarrar deploy ao error budget também resolve o eterno cabo de guerra entre velocidade e estabilidade: enquanto há orçamento, o time entrega rápido; quando o orçamento aperta, o próprio processo segura a mão. Ninguém precisa ser o vilão que "trava o deploy".
O detalhe que quebra tudo: migração de schema
Progressive delivery pressupõe que as versões antiga e nova convivem ao mesmo tempo — durante o canário, as duas estão no ar. Isso funciona lindamente para código stateless e vira um pesadelo quando há mudança de banco de dados.
Se a versão nova exige uma coluna que a antiga não conhece — ou pior, renomeia algo que a antiga ainda usa — o rollback te leva de volta a um código incompatível com o schema já migrado. A regra é migração compatível para trás em duas fases: primeiro você adiciona (colunas, tabelas) de forma que a versão antiga continue funcionando; só depois de a versão nova estar 100% e estável você remove o que ficou obsoleto. Expand and contract. Sem isso, o rollback automático que deveria te salvar vira a causa do segundo incidente.
Numa escala real
Numa fintech brasileira em escala (grupo global de pagamentos), processando dezenas de milhões de transações por dia, adotar canário com análise por métrica e rollback automático não eliminou bugs — bug é inevitável. O que mudou foi o tamanho do estrago: a maioria dos deploys ruins passou a ser revertida ainda no degrau de 5%, antes de virar página de status ou de acordar alguém de plantão. O incidente total virou exceção rara, não rotina de sexta-feira.
Por onde começar
Você não precisa de service mesh completo nem de plataforma sofisticada no dia um:
- Comece pelo serviço mais crítico, não pelo mais fácil. É onde o retorno aparece primeiro.
- Instrumente antes de automatizar. Sem métricas confiáveis de erro e latência, não há análise automática possível.
- Rollback automático só depois de calibrar os limiares. Automação que reverte no limiar errado gera mais ruído do que confiança.
O objetivo final não é deploy bonito. É poder subir código na sexta às 18h e ir para casa tranquilo — porque, se der errado, o sistema recua sozinho antes de você abrir o laptop.