O Apache Kafka é uma plataforma de streaming de eventos de código aberto para a captura de dados em tempo real, usada por milhares de empresas, incluindo a New Relic. É distribuída, altamente escalável, e tolerante a falhas, mas pode ser um desafio monitorar conjuntos Kafka. Idealmente, você deveria usar o rastreamento distribuído para rastrear solicitações em seu sistema, mas o Kafka separa produtores e consumidores, o que significa que não há transações diretas para rastrear entre eles. O Kafka também usa processos assíncronos, que têm dependências implícitas, e dependências não explícitas. Isso torna difícil entender como os seus microsserviços estão funcionando juntos.

No entanto, é possível monitorar conjuntos Kafka com o rastreamento distribuído e a OpenTelemetry. Você pode, então, analisar e visualizar os seus rastreamentos em uma ferramenta de rastreamento distribuído de código aberto como a Jaeger, ou em uma plataforma completa de observabilidade, como a New Relic. Neste blogpost, você aprenderá sobre os benefícios de monitorar o Kafka com o rastreamento distribuído, e como você mesmo pode configurá-lo.

NEW RELIC INTEGRAÇÃO KAFKA
kafka logo
Comece a monitoramento seus dados Kafka hoje mesmo.
Instale o início rápido do Kafka Instale o início rápido do Kafka

Porquê a New Relic rastreia os dados do Kafka

A New Relic é uma plataforma de observabilidade full stack, que ingere mais de 200 petabytes por mês, ou aproximadamente nove milhões de pontos de dados por segundo. São muitos dados.

Para ingerir esses dados sem a perda dos mesmos, a New Relic usa o Apache Kafka para salvar dados provenientes de agentes em armazenamento durável."

New Relic usa Kafka para processar dados de telemetria.

Depois que os dados são gravados no Kafka, muitos serviços diferentes os agregam, normalizam, decoram, e transformam, antes de serem gravados em nosso banco de dados. Há muitas coisas que podem dar errado durante o processo, por isso precisamos monitorar a nossa instalação Kafka de perto, e corrigir problemas rapidamente.

É aí que entra o rastreamento distribuído.

As equipes de engenharia que trabalham na plataforma de dados de telemetria (TDP) da New Relic usam o rastreamento distribuído para otimizar o que chamamos de time to glass. O time to glass mede quanto tempo leva para que os dados ingeridos se tornem consultáveis para os nossos clientes."

Benefícios do rastreamento distribuído com o Kafka

Vamos dar uma olhada em alguns dos benefícios que você terá ao usar o rastreamento distribuído com o Kafka.

Identifique gargalos em seu pipeline de streaming

Sem o rastreamento distribuído, você precisa correlacionar manualmente os logs entre serviços e soluções SaaS. Com o rastreamento distribuído, você não precisa correlacionar manualmente os seus logs. Você pode rastrear a jornada de uma solicitação em seus sistemas, revelando gargalos instantaneamente.

Obtenha visibilidade dos impactos posteriores aos seus serviços

Mesmo pequenas alterações em um serviço no pipeline de streaming têm o potencial de interromper dependências upstream e downstream. Com o rastreamento distribuído, você pode diminuir as interrupções rapidamente.

Rastreando a perda de dados

Um dos desafios do Kafka em escala é rastrear a perda de dados. E se um corretor Kafka excluir dados após o tempo de retenção configurado ter passado? Esses dados desapareceram, e é difícil saber o que aconteceu, e quantos dados estão faltando. Com o rastreamento distribuído, você pode medir como, e onde, ocorre a perda de dados, procurando rastreamentos com períodos órfãos.

Ajuste a pesquisa do consumidor

Os consumidores Kafka pesquisam os serviços upstream quando estão prontos para novas mensagens. Quando há muitas mensagens na fila, os consumidores param de solicitar novos registros, o que resulta em um maior atraso do consumidor.

O rastreamento distribuído pode ajudá-lo a otimizar a configuração do consumidor para encontrar um equilíbrio ideal entre latência e taxa de transferência. Você pode ajustar os intervalos da pesquisa com max.poll.interval.ms, e o total de mensagens a serem consumidas por pesquisa com max.poll.records e, em seguida, usar o rastreamento distribuído para ver como os seus consumidores são afetados.

Ajustando variáveis ​​env para produtores do Kafka

Você também pode usar o rastreamento distribuído para otimizar a configuração dos tamanhos de lote do produtor. Os produtores do Kafka tentam agrupar registros em menos solicitações quando vários registros estão sendo enviados para a mesma partição. O Kafka pode obter uma melhor compactação quando há mais mensagens em um lote, porque é provável que haja mais blocos de dados repetíveis para compactar. A variável batch.size controla o tamanho padrão do lote em bytes. Sempre que o número de mensagens em um lote atinge o limite definido por batch.size, ele é compactado e enviado para o corretor.

Um lote pequeno torna o processamento em lote menos comum, e pode reduzir o rendimento (um tamanho de lote igual a zero desabilitará totalmente o processamento em lote). Um lote muito grande pode usar mais memória, porque o tamanho do lote terá um buffer (do tamanho de lote) especificado para registros adicionais.

