Contrôlez vos services avec OTEL, Jaeger et Prometheus

Discutons d’une question importante : comment surveillons-nous nos services si quelque chose ne va pas ?

D’une part, nous avons Prometheus avec des alertes et Kibana pour les tableaux de bord et d’autres fonctionnalités utiles. Nous savons également comment rassembler des journaux — la pile ELK est notre solution de prédilection. Cependant, un simple enregistrement n’est pas toujours suffisant : il ne fournit pas une vue d’ensemble du parcours d’une requête à travers l’ensemble de l’écosystème de composants.

Vous pouvez trouver plus d’infos sur ELK ici.

Mais que faire si nous voulons visualiser les requêtes ? Que faire si nous devons corréler les requêtes voyageant entre les systèmes ? Cela s’applique aussi bien aux microservices qu’aux monolithes — peu importe combien de services nous avons ; ce qui compte, c’est comment nous gérons leur latence.

En effet, chaque requête utilisateur peut passer par toute une chaîne de services indépendants, de bases de données, de files d’attente de messages et d’API externes.

Dans un environnement aussi complexe, il devient extrêmement difficile de déterminer exactement où les retards se produisent, d’identifier quelle partie de la chaîne agit comme un goulot d’étranglement en termes de performance, et de trouver rapidement la cause profonde des pannes lorsqu’elles se produisent.

Pour relever ces défis efficacement, nous avons besoin d’un système centralisé et cohérent pour collecter les données de télémétrie — traces, métriques et journaux. C’est là qu’OpenTelemetry et Jaeger interviennent.

Regardons les bases

Il y a deux termes principaux que nous devons comprendre :

ID de trace

Un ID de trace est un identifiant de 16 octets, souvent représenté sous la forme d’une chaîne hexadécimale de 32 caractères. Il est automatiquement généré au début d’une trace et reste le même pour tous les spans créés par une demande particulière. Cela permet de voir facilement comment une demande circule à travers différents services ou composants dans un système.

ID de span

Chaque opération individuelle dans une trace obtient son propre ID de span, qui est généralement une valeur aléatoire de 64 bits. Les spans partagent le même ID de trace, mais chacun a un ID de span unique, ce qui vous permet de déterminer exactement quelle partie du flux de travail chaque span représente (comme une requête de base de données ou un appel à un autre microservice).

Comment sont-ils liés ?

ID de trace et ID de span se complètent mutuellement.

Lorsque qu’une demande est initiée, un ID de trace est généré et transmis à tous les services impliqués. Chaque service, à son tour, crée un span avec un ID de span unique lié à l’ID de trace, vous permettant de visualiser l’ensemble du cycle de vie de la demande du début à la fin.

D’accord, alors pourquoi ne pas simplement utiliser Jaeger? Pourquoi avons-nous besoin d’OpenTelemetry (OTEL) et de toutes ses spécifications? C’est une excellente question ! Décomposons cela étape par étape.

Trouvez plus d’informations sur Jaeger ici.

TL;DR

  • Jaeger est un système de stockage et de visualisation des traces distribuées. Il collecte, stocke, recherche et affiche des données montrant comment les requêtes « voyagent » à travers vos services.
  • OpenTelemetry (OTEL) est une norme (et un ensemble de bibliothèques) pour collecter des données de télémétrie (traces, métriques, journaux) de vos applications et infrastructures. Il n’est pas lié à un outil de visualisation ou un backend spécifique.

Pour faire simple :

  • OTEL est comme un « langage universel » et un ensemble de bibliothèques pour la collecte de télémétrie.
  • Jaeger est un backend et une interface utilisateur pour visualiser et analyser des traces distribuées.

Pourquoi avons-nous besoin d’OTEL si nous avons déjà Jaeger ?

1. Une norme unique pour la collecte

Dans le passé, il y avait des projets comme OpenTracing et OpenCensus. OpenTelemetry unifie ces approches de collecte de métriques et de traces en une seule norme universelle.

2. Intégration facile

Vous écrivez votre code en Go (ou dans un autre langage), ajoutez des bibliothèques OTEL pour l’injection automatique des intercepteurs et des spans, et c’est tout. Ensuite, peu importe où vous souhaitez envoyer ces données — Jaeger, Tempo, Zipkin, Datadog, un backend personnalisé — OpenTelemetry s’occupe de la plomberie. Vous n’avez qu’à changer l’exportateur.

3. Pas seulement des traces

OpenTelemetry couvre les traces, mais gère également les métriques et les journaux. Vous vous retrouvez avec un seul ensemble d’outils pour tous vos besoins en télémétrie, pas seulement pour le traçage.

4. Jaeger comme backend

Jaeger est un excellent choix si vous êtes principalement intéressé par la visualisation de la traçabilité distribuée. Mais il ne fournit pas l’instrumentation inter-langage par défaut. OpenTelemetry, en revanche, vous offre un moyen standardisé de collecter des données, puis vous décidez où les envoyer (y compris vers Jaeger).

En pratique, ils travaillent souvent ensemble :

Votre application utilise OpenTelemetry → communique via le protocole OTLP → va vers le Collecteur OpenTelemetry (HTTP ou grpc) → exporte vers Jaeger pour la visualisation.


Partie technique

Conception système (un petit peu)

