Home Comunidade Eventos YouTube Networks Desafios // produtos Academy News CTI | CVE Contato

03 - Tracing Distribuído (Jaeger, Zipkin, X-Ray)

#jaeger#opentelemetry#tracing#w3c-tracecontext
video explicativo
Video explicativoEspaco reservado para o video desta anotação — adicione video: https://youtu.be/... no frontmatter da nota.

Tracing Distribuído (Jaeger, Zipkin, X-Ray)

Tracing distribuído é a técnica de observabilidade que reconstrói o caminho completo de uma requisição através de todos os serviços, filas e bancos que ela atravessa, representando esse caminho como um grafo de operações temporizadas. Em arquiteturas distribuídas, um único pedido do usuário pode percorrer dezenas de componentes, e métricas agregadas e logs isolados não conseguem mostrar onde exatamente a latência foi adicionada ou em qual salto o erro nasceu. O tracing resolve isso atribuindo a cada requisição um identificador único, o trace id, e registrando cada trecho de trabalho como um span com início, fim, status e metadados. Ferramentas como Jaeger, Zipkin e AWS X-Ray armazenam, correlacionam e visualizam esses dados, enquanto o OpenTelemetry se consolidou como a camada de instrumentação neutra que alimenta todas elas. Em produção, tracing é a principal ferramenta de diagnóstico de latência em cascata e de mapeamento de dependências reais entre serviços.

O problema que o tracing resolve

Sem tracing, a pergunta "onde está a lentidão" em um fluxo com dez serviços exige abrir dez painéis de métricas e cruzar timestamps manualmente, com precisão de segundos e sob pressão de incidente. Métricas dizem que o p99 subiu, mas não dizem qual chamada dentro do fluxo puxou a curva; logs dizem o que cada serviço viu, mas não revelam a ordem e o aninhamento das operações. O tracing fecha essa lacuna ao preservar a relação causal entre operações, incluindo o tempo gasto na rede e na fila, invisível para qualquer outra ferramenta. Ele também resolve o problema de conhecimento institucional: em vez de depender de alguém que sabe como os serviços se conectam, o service map registra a verdade observada em produção.

Conceito fundamental: trace, span e contexto

Um trace é a árvore completa da execução de uma requisição, e um span é um nó dessa árvore, representando uma operação com nome, duração, status e atributos. Spans carregam referência aos pais, formando a hierarquia que revela sequência e paralelismo, e podem ter eventos internos, como a gravação de um erro, e links para outros traces, como no consumo de uma fila. O contexto é a informação que viaja pela requisição mantendo a ligação entre os spans: trace id, span id e flags de amostragem. Sem propagação de contexto, cada serviço geraria traces soltos e a reconstrução seria impossível. A qualidade do tracing depende menos da ferramenta de visualização e mais da disciplina de instrumentação: toda chamada entre serviços deve criar spans no cliente e no servidor, e todo acesso a banco e a fila deve aparecer como span filho.

Um ponto de disciplina importante é a granularidade dos nomes de span. O OpenTelemetry publica convenções semânticas com nomes padronizados, como GET /pedidos/{id} para spans HTTP e SELECT pagamentos para spans de banco, o que permite que agregações e service maps agrupem spans equivalentes de instâncias diferentes. Nomes com valores dinâmicos embutidos, como GET /pedidos/88213, fragmentam a análise e violam o mesmo princípio de cardinalidade que rege as métricas.

Propagação de contexto com W3C TraceContext

A propagação é feita por headers HTTP ou propriedades de mensagem que viajam junto com a requisição. O padrão W3C TraceContext se consolidou como formato universal, e em 2025 o OpenTelemetry adotou o W3C TraceContext Level 2 como base de amostragem consistente entre SDKs e Collectors, o que garante que a decisão de amostrar ou não um trace seja respeitada de ponta a ponta. O formato usa dois headers principais, como mostra o exemplo.

# Headers W3C TraceContext enviados pelo serviço A ao serviço B
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
#           versão-trace id-span id-flags, onde 01 indica trace amostrado
tracestate: vendor=acme-observability
#           Estado adicional de fornecedores, preservado ao longo do caminho