A variável linger.ms é o tempo máximo que um produtor do Kafka esperará por novas mensagens antes que elas sejam agrupadas em lote, e enviadas ao corretor. Por exemplo, definir linger.ms=5 reduz o número de solicitações enviadas, mas adiciona até 5ms de latência aos registros.

O rastreamento distribuído permite que você veja os impactos completos das alterações feitas nas configurações batch.size e linger.ms, ajudando a otimizar a compactação e o desempenho.

Como rastrear o Kafka com a OpenTelemetry

Para criar rastreamentos, o rastreamento distribuído usa contexto, que contém informações importantes sobre solicitações. No entanto, as mensagens Kafka não incluem contexto automaticamente, o que significa que você mesmo precisa adicioná-lo manualmente.

Felizmente, você pode instruir os seus produtores do Kafka a injetar contexto nos cabeçalhos das mensagens, que são apenas pares de valores-chave que contêm metadados, semelhantes aos cabeçalhos HTTP. Esta postagem mostrará como injetar contexto com a biblioteca Otelsarama do Shopify, mas você também pode usar bibliotecas oficiais do Kafka para injetar contexto. Para obter dados de rastreamento completos, você precisará injetar dados de span quando uma mensagem for criada (no nível do produtor) e quando a mensagem for consumida (no nível do consumidor).

Há também outra parte importante da ''equação'': você precisa adicionar código para que os seus consumidores do Kafka entendam o contexto de extensão que você adiciona. Você também pode usar Otelsarama para fazer isso.

Você pode encontrar todo o código neste repositório de exemplo.O código na ramificação principal pode ser usado para ativar automaticamente um produtor e um consumidor do Kafka, junto de uma visualização Jaeger. A ramificação new-relic inclui o código para conectar o repositório de amostra a New Relic. Se você quiser testar o código na ramificação principal, precisará ter o Docker e o Golang instalados. Basta clonar o repositório e executar o seguinte comando no diretório raiz do projeto para ativar o Apache Zookeeper, Kafka e Jaeger.

docker-compose up -d zoo kafka jaeger

Observe que se você quiser executar este exemplo com a New Relic, existem algumas variáveis de ambiente adicionais para configurar, que serão abordadas posteriormente neste blogpost. Caso contrário, o código para ambas as implementações é muito semelhante.

Em seguida, você precisa iniciar o produtor e o consumidor do Kafka com os seguintes comandos.

De dentro do diretório do produtor, execute:

go run producer.go

Então, de dentro do diretório do consumidor, execute:

go run consumer.go

Agora vamos ver o que o código faz.

Injetando contexto em mensagens do produtor do Kafka

Os produtores do Kafka não incluem dados de extensão nos cabeçalhos das mensagens, mas a biblioteca Otelsarama fornece a função WrapAsyncProducer, que faz exatamente o que o nome indica. Ele adiciona um wrapper a um produtor do Kafka assíncrono, que fornece novas funcionalidades. A função usa o design do propagador padrão, o que significa que os propagadores são usados para adicionar contexto a partes de um sistema que, de outra forma, são independentes. Nesse caso, o produtor envolto usa o padrão propagador para passar um intervalo exclusivo em uma mensagem. O contexto nesse intervalo é passado downstream, acumulando mais contexto ao longo do caminho, para que nada seja perdido—exatamente o que você precisa para o rastreamento distribuído.Aqui está o código que envolve um produtor do Kafka assíncrono e fornece essa funcionalidade. O código completo do produtor pode ser encontrado neste arquivo:

producer = otelsarama.WrapAsyncProducer(config, producer,otelsarama.WithTracerProvider(tracerProvider),otelsarama.WithPropagators(propagators))

Este código envolve o produtor do Kafka para que todas as mensagens que ele produz sejam associadas a um intervalo, o que permite que os serviços downstream extraiam o intervalo-pai e criem novos intervalo-filhos.

otelsarama.WithPropagators é passado como um argumento, porque o consumidor deve usar os propagadores do produtor, não do provedor global, para criar um rastreamento contínuo.

Agora que o seu produtor está criando mensagens com intervalos que podem ser rastreados, você precisa realmente transmitir o contexto que torna esses rastreamentos úteis. Você pode injetar contexto como cabeçalho em mensagens do Kafka com (&msg) usando a API OTel Propagators. Abaixo está o código que faz isso:

propagators := propagation.TraceContext{}
propagators.Inject(ctx, otelsarama.NewProducerMessageCarrier(&msg))
producer.Input() <- &msg

A primeira linha deste código registra o propagador TraceContext do OTel globalmente. Em seguida, o código usa o método Inject do OTel junto com otelsarama.NewProducerMessageCarrier para injetar o contexto de intervalo diretamente nos cabeçalhos da mensagem Kafka.

Depois que o contexto é adicionado a uma mensagem, o produtor a envia ao corretor do Kafka.

