Circuit Breaker em Istio: Proteção Contra Falhas em Cascata
O Circuit Breaker é um padrão de design que atua como um "disjuntor" para suas requisições. Quando um serviço downstream está com problemas, ao invés de continuar enviando requisições que falharão, o Circuit Breaker detecta o padrão de falha e temporariamente impede novas tentativas, evitando desperdício de recursos e degradação em cascata da arquitetura inteira.
Em Istio, o Circuit Breaker é configurado através do recurso DestinationRule. Você define limites como número de conexões simultâneas, requisições pendentes e detecta outliers (hosts que se comportam mal) removendo-os do pool de balanceamento. O comportamento é automático e transparente para a aplicação, funcionando no nível da malha.
Configurando um Circuit Breaker básico
Considere um cenário onde você tem um serviço payment-api que frequentemente falha sob carga. Você quer proteger seus consumidores:
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: payment-api-circuit-breaker
namespace: production
spec:
host: payment-api
trafficPolicy:
connectionPool:
http:
http1MaxPendingRequests: 100
maxRequestsPerConnection: 2
h2UpgradePolicy: UPGRADE
tcp:
maxConnections: 100
outlierDetection:
consecutive5xxErrors: 5
interval: 30s
baseEjectionTime: 30s
maxEjectionPercent: 50
minRequestVolume: 5
splitExternalLocalOriginErrors: true
Nesta configuração, o Circuit Breaker:
- Limita 100 requisições HTTP pendentes
- Detecta 5 erros 5xx consecutivos e remove o host por 30 segundos
- Nunca remove mais de 50% dos hosts disponíveis
- Requer no mínimo 5 requisições antes de considerar outliers
Entendendo os parâmetros de Outlier Detection
O outlierDetection é o coração inteligente do Circuit Breaker. A propriedade consecutive5xxErrors: 5 significa que após 5 falhas 5xx consecutivas, Istio remove aquele pod do balanceamento por baseEjectionTime. O campo maxEjectionPercent: 50 é crítico em produção — garante que você nunca perde mais da metade da sua capacidade, mesmo com falhas em cascata.
A minRequestVolume: 5 evita que decisões sejam tomadas com base em tráfego muito baixo, reduzindo falsos positivos. Em ambientes com pouco tráfego, esse valor deve ser ajustado para refletir o volume real esperado.
Fault Injection: Injetando Falhas de Forma Controlada
Fault Injection é a prática de injetar falhas (delays, aborts) propositalmente em seu sistema para testar resiliência. Istio permite fazer isso sem modificar código algum — é configuração pura na malha. Você pode simular lentidão de rede ou indisponibilidade temporária para validar se seus circuit breakers, retries e timeouts funcionam corretamente.
Este padrão é essencial para Chaos Engineering. Em vez de esperar que falhas aconteçam naturalmente, você as provoca de forma controlada em ambientes de staging ou produção, identificando fragilidades antes que afetem usuários reais.
Injetando delays e aborts
Use um VirtualService para configurar injeção de falhas. Aqui está um exemplo prático:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: payment-api-fault-injection
namespace: production
spec:
hosts:
- payment-api
http:
- match:
- sourceLabels:
version: canary
fault:
delay:
percentage: 10
fixedDelay: 5s
abort:
percentage: 3
grpcStatus: UNAVAILABLE
route:
- destination:
host: payment-api
port:
number: 8080
- route:
- destination:
host: payment-api
port:
number: 8080
Aqui, requisições originadas de pods com label version: canary receberão:
- 10% de atraso de 5 segundos
- 3% de respostas imediatas com erro UNAVAILABLE
Tráfego de outras fontes passa direto para o destino sem injeção. Isso permite testar código novo (canary) com falhas controladas enquanto mantém o tráfego regular estável.
Diferença entre delay e abort
delay simula rede lenta — a requisição ainda é processada, mas com latência extra. Útil para testar timeouts. abort encerra a conexão imediatamente com um código de erro, simulando indisponibilidade. Combining ambos oferece testes mais realistas: parte falha rápido (abort), parte falha lentamente (delay).
Para Debug, você pode aumentar temporariamente as percentagens em staging:
fault:
delay:
percentage: 100
fixedDelay: 2s
abort:
percentage: 50
grpcStatus: RESOURCE_EXHAUSTED
Com 100% de delay e 50% de abort, você verá imediatamente se sua aplicação comporta-se corretamente sob essas condições extremas.
Observabilidade: Rastreando Tudo na Malha
Observabilidade em Istio funciona em três pilares: métricas, logs e traces distribuídos. A malha injeta sidecar Envoy em cada pod, e esses proxies coletam dados detalhados sobre tráfego, latência, erros e dependências. Você obtém visibilidade total sem instrumentar código.
Métricas são números (requisições por segundo, latência p99). Logs são eventos estruturados. Traces mostem o caminho completo de uma requisição através de múltiplos serviços, essencial para debugging de problemas em arquiteturas distribuídas.
Coletando métricas com Prometheus
Istio expõe métricas em formato Prometheus nos sidecars. Configure um ServiceMonitor (se usar Prometheus Operator) ou um scrape_config manual:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: istio-mesh
namespace: istio-system
spec:
selector:
matchLabels:
release: istio
endpoints:
- port: http-monitoring
interval: 30s
path: /stats/prometheus
Com isso configurado, você terá métricas como:
# Requisições bem-sucedidas
istio_requests_total{destination_service="payment-api", response_code="200"} 15234
# Latência em percentis
istio_request_duration_milliseconds_bucket{le="100", destination_service="payment-api"} 5000
istio_request_duration_milliseconds_bucket{le="500", destination_service="payment-api"} 14500
# Taxa de erro
istio_requests_total{destination_service="payment-api", response_code="500"} 42
Você pode criar alertas baseados nessas métricas. Por exemplo, alertar quando a taxa de erro 5xx de um serviço ultrapassa 5%:
groups:
- name: istio_alerts
rules:
- alert: HighErrorRate
expr: |
sum(rate(istio_requests_total{response_code=~"5.."}[5m])) by (destination_service) /
sum(rate(istio_requests_total[5m])) by (destination_service) > 0.05
for: 5m
annotations:
summary: "High error rate for {{ $labels.destination_service }}"
Distributed Tracing com Jaeger
Istio integra-se nativamente com Jaeger. Cada sidecar propaga headers de trace (x-trace-id, x-parent-span-id) automaticamente:
apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
name: tracing-sampling
namespace: istio-system
spec:
tracing:
- providers:
- name: jaeger
randomSamplingPercentage: 10
Com 10% de sampling, 10% das requisições são traçadas. Em produção, ajuste conforme volume — muita amostragem consome recursos, pouca perde contexto.
Uma vez configurado, você verá traces como este em Jaeger:
Request ID: a1b2c3d4e5f6g7h8
├─ payment-service (5ms)
│ ├─ database query (3ms)
│ └─ cache lookup (0.5ms)
├─ fraud-service (8ms)
│ └─ external ML model (7ms)
└─ notification-service (1ms)
Total: 14ms
Isso permite identificar exatamente qual serviço é lento em uma cadeia de chamadas.
Logs Estruturados com Fluentd
Configure coleta de logs dos sidecars:
apiVersion: v1
kind: ConfigMap
metadata:
name: fluent-bit-config
namespace: istio-system
data:
parsers.conf: |
[PARSER]
Name docker
Format json
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L%z
fluent-bit.conf: |
[SERVICE]
Flush 5
Log_Level info
[INPUT]
Name tail
Path /var/log/containers/*_istio-proxy_*.log
Parser docker
Tag istio.*
[OUTPUT]
Name stackdriver
Match *
resource k8s_pod
Cada requisição processada por Envoy gera um log JSON estruturado, facilitando buscas e agregações em ferramentas como Stackdriver ou ELK.
Integrando Tudo: Um Exemplo Completo de Produção
Agora vamos integrar Circuit Breaker, Fault Injection e observabilidade em um cenário real. Suponha você tem dois serviços: order-service (frontend) e inventory-service (backend).
# Namespace
apiVersion: v1
kind: Namespace
metadata:
name: ecommerce
labels:
istio-injection: enabled
---
# DestinationRule com Circuit Breaker
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: inventory-circuit-breaker
namespace: ecommerce
spec:
host: inventory-service
trafficPolicy:
connectionPool:
http:
http1MaxPendingRequests: 50
maxRequestsPerConnection: 1
tcp:
maxConnections: 50
outlierDetection:
consecutive5xxErrors: 3
interval: 15s
baseEjectionTime: 30s
maxEjectionPercent: 100
minRequestVolume: 3
---
# VirtualService com Fault Injection para teste
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: inventory-fault-injection
namespace: ecommerce
spec:
hosts:
- inventory-service
http:
- match:
- sourceLabels:
app: order-service
test: true
fault:
delay:
percentage: 20
fixedDelay: 2s
timeout: 5s
retries:
attempts: 3
perTryTimeout: 2s
route:
- destination:
host: inventory-service
port:
number: 8080
- route:
- destination:
host: inventory-service
port:
number: 8080
---
# Telemetria para observabilidade
apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
name: production-tracing
namespace: ecommerce
spec:
tracing:
- providers:
- name: jaeger
randomSamplingPercentage: 5
useRequestIdForTraceSampling: true
Deploy seu order-service com labels para testes:
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
namespace: ecommerce
spec:
replicas: 2
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
version: v1
test: "true"
spec:
containers:
- name: order-api
image: order-service:1.2.0
ports:
- containerPort: 8080
env:
- name: INVENTORY_SERVICE_URL
value: "http://inventory-service:8080"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
Com esta configuração:
- Circuit Breaker atuará quando inventory-service falhar 3x consecutivamente
- Fault Injection adicionará delay de 2s em 20% das requisições do order-service (test=true)
- Observabilidade traçará 5% das requisições em Jaeger, permitindo investigar latências
Você pode validar com um teste simples:
# Terminal 1: Monitorar traces em Jaeger
kubectl port-forward -n istio-system svc/jaeger 16686:16686
# Terminal 2: Gerar tráfego
kubectl run -it --rm -n ecommerce \
--image=curlimages/curl:latest \
--restart=Never \
test-curl -- \
bash -c 'for i in {1..100}; do curl http://order-service:8080/api/orders; sleep 0.1; done'
Em Jaeger, você verá traces como:
- Requisições normais (~2ms)
- Requisições com delay injetado (~2000ms)
- Requisições rejeitadas pelo Circuit Breaker (após falhas)
Conclusão
Istio oferece três capacidades transformadoras para resiliência e observabilidade em Kubernetes:
-
Circuit Breaker automatiza proteção contra falhas em cascata através de
DestinationRulee outlier detection, eliminando necessidade de instrumentação em código. A chave é dimensionar corretamentemaxEjectionPercenteconsecutive5xxErrorspara sua carga de trabalho. -
Fault Injection via
VirtualServicepermite testar resiliência de forma controlada sem esperar por falhas reais, transformando Chaos Engineering em prática segura e previsível. Combine com retries e timeouts para cenários realistas. -
Observabilidade através de métricas Prometheus, logs estruturados e traces distribuídos fornece visibilidade completa sem alterar aplicações, sendo essencial para debugging em microsserviços. Ajuste sampling de traces conforme volume para balancear fidelidade vs. custo.
A combinação desses três pilares transforma sua arquitetura de microsserviços de frágil para robusto, permitindo confiança em produção.