Instrumenter les informations de trace à l'aide d'OpenTelemetry

1. Introduction

5af4a7e43b0feaab.png

Dernière mise à jour:05/03/2021

Observabilité de l'application

Observabilité et OpenTelemetry

L'observabilité est le terme utilisé pour décrire un attribut d'un système. Un système avec observabilité permet aux équipes de déboguer activement leur système. Dans ce contexte, les trois piliers de l'observabilité : Les journaux, les métriques et les traces constituent l'instrumentation fondamentale pour que le système acquière l'observabilité.

OpenTelemetry est un ensemble de spécifications et de SDK qui accélère l'instrumentation et l'exportation des données de télémétrie (journaux, métriques et traces) requises par l'observabilité. OpenTelemetry est un projet de norme CNCF ouvert au sein de la communauté. En utilisant les bibliothèques fournies par le projet et son écosystème, les développeurs peuvent instrumenter leurs applications de manière neutre du point de vue du fournisseur et avec plusieurs architectures.

Trace distribuée

Parmi les journaux, les métriques et les traces, la trace est la télémétrie qui indique la latence d'une partie spécifique du processus dans le système. À l'ère des microservices, en particulier, la trace distribuée est le principal moteur de détection des goulots d'étranglement de latence dans l'ensemble du système distribué.

Lors de l'analyse de traces distribuées, la visualisation des données de trace est essentielle pour comprendre en un coup d'œil les latences globales du système. Dans une trace distribuée, nous traitons un ensemble d'appels pour traiter une requête unique adressée au point d'entrée du système sous une forme de trace contenant plusieurs segments.

Le segment représente une unité de travail individuelle dans un système distribué, qui enregistre les heures de début et de fin. Les segments ont souvent des relations hiérarchiques entre eux. Dans l'image ci-dessous, tous les segments plus petits sont des segments enfants d'un grand segment /messages, et sont assemblés en une seule trace qui montre le cheminement des tâches à travers un système.

adbd3ecd69d410cb.png

Google Cloud Trace est l'une des options de backend de trace distribué et s'intègre bien aux autres produits de Google Cloud.

Objectifs de l'atelier

Dans cet atelier de programmation, vous allez instrumenter les informations de trace dans les services appelés "Shakesapp". qui s'exécute sur un cluster Kubernetes s'exécutant sur Google Kubernetes Engine. L'architecture de Shakesapp est décrite ci-dessous:

68873c018a7be7de.png

  • Le client envoie une chaîne de requête au serveur
  • Le serveur accepte la requête du client, extrait tous les travaux de Shakespare au format texte à partir de Google Cloud Storage, recherche les lignes contenant la requête et renvoie le numéro de la ligne qui correspond au client.

Vous instrumentez les informations de trace dans la requête.

Points abordés

  • Premiers pas avec les bibliothèques OpenTelemetry Trace dans un projet Python
  • Créer un segment avec la bibliothèque
  • Propager des contextes de segments sur le réseau entre les composants d'application
  • Envoyer des données de trace à Google Cloud Trace
  • Analyser la trace sur Google Cloud Trace

Cet atelier de programmation vous explique comment instrumenter vos microservices. Pour faciliter la compréhension, cet exemple ne contient que trois composants (générateur de charge, client et serveur), mais vous pouvez appliquer le même processus expliqué dans cet atelier de programmation à des systèmes plus complexes et de grande taille.

Prérequis

  • Connaissances sur Python 3

2. Préparation

Configuration de l'environnement au rythme de chacun

Si vous ne possédez pas encore de compte Google (Gmail ou Google Apps), vous devez en créer un. Connectez-vous à la console Google Cloud Platform (console.cloud.google.com) et créez un projet.

Si vous avez déjà un projet, cliquez sur le menu déroulant de sélection du projet dans l'angle supérieur gauche de la console :

15b8b6ac4d917005.png

Cliquez ensuite sur le bouton "NEW PROJECT" (NOUVEAU PROJET) dans la boîte de dialogue qui s'affiche pour créer un projet :

7136b3ee36ebaf89.png

Si vous n'avez pas encore de projet, une boîte de dialogue semblable à celle-ci apparaîtra pour vous permettre d'en créer un :

90977ce514204b51.png

La boîte de dialogue de création de projet suivante vous permet de saisir les détails de votre nouveau projet :

6d9573e346e930b4.png

Notez l'ID du projet. Il s'agit d'un nom unique pour tous les projets Google Cloud, ce qui implique que le nom ci-dessus n'est plus disponible pour vous… Désolé ! Il sera désigné par le nom "PROJECT_ID" dans la suite de cet atelier de programmation.

