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 o Prometheus com alertas e o Kibana para painéis e outras funcionalidades úteis. Também sabemos como coletar logs — o conjunto ELK é nossa solução padrão. No entanto, apenas o registro simples nem sempre é suficiente: ele não fornece uma visão holística da jornada de uma solicitação em todo o ecossistema de componentes.

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

Mas e se quisermos visualizar solicitações? E se precisarmos correlacionar solicitações que viajam 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 solicitaçã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 atrasos, identificar qual parte da cadeia atua como gargalo de desempenho e encontrar rapidamente a causa raiz das falhas quando ocorrem.

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

Vamos Olhar para os Básicos

Existem dois termos principais que precisamos entender:

ID de Rastro

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

ID de Span

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

Como Eles Estão Relacionados?

ID de Rastro e ID de Span se complementam. 

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

Ok, 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 detalhar passo a passo.

Saiba mais sobre o Jaeger aqui.

Resumindo

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

Em resumo:

  • OTEL é como uma “linguagem universal” e conjunto de bibliotecas para coleta de telemetria.
  • Jaeger é um backend e interface de usuário para visualizar e analisar rastreamentos distribuídos.

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

1. Um Único Padrão para Coleta

No passado, existiam projetos como OpenTracing e OpenCensus. O OpenTelemetry unifica essas abordagens para coleta de métricas e rastreamentos em um único padrão universal.

2. Integração Fácil

Você escreve seu código em Go (ou outra linguagem), adiciona bibliotecas OTEL para autoinjetar interceptadores e spans, e pronto. Depois, não importa para onde você queira enviar esses dados—Jaeger, Tempo, Zipkin, Datadog, um backend personalizado—o OpenTelemetry cuida disso. Você só precisa trocar o exportador.

3. Não Apenas Rastreamentos

O OpenTelemetry cobre rastreamentos, 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 rastreamento.

4. Jaeger como um Backend

Jaeger é uma excelente escolha se você está principalmente interessado na visualização de rastreamento distribuído. Mas não fornece a instrumentação de linguagem cruzada por padrão. Por outro lado, o OpenTelemetry 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 → comunica via protocolo OTLP → vai para o Coletor OpenTelemetry (HTTP ou grpc) → exporta para o 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 – ouve as mudanças 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 pesquisar o user_id para validação
  4. Serviço de Autenticação – um simples serviço de usuário

Em resumo:

  • 3 serviços em 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 iremos configurar o coletor — o componente que reúne telemetria.

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

YAML

 

Verifique se ajustou quaisquer endereços conforme necessário, e você concluiu a configuração básica.

Já sabemos que o OpenTelemetry (OTEL) utiliza dois conceitos-chave — ID de Rastreamento 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

 

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

Go

 

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

Go

 

Temos o 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. Vá para a interface do Jaeger, consulte as últimas 20 traces em auth-service, e você verá todos os spans e como se conectam em um só lugar.

Agora, tudo está visível. Se precisar, pode incluir a consulta inteira nas tags. No entanto, lembre-se de não sobrecarregar sua telemetria — adicione dados de forma deliberada. Estou simplesmente demonstrando o que é possível, mas incluir a consulta completa dessa forma não é algo que eu geralmente recomendaria.

Cliente-Servidor gRPC

Se você deseja ver um rastreamento que abrange dois serviços gRPC, é bastante simples. Tudo que você precisa fazer é adicionar os interceptadores prontos da biblioteca. Por exemplo, do lado do servidor:

Go

 

No lado do cliente, o código é tão curto quanto:

Go

 

É isso! Certifique-se de que seus exportadores estejam configurados corretamente, e você verá um único ID de Rastreamento registrado entre esses serviços quando o cliente chama o servidor.

Manipulando Eventos do CDC e Rastreamento

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

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

Go

 

O Debezium então pega esse objeto (incluindo trace_id) e o envia para o Kafka. Do lado do consumidor, basta analisar a mensagem recebida, extrair o trace_id e mesclá-lo em seu contexto de rastreamento:

Go

 

Go

 

Alternativa: Usando Cabeçalhos do Kafka

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

Inserindo um ID de Rastreamento nos Cabeçalhos

Go

 

Extraindo um ID de Rastreamento no Lado do Consumidor

Go

 

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

  1. Incorpore o ID de Rastreamento no registro do banco de dados para que ele flua naturalmente via CDC.
  2. Use os cabeçalhos do Kafka se tiver mais controle sobre o lado do produtor ou se quiser evitar inflar a carga da mensagem.

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

Conclusão

O uso do OpenTelemetry e do Jaeger fornece rastreamentos detalhados de solicitações, ajudando a identificar onde e por que ocorrem atrasos em sistemas distribuídos.

A adição do Prometheus completa o quadro com métricas — indicadores-chave de desempenho e estabilidade. Juntos, essas ferramentas formam um conjunto abrangente de observabilidade, permitindo uma detecção e resolução mais rápidas de problemas, 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 microsserviços e é uma das primeiras coisas que implementamos em nossos projetos.

Links

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