BLOG | NGINX

Tutoriel NGINX : Comment utiliser le traçage OpenTelemetry pour comprendre vos microservices

NGINX-Partie-de-F5-horiz-black-type-RGB
Vignette de Javier Evans
Javier Evans
Publié le 28 mars 2023

Cet article est l'un des quatre tutoriels qui vous aident à mettre en pratique les concepts de Microservices de mars 2023 : Commencez à fournir des microservices :

 

Une architecture de microservices présente de nombreux avantages, notamment une autonomie accrue de l’équipe et une flexibilité accrue en matière de mise à l’échelle et de déploiement. En revanche, plus il y a de services dans un système (et une application de microservices peut en avoir des dizaines, voire des centaines), plus il devient difficile de conserver une image claire du fonctionnement global du système. En tant qu’auteurs et mainteneurs de systèmes logiciels complexes, nous savons à quel point il est essentiel d’avoir une vision claire de la situation. Observabilité les outils nous donnent le pouvoir de construire cette image à travers de nombreux services et infrastructures de support.

Dans ce tutoriel, nous mettons en évidence un type d'observabilité très important pour les applications de microservices : le traçage. Avant de commencer, définissons quelques termes couramment utilisés pour parler d’observabilité :

  • Observabilité – La capacité de comprendre l’état ou la condition interne d’un système complexe (comme une application de microservices) en se basant uniquement sur la connaissance de ses sorties externes (telles que les traces, les journaux et les métriques).
  • Surveillance – La capacité d’observer et de vérifier la progression ou l’état d’un objet sur une période donnée. Par exemple, vous pouvez surveiller le trafic arrivant sur votre application pendant les heures de pointe et utiliser ces informations pour l'adapter de manière appropriée en conséquence.
  • Télémétrie – L’acte de collecter des métriques, des traces et des journaux et de les transférer de leur point d’origine vers un autre système pour stockage et analyse. Et aussi les données elles-mêmes.
  • Traçage/traces – Un compte rendu du parcours d’une requête ou d’une action à travers tous les nœuds d’un système distribué.
  • Span – Un enregistrement dans une trace d’une opération et ses métadonnées associées. Les traces sont constituées de plusieurs étendues imbriquées.
  • Journalisation/journaux d’événements – Un enregistrement texte horodaté avec des métadonnées.
  • Métrique – Une mesure capturée lors de l’exécution. Par exemple, la quantité de mémoire utilisée par une application à un moment donné.

Nous pouvons utiliser tous ces concepts pour obtenir un aperçu des performances de nos microservices. Le traçage est un élément particulièrement utile d’une stratégie d’observabilité, car les traces offrent une « vue d’ensemble » de ce qui se passe sur plusieurs composants, souvent faiblement couplés, lorsqu’une demande est effectuée. C’est également un moyen particulièrement efficace d’identifier les goulots d’étranglement des performances.

Ce didacticiel utilise la boîte à outils de traçage d' OpenTelemetry (OTel), une norme open source indépendante du fournisseur pour la collecte, le traitement et l'exportation de télémétrie qui gagne rapidement en popularité. Dans la conception d'OTel, une trace découpe un flux de données, qui peut impliquer plusieurs services, en une série de « morceaux » classés chronologiquement qui peuvent vous aider à comprendre facilement :

  • Toutes les étapes qui se sont déroulées dans un « morceau »
  • Combien de temps ont pris toutes ces étapes
  • Métadonnées sur chaque étape

Si vous n'êtes pas familier avec OTel, consultez Qu'est-ce qu'OpenTelemetry ? pour une introduction complète à la norme et aux considérations relatives à sa mise en œuvre.

Présentation du didacticiel

Ce tutoriel se concentre sur le traçage des opérations d'une application de microservices avec OTel. Dans les quatre défis de ce tutoriel, vous apprendrez à suivre une demande via votre système et à répondre à des questions sur vos microservices :

Ces défis illustrent notre processus recommandé lors de la configuration du traçage pour la première fois. Les étapes sont les suivantes :

  1. Comprendre le système ainsi que l’opération particulière que vous instrumentez.
  2. Décidez de ce que vous devez savoir sur le système en cours d’exécution.
  3. Instrumentez le système « naïvement » – c’est-à-dire en utilisant une configuration par défaut sans essayer d’éliminer les informations dont vous n’avez pas besoin ou de collecter des points de données personnalisés – et évaluez si l’instrumentation vous aide à répondre à vos questions.
  4. Modifiez les informations rapportées pour vous permettre de répondre plus rapidement à ces questions.

Note: Notre intention dans ce tutoriel est d’illustrer certains concepts de base sur la télémétrie, et non de montrer la bonne façon de déployer des microservices en production. Bien qu'il utilise une véritable architecture de « microservices », il existe quelques réserves importantes :

  • Le tutoriel n'utilise pas de framework d'orchestration de conteneurs tel que Kubernetes ou Nomad. Cela vous permet d’en apprendre davantage sur les concepts des microservices sans vous perdre dans les spécificités d’un certain framework. Les modèles introduits ici sont portables sur un système exécutant l’un de ces frameworks.
  • Les services sont optimisés pour faciliter la compréhension plutôt que pour la rigueur de l'ingénierie logicielle. L’objectif est d’examiner le rôle d’un service dans le système et ses modèles de communication, et non les spécificités du code. Pour plus d'informations, consultez les fichiers README des services individuels.

Tutoriel Architecture et objectifs de télémétrie

Architecture et flux utilisateur

Ce diagramme illustre l’architecture globale et le flux de données entre les microservices et autres éléments utilisés dans le didacticiel.

Diagramme montrant la topologie utilisée dans le didacticiel, avec le traçage OpenTelemetry d'un système de messagerie avec deux microservices, NGINX et RabbitMQ

Les deux microservices sont :

  • Le service de messagerie – Une API de chat simple avec des capacités de stockage de messages
  • Le service de notification – Un écouteur qui déclenche des événements pour alerter les utilisateurs, en fonction de leurs préférences

Les trois éléments de l'infrastructure de soutien sont :

  • NGINX Open Source – Un point d’entrée vers le service de messagerie et le système dans son ensemble
  • RabbitMQ – Un courtier de messages open source populaire qui permet aux services de communiquer de manière asynchrone
  • Jaeger – Un système de traçage distribué de bout en bout open source pour collecter et visualiser la télémétrie des composants du système qui la produisent

En laissant OTel de côté pour un instant, nous pouvons nous concentrer sur la séquence d’événements que nous suivons : ce qui se passe lorsqu’un utilisateur envoie un nouveau message de chat et que le destinataire en est informé.

Diagramme montrant le flux d'informations dans le système de messagerie utilisé dans le didacticiel

Le flux se décompose comme suit :

  1. Un utilisateur envoie un message au service de messagerie . Le proxy inverse NGINX intercepte le message et le transmet à l’une des nombreuses instances du service de messagerie .
  2. Le service de messagerie écrit le nouveau message dans sa base de données.
  3. Le service de messagerie produit un événement sur une file d'attente de messages RabbitMQ appelée chat_queue pour indiquer qu'un message a été envoyé. L'événement est générique et n'a pas de cible spécifique.
  4. En même temps:

    • 4a. Le service de messagerie renvoie une réponse à l'expéditeur indiquant que le message a été envoyé avec succès.
    • 4b. Le service de notification remarque le nouvel événement sur la file d'attente de discussion et le consomme.
  5. Le service de notification vérifie dans sa base de données les préférences de notification du destinataire du nouveau message.
  6. Le service de notification utilise la méthode préférée du destinataire pour envoyer une ou plusieurs notifications (dans ce tutoriel, les choix de méthode sont SMS et e-mail).

