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é :
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 :
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.
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 :
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 :
Ce diagramme illustre l’architecture globale et le flux de données entre les microservices et autres éléments utilisés dans le didacticiel.
Les deux microservices sont :
Les trois éléments de l'infrastructure de soutien sont :
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é.
Le flux se décompose comme suit :
En même temps:
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 :
Notez que ces objectifs sont liés à la fois au fonctionnement technique du système et à l’expérience utilisateur.
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.
bash
(mais tout le code et les commandes sont fournis et expliqués, vous pouvez donc toujours réussir avec des connaissances limitées)Node.js 19.x ou version ultérieure
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)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 .
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
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.
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.
-d
indique à Docker Compose de se détacher des conteneurs lorsqu'ils ont démarré (sinon les conteneurs resteront attachés à votre terminal).--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.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
Installer les dépendances :
installation de npm
Démarrez la base de données PostgreSQL pour le service de messagerie :
docker compose -d
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
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.
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 courantesNote: 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.
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
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 :
Crée une nouvelle instance de NodeSDK et la configure pour :
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
)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.
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 :
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).
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.
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.
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
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.
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 :
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 .
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 ).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()], });
Redémarrer le service de messagerie :
nœud --import ./tracing.mjs index.mjs
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.
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) :
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) :
Cliquez sur une trace pour afficher les intervalles qu'elle contient. Chaque segment est correctement étiqueté comme provenant du service de messagerie :
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 .
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
Installer les dépendances :
installation de npm
Démarrez la base de données PostgreSQL pour le service de notification :
docker compose -d
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
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
Créez un nouveau fichier appelé tracing.mjs :
traçage tactile.mjs
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
.
Démarrer le service de notification avec l'auto-instrumentation OTel :
nœud --import ./tracing.mjs index.mjs
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.
Confirmez qu'un nouveau service appelé notifier apparaît dans l'interface utilisateur Jaeger du navigateur :
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.
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
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
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 :
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
.
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;
Créez une image Docker contenant NGINX ainsi que le module NGINX OTel :
docker build -t messager-lb .
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
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.
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.
Dans Architecture et flux utilisateur , nous avons décrit les étapes du flux utilisateur, mais pour récapituler :
Les objectifs de la mise en œuvre de la télémétrie sont les suivants :
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 .
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
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 :
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 :
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 :
Sous Tags, vous voyez les attributs suivants :
http.method
– POST
(en termes REST, cela signifie création)http.status_code
–201
(indiquant une création réussie)http.target
– conversations/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 ).
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.
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.)
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.
La couche proxy inverse NGINX n'inclut aucune information à ce sujet, vous pouvez donc passer aux étendues de messagerie .
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 :
Sous Tags, vous voyez les attributs suivants :
http.method
– POST
(encore une fois, en termes REST, cela signifie création)http.route
– /conversations/:conversationId/messages
(la route du message)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.
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.)
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.
Comme les étendues NGINX, les étendues de messagerie n'incluent pas ces informations, vous pouvez donc passer aux étendues de notification .
La section de notification de la trace ne contient que deux étendues :
processus
chat_queue
span – Confirme que le service de notification a traité un événement de la file d'attente de messages chat_queuepg-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éesLes 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 :
Cela indique que vous devez effectuer les opérations suivantes pour bien comprendre le flux du service de notification :
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.
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.
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.
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 :
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 :
Cela signifie que l'instrumentation de base remplit le dernier objectif :
Cependant, il n’y a pas suffisamment d’informations pour atteindre les deux premiers objectifs :
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.
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
.
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.
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
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'
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).
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.
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";
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 :
traceur
, qui est un objet global permettant d'interagir avec les traces OTel.notification.send_all
et définit l'attribut user_id
pour identifier l'expéditeur du message.notification.send
est créée sous notification.send_all
. Chaque notification génère une nouvelle période.Définit plus d’attributs pour l’étendue enfant :
notification_type
– SMS
ou e-mail
user_id
– L’ID de l’utilisateur recevant la notificationnotification
enfant.send span à tour de rôle.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.
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
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'
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 » :
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.
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 .
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.
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 .
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
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'
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
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
},
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 :
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.
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
Félicitations, vous avez terminé le didacticiel !
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."