Ensuite, si vous ne l'avez pas déjà fait, vous devez activer la facturation dans la Play Console pour pouvoir utiliser les ressources Google Cloud et activer l'API Cloud Trace.

eb5325f65619ad6a.png

Suivre cet atelier de programmation ne devrait pas vous coûter plus d'un euro. Cependant, cela peut s'avérer plus coûteux si vous décidez d'utiliser davantage de ressources ou si vous n'interrompez pas les ressources (voir la section "Effectuer un nettoyage" à la fin du présent document). Les tarifs de Google Cloud Trace, de Google Kubernetes Engine et de Google Artifacat Registry sont indiqués dans la documentation officielle.

Les nouveaux utilisateurs de Google Cloud Platform peuvent bénéficier d'un Essai gratuit avec 300 $ de crédits afin de suivre gratuitement le présent atelier.

Configuration de Google Cloud Shell

Bien que vous puissiez utiliser Google Cloud et Google Cloud Trace à distance depuis votre ordinateur portable, dans cet atelier de programmation, nous allons utiliser Google Cloud Shell, un environnement de ligne de commande exécuté dans le cloud.

Cette machine virtuelle basée sur Debian contient tous les outils de développement dont vous aurez besoin. Elle intègre un répertoire d'accueil persistant de 5 Go et s'exécute sur Google Cloud, ce qui améliore nettement les performances du réseau et l'authentification. Cela signifie que tout ce dont vous avez besoin pour cet atelier de programmation est un navigateur (oui, tout fonctionne sur un Chromebook).

Pour activer Cloud Shell depuis la console Cloud, il vous suffit de cliquer sur Activer Cloud Shell gcLMt5IuEcJJNnMId-Bcz3sxCd0rZn7IzT_r95C8UZeqML68Y1efBG_B0VRp7hc7qiZTLAF-TXD7SsOadxn8uadgHhaLeASnVS3ZHK39eOlKJOgj9SJua_oeGhMxRrbOg3qigddS2A. Le provisionnement et la connexion à l'environnement ne devraient pas prendre plus de quelques minutes.

ff81d016724c4f67.png

fbe156ee6edfbb2e.png

Une fois connecté à Cloud Shell, vous êtes normalement déjà authentifié et le projet PROJECT_ID est sélectionné :

gcloud auth list

Résultat de la commande

Credentialed accounts:
 - <myaccount>@<mydomain>.com (active)
gcloud config list project

Résultat de la commande

[core]
project = <PROJECT_ID>

Si, pour une raison quelconque, le projet n'est pas défini, exécutez simplement la commande suivante :

gcloud config set project <PROJECT_ID>

Vous recherchez votre PROJECT_ID ? Vérifiez l'ID que vous avez utilisé pendant les étapes de configuration ou recherchez-le dans le tableau de bord Cloud Console :

a3e716fc9e7454e9.png

Par défaut, Cloud Shell définit certaines variables d'environnement qui pourront s'avérer utiles pour exécuter certaines commandes dans le futur.

echo $GOOGLE_CLOUD_PROJECT

Sortie de la commande

<PROJECT_ID>

Pour finir, définissez la configuration du projet et de la zone par défaut :

gcloud config set compute/zone us-central1-f

Vous pouvez choisir parmi différentes zones. Pour en savoir plus, consultez la page Régions et zones.

Configuration de Python

Dans cet atelier de programmation, nous utilisons le terme "poésie" de gérer strictement les versions des packages. Exécutez la commande suivante dans Cloud Shell:

curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python3 -
source $HOME/.poetry/env

Configurer un cluster Google Kubernetes

Dans cet atelier de programmation, vous allez exécuter un cluster de microservices sur Google Kubernetes Engine (GKE). Le processus de cet atelier de programmation est le suivant:

  1. Télécharger le projet de référence dans Cloud Shell
  2. Construire des microservices en conteneurs
  3. Importer des conteneurs sur Google Artifact Registry (GAR)
  4. Déployer des conteneurs sur GKE
  5. Modifier le code source des services pour l'instrumentation de traces
  6. Accéder à l'étape 2

Activer Kubernetes Engine

Tout d'abord, nous avons configuré un cluster Kubernetes dans lequel Shakesapp s'exécute sur GKE. Nous devons donc activer GKE. Accédez au menu "Kubernetes Engine". et appuyez sur le bouton ACTIVER.

56c680e93e169731.png

Vous êtes maintenant prêt à créer un cluster Kubernetes.

Créer un cluster Kubernetes

Dans Cloud Shell, exécutez la commande suivante pour créer un cluster Kubernetes. Veuillez confirmer que la valeur de la zone se trouve sous la région que vous avez utilisée pour créer le dépôt Artifact Registry. Modifiez la valeur de zone us-central1-f si la région de votre dépôt ne couvre pas la zone.

