Controle Seus Serviços Com OTEL, Jaeger e Prometheus

Vamos discutir uma questão importante: como monitoramos nossos serviços se algo der errado?

Por um lado, temos Prometheus com alertas e Kibana para dashboards e outras funcionalidades úteis. Também sabemos como coletar logs — a pilha ELK é nossa solução preferida. No entanto, o registro simples nem sempre é suficiente: ele não fornece uma visão holística da jornada de uma requisição através de todo o ecossistema de componentes.

Você pode encontrar mais informações sobre ELK aqui.

Mas e se quisermos visualizar as requisições? E se precisarmos correlacionar requisições que transitam entre sistemas? Isso se aplica tanto a microsserviços quanto a monólitos — não importa quantos serviços temos; o que importa é como gerenciamos sua latência.

De fato, cada requisição de usuário pode passar por toda uma cadeia de serviços independentes, bancos de dados, filas de mensagens e APIs externas.

Em um ambiente tão complexo, torna-se extremamente difícil identificar exatamente onde ocorrem os atrasos, descobrir qual parte da cadeia atua como um gargalo de desempenho e encontrar rapidamente a causa raiz das falhas quando elas acontecem.

Para enfrentar esses desafios de forma eficaz, precisamos de um sistema centralizado e consistente para coletar dados de telemetria — rastros, métricas e logs. É aqui que o OpenTelemetry e o Jaeger entram em cena.

Vamos Olhar para os Fundamentos

Existem dois termos principais que precisamos entender:

ID de Rastreio

Um ID de Rastreio é um identificador de 16 bytes, frequentemente representado como uma string hexadecimal de 32 caracteres. Ele é gerado automaticamente no início de um rastreamento e permanece o mesmo em todas as spans criadas por uma solicitação específica. Isso facilita a visualização de como uma solicitação percorre diferentes serviços ou componentes em um sistema.

ID de Span

Cada operação individual dentro de um rastreamento recebe seu próprio ID de Span, que é tipicamente um valor de 64 bits gerado aleatoriamente. As spans compartilham o mesmo ID de Rastreio, mas cada uma tem um ID de Span exclusivo, permitindo que você identifique exatamente qual parte do fluxo de trabalho cada span representa (como uma consulta a um banco de dados ou uma chamada para outro microsserviço).

Como Eles Estão Relacionados?

ID de Rastreio e ID de Span se complementam. 

Quando uma solicitação é iniciada, um ID de Rastreio é gerado e passado para todos os serviços envolvidos. Cada serviço, por sua vez, cria uma span com um ID de Span único vinculado ao ID de Rastreio, permitindo que você visualize todo o ciclo de vida da solicitação do início ao fim.

Certo, então por que não usar apenas o Jaeger? Por que precisamos do OpenTelemetry (OTEL) e todas as suas especificações? Essa é uma ótima pergunta! Vamos desmembrá-la passo a passo.

Saiba mais sobre o Jaeger aqui.

Resumindo

  • Jaeger é um sistema para armazenar e visualizar rastros distribuídos. Ele coleta, armazena, pesquisa e exibe dados mostrando como as requisições “viajam” pelos seus serviços.
  • OpenTelemetry (OTEL) é um padrão (e um conjunto de bibliotecas) para coletar dados de telemetria (rastros, métricas, logs) de suas aplicações e infraestrutura. Não está vinculado a nenhuma ferramenta de visualização ou backend específico.

Resumindo:

  • OTEL é como uma “língua universal” e um conjunto de bibliotecas para coleta de telemetria.
  • Jaeger é um backend e UI para visualizar e analisar rastros distribuídos.

Por que precisamos do OTEL se já temos o Jaeger?

1. Um Padrão Único para Coleta

No passado, havia projetos como OpenTracing e OpenCensus. O OpenTelemetry unifica essas abordagens para coletar métricas e rastros em um único padrão universal.

2. Integração Fácil

Você escreve seu código em Go (ou outra linguagem), adiciona bibliotecas do OTEL para injetar automaticamente interceptores e spans, e é isso. Depois, não importa para onde você quer enviar esses dados—Jaeger, Tempo, Zipkin, Datadog, um backend personalizado—o OpenTelemetry cuida da parte técnica. Você apenas troca o exportador.

3. Não Apenas Rastros

O OpenTelemetry abrange rastros, mas também lida com métricas e logs. Você acaba com um único conjunto de ferramentas para todas as suas necessidades de telemetria, não apenas para rastreamento.

4. Jaeger como um Backend

Jaeger é uma excelente escolha se você está principalmente interessado na visualização de rastreamento distribuído. Mas ele não fornece a instrumentação entre linguagens por padrão. OpenTelemetry, por outro lado, oferece uma maneira padronizada de coletar dados, e então você decide para onde enviá-los (incluindo Jaeger).

Na prática, eles frequentemente trabalham juntos:

Seu aplicativo usa OpenTelemetry → se comunica via protocolo OTLP → vai para o Coletor OpenTelemetry (HTTP ou grpc) → exporta para Jaeger para visualização.


Parte Técnica

Design do Sistema (Um Pouco)

Vamos rapidamente esboçar alguns serviços que farão o seguinte:

  1. Serviço de Compra – processa um pagamento e o registra no MongoDB
  2. CDC com Debezium – escuta por alterações na tabela MongoDB e as envia para o Kafka
  3. Processador de Compra – consome a mensagem do Kafka e chama o Serviço de Autenticação para procurar o user_id para validação
  4. Serviço de Autenticação – um serviço simples de usuários