Objectifs de la télémétrie

Lors de la mise en place d’une instrumentation de télémétrie, il est préférable de commencer par un ensemble d’objectifs d’instrumentation plus définis que « tout envoyer et espérer obtenir des informations ». Nous avons trois objectifs clés en matière de télémétrie pour ce tutoriel :

  1. Comprendre toutes les étapes par lesquelles passe une demande au cours du flux du nouveau message
  2. Soyez sûr que le flux s'exécute de bout en bout dans les cinq secondes dans des conditions normales
  3. Voyez combien de temps il faut au service de notification pour commencer à traiter l'événement envoyé par le service de messagerie (un délai excessif peut signifier que le service de notification a du mal à lire la file d'attente des événements et que les événements sont en cours de sauvegarde)

Notez que ces objectifs sont liés à la fois au fonctionnement technique du système et à l’expérience utilisateur.

Prérequis et configuration du didacticiel

Prérequis

Pour réaliser le didacticiel dans votre propre environnement, vous avez besoin de :

  • Un environnement compatible Linux/Unix

    Note: Les activités de ce didacticiel qui impliquent le traçage de NGINX ne fonctionnent pas sur les processeurs ARM, car le module OpenTelemetry pour NGINX n'est pas compatible. (Cela inclut les architectures Linux aarch64 et les machines Apple avec la puce M1 ou M2.) Les activités impliquant les services de messagerie et de notification fonctionnent sur toutes les architectures.

  • Connaissance de base de la ligne de commande Linux, JavaScript et bash (mais tout le code et les commandes sont fournis et expliqués, vous pouvez donc toujours réussir avec des connaissances limitées)
  • Docker et Docker Compose
  • Node.js 19.x ou version ultérieure

    • Nous avons testé la version 19.x, mais nous espérons que les versions plus récentes de Node.js fonctionneront également.
    • Pour des informations détaillées sur l'installation de Node.js, consultez le fichier README dans le référentiel du service de messagerie . Vous pouvez également installer asdf pour obtenir exactement la même version Node.js utilisée dans le tutoriel.
  • curl (déjà installé sur la plupart des systèmes)
  • Les technologies répertoriées dans Architecture et flux utilisateur : Messenger et Notifier (vous les téléchargerez dans la section suivante), NGINX Open Source , Jaeger et RabbitMQ .

Note: Le didacticiel utilise le SDK JavaScript car les services de messagerie et de notification sont écrits en Node.js. Vous pouvez également configurer la fonction d’instrumentation automatique d’OTel (également appelée auto-instrumentation ) afin d’avoir une idée du type d’informations disponibles auprès d’OTel. Le tutoriel explique tout ce que vous devez savoir sur le SDK OTel Node.js, mais pour plus de détails, consultez la documentation OTel .

Installation

  1. Démarrer une session de terminal.
  2. Dans votre répertoire personnel, créez le répertoire microservices-march et clonez-y les référentiels GitHub de ce tutoriel. (Vous pouvez également utiliser un nom de répertoire différent et adapter les instructions en conséquence.)

    Note: Tout au long du didacticiel, l'invite sur la ligne de commande Linux est omise, pour faciliter la copie et le collage des commandes dans votre terminal. Le tilde ( ~ ) représente votre répertoire personnel.

    mkdir ~/microservices-marchcd ~/microservices-march
    git clone https://github.com/microservices-march/messenger --branch mm23-metrics-start
    git clone https://github.com/microservices-march/notifier --branch mm23-metrics-start
    git clone https://github.com/microservices-march/platform --branch mm23-metrics-start
    

Défi 1 : Configurer l'instrumentation OTel de base

Dans ce défi, vous démarrez le service de messagerie et configurez l’auto-instrumentation OTel pour envoyer la télémétrie à la console.

Lancer le service de messagerie

  1. Accédez au référentiel de la plateforme et démarrez Docker Compose :

    cd ~/microservices-march/platformdocker compose up -d --build
    

    Cela démarre RabbitMQ et Jaeger, qui seront utilisés dans les défis ultérieurs.

    • L'indicateur -d indique à Docker Compose de se détacher des conteneurs lorsqu'ils ont démarré (sinon les conteneurs resteront attachés à votre terminal).
    • L'indicateur --build indique à Docker Compose de reconstruire toutes les images au lancement. Cela garantit que les images que vous exécutez restent à jour malgré toutes les modifications potentielles apportées aux fichiers.
  2. Accédez au répertoire de l'application dans le référentiel Messenger et installez Node.js (vous pouvez remplacer cette méthode par une autre si vous le souhaitez) :

    cd ~/microservices-march/messenger/appasdf install
    
  3. Installer les dépendances :

    installation de npm
    
  4. Démarrez la base de données PostgreSQL pour le service de messagerie :

    docker compose -d
    
  5. Créez le schéma et les tables de la base de données et insérez des données de départ :

    npm exécuter actualiser-db
    

Configurer l'auto-instrumentation OTel envoyée à la console

Avec l’auto-instrumentation OTel, vous n’avez pas besoin de modifier quoi que ce soit dans la base de code du messager pour configurer le traçage. Au lieu d'être importée dans le code de l'application elle-même, toute la configuration de traçage est définie dans un script qui est importé dans le processus Node.js au moment de l'exécution.

Ici, vous configurez l’auto-instrumentation du service de messagerie avec la destination la plus basique pour les traces, la console. Dans le défi 2 , vous modifierez la configuration pour envoyer des traces à Jaeger en tant que collecteur externe.

  1. Toujours en travaillant dans le répertoire d'applications du référentiel Messenger , installez les packages principaux OTel Node.js :

    npm install @opentelemetry/sdk-node@0.36.0 \
    @opentelemetry/auto-instrumentations-node@0.36.4
    

    Ces bibliothèques fournissent les fonctionnalités suivantes :

    • @opentelemetry/sdk-node – Génération et exportation de données OTel
    • @opentelemetry/auto-instrumentations-node – Configuration automatique avec configuration par défaut de toutes les instrumentations Node.js les plus courantes

    Note: C’est une particularité d’OTel que ses SDK JavaScript sont divisés en très, très petits morceaux. Vous allez donc installer quelques packages supplémentaires juste pour l'exemple de base de ce tutoriel. Pour comprendre de quels packages vous pourriez avoir besoin pour accomplir des tâches d'instrumentation au-delà de celles abordées dans ce didacticiel, parcourez les (très bons) guides de démarrage d'OTel et parcourez le référentiel GitHub d'OTel.

  2. Créez un nouveau fichier appelé tracing.mjs pour contenir le code d'installation et de configuration du traçage OTel :

    traçage tactile.mjs
    
  3. Dans votre éditeur de texte préféré, ouvrez tracing.mjs et ajoutez ce code :

    //1
    importer opentelemetry depuis "@opentelemetry/sdk-node";
    importer { getNodeAutoInstrumentations } depuis "@opentelemetry/auto-instrumentations-node";
    
    //2
    const sdk = new opentelemetry.NodeSDK({
    traceExporter: new opentelemetry.tracing.ConsoleSpanExporter(),
    instrumentations: [getNodeAutoInstrumentations()],
    });
    
    //3
    sdk.start();
    

    Le code effectue les opérations suivantes :

    1. Importe les fonctions et objets requis à partir du SDK OTel.
    2. Crée une nouvelle instance de NodeSDK et la configure pour :

      • Envoyer des étendues à la console ( ConsoleSpanExporter ).
      • Utilisez l’auto-instrumentateur comme ensemble d’instrumentation de base. Cette instrumentation charge toutes les bibliothèques d’instrumentation automatique les plus courantes. Dans le tutoriel, les éléments pertinents sont :

        • @opentelemetry/instrumentation-pg pour la bibliothèque de base de données Postgres ( pg )
        • @opentelemetry/instrumentation-express pour le framework Node.js Express
        • @opentelemetry/instrumentation-amqplib pour la bibliothèque RabbitMQ ( amqplib )
    3. Démarre le SDK.
  4. Démarrez le service de messagerie en important le script d’auto-instrumentation que vous avez créé à l’étape 3.

    nœud --import ./tracing.mjs index.mjs
    

    Après un moment, de nombreuses sorties liées au traçage commencent à apparaître dans la console (votre terminal) :

    ...
    {
    traceId: '9c1801593a9d3b773e5cbd314a8ea89c',
    parentId: undefined,
    traceState: undefined,
    name: 'fs statSync',
    id: '2ddf082c1d609fbe',
    genre : 0,
    horodatage : 1676076410782000,
    durée : 3,
    attributs : {},
    statut : { code : 0 },
    événements : [],
    liens : []
    }
    ...
    

