Dominando Istio Avançado: Circuit Breaker, Fault Injection e Observabilidade em Projetos Reais Já leu

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 . 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 que frequentemente falha sob carga. Você quer proteger seus consumidores: 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

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:

  1. Circuit Breaker atuará quando inventory-service falhar 3x consecutivamente
  2. Fault Injection adicionará delay de 2s em 20% das requisições do order-service (test=true)
  3. 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:

  1. Circuit Breaker automatiza proteção contra falhas em cascata através de DestinationRule e outlier detection, eliminando necessidade de instrumentação em código. A chave é dimensionar corretamente maxEjectionPercent e consecutive5xxErrors para sua carga de trabalho.

  2. Fault Injection via VirtualService permite 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.

  3. 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.


Referências

  1. Istio Documentation - Circuit Breaker

  2. Istio Documentation - Fault Injection

  3. Envoy Proxy - Outlier Detection

  4. Jaeger Distributed Tracing Documentation

  5. Prometheus Metrics Best Practices


Artigos relacionados