Como transformar "e se tudo der errado?" em um jogo de cartas que deixa o seu sistema mais forte.
Toda equipe tem aquela história: sexta-feira à tarde, um serviço de pagamento começa a responder devagar, o checkout trava, os alertas disparam e ninguém sabe ao certo o que está quebrando nem por quê. O pior é a frase que vem depois: "mas isso nunca tinha acontecido em staging".
Neste artigo eu mostro como a nossa equipe passou a descobrir esses problemas antes dos clientes, usando Chaos Engineering e um artefato que batizamos de ChaosEngineeringMasterDeck: um baralho de experimentos de falha que organiza o caos de forma segura, repetível e até divertida.
1. Introdução ao Chaos Engineering
O que é
Chaos Engineering é a disciplina de realizar experimentos controlados em um sistema para ganhar confiança de que ele aguenta condições turbulentas em produção. A prática foi popularizada pela Netflix, que criou o Chaos Monkey para derrubar instâncias aleatoriamente e forçar os times a construírem serviços tolerantes a falhas.
Não se trata de "quebrar coisas por quebrar". É método científico aplicado à confiabilidade:
- Definir o comportamento normal do sistema (o estado estável);
- Formular uma hipótese de que ele continuará normal sob uma falha;
- Injetar a falha de forma controlada;
- Comparar o resultado com a hipótese;
- Corrigir o que for descoberto e repetir.
Por que quebrar coisas de propósito?
Em sistemas distribuídos, a pergunta nunca é "se" algo vai falhar, e sim "quando". Redes particionam, discos enchem, dependências ficam lentas, zonas de disponibilidade caem. Testes unitários e de integração validam a lógica, mas não revelam como os componentes interagem sob estresse: timeouts mal configurados, retries que amplificam o problema (retry storm), filas que crescem sem limite, fallbacks que nunca foram exercitados.
Descobrir isso às 3 da manhã, em um incidente real, custa caro. Descobrir em um experimento planejado, com a equipe de prontidão e um botão de aborto, custa uma tarde.
A mudança de mentalidade
| Mentalidade tradicional | Mentalidade do Chaos Engineering |
|---|---|
| Evitar falhas a todo custo | Assumir que falhas acontecerão |
| Medir sucesso por "tempo sem incidentes" | Medir por tempo de recuperação e degradação graciosa |
| Descobrir fragilidades em incidentes | Descobrir fragilidades em experimentos |
| Confiar na arquitetura "no papel" | Validar a arquitetura com evidências |
| Confiabilidade é tarefa do time de Ops | Confiabilidade é responsabilidade de todos |
Staging ou produção?
O ideal, segundo os princípios da área, é experimentar em produção, porque só lá existe o tráfego, a escala e as dependências reais. Mas chegamos lá gradualmente: começamos em staging, depois produção com raio de explosão (blast radius) minúsculo, e só então ampliamos. Quem começa derrubando produção inteira não está fazendo engenharia, está causando um incidente.
2. Dissecando o ChaosEngineeringMasterDeck
O que é o Master Deck?
No nosso projeto, o ChaosEngineeringMasterDeck é um framework de experimentos em formato de baralho. Cada carta é um experimento de caos padronizado, documentado em um arquivo YAML versionado no repositório. O "Master" vem de ser o baralho central: uma biblioteca viva, mantida pela equipe, que cresce a cada incidente real (todo post-mortem gera uma carta nova).
A gamificação tem propósito: nos Game Days (dias de simulação de desastres), o time "compra" uma carta, lê o cenário e executa o experimento. Isso torna o exercício leve, reduz o medo de falhar e facilita o aprendizado coletivo.
Anatomia de uma carta
Cada carta responde às mesmas perguntas, garantindo que nenhum experimento seja improvisado:
| Campo | Pergunta que responde |
|---|---|
| Nome / Naipe | Que tipo de falha é? (Rede, Computação, Dados, Dependência, Infraestrutura) |
| Estado estável | Quais métricas definem "tudo normal"? |
| Hipótese | O que acreditamos que vai acontecer? |
| Variáveis (injeção) | Que falha será injetada, com qual intensidade e duração? |
| Blast Radius | Quem pode ser afetado? Qual o limite? |
| Métricas de controle | O que observar durante o experimento? |
| Condições de aborto | Quando interromper imediatamente? |
| Rollback | Como desfazer a injeção? |
Os naipes do baralho
- ♠ Rede: latência, perda de pacotes, partição de rede, DNS falhando.
- ♥ Computação: instância ou pod morto, CPU saturada, memória esgotada.
- ♦ Dados: banco indisponível, failover de réplica, disco cheio, replicação atrasada.
- ♣ Dependência: API externa lenta, erros 5xx de terceiros, certificado expirado.
- ★ Coringa: cenários de larga escala, como a queda de uma zona de disponibilidade inteira.
Exemplo de carta em YAML
carta:
id: NET-007
nome: "Latência Fantasma"
naipe: rede
nivel_de_risco: medio
estado_estavel:
- "p95 do checkout < 800 ms"
- "taxa de erro do checkout < 1%"
- "taxa de conversão dentro de ±5% da média da hora"
hipotese: >
Se o payment-service ficar com 2 s de latência adicional,
o circuit breaker do checkout-service abrirá em até 30 s e o
fallback (pagamento assíncrono) manterá a taxa de erro abaixo de 2%.
injecao:
alvo: payment-service
tipo: latencia
atraso_ms: { min: 2000, max: 3000 }
duracao: 10m
blast_radius:
ambiente: staging -> producao (canary)
percentual_trafego: 5
contas_de_teste_primeiro: true
metricas_de_controle:
- latencia_p95_checkout
- taxa_de_erro_5xx
- estado_do_circuit_breaker
- profundidade_da_fila_de_pagamentos
aborto_automatico:
- "taxa de erro do checkout > 5% por 1 min"
- "p95 do checkout > 3 s por 2 min"
rollback: "Desativar assault do Chaos Monkey (POST /actuator/chaosmonkey/disable)"
Essa estrutura é o coração do método: sem hipótese, é só bagunça; sem condição de aborto, é irresponsabilidade.
3. Estudo de Caso: a carta "Latência Fantasma"
O cenário de arquitetura
Nosso e-commerce roda na AWS, com a seguinte topologia simplificada:
Cliente --> ALB --> [ checkout-service (Spring Boot) ] --HTTP--> [ payment-service (Spring Boot) ] --> Gateway externo
| |
+--> inventory-service +--> RDS PostgreSQL (Multi-AZ)
+--> fila SQS "pagamentos-pendentes"
-
checkout-serviceepayment-servicerodam em ECS/Fargate, em duas zonas de disponibilidade, com Auto Scaling por CPU e latência. - O
checkout-servicechama opayment-servicede forma síncrona e usa Resilience4j para proteção. - Como fallback, se o pagamento não responder, o pedido é aceito como "pagamento em processamento" e uma mensagem é enfileirada no SQS para cobrança assíncrona.
A carta sorteada
No Game Day, o time sorteou NET-007, "Latência Fantasma": uma dependência crítica não cai, fica lenta. É um dos cenários mais traiçoeiros, porque uma falha total é fácil de detectar, enquanto uma lentidão silenciosa consome threads, conexões e paciência dos usuários.
Hipótese: com 2 a 3 segundos de latência extra no payment-service, o circuit breaker abrirá em até 30 s e o fallback manterá a taxa de erro do checkout abaixo de 2%.
Configuração de resiliência sob teste
# application.yml do checkout-service
resilience4j:
timelimiter:
instances:
payment:
timeout-duration: 1500ms
circuitbreaker:
instances:
payment:
sliding-window-type: COUNT_BASED
sliding-window-size: 20
failure-rate-threshold: 50
slow-call-duration-threshold: 1000ms
slow-call-rate-threshold: 60
wait-duration-in-open-state: 30s
permitted-number-of-calls-in-half-open-state: 5
retry:
instances:
payment:
max-attempts: 2
wait-duration: 200ms
@CircuitBreaker(name = "payment", fallbackMethod = "pagamentoAssincrono")
@TimeLimiter(name = "payment")
@Retry(name = "payment")
public CompletableFuture<ResultadoPagamento> cobrar(Pedido pedido) {
return CompletableFuture.supplyAsync(() -> paymentClient.cobrar(pedido));
}
// Fallback: aceita o pedido e enfileira a cobrança para processamento assíncrono
private CompletableFuture<ResultadoPagamento> pagamentoAssincrono(Pedido pedido, Throwable t) {
sqs.enviar("pagamentos-pendentes", pedido);
return CompletableFuture.completedFuture(ResultadoPagamento.emProcessamento(pedido.getId()));
}
A injeção de falha
Usamos o Chaos Monkey for Spring Boot no payment-service, ativando o ataque de latência apenas quando o experimento começa:
# application-chaos.yml do payment-service
chaos:
monkey:
enabled: true
watcher:
rest-controller: true
assaults:
level: 1 # afeta todas as requisições do ambiente do experimento
latency-active: true
latency-range-start: 2000
latency-range-end: 3000
E o controle em tempo de execução, sem redeploy, pelos endpoints do Actuator:
# iniciar o experimento
curl -X POST http://payment-canary:8080/actuator/chaosmonkey/enable
# abortar imediatamente
curl -X POST http://payment-canary:8080/actuator/chaosmonkey/disable
O Blast Radius
Definir o raio de explosão foi a parte mais importante do planejamento:
| Dimensão | Limite adotado |
|---|---|
| Ambiente | 1º rodada em staging; 2º rodada em produção |
| Tráfego afetado | Apenas 5% (tarefas canary do payment-service recebendo 5% das requisições via peso no ALB) |
| Duração | 10 minutos, com monitoramento ativo |
| Horário | Janela de baixo movimento, equipe de prontidão presente |
| Usuários | Contas de teste primeiro; depois clientes reais, dentro dos 5% |
| Aborto automático | Erro do checkout > 5% por 1 min ou p95 > 3 s por 2 min |
| Fora do escopo | Banco de dados, inventory-service, qualquer outra dependência |
Regra de ouro: o raio de explosão deve ser pequeno o bastante para que o pior cenário seja aceitável, e a hipótese, grande o bastante para que valha a pena testar.
4. Observabilidade e Resultados
Não existe Chaos Engineering sem observabilidade. Se você não consegue ver o efeito da falha, o experimento não ensina nada. Montamos um painel único (Grafana + CloudWatch + tracing com OpenTelemetry) com os Quatro Sinais Dourados:
- Latência: p50, p95 e p99 do checkout e do payment-service.
- Tráfego: requisições por segundo.
- Erros: taxa de 5xx e de fallback acionado.
- Saturação: threads ativas, conexões, profundidade da fila SQS.
Além deles, métricas de negócio, como pedidos concluídos por minuto, e o estado do circuit breaker, exposto via Actuator e Micrometer (resilience4j_circuitbreaker_state).
Rodada 1 (staging): a hipótese falhou, e foi ótimo
A primeira execução refutou nossa hipótese. O que observamos:
- A latência do
payment-servicesubiu, e o p95 do checkout foi para ~3,4 s. - O circuit breaker demorou ~70 s para abrir, não os 30 s esperados.
- Motivo: o timeout de 1,5 s estava correto, mas o retry dobrava cada chamada lenta, mantendo as threads ocupadas e atrasando o acúmulo de falhas necessário para abrir o circuito.
- O thread pool do checkout chegou perto da saturação.
Descoberta valiosa: o retry, pensado para ajudar, estava amplificando o problema. Esse tipo de interação não aparece em teste unitário.
Correções aplicadas:
- Retry apenas para erros de conexão, não para timeouts e chamadas lentas;
-
sliding-window-sizereduzido de 20 para 10, para o circuito reagir mais rápido; - Bulkhead dedicado às chamadas de pagamento, isolando o restante do checkout.
Rodada 2 (produção, 5% do tráfego): a arquitetura sobreviveu
Após os ajustes, repetimos a carta, agora em produção com blast radius reduzido:
| Métrica | Estado estável | Durante o experimento |
|---|---|---|
| p95 do checkout | ~600 ms | pico de ~1,1 s, estabilizando em ~700 ms |
| Taxa de erro do checkout | < 1% | ~0,8% |
| Tempo até o circuit breaker abrir | n/a | ~20 s |
| Pedidos aceitos como "em processamento" | 0 | ~100% dos pedidos do canary durante o circuito aberto |
| Profundidade da fila SQS | ~0 | subiu e drenou em ~4 min após o rollback |
| Pedidos concluídos por minuto (global) | linha de base | variação dentro de ±3% |
Os números acima ilustram o tipo de evidência coletada no exercício; em um artigo real, substitua-os pelas medições do seu ambiente.
Padrões de resiliência que entraram em ação:
- Timeout (TimeLimiter): impediu que chamadas lentas segurassem threads indefinidamente;
- Circuit Breaker: parou de enviar tráfego a um serviço degradado, dando-lhe tempo de se recuperar;
- Fallback automático: o pedido foi aceito e a cobrança, enfileirada no SQS, preservando a experiência e a receita;
- Bulkhead: isolou o impacto, sem contaminar o restante do checkout;
- Auto Scaling: reagiu à saturação do pool de conexões adicionando tarefas ao serviço.
Ao desativar o ataque, o circuito passou por half-open, validou 5 chamadas saudáveis e voltou a closed, e a fila foi drenada sem perda de pedidos.
Próximas cartas
O aprendizado gerou novas cartas para o baralho: COMP-012 "Queda de AZ" (derrubar todas as tarefas de uma zona de disponibilidade), DATA-003 "Failover do RDS" (forçar o failover Multi-AZ) e DEP-002 "Gateway Instável" (erros 5xx intermitentes no provedor de pagamento). Para essas, o AWS Fault Injection Service (FIS) é um bom aliado, pois permite parar instâncias, interromper tarefas e degradar rede com stop conditions ligadas a alarmes do CloudWatch.
5. Conclusão
Os experimentos com o Master Deck nos deixaram três lições principais:
- Resiliência não é suposição, é evidência. Nossa configuração de circuit breaker estava "certa no papel" e só falhou quando foi testada. Sem a carta, teríamos descoberto o problema do retry em um incidente real.
- Observabilidade vem primeiro. Sem métricas, traces e um estado estável bem definido, não há como saber se o experimento deu certo.
- Comece pequeno e cresça. Hipótese clara, blast radius limitado e condições de aborto transformam o caos em ciência.
O impacto na cultura DevOps
O maior ganho não foi técnico. Mudou a cultura:
- Falhas deixaram de ser motivo de culpa e viraram fonte de aprendizado (blameless);
- Desenvolvedores passaram a pensar em modos de falha durante o design, não só depois;
- Post-mortems viram cartas novas, fechando o ciclo entre incidente real e prevenção;
- Os Game Days aproximaram Dev, Ops e Produto em torno de um objetivo comum: um sistema que falha bem.
Se você quer começar amanhã: escolha uma dependência crítica, escreva uma hipótese, injete uma falha pequena em staging e observe. Depois escreva a carta e guarde no repositório. Esse é o primeiro naipe do seu baralho.
Quebre as coisas com cuidado. O seu sistema (e os seus usuários) agradecem.
Referências para aprofundar
- Principles of Chaos Engineering: principlesofchaos.org
- Documentação do Chaos Monkey for Spring Boot (codecentric)
- Documentação do Resilience4j
- AWS Fault Injection Service: documentação oficial da AWS
- Chaos Engineering, Casey Rosenthal e Nora Jones (O'Reilly)
Gostou? Deixe nos comentários qual seria a primeira carta do seu baralho.
Tags: #chaosengineering #resilience #devops #springboot #aws #microservices
Top comments (0)