No resumo:

  • 3 serviços Go
  • Kafka
  • CDC (Debezium)
  • MongoDB

Parte do Código

Vamos começar com a infraestrutura. Para unir tudo em um sistema, vamos criar um grande arquivo Docker Compose. Começaremos configurando a telemetria.

Nota: Todo o código está disponível através de um link no final do artigo, incluindo a infraestrutura.

YAML

 

Também vamos configurar o coletor — o componente que reúne a telemetria.

Aqui, escolhemos gRPC para transferência de dados, o que significa que a comunicação ocorrerá via HTTP/2:

YAML

 

Certifique-se de ajustar quaisquer endereços conforme necessário, e você terminou a configuração básica.

Já sabemos que o OpenTelemetry (OTEL) utiliza dois conceitos chave — ID de Rastreio e ID de Span — que ajudam a rastrear e monitorar solicitações em sistemas distribuídos.

Implementando o Código

Agora, vamos ver como fazer isso funcionar no seu código Go. Precisamos das seguintes importações:

Go

 

Então, adicionamos uma função para inicializar nosso rastreador em main() quando a aplicação inicia:

Go

 

Com o rastreamento configurado, só precisamos inserir spans no código para rastrear chamadas. Por exemplo, se quisermos medir as chamadas ao banco de dados (já que esse é geralmente o primeiro lugar que olhamos para problemas de desempenho), podemos escrever algo assim:

Go

 

Temos rastreamento na camada de serviço — ótimo! Mas podemos ir ainda mais fundo, instrumentando a camada do banco de dados:

Go

 

Agora, temos uma visão completa da jornada da solicitação. Acesse a interface do Jaeger, consulte os últimos 20 rastreamentos sob auth-service, e você verá todos os spans e como eles se conectam em um só lugar.

Agora, tudo está visível. Se você precisar, pode incluir a consulta completa nas tags. No entanto, tenha em mente que você não deve sobrecarregar sua telemetria — adicione dados de forma deliberada. Estou apenas demonstrando o que é possível, mas incluir a consulta completa, dessa forma, não é algo que eu geralmente recomendaria.

Cliente-servidor gRPC

Se você quiser ver um rastreamento que abrange dois serviços gRPC, é bastante simples. Tudo o que você precisa fazer é adicionar os interceptores prontos para uso da biblioteca. Por exemplo, no lado do servidor:

Go

 

No lado do cliente, o código é igualmente curto:

Go

 

É isso! Certifique-se de que seus exportadores estão configurados corretamente e você verá um único ID de Rastreio registrado nesses serviços quando o cliente chamar o servidor.

Tratamento de Eventos CDC e Rastreio

Quer lidar com eventos do CDC também? Uma abordagem simples é incorporar o ID de Rastreio no objeto que o MongoDB armazena. Dessa forma, quando o Debezium captura a mudança e a envia para o Kafka, o ID de Rastreio já faz parte do registro.

Por exemplo, se você estiver usando o MongoDB, pode fazer algo assim:

Go

 

O Debezium então captura este objeto (incluindo trace_id) e o envia para o Kafka. No lado do consumidor, você simplesmente analisa a mensagem recebida, extrai o trace_id e o mescla no seu contexto de rastreamento:

Go

 

Go

 

Alternativa: Usando Cabeçalhos do Kafka

Às vezes, é mais fácil armazenar o ID de Rastreio nos cabeçalhos do Kafka em vez de no próprio payload. Para fluxos de trabalho de CDC, isso pode não estar disponível “pronto para uso” — o Debezium pode limitar o que é adicionado aos cabeçalhos. Mas se você controla o lado do produtor (ou se está usando um produtor padrão do Kafka), você pode fazer algo assim com o Sarama:

Injetando um ID de Rastreio nos Cabeçalhos

Go

 

Extraindo um ID de Rastreio no Lado do Consumidor

Go

 

Dependendo do seu caso de uso e como seu pipeline de CDC está configurado, você pode escolher a abordagem que funciona melhor:

  1. Incorporar o ID de Rastreio no registro do banco de dados para que flua naturalmente via CDC.
  2. Usar cabeçalhos do Kafka se você tiver mais controle sobre o lado do produtor ou quiser evitar inflar o payload da mensagem.

De qualquer forma, você pode manter seus rastros consistentes entre vários serviços — mesmo quando os eventos são processados de forma assíncrona via Kafka e Debezium.

Conclusão

Usar OpenTelemetry e Jaeger fornece rastros de requisição detalhados, ajudando você a identificar onde e por que ocorrem atrasos em sistemas distribuídos.

Adicionar o Prometheus completa o quadro com métricas — indicadores-chave de desempenho e estabilidade. Juntas, essas ferramentas formam uma pilha de observabilidade abrangente, possibilitando uma detecção e resolução de problemas mais rápidas, otimização de desempenho e confiabilidade geral do sistema.

Posso dizer que essa abordagem acelera significativamente a solução de problemas em um ambiente de microserviços e é uma das primeiras coisas que implementamos em nossos projetos.

Links

Source:
https://dzone.com/articles/control-services-otel-jaeger-prometheus