Optimisation du traçage Kafka avec OpenTelemetry : améliorez la visibilité et les performances

Découvrez comment OpenTelemetry peut améliorer vos efforts de traçage Kafka pour améliorer les informations opérationnelles et les performances des applications.

Publié Lecture : 9 min

Idéalement, vous devriez utiliser le traçage (ou tracing) distribué pour suivre les demandes via votre système, mais Kafka dissocie les producteurs et les consommateurs, ce qui signifie qu’il n’y a pas de transactions directes à tracer entre eux. Kafka utilise également des processus asynchrones, qui ont des dépendances implicites, mais pas explicites. Il est donc difficile de comprendre comment vos microservices fonctionnent ensemble.

Cependant, il est possible de monitorer vos clusters Kafka avec le traçage distribué et OpenTelemetry. Vous pouvez ensuite analyser et visualiser vos traces dans un outil de tracing distribué open source comme Jaeger ou une plateforme d’observabilité complète comme New Relic. Dans ce billet de blog, je vais utiliser une application simple pour vous montrer comment vous pouvez y parvenir.

Considérations et directives de conception

OpenTelemetry est généralement disponible en deux versions :

saveurs

Lorsque je parle de ces saveurs, j’utilise généralement l’analogie ci-dessus. Vous pouvez soit acheter un gâteau prêt à être dégusté, soit acheter tous les ingrédients et faire le gâteau vous-même. Avec OpenTelemetry, l'approche est très similaire et les saveurs sont :

  • Instrumentation zéro-code : dans cette approche, vous utilisez un agent OpenTelemetry que vous attachez à votre application au démarrage. Cet agent laisse alors opérer sa magie et fournit automatiquement (sans aucune modification du code source) de nombreux signaux de télémétrie (métriques, traces et logs) et des informations détaillées sur votre application.
    • Avantages :
      • Un démarrage rapide
      • Aucune modification du code source
    • Inconvénients :
      • Une personnalisation limitée
      • Une profondeur de visibilité de votre application potentiellement limitée
         
  • Instrumentation manuelle : cette option nécessite que vous ajoutiez des dépendances et des packages à votre code source que vous devez gérer dans le cadre de votre cycle de vie de développement logiciel (SDLC) habituel. Cependant, cela vous permet également d'être plus précis et personnalisé dans votre instrumentation. Vous pouvez facilement ajouter des métriques personnalisées, des traces et des attributs à votre télémétrie.
    • Avantages :
      • Une bien plus grande flexibilité de personnalisation de la télémétrie
      • La possibilité d'ajouter, de supprimer et de modifier facilement la profondeur de votre instrumentation
    • Inconvénients :
      • Des dépendances dans votre code source
      • Plus d'efforts requis pour l'implémentation

Exemple d'application

L’application (disponible dans ce référentiel GitHub public) que j’utilise en exemple dans ce blog est basée sur l'architecture de haut niveau ci-dessous :

 

Exemple d'application

Il contient les composants suivants :

  • kafka-java-producer : une application Java Spring Boot qui produit des messages dans une rubrique Kafka
  • Courtier (ou broker) Kafka
  • kafka-java-consommateur : une application Java Spring Boot qui s'abonne à une rubrique Kafka et lit les messages à partir de celle-ci. Ce composant effectue également des appels vers un service API REST externe (qui n'est pas sous notre contrôle)
  • kafka-java-service : une application Java Spring Boot en aval qui est appelée depuis le service consommateur

Instrumentation zéro-code

Commençons par l’instrumentation zéro-code, également appelée instrumentation automatique.

Configuration

Chacun des différents services contient un script « run.sh » pour que le service soit opérationnel. Le script ressemble à ceci :

Configuration

La phrase clé ici est la première. Ici, nous définissons les JAVA_TOOL_OPTIONS et configurons le `-javaagent` pour pointer vers l'emplacement de l'agent Java OpenTelemetry

Les trois lignes suivantes configurent la manière dont nous souhaitons traiter les différents signaux de télémétrie. Dans notre cas, je définis les traces, les métriques et les logs à exporter via OpenTelemetry Line Protocol (OTLP).

Il existe trois variables d’environnement supplémentaires qu’il est très important de configurer :

  • OTEL_EXPORTER_OTLP_ENDPOINT : le système cible vers lequel nous souhaitons exporter les données, c'est-à-dire notre backend de télémétrie. Dans mon cas, il s'agit bien de New Relic et je configure donc le point de terminaison OTLP natif de New Relic
  • OTEL_EXPORTER_OTLP_HEADERS : le point de terminaison de l'exportateur ci-dessus est une API ouverte, nous devons donc configurer une clé API. Dans le cas de New Relic, il s’agit d’une clé de licence New Relic.
  • OTEL_SERVICE_NAME : idéalement, nous souhaitons donner au service un nom significatif, afin que New Relic puisse créer une entité appropriée à partir de celui-ci.

