Tracing distribuído no Kubernetes com Jaeger ou Tempo
1. Fundamentos do Tracing Distribuído em Ambientes Cloud-Native
Em arquiteturas de microsserviços executadas no Kubernetes, a observabilidade tradicional baseada apenas em métricas e logs mostra-se insuficiente. Uma requisição que atravessa dezenas de serviços pode falhar em qualquer ponto, e sem um mapa claro do fluxo, o debug se torna um exercício de adivinhação.
O tracing distribuído resolve esse problema introduzindo três conceitos fundamentais:
- Span: unidade de trabalho individual dentro de um serviço (ex: consulta ao banco, chamada HTTP)
- Trace: conjunto de spans que representam o caminho completo de uma requisição
- Context Propagation: mecanismo que transporta o identificador do trace entre serviços via headers HTTP/gRPC
O OpenTelemetry tornou-se o padrão da indústria para instrumentação, oferecendo SDKs unificados e um formato de exportação chamado OTLP (OpenTelemetry Protocol). Tanto Jaeger quanto Tempo aceitam OTLP nativamente, permitindo que você troque o backend sem alterar o código da aplicação.
2. Arquitetura e Componentes do Jaeger no Kubernetes
O Jaeger é o sistema mais maduro de tracing distribuído, com componentes bem definidos:
Jaeger Collector: recebe spans, valida e armazena. Pode escalar horizontalmente.
Jaeger Query Service: API e UI para consultar traces armazenados.
Jaeger Agent: sidecar ou DaemonSet que faz buffering e roteamento de spans para o Collector.
Para armazenamento, as opções incluem Elasticsearch (recomendado para produção), Cassandra ou Badger (apenas all-in-one). A instalação no Kubernetes é simplificada pelo Jaeger Operator:
kubectl create namespace observability
kubectl apply -f https://github.com/jaegertracing/jaeger-operator/releases/latest/download/jaeger-operator.yaml -n observability
Após o operator estar pronto, crie uma instância Jaeger:
apiVersion: jaegertracing.io/v1
kind: Jaeger
metadata:
name: simplest
namespace: observability
spec:
strategy: production
storage:
type: elasticsearch
options:
es:
server-urls: http://elasticsearch:9200
O operator gerencia automaticamente deployments, services e configurações de rede.
3. Arquitetura e Componentes do Tempo no Kubernetes
Tempo é a alternativa da Grafana Labs, projetada para ser distribuída, escalável e de baixo custo. Seu diferencial é usar object storage (S3, GCS, Azure Blob) em vez de bancos relacionais ou de busca.
Componentes principais:
- Distributor: recebe spans via OTLP e os replica para os Ingesters
- Ingester: mantém spans em memória por blocos de tempo e faz flush para o storage
- Querier: busca traces no storage e nos Ingesters ativos
- Compactor: mescla blocos pequenos em blocos maiores para eficiência
A instalação via Helm é direta:
helm repo add grafana https://grafana.github.io/helm-charts
helm upgrade --install tempo grafana/tempo -n observability --create-namespace \
--set tempo.storage.trace.backend=s3 \
--set tempo.storage.trace.s3.bucket=tempo-traces \
--set tempo.storage.trace.s3.endpoint=minio:9000
4. Instrumentação de Aplicações com OpenTelemetry
A instrumentação pode ser feita manualmente (SDK) ou via auto-instrumentação (pacotes que interceptam chamadas HTTP, gRPC e banco de dados).
Exemplo em Python com auto-instrumentação:
pip install opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap -a install
Configure o exportador via variáveis de ambiente:
OTEL_EXPORTER_OTLP_ENDPOINT=http://tempo-distributor:4318
OTEL_SERVICE_NAME=meu-servico-python
OTEL_TRACES_SAMPLER=parentbased_traceidratio
OTEL_TRACES_SAMPLER_ARG=0.1
Para propagação manual em Go:
import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/propagation"
)
func handler(w http.ResponseWriter, r *http.Request) {
ctx := otel.GetTextMapPropagator().Extract(r.Context(), propagation.HeaderCarrier(r.Header))
tracer := otel.Tracer("meu-servico")
ctx, span := tracer.Start(ctx, "processar-requisicao")
defer span.End()
// lógica do serviço
}
5. Deploy e Configuração no Cluster Kubernetes
Para Jaeger com Helm:
helm repo add jaegertracing https://jaegertracing.github.io/helm-charts
helm install jaeger jaegertracing/jaeger -n observability \
--set collector.service.otlp.enabled=true \
--set storage.elasticsearch.host=elasticsearch
Para Tempo com Grafana Stack (inclui Grafana, Loki e Prometheus):
helm upgrade --install loki-stack grafana/loki-stack -n observability \
--set grafana.enabled=true \
--set tempo.enabled=true
Configure network policies para permitir tráfego OTLP:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-otlp
namespace: observability
spec:
podSelector:
matchLabels:
app: tempo-distributor
ingress:
- ports:
- port: 4318
6. Sampling, Performance e Boas Práticas
O sampling é crucial para controlar custos e desempenho:
- Head-based: decide no início do trace se ele será amostrado (ex: 10% de todas as requisições)
- Tail-based: amostra após o trace completo, permitindo capturar erros raros
- Rate-limiting: limita o número de spans por segundo por pod
Configuração de sampling no Tempo:
OTEL_TRACES_SAMPLER=parentbased_traceidratio
OTEL_TRACES_SAMPLER_ARG=0.05 # 5% das requisições
Para evitar sobrecarga de storage, defina retenção apropriada. No Tempo, configure blocos e retenção:
tempo:
retention: 336h # 14 dias
storage:
trace:
block:
bloom_filter_false_positive: 0.05
encoding: zstd
7. Debugging e Análise de Problemas com Traces
Com traces em mãos, é possível identificar gargalos. Na Jaeger UI, busque por http.status_code=500 ou error=true para encontrar falhas. Use tags como db.statement para ver queries lentas.
No Grafana Explore com Tempo:
{resource.service.name="api-gateway"} | latency > 500ms
Exemplo de análise: um trace mostra que o serviço pedidos leva 2s para responder. Dentro do trace, um span consulta-estoque ocupa 1.8s. Isso indica um problema no banco de dados de estoque ou na rede entre serviços.
8. Comparação Final: Jaeger vs. Tempo no Ecossistema DevOps
| Característica | Jaeger | Tempo |
|---|---|---|
| Armazenamento | Elasticsearch, Cassandra | Object storage (S3, GCS) |
| Maturidade | Muito maduro (2015+) | Crescendo rápido |
| Integração Grafana | Via plugin | Nativo |
| Custo de storage | Alto (ES indexa tudo) | Baixo (object storage) |
| Escalabilidade | Boa | Excelente |
| Complexidade operacional | Média (gerenciar ES) | Baixa |
Escolha Jaeger se você já tem Elasticsearch na stack, precisa de uma UI rica e madura, ou trabalha com times que já conhecem a ferramenta.
Escolha Tempo se prioriza baixo custo de armazenamento, escalabilidade massiva, ou já usa Grafana como ferramenta central de observabilidade.
Ambos integram bem com Prometheus (métricas) e Loki (logs), formando a tríade de observabilidade cloud-native.
Referências
- OpenTelemetry Documentation — Documentação oficial do padrão de instrumentação, incluindo SDKs e exporters OTLP
- Jaeger Operator for Kubernetes — Guia oficial de instalação e gerenciamento do Jaeger via operator no Kubernetes
- Grafana Temo Documentation — Documentação completa do Tempo, incluindo arquitetura, configuração e integração com Grafana
- Helm Chart: Jaeger Distribution — Repositório oficial do Helm chart para deploy do Jaeger no Kubernetes
- Helm Chart: Grafana Tempo Stack — Helm chart oficial para deploy do Tempo com integração Grafana e Loki
- OpenTelemetry Sampling Strategies — Explicação detalhada sobre estratégias de sampling head-based, tail-based e rate-limiting
- Grafana Labs Blog: Tempo vs Jaeger — Comparação prática entre Tempo e Jaeger com casos de uso reais