gcloud container clusters create otel-trace-codelab --zone us-central1-f \
--num-nodes 1 \
--machine-type e2-highcpu-4

Résultat de la commande

Creating cluster otel-trace-codelab in us-central1-f... Cluster is being health-checked (master is healthy)...done.
Created [https://container.googleapis.com/v1/projects/psychic-order-307806/zones/us-central1-f/clusters/otel-trace-codelab].
To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/workload_/gcloud/us-central1-f/otel-trace-codelab?project=psychic-order-307806
kubeconfig entry generated for otel-trace-codelab.
NAME                LOCATION       MASTER_VERSION    MASTER_IP        MACHINE_TYPE  NODE_VERSION      NUM_NODES  STATUS
otel-trace-codelab  us-central1-f  1.18.12-gke.1210  104.154.162.176  e2-medium     1.18.12-gke.1210  3          RUNNING

Configurer Artifact Registry et Skaffold

Nous disposons maintenant d'un cluster Kubernetes prêt à être déployé. Nous allons ensuite préparer un registre de conteneurs pour l'envoi et le déploiement de conteneurs. Pour ces étapes, nous devons configurer GAR et Skaffold pour l'utiliser.

Configurer Artifact Registry

Accédez au menu "Artifact Registry" et appuyez sur le bouton ACTIVER.

f7493243bae0cdf7.png

Après quelques instants, le navigateur de dépôt de GAR s'affiche. Cliquez sur CRÉER UN DÉPÔT. et saisissez le nom du dépôt.

f97f337f5476651.png

Dans cet atelier de programmation, je nomme le nouveau dépôt trace-codelab. Le format de l'artefact est "Docker" et le type d'emplacement est "Région". Choisissez une région proche de celle que vous avez définie pour la zone Google Compute Engine par défaut. Dans cet exemple, nous choisissons "us-central1-f". ci-dessus. C'est pourquoi nous choisissons ici "us-central1 (Iowa)". Cliquez ensuite sur le bouton CRÉER .

2f04143077ca56db.png

"trace-codelab" s'affiche dans le navigateur de dépôt.

7a3c1f47346bea15.png

Nous reviendrons ici plus tard pour vérifier le chemin d'accès au registre.

Configuration de Skaffold

Skaffold est un outil pratique lorsque vous créez des microservices exécutés sur Kubernetes. Elle gère le workflow de création, de transfert et de déploiement de conteneurs d'applications avec un petit ensemble de commandes. Skaffold utilise par défaut le registre Docker comme registre de conteneurs. Vous devez donc configurer Skaffold pour qu'il reconnaisse GAR lors du transfert de conteneurs.

Ouvrez à nouveau Cloud Shell et vérifiez si Skaffold est installé. (Cloud Shell installe Skaffold dans l'environnement par défaut.) Exécutez la commande suivante et affichez la version Skaffold.

skaffold version

Résultat de la commande

v1.20.0

Vous pouvez maintenant enregistrer le dépôt par défaut que Skaffold utilisera. Pour obtenir le chemin d'accès au registre, accédez au tableau de bord Artifact Registry, puis cliquez sur le nom du dépôt que vous venez de configurer.

55173fe922f40327.png

Des fils d'Ariane s'affichent alors en haut de la page. Cliquez sur l'icône e157b1359c3edc06.png pour copier le chemin d'accès au registre dans le presse-papiers.

a9b0fa44c37e0178.png

Lorsque vous cliquez sur le bouton "Copier", la boîte de dialogue qui s'affiche en bas du navigateur affiche le message suivant:

&quot;us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab&quot; a été copié(e)

Revenez à Cloud Shell. Exécutez la commande skaffold config set default-repo avec la valeur que vous venez de copier depuis le tableau de bord.

skaffold config set default-repo us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab

Résultat de la commande

set value default-repo to us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab for context gke_stackdriver-sandbox-3438851889_us-central1-b_stackdriver-sandbox

Vous devez également appliquer la configuration Docker au registre. Exécutez la commande suivante :

gcloud auth configure-docker us-central1-docker.pkg.dev --quiet

Résultat de la commande

{
  "credHelpers": {
    "gcr.io": "gcloud",
    "us.gcr.io": "gcloud",
    "eu.gcr.io": "gcloud",
    "asia.gcr.io": "gcloud",
    "staging-k8s.gcr.io": "gcloud",
    "marketplace.gcr.io": "gcloud",
    "us-central1-docker.pkg.dev": "gcloud"
  }
}
Adding credentials for: us-central1-docker.pkg.dev

Vous pouvez maintenant passer à l'étape suivante, qui consiste à configurer un conteneur Kubernetes sur GKE.

Résumé

Au cours de cette étape, vous allez configurer l'environnement de votre atelier de programmation:

  • Configurer Cloud Shell
  • Créer un dépôt Artifact Registy pour Container Registry
  • Configurer Skaffold pour utiliser Container Registry
  • Création d'un cluster Kubernetes dans lequel les microservices de l'atelier de programmation s'exécutent

Étape suivante

À l'étape suivante, vous allez créer, transférer et déployer vos microservices sur le cluster.

3. Créer, transférer et déployer les microservices

Télécharger les supports de l'atelier de programmation

À l'étape précédente, nous avons rempli toutes les conditions préalables pour cet atelier de programmation. Vous êtes maintenant prêt à exécuter des microservices entiers sur ces microservices. Les supports de l'atelier de programmation étant hébergés sur GitHub, téléchargez-les dans l'environnement Cloud Shell à l'aide de la commande Git suivante.

cd ~
git clone https://github.com/GoogleCloudPlatform/opentelemetry-trace-codelab-python.git

La structure de répertoires du projet est la suivante:

shakesapp-python
├── LICENSE
├── manifests
│   ├── client.yaml
│   ├── loadgen.yaml
│   └── server.yaml
├── proto
│   └── shakesapp.proto
├── skaffold.yaml
└── src
    ├── client
    ├── loadgen
    └── server
  • manifestes: fichiers manifestes Kubernetes
  • proto: définition du proto pour la communication entre le client et le serveur
  • src: répertoires pour le code source de chaque service
  • skaffold.yaml: fichier de configuration de Skaffold

Exécuter la commande Skaffold

Enfin, vous êtes prêt à créer, transférer et déployer tout le contenu sur le cluster Kubernetes que vous venez de créer. Cela semble comporter plusieurs étapes, mais Skaffold s'occupe de tout pour vous. Essayons avec la commande suivante:

cd shakesapp-python
skaffold run --tail

Dès l'exécution de la commande, la sortie de journal de docker build s'affiche, et vous pouvez confirmer qu'ils ont bien été transférés vers le registre.

Résultat de la commande

...
---> Running in c39b3ea8692b
 ---> 90932a583ab6
Successfully built 90932a583ab6
Successfully tagged us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/serverservice:step1
The push refers to repository [us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/serverservice]
cc8f5a05df4a: Preparing
5bf719419ee2: Preparing
2901929ad341: Preparing
88d9943798ba: Preparing
b0fdf826a39a: Preparing
3c9c1e0b1647: Preparing
f3427ce9393d: Preparing
14a1ca976738: Preparing
f3427ce9393d: Waiting
14a1ca976738: Waiting
3c9c1e0b1647: Waiting
b0fdf826a39a: Layer already exists
88d9943798ba: Layer already exists
f3427ce9393d: Layer already exists
3c9c1e0b1647: Layer already exists
14a1ca976738: Layer already exists
2901929ad341: Pushed
5bf719419ee2: Pushed
cc8f5a05df4a: Pushed
step1: digest: sha256:8acdbe3a453001f120fb22c11c4f6d64c2451347732f4f271d746c2e4d193bbe size: 2001

Une fois tous les conteneurs de service transférés, les déploiements Kubernetes démarrent automatiquement.

Résultat de la commande

sha256:b71fce0a96cea08075dc20758ae561cf78c83ff656b04d211ffa00cedb77edf8 size: 1997
Tags used in deployment:
 - serverservice -> us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/serverservice:step4@sha256:8acdbe3a453001f120fb22c11c4f6d64c2451347732f4f271d746c2e4d193bbe
 - clientservice -> us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/clientservice:step4@sha256:b71fce0a96cea08075dc20758ae561cf78c83ff656b04d211ffa00cedb77edf8
 - loadgen -> us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/loadgen:step4@sha256:eea2e5bc8463ecf886f958a86906cab896e9e2e380a0eb143deaeaca40f7888a
Starting deploy...
 - deployment.apps/clientservice created
 - service/clientservice created
 - deployment.apps/loadgen created
 - deployment.apps/serverservice created
 - service/serverservice created

Attention: Si le message d'erreur "Aucun accès push au dépôt d'images spécifié" s'affiche, vérifiez si la commande skaffold tente de transférer des images vers Docker Hub (docker.io), quelle que soit votre configuration sur le dépôt par défaut dans Skaffold. Dans ce cas, essayez d'ajouter "–default-repo" l'option "Skaffold run" comme ci-dessous.

$ skaffold run –tail –default-repo=us-central1-docker.pkg.dev/[ID du projet]/[nom du dépôt]

Après le déploiement, vous verrez les journaux de l'application émis vers stdout dans chaque conteneur, comme suit:

Résultat de la commande

[server] {"event": "starting server: 0.0.0.0:5050", "severity": "info", "timestamp": "2021-03-17T05:25:56.758575Z"}
[client] [2021-03-17 05:25:54 +0000] [1] [INFO] Starting gunicorn 20.0.4
[client] [2021-03-17 05:25:54 +0000] [1] [INFO] Listening at: http://0.0.0.0:8080 (1)
[client] [2021-03-17 05:25:54 +0000] [1] [INFO] Using worker: threads
[client] [2021-03-17 05:25:54 +0000] [7] [INFO] Booting worker with pid: 7
[client] {"event": "server address is serverservice:5050", "severity": "info", "timestamp": "2021-03-17T05:25:54.888627Z"}
[client] {"event": "request to server with query: world", "severity": "info", "timestamp": "2021-03-17T05:26:11.550923Z"}
[server] {"event": "query: world", "severity": "info", "timestamp": "2021-03-17T05:26:11.567048Z"}
[loadgen] {"event": "check connectivity: http://clientservice:8080/_healthz", "severity": "info", "timestamp": "2021-03-17T05:26:11.533605Z"}
[loadgen] {"event": "/_healthz response: ok", "severity": "info", "timestamp": "2021-03-17T05:26:11.544267Z"}
[loadgen] {"event": "confirmed connection ot clientservice", "severity": "info", "timestamp": "2021-03-17T05:26:11.544527Z"}

Enfin, vous êtes prêt à instrumenter votre application avec OpenTelemetry pour le traçage distribué des services.

Résumé

Au cours de cette étape, vous avez préparé le matériel de l'atelier de programmation dans votre environnement et confirmé que Skaffold s'exécute comme prévu.

Étape suivante

À l'étape suivante, vous allez modifier le code source du service loadgen pour instrumenter les informations de trace.

4. Instrumentation pour HTTP

Concept d'instrumentation et de propagation des traces

Avant de modifier le code source, voyons brièvement comment fonctionnent les traces distribuées dans un simple diagramme.

c8c659deaa9c9091.png

Dans cet exemple, nous instrumentons le code pour exporter les informations de trace et de délai vers Cloud Trace et propageons le contexte de trace dans la requête du service loadgen vers le service serveur.

L'application doit envoyer des métadonnées de trace telles que l'ID de trace et l'ID de délai pour que Cloud Trace assemble en une seule trace tous les segments ayant le même ID de trace. De plus, l'application doit propager des contextes de trace (combinaison de l'ID de trace et de l'ID de délai du délai parent) sur les requêtes de services en aval, afin de savoir quel contexte de trace traite.

Avec OpenTelemetry, vous pouvez:

  • pour générer un ID de trace et un ID de délai uniques
  • pour exporter l'ID de trace et l'ID de délai vers le backend
  • pour propager des contextes de trace vers d'autres services

Instrumenter le premier span

Service de générateur de charge d'instrument

Ouvrez l'éditeur Cloud Shell en appuyant sur le bouton 776a11bfb2122549.png en haut à droite de la fenêtre Cloud Shell. Ouvrez src/loadgen/loadgen.py à partir de l'explorateur dans le volet de gauche et recherchez la fonction main.

src/loadgen/loadgen.py

def main():
    ...
    # start request loop to client service
    logger.info("start client request loop")
    addr = f"http://{target}"
    while True:
        logger.info("start request to client")
        call_client(addr)
        logger.info("end request to client")
        time.sleep(2.0)

Dans la fonction main, vous voyez la boucle qui appelle la fonction call_client. Dans l'implémentation actuelle, la section comporte deux lignes de journal qui enregistrent le début et la fin de l'appel de fonction. Instrumentons maintenant les informations relatives aux segments pour suivre la latence de l'appel de fonction.

Vous devez d'abord créer un segment avec un ID de trace et un ID de délai uniques. OpenTelemetry fournit une bibliothèque pratique pour cela. Ajoutez les lignes suivantes pour importer les bibliothèques OpenTelemetry dans votre code.

 import structlog
+from opentelemetry import propagate, trace
+from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter
+from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.instrumentation.requests import RequestsInstrumentor
+from opentelemetry.sdk.trace.export import SimpleSpanProcessor
+from opentelemetry.propagators.cloud_trace_propagator import CloudTraceFormatPropagator

Étant donné que le générateur de charge appelle l'application cliente en HTTP via le module requests, nous utilisons le package d'extension pour requests et activons l'instrumentation.

 from opentelemetry.propagators.cloud_trace_propagator import CloudTraceFormatPropagator
+
+RequestsInstrumentor().instrument()

Configurez ensuite une instance Tracer qui gère les paramètres "Contenu de trace" et "Exporter"

     target = os.environ.get("CLIENT_ADDR", "0.0.0.0:8080")

+    exporter = CloudTraceSpanExporter()
+    trace.get_tracer_provider().add_span_processor(SimpleSpanProcessor(exporter))
+    tracer = trace.get_tracer(__name__)
+    propagate.set_global_textmap(CloudTraceFormatPropagator())
+    trace.set_tracer_provider(TracerProvider())
+
     # connectivity check to client service
     healthz = f"http://{target}/_healthz"
     logger.info(f"check connectivity: {healthz}")

Comme il s'agit d'un atelier de programmation destiné à comprendre le fonctionnement de l'instrumentation de traces, nous configurons Tracer pour enregistrer chaque requête et les envoyer au backend. (SimpleSpanProcessor()) Ce paramètre ne convient pas aux environnements de production. Veillez donc à modifier cette partie lorsque vous instrumentez votre application de production.

Vous pouvez maintenant instrumenter les segments avec Tracer. Le but ici est que vous devez générer explicitement un Span. C'est tout ! Bien que deux lignes permettent d'ajouter des métadonnées d'événement au segment, vous n'avez pas besoin de générer manuellement l'ID de trace et l'ID de segment uniques, ni de les intégrer au segment.

     logger.info("start client request loop")
     addr = f"http://{target}"
     while True:
-        logger.info("start request to client")
-        call_client(addr)
-        logger.info("end request to client")
+        with tracer.start_as_current_span("loadgen") as root_span:
+            root_span.add_event(name="request_start")
+            logger.info("start request to client")
+            call_client(addr)
+            root_span.add_event(name="request_end")
+            logger.info("end request to client")
         time.sleep(2.0)

Pour que la compilation Docker récupère les packages OpenTelemetry requis, exécutez la commande suivante:

poetry add "opentelemetry-exporter-gcp-trace=^1.0.0rc0"
poetry add "opentelemetry-propagator-gcp=^1.0.0rc0"
poetry add "opentelemetry-instrumentation-requests=^0.20b0"

Vous pouvez vérifier que la description de la dépendance correspondante est écrite dans pyproject.toml.

Instrumenter le service client

Dans la section précédente, nous avons instrumenté la partie contenue dans le rectangle rouge du dessin ci-dessous. Nous avons instrumenté les informations sur les segments dans le service de générateur de charge. Comme pour le service de générateur de charge, nous devons maintenant instrumenter le service client. La différence avec le service de générateur de charge est que le service client doit extraire les informations de l'ID de trace propagées depuis le service de générateur de charge dans l'en-tête HTTP et utiliser l'ID pour générer des segments.

ae074d4513c9931f.png

Ouvrez l'éditeur Cloud Shell et ajoutez les modules requis comme nous l'avons fait pour le service de générateur de charge.

src/client/client.py

 import flask
 import grpc
 import structlog
+from opentelemetry import propagate, trace
+from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter
+from opentelemetry.instrumentation.flask import FlaskInstrumentor
+from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.sdk.trace.export import SimpleSpanProcessor
+from opentelemetry.propagators.cloud_trace_propagator import \
+    CloudTraceFormatPropagator

 import shakesapp_pb2
 import shakesapp_pb2_grpc

Vous venez d'importer FlaskInstrumentor, qui active l'instrumentation automatique de l'application Flask pour le compte des utilisateurs afin d'extraire les en-têtes HTTP pour obtenir des contextes de trace avec une seule ligne de code. La communauté OpenTelemetry fournit des intégrations utiles similaires avec d'autres bibliothèques majeures. Pour en savoir plus, vous pouvez consulter la documentation officielle.

 app = flask.Flask(__name__)
+FlaskInstrumentor().instrument_app(app)

Avant de démarrer l'instrumentation, vous devez préparer l'instance Tracer de la même manière que pour un service de générateur de charge.

 logger.info(f"server address is {SERVER_ADDR}")

+exporter = CloudTraceSpanExporter()
+trace.get_tracer_provider().add_span_processor(SimpleSpanProcessor(exporter))
+propagate.set_global_textmap(CloudTraceFormatPropagator())
+trace.set_tracer_provider(TracerProvider())

 @app.route("/")
 def main_handler():
    ....

Il est maintenant prêt à ajouter une instrumentation dans le gestionnaire. Recherchez main_handler() et modifiez la partie qui envoie la requête gRPC au service du serveur.

@app.route("/")
def main_handler():
    q, count = random.choice(list(queries.items()))

    # get Tracer
    tracer = trace.get_tracer(__name__)

    with tracer.start_as_current_span("client") as cur_span:
        channel = grpc.insecure_channel(SERVER_ADDR)
        stub = shakesapp_pb2_grpc.ShakespeareServiceStub(channel)
        logger.info(f"request to server with query: {q}")
        cur_span.add_event("server_call_start")
        resp = stub.GetMatchCount(shakesapp_pb2.ShakespeareRequest(query=q))
        cur_span.add_event("server_call_end")
        if count != resp.match_count:
            raise UnexpectedResultError(
                f"The expected count for '{q}' was {count}, but result was {resp.match_count } obtained"
            )
        result = str(resp.match_count)
        logger.info(f"matched count for '{q}' is {result}")
    return result

Comme pour le service de générateur de charge, ajoutez les packages requis dans pyproject.toml à l'aide de la commande suivante.

poetry add "opentelemetry-exporter-gcp-trace=^1.0.0rc0"
poetry add "opentelemetry-propagator-gcp=^1.0.0rc0"
poetry add "opentelemetry-instrumentation-flask=^0.20b0"

Essayez ensuite de lancer l'application à l'aide de la commande skaffold run et observez ce que le tableau de bord Cloud Trace affiche:

skaffold run --tail

Après avoir vu des messages de compilation, de transfert et de déploiement, vous verrez les journaux d'application au format JSON. Accédez à Cloud Trace > Liste de traces pour vérifier si vous obtenez les informations de trace. Étant donné que le service de générateur de charge envoie régulièrement des requêtes au service client et que vous avez activé les traces pour toutes les requêtes, vous commencez à voir de nombreux points sur la liste de traces.

f7440360551980e.png

En cliquant sur l'un d'eux, vous verrez un graphique en cascade comme celui ci-dessous, qui explique la latence de chaque partie du processus de requête et de réponse. Cochez la case "Afficher les événements". Vous obtiendrez alors les annotations dans le graphique en cascade. Ces annotations sont celles que vous avez instrumentées dans le code avec la méthode span.add_event().

67596a4a313738.png

Vous remarquerez peut-être que vous ne voyez pas les segments du service du serveur. C'est exact, car nous n'avons pas instrumenté les segments dans le service serveur.

Résumé

Au cours de cette étape, vous avez instrumenté le service de générateur de charge et le service client, et vous avez confirmé que vous pouviez propager le contexte de trace entre les services et exporter les informations sur les segments des deux services vers Cloud Trace.

Étape suivante

À l'étape suivante, vous allez instrumenter le service client et le service de serveur pour confirmer la propagation du contexte de trace via gRPC.

5. Instrumentation pour gRPC

À l'étape précédente, nous avons instrumenté la première moitié de la requête dans ce microservice. Au cours de cette étape, nous essayons d'instrumenter la communication gRPC entre le service client et le service de serveur. (Rectangle vert et violet sur l'image ci-dessous)

c4dec3e741c3ab4f.png

Instrumentation automatique pour le client gRPC

L'écosystème d'OpenTelemetry propose de nombreuses bibliothèques pratiques qui aident les développeurs à instrumenter les applications. À l'étape précédente, nous avons utilisé une instrumentation automatique pour les "requêtes" de ce module. Dans cette étape, nous essayons de propager le contexte de trace via gRPC. Nous utilisons la bibliothèque pour cela.

src/client/client.py

 import flask
 import grpc
 import structlog
 from opentelemetry import propagate, trace
 from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter
 from opentelemetry.instrumentation.flask import FlaskInstrumentor
+from opentelemetry.instrumentation.grpc import GrpcInstrumentorClient
 from opentelemetry.sdk.trace import TracerProvider
 from opentelemetry.sdk.trace.export import SimpleSpanProcessor
 from opentelemetry.propagators.cloud_trace_propagator import \
     CloudTraceFormatPropagator
 import shakesapp_pb2
 import shakesapp_pb2_grpc


 app = flask.Flask(__name__)
 FlaskInstrumentor().instrument_app(app)
+GrpcInstrumentorClient().instrument()

Concernant le service client, l'instrumentation est relativement limitée. Ce que nous voulons faire, c'est propager le contexte de trace, qui combine l'ID de trace et l'ID de délai du segment actuel via gRPC. Nous appelons donc GrpcInstrumentatorClient.instrument() pour que le client gRPC dans la fonction de manuel puisse intégrer le contexte de trace dans l'en-tête HTTP situé en dessous.

Veillez à ajouter les nouvelles dépendances à pyproject.toml avec la commande poetry add:

poetry add "opentelemetry-instrumentation-grpc=^0.20b0"

Instrumentation automatique pour le serveur gRPC

Comme nous l'avons fait pour le client gRPC, nous appelons l'instrumentation automatique pour le serveur gRPC. Ajoutez des importations comme les suivantes et appelez GrpcInstrumentationServer().instrument() en haut du fichier.

Attention: Veillez à appeler

GrpcInstrumentationServe() 

à cette étape, et non

GrpcInstrumentationClient()

.

src/server/server.py

 import grpc
 import structlog
 from google.cloud import storage
 from grpc_health.v1 import health_pb2, health_pb2_grpc
+from opentelemetry import propagate, trace
+from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter
+from opentelemetry.instrumentation.grpc import GrpcInstrumentorServer
+from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.sdk.trace.export import SimpleSpanProcessor
+from opentelemetry.propagators.cloud_trace_propagator import CloudTraceFormatPropagator

 import shakesapp_pb2
 import shakesapp_pb2_grpc


 BUCKET_NAME = "dataflow-samples"
 BUCKET_PREFIX = "shakespeare/"

+# enable auto gRPC server trace instrumentation
+GrpcInstrumentorServer().instrument()
+

Vous allez ensuite ajouter l'exportateur pour envoyer des informations de trace au backend Cloud Trace. Ajoutez le code suivant dans la fonction serve().

def serve():
+    # start trace exporter
+    trace.set_tracer_provider(TracerProvider())
+    trace.get_tracer_provider().add_span_processor(
+        SimpleSpanProcessor(CloudTraceSpanExporter())
+    )
+    propagators.set_global_textmap(CloudTraceFormatPropagator())
+
+    # add gRPC services to server
     server = grpc.server(futures.ThreadPoolExecutor(max_workers=4))
     service = ShakesappService()
     shakesapp_pb2_grpc.add_ShakespeareServiceServicer_to_server(service, server)
     health_pb2_grpc.add_HealthServicer_to_server(service, server)

Veillez à ajouter les packages nouvellement ajoutés au service du serveur.

poetry add "opentelemetry-exporter-gcp-trace=^1.0.0rc0"
poetry add "opentelemetry-instrumentation-grpc=^0.20b0"
poetry add "opentelemetry-propagator-gcp=^1.0.0rc0"
poetry add "opentelemetry-instrumentation=^0.20b0"

Exécuter le microservice et confirmer la trace

Exécutez ensuite votre code modifié avec la commande Skaffold.

skaffold run --tail

Là encore, vous voyez de nombreuses traces sur la page "Liste de traces" de Cloud Trace. Cliquez sur l'une des traces. Vous constatez maintenant qu'elle s'étend sur la requête du service de générateur de charge au service du serveur.

141cb620245b689d.png

Résumé

Au cours de cette étape, vous avez instrumenté la communication basée sur gRPC, compatible avec les bibliothèques de l'écosystème OpenTelemetry. Vous avez également confirmé que le contexte de trace généré dans le service de générateur de charge a bien été transmis au service du serveur.

6. Félicitations

Vous avez créé des traces distribuées avec OpenTelemery et confirmé les latences des requêtes dans le microservice sur Google Cloud Trace.

Pour des exercices plus longs, vous pouvez essayer les sujets suivants par vous-même.

  • L'implémentation actuelle envoie tous les délais générés par la vérification de l'état. Comment filtrer ces segments dans Cloud Trace ? Pour découvrir un indice, cliquez ici.
  • Mettez en corrélation les journaux d'événements avec les segments et observez leur fonctionnement sur Google Cloud Trace et Google Cloud Logging. Pour découvrir un indice, cliquez ici.
  • Remplacez un service par celui dans une autre langue et instrumentez-le avec OpenTelemetry pour ce langage.

Attention: Google Kubernetes Engine et Google Artifact Registry consomment constamment la ressource.

Nettoyage

Après cet atelier de programmation, veuillez arrêter le cluster Kubernetes et supprimer le projet afin d'éviter des frais inattendus sur Google Kubernetes Engine, Google Cloud Trace et Google Artifact Registry.

Commencez par supprimer le cluster à l'aide de la commande suivante:

skaffold delete

Résultat de la commande

Cleaning up...
 - deployment.apps "clientservice" deleted
 - service "clientservice" deleted
 - deployment.apps "loadgen" deleted
 - deployment.apps "serverservice" deleted
 - service "serverservice" deleted

Après avoir supprimé le cluster, dans le volet de menu, sélectionnez "IAM et Admin" &gt; "Paramètres", puis cliquez sur "ARRÊTER" .

578ca2b72a161e9d.png

Saisissez ensuite l'ID du projet (et non le nom du projet) dans le formulaire de la boîte de dialogue, puis confirmez l'arrêt.