C’est essentiellement tout ce que nous devons configurer. Tout le reste est géré par l'agent Java OpenTelemetry. Il n’est pas nécessaire de modifier quoi que ce soit dans notre code source.

Observabilité

Voyons quel niveau de visibilité sur les services nous pouvons atteindre à partir d’une instrumentation sans code.

Lorsque je navigue vers mon compte New Relic, je peux voir tous les services liés à des entités distinctes.

Observabilité

Commençons par explorer le service kafka-java-producer.

La vue Summary offre un excellent aperçu de toutes les télémétries et métriques les plus importantes sur lesquelles je dois me concentrer.

Récapitulatif

Dans le cadre de ce blog, je m'intéresse principalement à la section Distributed Tracing, plongeons donc plus profondément dans ce domaine.

Tracing distribué

En examinant une seule trace, cela me permet de visualiser les informations détaillées sur le temps que cette trace spécifique a mis à s'exécuter et où le temps a été passé.

create order publish

Nous effectuons également le mappage automatique des entités de tous les différents services impliqués dans une trace donnée.

Entity map

Je souhaite attirer votre attention sur la répartition des traces et des spans . Vous pouvez voir comment la trace est initiée sur le producteur (producer), puis que le consommateur (consumer) récupère le message et envoie ensuite deux appels distincts au service en aval.

Uninstrumented time

Ce qui est intéressant ici, c'est la plage qui indique « Uninstrumented time » (temps non instrumenté). Il s’agit d’un code dans le consommateur où l’agent n’a pas pu capturer des informations plus détaillées sur ce qui se passe dans ses méthodes internes.

Cela montre déjà les limites de l’instrumentation zéro-code. Par défaut, l’agent n’instrumente pas les différentes méthodes et le code source, mais s’arrête plutôt — par définition — à un certain niveau pour obtenir une visibilité plus approfondie de votre code.

Instrumentation manuelle

Dans la section précédente, vous avez vu comment l’instrumentation zéro code présente certaines limites en matière de visibilité sur votre application. C’est exactement là que l’instrumentation manuelle entre en jeu.

Configuration

J'ai configuré la même application, mais cette fois, aucun agent n'est configuré au démarrage de l'application.

Configure

J'utilise simplement le wrapper Maven pour exécuter l'application.

Les autres détails de configuration font alors partie de mon application.properties :

application.properties

Ces propriétés sont ensuite utilisées dans mon code d’application Spring Boot pour définir la configuration OpenTelemetry pour les traces, les métriques et les logs.

métriques et logs

Observabilité

Avant d’entrer dans les détails de la méthode d'implémentation de l'instrumentation manuelle que j'ai utilisée, examinons d’abord le résultat.

instrumentation manuelle

Avez-vous remarqué comment la durée (span), qui était auparavant indiquée par « Uninstrumented time », affiche désormais des informations beaucoup plus détaillées ? Je peux maintenant voir ces spans supplémentaires :

  • ExecuteLongRunningTask
  • WhyTheHeckDoWeSleepHere
  • SomeTinyTask
  • AnotherShortRunningTask

« WhyTheHeckDoWeSleepHere » semble prendre le plus de temps. Ce n'est étonnant, vu ce que son nom suggère 😉.

Jetons un coup d'œil au code source pour voir l'instrumentation manuelle que j'ai mise en place.

RunningTask

Dans la méthode nommée ExecuteLongRunningTask, j'ai créé un nouveau span sur le traceur actuel en utilisant la méthode spanBuilder().

En plus de cela, vous remarquerez peut-être également que — juste pour le plaisir — j'ai créé un autre span appelé « WhyTheHeckDoWeSleepHere » qui contient une unité de travail artificielle ou plutôt une instruction de sommeil sur le thread actuel.

Ces concepts pour exploiter le SDK OpenTelemetry me permettent d’être beaucoup plus précis dans ma demande d'informations détaillées sur mon application et mon code source. Mais, comme vous pouvez l’imaginer, j’ai également la restriction que je dois avoir des dépendances et le code personnalisé disponibles dans mon code source.

Conclusion

J’espère avoir pu vous montrer à quel point il peut être facile d’exploiter OpenTelemetry afin d’obtenir des informations détaillées sur votre application et vos services. Nous avons étudié l’instrumentation zéro-code pour démarrer sans aucune modification du code, mais le niveau de détails peut être limité. Nous nous sommes ensuite penchés sur l’instrumentation manuelle. Cela nous a permis d'être plus précis et de personnaliser l'instrumentation, mais l'effort pour démarrer est un peu plus élevé.

Je vous encourage à jeter un œil à OpenTelemetry et à ses capacités fascinantes. Faites-moi part de vos réflexions et n'hésitez pas à me contacter si vous avez des questions ou si vous avez besoin de plus amples informations.

Bon codage !

New Relic Now Regardez la démo des intégrations agentiques dès aujourd'hui.
Regarder.