Esquissons rapidement quelques services qui feront ce qui suit :

  1. Service d’achat – traite un paiement et l’enregistre dans MongoDB
  2. CDC avec Debezium – écoute les changements dans la table MongoDB et les envoie à Kafka
  3. Processeur d’achat – consomme le message de Kafka et appelle le Service d’authentification pour rechercher le user_id pour validation
  4. Service d’authentification – un service utilisateur simple

En résumé :

  • 3 services Go
  • Kafka
  • CDC (Debezium)
  • MongoDB

Partie code

Commençons par l’infrastructure. Pour rassembler le tout en un seul système, nous créerons un grand fichier Docker Compose. Nous commencerons par configurer la télémétrie.

Remarque : Tout le code est disponible via un lien à la fin de l’article, y compris l’infrastructure.

YAML

 

Nous allons également configurer le collecteur — le composant qui recueille la télémétrie.

Ici, nous choisissons gRPC pour le transfert de données, ce qui signifie que la communication se fera via HTTP/2 :

YAML

 

Assurez-vous d’ajuster les adresses si nécessaire, et vous avez terminé avec la configuration de base.

Nous savons déjà qu’OpenTelemetry (OTEL) utilise deux concepts clés — ID de trace et ID de span — qui aident à suivre et surveiller les requêtes dans les systèmes distribués.

Implémentation du code

Maintenant, examinons comment faire fonctionner cela dans votre code Go. Nous avons besoin des imports suivants :

Go

 

Ensuite, nous ajoutons une fonction pour initialiser notre traceur dans main() lorsque l’application démarre :

Go

 

Avec le traçage configuré, nous devons simplement placer des spans dans le code pour suivre les appels. Par exemple, si nous voulons mesurer les appels à la base de données (car c’est généralement le premier endroit où nous cherchons des problèmes de performance), nous pouvons écrire quelque chose comme ceci :

Go

 

Nous avons le traçage au niveau du service — super ! Mais nous pouvons aller encore plus loin, en instrumentant le niveau de la base de données :

Go

 

Maintenant, nous avons une vue complète du parcours de la requête. Rendez-vous sur l’interface utilisateur de Jaeger, interrogez les 20 dernières traces sous auth-service, et vous verrez tous les spans et comment ils se connectent en un seul endroit.

Maintenant, tout est visible. Si vous en avez besoin, vous pouvez inclure la requête entière dans les balises. Cependant, gardez à l’esprit que vous ne devez pas surcharger votre télémétrie — ajoutez les données de manière délibérée. Je ne fais que démontrer ce qui est possible, mais inclure la requête complète de cette manière n’est généralement pas quelque chose que je recommanderais.

Client-serveur gRPC

Si vous souhaitez voir une trace qui s’étend sur deux services gRPC, c’est assez simple. Tout ce que vous devez faire est d’ajouter les intercepteurs prêts à l’emploi de la bibliothèque. Par exemple, du côté du serveur :

Go

 

Du côté client, le code est tout aussi court :

Go

 

C’est tout ! Assurez-vous que vos exportateurs sont configurés correctement, et vous verrez un seul ID de trace enregistré à travers ces services lorsque le client appelle le serveur.

Gestion des événements CDC et traçage

Vous voulez également gérer les événements du CDC ? Une approche simple consiste à intégrer l’ID de trace dans l’objet que MongoDB stocke. De cette façon, lorsque Debezium capture le changement et l’envoie à Kafka, l’ID de trace fait déjà partie de l’enregistrement.

Par exemple, si vous utilisez MongoDB, vous pouvez faire quelque chose comme ceci :

Go

 

Debezium récupère ensuite cet objet (y compris trace_id) et l’envoie à Kafka. Du côté du consommateur, vous analysez simplement le message entrant, extrayez le trace_id et l’intégrez dans votre contexte de traçage :

Go

 

Go

 

Alternative : Utiliser les en-têtes Kafka

Parfois, il est plus facile de stocker l’ID de trace dans les en-têtes Kafka plutôt que dans le payload lui-même. Pour les flux de travail CDC, cela peut ne pas être disponible par défaut — Debezium peut limiter ce qui est ajouté aux en-têtes. Mais si vous contrôlez le côté producteur (ou si vous utilisez un producteur Kafka standard), vous pouvez faire quelque chose comme ceci avec Sarama :

Injection d’un ID de trace dans les en-têtes

Go

 

Extraction d’un ID de trace du côté consommateur

Go

 

Selon votre cas d’utilisation et la manière dont votre pipeline CDC est configuré, vous pouvez choisir l’approche qui fonctionne le mieux :

  1. Intégrer l’ID de trace dans l’enregistrement de la base de données afin qu’il circule naturellement via CDC.
  2. Utiliser les en-têtes Kafka si vous avez plus de contrôle sur le côté producteur ou si vous souhaitez éviter d’enflammer le payload du message.

Dans tous les cas, vous pouvez garder vos traces cohérentes à travers plusieurs services, même lorsque les événements sont traités de manière asynchrone via Kafka et Debezium.

Conclusion

Utiliser OpenTelemetry et Jaeger fournit des traces de requêtes détaillées, vous aidant à identifier où et pourquoi des retards se produisent dans les systèmes distribués.

Ajouter Prometheus complète le tableau avec des métriques — des indicateurs clés de performance et de stabilité. Ensemble, ces outils forment une pile d’observabilité complète, permettant une détection et une résolution plus rapides des problèmes, une optimisation des performances et une fiabilité globale du système.

Je peux dire que cette approche accélère considérablement le dépannage dans un environnement de microservices et est l’une des premières choses que nous mettons en œuvre dans nos projets.

Liens

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