O SDK recebe o header, cria um span filho apontando para o span id recebido e repassa um novo header com o span id atualizado. O mesmo contexto precisa ser propagado em mensagens assíncronas, injetando os atributos nos metadados de eventos Kafka, mensagens SQS e payloads de jobs, caso contrário o trace se quebra exatamente no limite que mais interessa investigar. Bibliotecas de instrumentação automática do OpenTelemetry fazem essa injeção para os protocolos mais comuns, mas integrações próprias precisam propagar o contexto manualmente.

OpenTelemetry como instrumentação neutra

O OpenTelemetry unificou métricas, logs e traces em um único conjunto de APIs, SDKs e protocolos, encerrando a era de instrumentação acoplada a um vendor específico. A arquitetura de aplicação usa a API para criar spans, o SDK configura processamento, amostragem e exportação, e o Collector é o processo independente que recebe telemetria de todos os serviços, aplica filtragem, processamento em lote e amostragem tail, e encaminha para o backend escolhido. O acoplamento do código é apenas com a API, então trocar de Jaeger para Datadog ou X-Ray muda configuração, não código. Essa neutralidade é o principal motivo para que novos projetos instrumentem com OpenTelemetry mesmo quando o backend atual é único.

# Configuração resumida do OpenTelemetry Collector
receivers:
  otlp:
    protocols:
      grpc:          # Serviços enviam spans via OTLP/gRPC
        endpoint: 0.0.0.0:4317
processors:
  batch:
    timeout: 5s      # Agrupa spans em lotes para eficiência de rede
  tail_sampling:
    decision_wait: 30s
    policies:
      - name: erros-completos   # Mantém todos os traces com erro
        type: status_code
        status_code: {status_codes: [ERROR]}
      - name: latencia-alta     # Mantém traces acima de 2 segundos
        type: latency
        latency: {threshold_ms: 2000}
      - name: base-probabilistica  # E amostra uma fração do restante
        type: probabilistic
        probabilistic: {sampling_percentage: 10}
exporters:
  otlp/jaeger:
    endpoint: jaeger-collector.observability:4317

Sampling: head versus tail

Amostragem é o mecanismo que controla quanto dos traces é de fato retido, e existe porque instrumentar tudo com 100 por cento de retenção é proibitivo em custo para sistemas de alto tráfego. As duas estratégias fundamentais funcionam de forma oposta.

Estratégia Quando decide Vantagem Limitação
Head sampling No início, antes do trace existir Previsível, sem custo de buffer Pode descartar exatamente os traces problemáticos
Tail sampling No fim, com o trace completo Retém 100 por cento de erros e outliers Exige Collector com memória e decisão atrasada

A prática recomendada combina as duas: head sampling probabilístico baixo para tráfego normal, complementado por regras de tail que garantem retenção integral de traces com erro, latência acima de um limiar e traces de rotas críticas. Outra técnica valiosa é a amostragem por prioridade de usuário, mantendo taxa integral para operações de pagamento, por exemplo, e reduzindo para navegação de catálogo.

O item que conecta as duas estratégias é a flag de decisão carregada no traceparent, que indica desde a origem se o trace será amostrado. Com o W3C TraceContext Level 2 adotado pelo OpenTelemetry, essa decisão fica consistente entre SDKs e Collectors, eliminando o comportamento antigo em que o cliente amostarava e o servidor decidia outra coisa, gerando traces parciais sem a parte mais importante.

Jaeger, Zipkin e X-Ray

Os três backends resolvem o mesmo problema com perfis de adoção distintos, e a comparação abaixo resume os pontos de decisão.

Aspecto Jaeger Zipkin AWS X-Ray
Origem Uber, CNCF graduado Twitter, pioneiro do modelo Nativo AWS
Protocolo OTLP nativo Zipkin v2 e compatibilidade OTLP Protocolo próprio via daemon
Armazenamento Cassandra, Elasticsearch, ClickHouse Memória, Elasticsearch, MySQL Gerenciado pela AWS
Service map Sim, nativo Parcial, por extensão Sim, console integrado
Operação Auto-hospedado ou via produto gerenciado Auto-hospedado, leve Zero operação, custo por trace
Cenário ideal Kubernetes com stack Grafana open source Sistemas legados compatíveis com Zipkin Arquiteturas fortemente AWS, com Lambda