Você pode ver os cabeçalhos de suas mensagens do Kafka instalando o kcat, uma CLI para visualizar tópicos e partições do Kafka.Se você instalou o kcat, e está executando o repositório, você pode executar o seguinte comando:

kcat -b localhost:9092 -t kafkademo

Suas mensagens devem incluir os cabeçalhos relevantes. A próxima imagem mostra que os cabeçalhos das mensagens agora incluem chaves tracestate e traceparent.

Permitindo que os consumidores do Kafka leiam o contexto da mensagem recebida

Você está na metade do caminho. Os dados de extensão de uma mensagem também devem incluir o contexto sobre quando a mensagem é consumida. Os dados de extensão que são injetados quando um produtor cria uma mensagem não fornecerão um rastreamento completo—apenas informam que uma mensagem foi criada. Quando um consumidor processa uma declaração, precisa registrar que a mensagem foi processada com êxito. Isso significa que você precisa injetar dados de intervalo de pós-processamento em cada mensagem.

A função startConsumerGroup em consumer.go usa a biblioteca Otelsarama para envolver os consumidores com a funcionalidade necessária para adicionar contexto adicional às mensagens recebidas. Em seguida, ele usa a biblioteca sarama do Shopify para instanciar um novo grupo de consumidores, e começar a consumir mensagens. Esta é a linha de código que permite aos consumidores adicionar contexto às mensagens:

handler := otelsarama.WrapConsumerGroupHandler(&consumerGroupHandler,otelsarama.WithPropagators(propagators))

Lembra daquele intervalo-pai que o produtor cria para cada mensagem? Nesse caso, a OpenTelemetry está extraindo esse intervalo-pai. A próxima etapa é adicionar um intervalo-filho com novo contexto:

_, span := openTelemetryTracer.Start(ctx, "consume message")

O openTelemetryTracer é o rastreador OpenTelemetry instanciado, que adiciona “consume message” ao contexto (ctx). Ao usar o modelo propagador, a OpenTelemetry pode injetar contexto onde quer que você instrumente manualmente o seu código, fornecendo dados de rastreamento detalhados conforme se move por cada serviço downstream.

Visualizando os dados em Jaeger

Finalmente, o código inclui um exportador Jaeger, para que você possa visualizar os rastreamentos com Jaeger em localhost:16686. Não entraremos em detalhes sobre como adicionar um exportador Jaeger. Basta executar o código, e verificar você mesmo o painel.

Você pode ver a jornada, do início ao fim, de uma solicitação em um gráfico em cascata, com a latência associada a cada solicitação, tal qual outros metadados como tópico de destino, ID da mensagem e partição em Jaeger.

Visualizando os dados na New Relic

Se você deseja visualizar e analisar os seus rastreamentos distribuídos do Kafka em escala, deve usar uma plataforma de observabilidade como a New Relic. Para experimentar este repositório com a New Relic, você precisa de uma conta e uma chave de licença.

Antes de ativar a imagem do Docker na ramificação new-relic, primeiro você precisa definir duas variáveis ambientais adicionais nos arquivos .env de consumer/.env e producer/.env:

OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp.nr-data.net:4317OTEL_EXPORTER_OTLP_HEADERS="api-key=<your_license_key>"

Substitua no trecho de código anterior pela sua chave de licença.

Esta implementação também utiliza a biblioteca Otelsarama, e o código é muito semelhante. A principal diferença (além das variáveis de ambiente adicionais) é que o provedor de rastreamento precisa ser configurado para usar o exportador de rastreamento OTLP ao invés do exportador Jaeger.

Se você observar o código com atenção, também verá que tanto consumer.go quanto producer.go incluem a seguinte linha em suas funções tracerProvider():

ctx := contexto.Background()

Isso ocorre porque a biblioteca de rastreamento OTLP exige que o contexto seja transmitido.

Depois de adicionar variáveis de ambiente, você pode ativar a imagem do Docker com o seguinte comando:

docker-compose up -d zoo kafka

A próxima imagem mostra vestígios do Kafka entrando na New Relic.

Conclusão

O rastreamento distribuído é uma ferramenta poderosa, que pode ajudá-lo a identificar rapidamente gargalos de desempenho, e a reduzir o tempo médio de resolução (MTTR). No entanto, não é uma tarefa fácil de implementar, principalmente em escala. É necessário muito trabalho multifuncional para que o rastreamento seja implementado com certeza em cada serviço, etapa e estágio do seu pipeline de streaming. Além disso, será necessário processar muitos dados. Mesmo quando você amostra as suas solicitações para cada 10.000, 50.000 ou 100.000 rastreamentos, muitos dados poderiam estar duplicados.

Quando você implementa o rastreamento distribuído com o Kafka, ele ajuda a obter melhor visibilidade dos problemas, e a otimizar o seu pipeline de ingestão de dados. Apenas com este exemplo de código, você pode coletar dados importantes sobre quando as mensagens são produzidas e consumidas, fornecendo informações valiosas sobre como otimizar os seus produtores e consumidores do Kafka para atingir desempenho máximo.