Note: Laissez la session du terminal ouverte pour la réutiliser dans le défi 2.

Défi 2 : Configurer l'instrumentation OTel et la visualisation des traces pour tous les services

Il existe de nombreux outils que vous pouvez utiliser pour visualiser et analyser les traces, mais ce didacticiel utilise Jaeger . Jaeger est un framework de traçage distribué de bout en bout simple et open source avec une interface utilisateur Web intégrée pour visualiser les étendues et autres données de traçage. L'infrastructure fournie dans le référentiel de la plateforme inclut Jaeger (vous l'avez démarré à l'étape 1 du défi 1), vous pouvez donc vous concentrer sur l'analyse des données au lieu de gérer des outils complexes.

Jaeger est accessible au point de terminaison http://localhost:16686 dans votre navigateur, mais si vous accédez au point de terminaison en ce moment, il n'y a rien à voir sur votre système. C'est parce que les traces que vous collectez actuellement sont envoyées à la console ! Pour afficher les données de trace dans Jaeger, vous devez exporter les traces à l'aide du format du protocole OpenTelemetry (OTLP).

Dans ce défi, vous instrumentez le flux utilisateur principal en configurant l'instrumentation pour :

Configurer l'auto-instrumentation OTel envoyée à un collecteur externe

Pour rappel, l'utilisation de l'auto-instrumentation OTel signifie que vous ne modifiez rien dans la base de code Messenger pour configurer le traçage. Au lieu de cela, toute la configuration de traçage se trouve dans un script qui est importé dans le processus Node.js au moment de l'exécution. Ici, vous modifiez la destination des traces générées par le service de messagerie depuis la console vers un collecteur externe (Jaeger dans ce tutoriel).

  1. Toujours dans le même terminal que dans le Challenge 1, et dans le répertoire d'applications du dépôt Messenger , installez le package Node.js de l'exportateur OTLP :

    npm install @opentelemetry/exporter-trace-otlp-http@0.36.0
    

    La bibliothèque @opentelemetry/exporter-trace-otlp-http exporte les informations de trace au format OTLP via HTTP. Il est utilisé lors de l'envoi de télémétrie à un collecteur externe OTel.

  2. Ouvrez tracing.mjs (que vous avez créé et modifié dans le défi 1) et apportez ces modifications :

    • Ajoutez cette ligne à l’ensemble des instructions d’importation en haut du fichier :

      importer { OTLPTraceExporter } depuis "@opentelemetry/exporter-trace-otlp-http";
      
    • Remplacez l'« exportateur » que vous fournissez au SDK OTel par l'exportateur de console utilisé dans le défi 1 par un exportateur capable d'envoyer des données OTLP via HTTP à un collecteur compatible OTLP. Remplacer:

      traceExporter : nouveau opentelemetry.tracing.ConsoleSpanExporter(),
      

      avec:

      traceExporter : nouveau OTLPTraceExporter({ en-têtes : {} }),
      

    Note: Par souci de simplicité, le didacticiel suppose que le collecteur se trouve à l'emplacement par défaut, http://localhost:4318/v1/traces . Dans un système réel, c'est une bonne idée de définir l'emplacement explicitement.

  3. Appuyez sur Ctrl+c pour arrêter le service de messagerie que vous avez démarré dans ce terminal à l'étape 4 de Configurer l'auto-instrumentation OTel envoyée à la console . Redémarrez-le ensuite pour utiliser le nouvel exportateur configuré à l’étape 2 :

    ^cnode --import ./tracing.mjs index.mjs
    
  4. Démarrez une deuxième session de terminal distincte. (Les instructions suivantes appellent cela le terminal client et le terminal d’origine – utilisé dans les étapes 1 et 3 – le terminal de messagerie .) Attendez environ dix secondes, puis envoyez une demande de vérification de l’état au service de messagerie (vous pouvez exécuter cette opération plusieurs fois si vous souhaitez voir plusieurs traces) :

    curl -X GET http://localhost:4000/santé
    

    Attendre dix secondes avant d’envoyer la requête permet de retrouver plus facilement votre trace, car elle intervient après les nombreuses traces générées par l’auto-instrumentation au démarrage du service.

  5. Dans un navigateur, accédez à l'interface utilisateur Jaeger à l'adresse http://localhost:16686 et vérifiez que l'exportateur OTLP fonctionne comme prévu. Cliquez sur Rechercher dans la barre de titre et dans le menu déroulant du champ Service , sélectionnez le service dont le nom commence par unknown_service . Cliquez sur le bouton Rechercher des traces :

  6. Cliquez sur une trace dans la partie droite de la fenêtre pour afficher une liste des étendues qu'elle contient. Chaque étendue décrit les opérations, impliquant parfois plusieurs services, qui ont été exécutées dans le cadre de la trace. L'étendue jsonParser dans la capture d'écran montre le temps qu'il a fallu pour exécuter la partie jsonParser du code de gestion des requêtes du service de messagerie .

    Capture d'écran de l'interface utilisateur graphique Jaeger affichant la liste des plages pour un service inconnu, avant que l'instrumentation automatique ne soit modifiée pour afficher les noms de service corrects

  7. Comme indiqué à l’étape 5, le nom du service tel qu’exporté par le SDK OTel ( unknown_service ) n’est pas significatif. Pour résoudre ce problème, dans le terminal de messagerie, appuyez sur Ctrl+c pour arrêter le service de messagerie . Ensuite, installez quelques packages Node.js supplémentaires :

    ^c 
    npm install @opentelemetry/semantic-conventions@1.10.0 \
    @opentelemetry/resources@1.10.0
    

    Ces deux bibliothèques fournissent les fonctionnalités suivantes :

    • @opentelemetry/semantic-conventions – Définit les attributs standard des traces tels que définis dans la spécification OTel.
    • @opentelemetry/resources – Définit un objet (ressource) qui représente la source générant les données OTel (dans ce tutoriel, le service de messagerie ).
  8. Ouvrez tracing.mjs dans un éditeur de texte et effectuez ces modifications :

    • Ajoutez ces lignes à l’ensemble des instructions d’importation en haut du fichier :

      importer { Ressource } depuis "@opentelemetry/resources"; importer { SemanticResourceAttributes } depuis "@opentelemetry/semantic-conventions";
      
    • Créez une ressource appelée Messenger sous la clé correcte dans la spécification OTel en ajoutant la ligne suivante après la dernière instruction d'importation :

      const resource = new Resource({ [SemanticResourceAttributes.SERVICE_NAME]: "messenger",
      });
      
    • Transmettez l'objet ressource au constructeur NodeSDK en ajoutant la ligne surlignée en orange entre les lignes en noir :

      const sdk = new opentelemetry.NodeSDK({ ressource, traceExporter : new OTLPTraceExporter({ en-têtes : {} }), instrumentations : [getNodeAutoInstrumentations()], });
      
  9. Redémarrer le service de messagerie :

    nœud --import ./tracing.mjs index.mjs
    
  10. Attendez environ dix secondes, puis dans le terminal client (que vous avez ouvert à l’étape 4), envoyez une autre demande de vérification de l’état au serveur (vous pouvez exécuter la commande plusieurs fois si vous souhaitez voir plusieurs traces) :

    curl -X GET http://localhost:4000/santé
    

    Note: Laissez le terminal client ouvert pour réutilisation dans la section suivante et le terminal de messagerie ouvert pour réutilisation dans le défi 3.

  11. Confirmez qu'un nouveau service appelé Messenger apparaît dans l'interface utilisateur Jaeger dans le navigateur (cela peut prendre quelques secondes et vous devrez peut-être actualiser l'interface utilisateur Jaeger) :

    Capture d'écran de l'interface graphique de Jaeger montrant Messenger dans la liste des services disponibles pour une inspection approfondie des psans

  12. Sélectionnez Messenger dans le menu déroulant Service et cliquez sur le bouton Rechercher des traces pour voir toutes les traces récentes provenant du service de messagerie (la capture d'écran montre les 2 plus récentes sur 20) :

    Capture d'écran de l'interface utilisateur graphique de Jaeger montrant les 2 traces les plus récentes pour le service de messagerie

  13. Cliquez sur une trace pour afficher les intervalles qu'elle contient. Chaque segment est correctement étiqueté comme provenant du service de messagerie :

    Capture d'écran de l'interface utilisateur de Jaeger montrant les détails d'une seule session de messagerie

Configurer l'auto-instrumentation OTel du service de notification

Lancez et configurez maintenant l’instrumentation automatique pour le service de notification , en exécutant essentiellement les mêmes commandes que dans les deux sections précédentes pour le service de messagerie .

  1. Ouvrez une nouvelle session de terminal (appelée terminal de notification dans les étapes suivantes). Accédez au répertoire de l'application dans le référentiel de notification et installez Node.js (vous pouvez remplacer une méthode différente si vous le souhaitez) :

    cd ~/microservices-mars/notifier/appasdf installer
    
  2. Installer les dépendances :

    installation de npm
    
  3. Démarrez la base de données PostgreSQL pour le service de notification :

    docker compose -d
    
  4. Créez le schéma et les tables de la base de données et insérez des données de départ :

    npm exécuter actualiser-db
    
  5. Installez les packages OTel Node.js (pour une description de ce que font les packages, voir les étapes 1 et 3 dans Configurer l'auto-instrumentation OTel envoyée à la console ) :

    npm install @opentelemetry/auto-instrumentations-node@0.36.4 \
    @opentelemetry/exporter-trace-otlp-http@0.36.0 \
    @opentelemetry/resources@1.10.0 \
    @opentelemetry/sdk-node@0.36.0 \
    @opentelemetry/semantic-conventions@1.10.0
    
  6. Créez un nouveau fichier appelé tracing.mjs :

    traçage tactile.mjs
    
  7. Dans votre éditeur de texte préféré, ouvrez tracing.mjs et ajoutez le script suivant pour que le SDK OTel soit opérationnel :

    importer opentelemetry depuis "@opentelemetry/sdk-node";
    importer { getNodeAutoInstrumentations } depuis "@opentelemetry/auto-instrumentations-node";
    importer { OTLPTraceExporter } depuis "@opentelemetry/exporter-trace-otlp-http";
    importer { Resource } depuis "@opentelemetry/resources";
    importer { SemanticResourceAttributes } depuis "@opentelemetry/semantic-conventions";
    
    const resource = new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: "notifier",
    });
    
    const sdk = new opentelemetry.NodeSDK({
    resource,
    traceExporter: new OTLPTraceExporter({ headers: {} }),
    instrumentations: [getNodeAutoInstrumentations()],
    });
    
    sdk.démarrer();
    

    Note: Ce script est exactement le même que celui du service de messagerie , sauf que la valeur du champ SemanticResourceAttributes.SERVICE_NAME est notifier .

  8. Démarrer le service de notification avec l'auto-instrumentation OTel :

    nœud --import ./tracing.mjs index.mjs
    
  9. Attendez environ dix secondes, puis, dans le terminal client, envoyez une demande de vérification de l’état au service de notification . Ce service écoute sur le port 5000 pour éviter tout conflit avec le service de messagerie qui écoute sur le port 4000 :

    boucle http://localhost:5000/health
    

    Note: Laissez les terminaux client et notificateur ouverts pour une réutilisation dans le défi 3.

  10. Confirmez qu'un nouveau service appelé notifier apparaît dans l'interface utilisateur Jaeger du navigateur :

    Capture d'écran de l'interface graphique de Jaeger montrant le notificateur dans la liste des services disponibles pour une inspection approfondie des travées

Configurer l'instrumentation OTel de NGINX

Pour NGINX, vous configurez le traçage manuellement au lieu d’utiliser la méthode d’instrumentation automatique OTel. Actuellement, la méthode la plus courante pour instrumenter NGINX à l'aide d'OTel consiste à utiliser un module écrit en C. Les modules tiers constituent une partie importante de l'écosystème NGINX, mais leur configuration nécessite un certain travail. Ce tutoriel effectue la configuration pour vous. Pour plus d’informations, consultez Compilation de modules dynamiques tiers pour NGINX et NGINX Plus sur notre blog.

  1. Démarrez une nouvelle session de terminal (le terminal NGINX ), changez de répertoire vers la racine du référentiel Messenger et créez un nouveau répertoire appelé load-balancer , ainsi que de nouveaux fichiers appelés Dockerfile , nginx.conf et opentelemetry_module.conf :

    cd ~/microservices-march/messenger/mkdir équilibreur de charge
    cd équilibreur de charge
    toucher Dockerfile
    toucher nginx.conf
    toucher opentelemetry_module.conf
    
  2. Dans votre éditeur de texte préféré, ouvrez Dockerfile et ajoutez ce qui suit (les commentaires expliquent ce que fait chaque ligne, mais vous pouvez créer et exécuter le conteneur Docker sans tout comprendre) :

    FROM --platform=amd64 nginx:1.23.1 # Remplacez le fichier nginx.conf par le nôtre COPY nginx.conf /etc/nginx/nginx.conf # Définissez la version du module NGINX OTel ARG OPENTELEMETRY_CPP_VERSION=1.0.3 # Définissez le chemin de recherche des bibliothèques partagées utilisées lors de la compilation et de l'exécution de NGINX ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/opentelemetry-webserver-sdk/sdk_lib/lib # 1. Téléchargez la dernière version du modèle Consul et du module de serveur Web OTel C++, otel-webserver-module ADD https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/download/webserver%2Fv${OPENTELEMETRY_CPP_VERSION}/opentelemetry-webserver-sdk-x64-linux.tgz /tmp RUN apt-get update \ && apt-get install -y --no-install-recommends dumb-init unzip \ # 2. Extraire les fichiers du module && tar xvfz /tmp/opentelemetry-webserver-sdk-x64-linux.tgz -C /opt \ && rm -rf /tmp/opentelemetry-webserver-sdk-x64-linux.tgz \ # 3. Installez et ajoutez la directive 'load_module' en haut du fichier de configuration principal de NGINX && /opt/opentelemetry-webserver-sdk/install.sh \ && echo "load_module /opt/opentelemetry-webserver-sdk/WebServerModule/Nginx/1.23.1/ngx_http_opentelemetry_module.so;\n$(cat /etc/nginx/nginx.conf)" > /etc/nginx/nginx.conf # 4. Copiez dans le fichier de configuration du module NGINX OTel COPY opentelemetry_module.conf /etc/nginx/conf.d/opentelemetry_module.conf EXPOSE 8085 STOPSIGNAL SIGQUIT
    
  3. Ouvrez nginx.conf et ajoutez ce qui suit :

    événements {}
    http {
    include /etc/nginx/conf.d/opentelemetry_module.conf;
    
    messager en amont {
    serveur localhost:4000;
    }
    
    serveur {
    écoute 8085;
    
    emplacement / {
    proxy_pass http://messenger;
    }
    }
    }
    

    Ce fichier de configuration NGINX extrêmement basique indique à NGINX de :

    • Configurer un groupe en amont appelé Messenger qui représente le groupe d'instances de service de messagerie
    • Écoutez les requêtes HTTP sur le port 8085
    • Transférer toutes les demandes entrantes pour les chemins commençant par / (c'est-à-dire toutes les demandes entrantes) au messager en amont

    Note: Cela est assez proche de la configuration réelle de NGINX en tant que proxy inverse et équilibreur de charge dans un environnement de production. La seule différence majeure est que l'argument de la directive server dans le bloc en amont est généralement un nom de domaine ou une adresse IP plutôt que localhost .

  4. Ouvrez opentelemetry_module.conf et ajoutez ce qui suit :

    NginxModuleEnabled ON;NginxModuleOtelSpanExporter otlp;
    NginxModuleOtelExporterEndpoint localhost:4317;
    NginxModuleServiceName messenger-lb;
    NginxModuleServiceNamespace MicroservicesMarchDemoArchitecture;
    NginxModuleServiceInstanceId DemoInstanceId;
    NginxModuleResolveBackends ON;
    NginxModuleTraceAsError ON;
    
  5. Créez une image Docker contenant NGINX ainsi que le module NGINX OTel :

    docker build -t messager-lb .
    
  6. Démarrez le conteneur Docker pour le proxy inverse et l'équilibreur de charge NGINX :

    docker run --rm --name messenger-lb -p 8085:8085 --network="hôte" messenger-lb
    
  7. Dans le terminal client, envoyez une demande de contrôle d’intégrité au service de messagerie via le proxy inverse et l’équilibreur de charge NGINX (il n’est pas nécessaire d’attendre avant d’envoyer cette demande) :

    boucle http://localhost:8085/health
    

    Note: Laissez les terminaux NGINX et client ouverts pour une réutilisation dans le défi 3.

  8. Dans le navigateur, confirmez que le nouveau service Messenger-lb est répertorié dans l'interface utilisateur Jaeger avec les services que vous avez précédemment démarrés. Vous devrez peut-être recharger l'interface utilisateur Jaeger dans votre navigateur.

    Capture d'écran de l'interface graphique de Jaeger montrant la liste des services disponibles pour une inspection approfondie des travées, incluant désormais Messenger-lb

Défi 3 : Apprenez à lire les traces d'OTel

Dans Architecture et flux utilisateur , nous avons décrit les étapes du flux utilisateur, mais pour récapituler :

  1. Un utilisateur démarre une conversation en envoyant un message à un autre utilisateur.
  2. Le proxy inverse NGINX intercepte le message et le transmet au service de messagerie .
  3. Le service de messagerie écrit le message dans sa base de données, puis envoie un événement via RabbitMQ.
  4. Le service de notification consomme cet événement, recherche les préférences de notification du destinataire (deuxième utilisateur) et envoie une notification au destinataire via la méthode préférée.

Les objectifs de la mise en œuvre de la télémétrie sont les suivants :

  1. Comprendre toutes les étapes qu’une demande subit pour accomplir le nouveau flux de messages.
  2. Soyez sûr que le flux s’exécute de bout en bout dans les cinq secondes dans des conditions normales.
  3. Voyez combien de temps il faut au service de notification pour commencer à traiter l'événement envoyé par le service de messagerie .

Dans ce défi, vous apprenez à évaluer si les traces générées par l'instrumentation OTel satisfont aux objectifs susmentionnés. Tout d’abord, vous faites tourner le système et créez des traces . Vous inspectez ensuite la trace d’un flux de messages et les sections de celui-ci générées par NGINX , le service de messagerie et le service de notification .

Créer des données de trace

Dans le terminal client, configurez une conversation et envoyez quelques messages entre deux utilisateurs :

curl -X POST \
-H "Type de contenu : application/json" \
-d '{"participant_ids": [1, 2]}' \
'http://localhost:8085/conversations'

curl -X POST \
-H "Identifiant utilisateur : 1" \
-H "Contenu-Type : application/json" \
-d '{"contenu": "Ceci est le premier message"}' \
'http://localhost:8085/conversations/1/messages'

curl -X POST \
-H "User-Id: 2" \
-H "Contenu-Type : application/json" \
-d '{"contenu": "Ceci est le deuxième message"}' \
'http://localhost:8085/conversations/1/messages'

Une sortie semblable à celle-ci est générée par le service de notification et apparaît dans le terminal de notification :

Nouveau message reçu : {"type":"new_message","channel_id":1,"user_id":1,"index":1,"participant_ids":[1,2]}Envoi d'une notification de nouveau message par SMS au 12027621401

Nouveau message reçu : {"type":"new_message","channel_id":1,"user_id":2,"index":2,"participant_ids":[1,2]}

Envoi d'une notification de nouveau message par e-mail à the_hotstepper@kamo.ze

Envoi d'une notification de nouveau message par SMS au 19147379938

Préparez-vous à lire les traces

Ouvrez l’interface utilisateur Jaeger dans le navigateur, sélectionnez messenger-lb dans le menu déroulant Service et cliquez sur le bouton Rechercher des traces . Une liste de traces apparaît, commençant au tout début du flux. Cliquez sur n'importe quelle trace pour afficher des détails à son sujet, comme dans cette capture d'écran :

Capture d'écran de l'interface utilisateur graphique Jaeger montrant l'ensemble des étendues dans le flux

Cliquez autour et explorez un peu. Ensuite, avant de continuer, réfléchissez à la manière dont les informations contenues dans les traces soutiennent vos objectifs en matière d’instrumentation, tels qu’énumérés dans l’ introduction du Défi 3. Les questions pertinentes incluent :

  • Quelles informations aident à atteindre les objectifs ?
  • Quelles informations manquent ?
  • Quelles informations ne sont pas pertinentes ?

Examinez la section NGINX (messenger-lb) de la trace

Objectif 1 : Voir toutes les étapes suivies par une demande dans le nouveau flux de messages

Commencez par l’étendue NGINX, qui comporte 11 étendues enfants dans l’étendue parent. Étant donné que la configuration NGINX actuelle est très simple, les étendues enfants ne sont pas très intéressantes et affichent simplement le temps nécessaire à chaque étape du cycle de vie de traitement des requêtes NGINX. Cependant, le parent span (le tout premier) contient quelques informations intéressantes :

Capture d'écran de l'interface graphique de Jaeger montrant la portée parent dans la section NGINX (messenger-lb) de la trace

  • Sous Tags, vous voyez les attributs suivants :

    • Champ http.methodPOST (en termes REST, cela signifie création)
    • Champ http.status_code201 (indiquant une création réussie)
    • Champ http.targetconversations/1/messages (le point de terminaison du message)

    Prises ensemble, ces trois informations se combinent pour dire : « Une requête POST a été envoyée à /conversations/1/messages et la réponse a été201 (créé avec succès)”. Cela correspond aux étapes 1 et 4a de Architecture et flux utilisateur ).

  • Sous Processus , le champ webengine.name indique qu'il s'agit de la partie NGINX de la demande.

De plus, étant donné que les plages pour Messenger et Notifier sont imbriquées dans la plage messenger-lb conversations/1 (comme indiqué dans la capture d'écran dans Préparez-vous à lire les traces ), vous pouvez dire que la demande envoyée au service de messagerie via le proxy inverse NGINX a atteint tous les composants attendus dans le flux.

Ces informations satisfont l’objectif car vous pouvez voir que le proxy inverse NGINX faisait partie du flux.

Objectif 2 : Vérifiez que le flux s'exécute dans les cinq secondes

Dans la liste des spans étiquetés messenger-lb , regardez le span le plus récent (il se trouve en bas de la liste) pour voir combien de temps a pris la partie NGINX de la demande. Dans la capture d'écran, la durée a commencé à 589 microsecondes (µs) et a duré 24 µs, ce qui signifie que l'opération complète de proxy inverse n'a pris que 613 µs, soit environ 0,6 milliseconde (ms). (Les valeurs exactes seront bien sûr différentes lorsque vous exécuterez le didacticiel vous-même.)

Capture d'écran de l'interface utilisateur graphique Jaeger montrant les étendues dans la section NGINX (messenger-lb) de la trace

Dans une configuration comme celle-ci, la plupart du temps, les valeurs ne sont utiles que par rapport à d’autres mesures et elles varient selon les systèmes. Dans ce cas, cependant, cette opération ne risque clairement pas d’atteindre une durée de cinq secondes.

Ces informations satisfont l’objectif car vous pouvez voir que les opérations NGINX n’ont pas pris près de cinq secondes. S'il y a une opération très lente dans le flux, elle doit se produire plus tard.

Objectif 3 : Découvrez combien de temps il faut au service de notification pour lire l'événement envoyé par le service de messagerie

La couche proxy inverse NGINX n'inclut aucune information à ce sujet, vous pouvez donc passer aux étendues de messagerie .

Examiner la section Messager de la Trace

Objectif 1 : Voir toutes les étapes suivies par une demande dans le nouveau flux de messages

La section du service de messagerie de la trace contient 11 autres étendues. Encore une fois, la plupart des tâches enfants concernent les étapes de base que le framework Express utilise lors du traitement d'une requête et ne sont pas très intéressantes. Cependant, le span parent (le tout premier) contient à nouveau quelques informations intéressantes :

Capture d'écran de l'interface utilisateur graphique de Jaeger montrant la portée parent dans la section de messagerie de la trace

Sous Tags, vous voyez les attributs suivants :

  • Champ http.methodPOST (encore une fois, en termes REST, cela signifie création)
  • Champ http.route/conversations/:conversationId/messages (la route du message)
  • Champ http.target/conversations/1/messages (le point de terminaison du message)

Ces informations satisfont l’objectif car elles nous montrent que le service de messagerie faisait partie du flux et que le point de terminaison atteint était le nouveau point de terminaison du message.

Objectif 2 : Vérifiez que le flux s'exécute dans les cinq secondes

Comme indiqué dans la capture d'écran suivante, la partie messager de la trace a commencé à 1,28 ms et s'est terminée à 36,28 ms, pour une durée globale de 35 ms. La majeure partie de ce temps a été consacrée à l'analyse de JSON ( middleware - jsonParser ) et, dans une plus large mesure, à la connexion à la base de données ( pg-pool.connect et tcp.connect ).

Cela a du sens étant donné que plusieurs requêtes SQL sont également effectuées lors du processus d’écriture du message. Cela suggère à son tour que vous souhaiterez peut-être augmenter la configuration de l’instrumentation automatique pour capturer le timing de ces requêtes. (Le didacticiel ne montre pas cette instrumentation supplémentaire, mais dans le défi 4, vous créez manuellement des étendues qui peuvent à leur tour être utilisées pour encapsuler des requêtes de base de données.)

Capture d'écran de l'interface utilisateur de Jaeger montrant les intervalles dans la section de messagerie de la trace et combien de temps ils ont pris

Ces informations satisfont l’objectif car elles montrent que les opérations de messagerie ne prennent pas près de cinq secondes. S'il y a une opération très lente dans le flux, elle doit se produire plus tard.

Objectif 3 : Découvrez combien de temps il faut au service de notification pour lire l'événement envoyé par le service de messagerie

Comme les étendues NGINX, les étendues de messagerie n'incluent pas ces informations, vous pouvez donc passer aux étendues de notification .

Examiner la section de notification de la trace

Objectif 1 : Voir toutes les étapes suivies par une demande dans le nouveau flux de messages

La section de notification de la trace ne contient que deux étendues :

Capture d'écran de l'interface utilisateur graphique de Jaeger montrant les deux étendues dans la section de notification de la trace

  • Le processus chat_queue span – Confirme que le service de notification a traité un événement de la file d'attente de messages chat_queue
  • L'étendue pg-pool.connect – Montre qu'après avoir traité l'événement, le service de notification a établi une sorte de connexion à sa base de données

Les informations disponibles à partir de ces travées ne répondent que partiellement à l’objectif de compréhension de chaque étape. Vous pouvez voir que le service de notification est arrivé au point de consommer l'événement de la file d'attente, mais vous ne savez pas si :

  • La notification de message envoyée par ce service correspond à l'événement envoyé par le service de messagerie
  • Les notifications de message pertinentes ont été correctement envoyées au destinataire du message

Cela indique que vous devez effectuer les opérations suivantes pour bien comprendre le flux du service de notification :

  • Instrumenter manuellement les intervalles qui montrent qu'une notification est envoyée
  • Assurez-vous qu'il existe une connexion explicite entre l'événement envoyé par le service de messagerie et l'événement consommé par le service de notification , sous la forme d'un ID de trace

Objectif 2 : Vérifiez que le flux s'exécute dans les cinq secondes

En examinant la durée globale des intervalles du service de notification , vous voyez que la demande a passé 30,77 ms dans la section de notification du flux. Cependant, comme il n'existe aucune plage signalant la « fin » de l'ensemble du flux (l'envoi de notifications au destinataire), vous ne pouvez pas déterminer la durée totale de cette section du flux ni le temps d'achèvement global de l'opération.

Objectif 3 : Découvrez combien de temps il faut au service de notification pour lire l'événement envoyé par le service de messagerie

Vous pouvez cependant voir qu'un processus chat_queue pour le service de notification a démarré à 6,12 ms, 2 ms après qu'un processus chat_queue d'envoi a démarré pour le service de messagerie à 4,12 ms.

Capture d'écran de l'interface utilisateur graphique de Jaeger montrant le service de notification consommant un événement envoyé par le service de messagerie

Cet objectif est atteint car vous savez que le notificateur a consommé un événement 2 ms après son envoi par le service de messagerie . Contrairement à l'objectif 2, atteindre cet objectif ne nécessite pas de savoir si l'événement a été entièrement traité ou combien de temps cela a pris.

Conclusions

Sur la base de notre analyse des traces produites par l’auto-instrumentation actuelle d’OTel, il ressort clairement que :

  • La plupart de ces périodes ne sont pas utiles dans leur forme actuelle :

    • NGINX produit des étendues liées à des fonctions (telles que la vérification des autorisations et la diffusion de fichiers) qui ne sont pas pertinentes pour le rôle qui vous intéresse, le proxy inverse. Cependant, à ce stade, l'instrumentation OTel pour NGINX ne permet pas d'omettre les intervalles non pertinents, donc rien ne peut être fait.
    • Parmi les spans pour les services Node.js (les services de messagerie et de notification ), certains semblent pertinents pour les objectifs : les spans pour l'analyse JSON, le gestionnaire de requêtes et toutes les opérations de base de données. Certaines extensions du middleware (comme expressInit et corsMiddleware ) ne semblent pas pertinentes et peuvent être supprimées.
  • Des portées critiques manquent pour les éléments suivants :

    • Notifications envoyées par le service de notification
    • Une correspondance claire entre l'événement RabbitMQ envoyé par le service de messagerie et celui traité par le service de notification

Cela signifie que l'instrumentation de base remplit le dernier objectif :

  • Voyez combien de temps il faut au service de notification pour commencer à traiter l'événement envoyé par le service de messagerie

Cependant, il n’y a pas suffisamment d’informations pour atteindre les deux premiers objectifs :

  • Comprendre toutes les étapes par lesquelles passe une demande au cours du nouveau flux de messages
  • Soyez assuré que le flux s'exécute de bout en bout en moins de cinq secondes dans des circonstances normales

Défi 4 : Optimiser l'instrumentation en fonction des relevés de traces

Dans ce défi, vous optimisez l'instrumentation OTel en fonction des analyses de traces que vous avez effectuées dans le défi 3. Cela inclut à la fois la suppression des intervalles inutiles , la création de nouveaux intervalles personnalisés et la confirmation que l'événement consommé par le service de notification est celui généré par le service de messagerie.

Supprimer les intervalles inutiles

  1. Dans votre éditeur de texte préféré, ouvrez le fichier tracing.mjs dans le répertoire app du référentiel Messenger et ajoutez ce qui suit à la fin de la liste des instructions d'importation en haut :

    const IGNORED_EXPRESS_SPANS = new Set([ "middleware - expressInit",
    "middleware - corsMiddleware",
    ]);
    

    Cela définit un ensemble de noms de span, dérivés de la liste des spans affichées dans la capture d'écran suivante de l'interface utilisateur Jaeger, à omettre de la trace car ils ne fournissent aucune information utile pour ce flux. Vous pouvez décider que d’autres étendues répertoriées dans la capture d’écran ne sont pas non plus nécessaires et les ajouter à la liste de IGNORED_EXPRESS_SPANS .

    Capture d'écran de l'interface utilisateur graphique de Jaeger montrant la liste de plusieurs segments du service de messagerie qui pourraient fournir des informations pertinentes et peuvent donc être omis de la trace

  2. Ajoutez des filtres à la configuration d’auto-instrumentation pour omettre les plages que vous ne souhaitez pas, en modifiant la ligne surlignée en orange :

    const sdk = new opentelemetry.NodeSDK({ ressource, traceExporter : new OTLPTraceExporter({ en-têtes : {} }), instrumentations : [getNodeAutoInstrumentations()], });
    

    à ceci :

    const sdk = new opentelemetry.NodeSDK({ resource, traceExporter: new OTLPTraceExporter({ headers: {} }), instrumentations: [ getNodeAutoInstrumentations({ "@opentelemetry/instrumentation-express": { ignoreLayers: [ (name) => { return IGNORED_EXPRESS_SPANS.has(name); }, ], }, }), ], });
    

    La fonction getNodeAutoInstrumentations référence l'ensemble des étendues définies à l'étape 1 pour les filtrer de la trace générée par @opentelemetry/instrumentation-express . En d’autres termes, l’instruction return est résolue sur true pour une étendue qui appartient à IGNORED_EXPRESS_SPANS et l’instruction ignoreLayers supprime cette étendue de la trace.

  3. Dans le terminal de messagerie, appuyez sur Ctrl+c pour arrêter le service de messagerie . Puis redémarrez-le :

    ^cnode --import ./tracing.mjs index.mjs
    
  4. Attendez une dizaine de secondes, puis dans le terminal client envoyez un nouveau message :

    curl -X POST \ -H "Identifiant utilisateur : 2" \
    -H "Contenu-Type : application/json" \
    -d '{"contenu": "Ceci est le deuxième message"}' \
    'http://localhost:8085/conversations/1/messages'
    
  5. Revérifiez les étendues de messagerie dans l'interface utilisateur de Jaeger. Les deux étendues de middleware , expressInit et corsMiddleware , n'apparaissent plus (vous pouvez les comparer à la capture d'écran de l'objectif 2 de la section Examiner la messagerie de la trace dans le défi 3).

    Capture d'écran de l'interface utilisateur graphique de Jaeger montrant que la trace n'inclut plus deux plages après avoir modifié l'instrumentation pour les filtrer hors de la trace

Configurer des plages personnalisées

Dans cette section, vous touchez le code de l'application pour la première fois. L’auto-instrumentation génère un grand nombre d’informations sans nécessiter de modifications de l’application, mais certaines informations ne sont possibles qu’en instrumentant des éléments spécifiques de la logique métier.

Pour le nouveau flux de messages que vous instrumentez, un exemple de ceci est le traçage de l'envoi de notifications au destinataire du message.

  1. Ouvrez index.mjs dans le répertoire d’application du référentiel de notification . Ce fichier contient toute la logique métier du service. Ajoutez la ligne suivante à la fin de la liste des instructions d’importation en haut du fichier :

    importer { trace } depuis "@opentelemetry/api";
    
  2. Remplacez ce code (environ à la ligne 91 du fichier) :

    for (let pref of préférences) {
    console.log(
    `Envoi d'une notification de nouveau message via ${pref.address_type} à ${pref.address}`
    );
    }
    

    avec:

    const tracer = trace.getTracer("notifier"); // 1tracer.startActiveSpan( // 2
    "notification.send_all",
    {
    attributs : {
    user_id : msg.user_id,
    },
    },
    (parentSpan) => {
    for (let pref of preferences) {
    tracer.startActiveSpan( // 3
    "notification.send",
    {
    attributs : { // 4
    notification_type : pref.address_type,
    user_id : pref.user_id,
    },
    },
    (span) => {
    console.log(
    `Envoi d'une notification de nouveau message via ${pref.address_type} à ${pref.address}`
    );
    span.end(); // 5
    }
    );
    }
    parentSpan.end(); // 6
    }
    );
    

    Le nouveau code effectue les opérations suivantes :

    1. Obtient le traceur , qui est un objet global permettant d'interagir avec les traces OTel.
    2. Démarre une nouvelle étendue parent appelée notification.send_all et définit l'attribut user_id pour identifier l'expéditeur du message.
    3. Entre dans une boucle où les préférences de notification du destinataire sont énumérées et une nouvelle étendue enfant appelée notification.send est créée sous notification.send_all . Chaque notification génère une nouvelle période.
    4. Définit plus d’attributs pour l’étendue enfant :

      • notification_typeSMS ou e-mail
      • user_id – L’ID de l’utilisateur recevant la notification
    5. Ferme chaque notification enfant.send span à tour de rôle.
    6. Ferme la notification parent.send_all span.

    Avoir un parent span garantit que chaque opération « envoyer une notification » est signalée même si aucune préférence de notification n'est découverte pour l'utilisateur.

  3. Dans le terminal de notification, appuyez sur Ctrl+c pour arrêter le service de notification . Puis redémarrez-le :

    ^cnode --import ./tracing.mjs index.mjs
    
  4. Attendez une dizaine de secondes, puis dans le terminal client envoyez un nouveau message :

    curl -X POST \ -H "Identifiant utilisateur : 2" \
    -H "Contenu-Type : application/json" \
    -d '{"contenu": "Ceci est le deuxième message"}' \
    'http://localhost:8085/conversations/1/messages'
    
  5. Revérifiez les plages de notification dans l'interface utilisateur de Jaeger. Vous voyez la plage parent et deux plages enfants, chacune avec une opération « envoyer une notification » :

    Capture d'écran de l'interface utilisateur graphique de Jaeger montrant le résultat de la définition de trois nouvelles étendues dans le code pour le service de notification

Vous pouvez désormais atteindre pleinement les premier et deuxième objectifs, car vous pouvez voir toutes les étapes qu'une demande subit au cours du nouveau flux de messages. Les timings de chaque segment révèlent tout décalage entre chacune de ces étapes.

Confirmer que le messager et le notificateur gèrent le même événement

Il y a encore une chose dont vous avez besoin pour avoir une vision complète du flux. L'événement traité par le service de notification est-il en fait celui envoyé par le service de messagerie ?

Vous n’avez pas besoin d’effectuer de modifications explicites pour connecter les deux traces, mais vous ne voulez pas non plus faire aveuglément confiance à la magie de l’auto-instrumentation.

Dans cet esprit, ajoutez un code de débogage rapide pour vérifier que la trace qui démarre dans le service NGINX est bien la même (a le même ID de trace) que celle consommée par le service de notification .

  1. Ouvrez le fichier index.mjs dans le répertoire app du référentiel Messenger et apportez les modifications suivantes :

    • Ajoutez la ligne suivante à la fin de la liste des instructions d’importation en haut :

      importer { trace } depuis "@opentelemetry/api";
      
    • Ajoutez les lignes surlignées en orange sous la ligne existante en noir :

      fonction asynchrone createMessageInConversation(req, res) { const tracer = trace.getActiveSpan(); console.log("TRACE_ID: ", tracer.spanContext().traceId);
      

      Les nouvelles lignes impriment le TRACE_ID à partir de la fonction de Messenger qui gère la création d'un nouveau message.

  2. Ouvrez le fichier index.mjs dans le répertoire app du référentiel notifier et ajoutez la ligne surlignée en orange sous la ligne existante en noir :

    exporter la fonction asynchrone handleMessageConsume(canal, msg, gestionnaires) { console.log("RABBIT_MQ_MESSAGE : ", msg);
    

    La nouvelle ligne imprime le contenu complet de l'événement AMQP reçu par le service de notification .

  3. Arrêtez et redémarrez les services de messagerie et de notification en exécutant ces commandes dans les terminaux de messagerie et de notification :

    ^cnode --import ./tracing.mjs index.mjs
    
  4. Attendez environ dix secondes, puis dans le terminal client, envoyez à nouveau un message :

    curl -X POST \ -H "Identifiant utilisateur : 2" \
    -H "Contenu-Type : application/json" \
    -d '{"contenu": "Ceci est le deuxième message"}' \
    'http://localhost:8085/conversations/1/messages'
    
  5. Consultez les journaux des services de messagerie et de notification . Le journal du service de messagerie inclut une ligne comme celle-ci qui indique l'ID de trace d'un message (l'ID réel sera différent lorsque vous exécuterez le didacticiel) :

    ID_TRACE :  29377a9b546c50be629c8e64409bbfb5
    
  6. De même, le journal du service de notification signale un ID de trace en sortie comme ceci :

    _spanContext : {
    traceId : '29377a9b546c50be629c8e64409bbfb5', spanId : 'a94e9462a39e6dbf', traceFlags : 1,
    traceState : indéfini
    },
    
  7. Les identifiants de trace correspondent dans la console, mais en guise d'étape finale, vous pouvez les comparer à l'identifiant de trace dans l'interface utilisateur Jaeger. Ouvrez l'interface utilisateur au point de terminaison de l'identifiant de trace pertinent (le vôtre sera différent, mais dans cet exemple, il s'agit de http://localhost:16686/trace/29377a9b546c50be629c8e64409bbfb5 ) pour voir l'intégralité de la trace. La trace de Jaeger confirme que :

    • L’auto-instrumentation AMQP dans le service de messagerie ajoute cet ID de trace dans le cadre des métadonnées lorsque l’événement est envoyé.
    • L’auto-instrumentation AMQP dans le service de notification attend ces métadonnées et définit le contexte de trace de manière appropriée.

Note: Dans un système de production réel, vous supprimeriez le code que vous avez ajouté dans cette section une fois que vous avez confirmé que le flux fonctionne comme prévu.

Nettoyage des ressources

Vous avez créé quelques conteneurs et images tout au long de ce tutoriel ! Utilisez ces instructions pour les supprimer.

  • Pour supprimer tous les conteneurs Docker en cours d’exécution :

    docker rm $(docker stop messager-lb)
    
  • Pour supprimer le service de plateforme et les services de base de données de messagerie et de notification :

    cd ~/microservices-march/platform && docker compose down
    cd ~/microservices-march/notifier && docker compose down
    cd ~/microservices-march/messenger && docker compose down
    

Prochaines étapes

Félicitations, vous avez terminé le didacticiel !

  • Vous configurez l'instrumentation OTel sur un proxy inverse NGINX et deux services Node.js.
  • Vous avez examiné les données fournies par l’auto-instrumentation OTel avec un œil critique et ajouté certaines données de télémétrie qui manquaient pour atteindre les objectifs du laboratoire OTel :
    • Vous avez obtenu une vue raisonnable du flux d’une demande spécifique via un système de messagerie sans modifier directement le code de l’application.
    • Vous avez confirmé que le flux s’exécutait de bout en bout en moins de cinq secondes dans des circonstances normales.

Et pourtant, vous n’avez fait qu’effleurer la surface de ce à quoi pourrait ressembler une configuration de traçage idéale ! Dans un environnement de production, vous souhaiterez peut-être ajouter des éléments tels que des étendues personnalisées pour chaque requête de base de données et des métadonnées supplémentaires sur toutes les étendues décrivant les détails d'exécution tels que l'ID de conteneur de chaque service. Vous pouvez également implémenter les deux autres types de données OTel (métriques et journalisation) pour obtenir une image complète de l'état de santé de votre système.

Pour poursuivre votre formation sur les microservices, consultez Microservices mars 2023. Dans l'unité 4 : Gérez le chaos et la complexité des microservices avec l'observabilité . Vous découvrirez les trois principales classes de données d'observabilité, l'importance de l'alignement de l'infrastructure et des applications, ainsi que les moyens de commencer à analyser des données approfondies.


« Cet article de blog peut faire référence à des produits qui ne sont plus disponibles et/ou qui ne sont plus pris en charge. Pour obtenir les informations les plus récentes sur les produits et solutions F5 NGINX disponibles, explorez notre famille de produits NGINX . NGINX fait désormais partie de F5. Tous les liens NGINX.com précédents redirigeront vers un contenu NGINX similaire sur F5.com."