Jaeger é o padrão de facto em ambientes cloud native e recebe spans diretamente via OTLP, sem conversores. Zipkin mantém relevância em bases legadas e em bibliotecas que já falam seu formato. X-Ray se destaca pela integração com Lambda, API Gateway e ELB, onde a instrumentação automática dos serviços AWS aparece no mesmo trace da aplicação.

Aplicação prática: investigação de latência

O fluxo típico de investigação começa em um alerta de p99 de latência na métrica de um endpoint. A partir daí, o analista abre o tracing, filtra traces da mesma operação no período de degradação e ordena por duração, revelando rapidamente o formato do problema. O waterfall mostra o span culpado e sua hierarquia, e três padrões aparecem com frequência.

  • Latência somada em cascata, quando várias chamadas síncronas sequenciais a serviços externos acumulam dezenas de milissegundos cada, e o caminho de correção é paralelizar chamadas independentes ou introduzir cache.
  • Fila de consumidor saturada, visível quando o span de processamento é curto, mas o intervalo entre a publicação e o início do consumo é longo, indicando backlog ou prefetch mal dimensionado.
  • Timeout oculto, quando o span do cliente termina por timeout, mas o serviço chamado continuou processando, sinal de que o trabalho deve ser tornado idempotente e o deadline propagado.

Cada um desses padrões leva a uma decisão arquitetural, não apenas a um ajuste de configuração, e é por isso que tracing costuma ser a ferramenta que mais acelera a evolução da arquitetura: ele torna visível o custo real de cada decisão de acoplamento tomada meses antes.

Service map e análise de dependências

Além do waterfal por trace, os backends constroem um service map, um grafo dos serviços com arestas pesadas por volume, taxa de erro e latência. Esse mapa é a melhor representação da arquitetura real, muitas vezes diferente da arquitetura documentada, e é capaz de revelar dependências esquecidas, chamadas circulares e serviços órfãos. Em um incidente, o mapa indica imediatamente qual aresta degradau, reduzindo o tempo de localização de causa. A leitura disciplinada do service map também alimenta decisões de arquitetura: arestas com latência dominante candidatam-se a cache ou troca para comunicação assíncrona, e nós com alta fan-out de chamadas síncronas indicam acoplamento excessivo.

Para que o mapa seja confiável, o nome do serviço precisa vir de um único ponto de configuração por processo, normalmente a variável OTEL_SERVICE_NAME ou o resource do SDK, e não de valores espalhados pelo código. Nomes inconsistentes do mesmo serviço dividem o nó em dois e distorcem o grafo, o mesmo fenômeno de duplicidade que afeta qualquer modelagem de dependências.

Erros comuns

Os problemas abaixo aparecem com frequência em implantações iniciais de tracing.

  • Não propagar contexto em chamadas assíncronas, quebrando o trace justamente no trecho entre produtor e consumidor.
  • Criar um span por operação trivial, como cada leitura de cache local, inflando custo e ruído sem valor de diagnóstico.
  • Amostragem head de 100 por cento sem política de custo, seguida de descarte silencioso no Collector quando o volume explode.
  • Atributos sensíveis em spans, como tokens e payloads completos, transformando a ferramenta de diagnóstico em risco de segurança.
  • Traces órfãos gerados por jobs agendados que iniciam contexto novo sem ligação com o gatilho original, dificultando a análise.
  • Ignorar a amostragem no planejamento de capacidade do Collector, que exige memória proporcional ao número de traces simultâneos em decisão de tail.

Boas práticas

O conjunto abaixo consolida a adoção madura de tracing distribuído.

  • Instrumente com OpenTelemetry desde o primeiro dia, usando instrumentação automática para frameworks e spans manuais apenas nos pontos de negócio relevantes.
  • Padronize atributos de span com as convenções semânticas do OpenTelemetry, incluindo nome do serviço, versão, ambiente e destinatário da chamada.
  • Combine head sampling probabilístico com tail sampling de erros e outliers, com metas de custo revisadas junto ao volume de tráfego.
  • Garanta que o trace id apareça em todos os logs do fluxo, criando a ponte de investigação entre logs e traces.
  • Revise o service map mensalmente como exercício de arquitetura, tratando arestas inesperadas como dívida técnica documentada.
  • Valide a propagação de contexto em testes de integração entre serviços, cobrindo também o caminho assíncrono via filas e tópicos.
  • Defina um orçamento de telemetria por serviço, com limite de spans por trace revisado junto à complexidade do fluxo de negócio.