1. Introduction

Dernière mise à jour : 05/03/2021
Observabilité de l'application
Observabilité et OpenTelemetry
L'observabilité est un 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é (journaux, métriques et traces) sont l'instrumentation fondamentale permettant au système d'acquérir l'observabilité.
OpenTelemetry est un ensemble de spécifications et de SDK qui accélèrent l'instrumentation et l'exportation des données de télémétrie (journaux, métriques et traces) requises par l'observabilité. OpenTelemetry est une norme ouverte et un projet communautaire sous CNCF. En utilisant les bibliothèques fournies par le projet et son écosystème, les développeurs peuvent instrumenter leurs applications de manière neutre vis-à-vis du fournisseur et sur 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. Surtout à l'ère des microservices, le traçage distribué est un outil puissant pour identifier les goulots d'étranglement de latence dans l'ensemble du système distribué.
Lorsque vous analysez des traces distribuées, la visualisation des données de trace est essentielle pour comprendre les latences globales du système en un coup d'œil. Dans le traçage distribué, nous gérons un ensemble d'appels pour traiter une seule requête au point d'entrée du système sous la forme d'une trace contenant plusieurs spans.
Une portée représente une unité de travail individuelle effectuée dans un système distribué, en enregistrant les heures de début et de fin. Les spans ont souvent des relations hiérarchiques entre eux. Dans l'image ci-dessous, tous les spans plus petits sont des spans enfants d'un grand span "/messages" et sont assemblés en une trace qui montre le chemin de travail dans un système.

Google Cloud Trace est l'une des options de backend de trace distribuée. Il est bien intégré aux autres produits de Google Cloud.
Objectifs de l'atelier
Dans cet atelier de programmation, vous allez instrumenter des informations de trace dans les services appelés "Shakesapp " qui s'exécutent sur un cluster Kubernetes fonctionnant sur Google Kubernetes Engine. L'architecture de Shakesapp est décrite ci-dessous :

- Le client envoie une chaîne de requête au serveur.
- Le serveur accepte la requête du client, récupère toutes les œuvres de Shakespeare au format texte à partir de Google Cloud Storage, recherche les lignes contenant la requête et renvoie au client le numéro de la ligne correspondante.
Vous instrumenterez les informations de trace dans la requête.
Points abordés
- Premiers pas avec les bibliothèques de trace OpenTelemetry dans un projet Python
- Créer un délai avec la bibliothèque
- Propager les contextes de portée sur le réseau entre les composants de l'application
- Envoyer des données de trace vers Google Cloud Trace
- Analyser la trace sur Google Cloud Trace
Cet atelier de programmation 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). Toutefois, vous pouvez appliquer le même processus expliqué dans cet atelier de programmation à des systèmes plus complexes et plus volumineux.
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 :

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

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 :

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

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 tout au long de cet atelier de programmation.
Ensuite, si ce n'est pas déjà fait, vous devez activer la facturation dans la console Développeurs afin de pouvoir utiliser les ressources Google Cloud, puis activer l'API Cloud Trace.

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, Google Kubernetes Engine et Google Artifact Registry sont indiqués dans la documentation officielle.
- Tarifs de Google Cloud Observability
- Tarifs | Documentation Kubernetes Engine
- Tarifs d'Artifact Registry | Documentation Artifact Registry
Les nouveaux utilisateurs de Google Cloud Platform peuvent bénéficier d'un essai sans frais avec 300$de crédits afin de suivre sans frais le présent atelier.
Configuration de Google Cloud Shell
Bien que Google Cloud et Google Cloud Trace puissent être utilisés à distance depuis votre ordinateur portable, nous allons utiliser Google Cloud Shell pour cet atelier de programmation, 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 à partir de la console Cloud, cliquez simplement sur Activer Cloud Shell
(le provisionnement de l'environnement et la connexion ne devraient prendre que quelques minutes).


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 :

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
Résultat 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 "poetry" pour 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). Voici le processus de cet atelier de programmation :
- Télécharger le projet de référence dans Cloud Shell
- Créer des microservices dans des conteneurs
- Importer des conteneurs dans Google Artifact Registry (GAR)
- Déployer des conteneurs sur GKE
- Modifier le code source des services pour l'instrumentation de trace
- Passez à l'étape 2.
Activer Kubernetes Engine
Nous allons d'abord configurer un cluster Kubernetes sur lequel Shakesapp s'exécute sur GKE. Nous devons donc activer GKE. Accédez au menu "Kubernetes Engine", puis cliquez sur le bouton "ACTIVER".

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 dans la région que vous avez utilisée pour créer le dépôt Artifact Registry. Modifiez la valeur de la 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 transférer et déployer des conteneurs. Pour cette étape, nous devons configurer GAR et skaffold pour l'utiliser.
Configurer Artifact Registry
Accédez au menu "Artifact Registry" et appuyez sur le bouton "ENABLE" (ACTIVER).

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

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 la région proche de celle que vous avez définie pour la zone Google Compute Engine par défaut. Par exemple, l'exemple ci-dessus a choisi "us-central1-f", nous choisissons donc "us-central1 (Iowa)". Cliquez ensuite sur le bouton "CRÉER".

Vous voyez maintenant "trace-codelab" dans l'explorateur de dépôt.

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. Il gère le workflow de création, de transfert et de déploiement des conteneurs d'applications avec un petit ensemble de commandes. Par défaut, Skaffold utilise Docker Registry 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 pour afficher la version de Skaffold.
skaffold version
Résultat de la commande
v1.20.0
Vous pouvez maintenant enregistrer le dépôt par défaut que Skaffold doit utiliser. Pour obtenir le chemin d'accès au registre, accédez au tableau de bord Artifact Registry et cliquez sur le nom du dépôt que vous venez de configurer à l'étape précédente.

Un fil d'Ariane s'affiche en haut de la page. Cliquez sur l'icône
pour copier le chemin d'accès au registre dans le presse-papiers.

Lorsque vous cliquez sur le bouton "Copier", la boîte de dialogue s'affiche en bas du navigateur avec un message semblable à celui-ci :
"us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab" a été copié
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 configurer le registre pour la configuration Docker. 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 êtes maintenant prêt à passer à l'étape suivante pour configurer un conteneur Kubernetes sur GKE.
Résumé
Dans cette étape, vous allez configurer votre environnement d'atelier de programmation :
- Configurer Cloud Shell
- Création d'un dépôt Artifact Registry pour le registre de conteneurs
- Configurer Skaffold pour utiliser le registre de conteneurs
- Création d'un cluster Kubernetes dans lequel s'exécutent les microservices de l'atelier de programmation
Étape suivante
Dans 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 le matériel de l'atelier de programmation
À l'étape précédente, nous avons configuré tous les prérequis pour cet atelier de programmation. Vous êtes maintenant prêt à exécuter des microservices entiers par-dessus. Le contenu de l'atelier de programmation est hébergé sur GitHub. Téléchargez-le 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épertoire du projet est la suivante :
shakesapp-python
├── LICENSE
├── manifests
│ ├── client.yaml
│ ├── loadgen.yaml
│ └── server.yaml
├── proto
│ └── shakesapp.proto
├── skaffold.yaml
└── src
├── client
├── loadgen
└── server
- manifests : fichiers manifestes Kubernetes
- proto : définition proto pour la communication entre le client et le serveur
- src : répertoires du code source de chaque service
- skaffold.yaml : fichier de configuration pour Skaffold
Exécuter la commande skaffold
Vous êtes enfin prêt à créer, transférer et déployer l'intégralité du contenu sur le cluster Kubernetes que vous venez de créer. Cela semble contenir plusieurs étapes, mais en réalité, Skaffold fait tout pour vous. Essayons avec la commande suivante :
cd shakesapp-python skaffold run --tail
Dès que vous exécutez la commande, vous voyez le résultat du journal docker build et pouvez confirmer qu'ils ont bien été envoyés au 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 vous recevez une erreur du type "No push access to specified image repository" (Aucun accès push au dépôt d'images spécifié), vérifiez si la commande skaffold tente d'envoyer 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 l'option "–default-repo" à "skaffold run" comme ci-dessous.
$ skaffold run –tail –default-repo=us-central1-docker.pkg.dev/[ID du projet]/[nom du dépôt]
Une fois le déploiement effectué, les journaux d'application réels émis sur stdout s'affichent 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"}
Vous êtes enfin prêt à instrumenter votre application avec OpenTelemetry pour le traçage distribué des services.
Résumé
Dans cette étape, vous avez préparé le contenu de l'atelier de programmation dans votre environnement et vérifié 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, je vais vous expliquer brièvement le fonctionnement des traces distribuées à l'aide d'un diagramme simple.

Dans cet exemple, nous instrumentons le code pour exporter les informations de trace et de span vers Cloud Trace, et pour propager le contexte de trace dans la requête du service loadgen au service de serveur.
Pour que Cloud Trace puisse regrouper tous les délais ayant le même ID de trace dans une seule trace, l'application doit envoyer des métadonnées de trace telles que l'ID de trace et l'ID de délai. L'application doit également propager les contextes de trace (combinaison de l'ID de trace et de l'ID de portée de la portée parente) lors de la demande de services en aval, afin qu'ils puissent savoir quel contexte de trace ils gèrent.
OpenTelemetry vous aide à :
- pour générer un ID de trace et un ID de portée uniques.
- Exporter l'ID de trace et l'ID de span vers le backend
- pour propager les contextes de trace à d'autres services.
Instrumenter la première étendue
Service de générateur de charge d'instrument
Ouvrez l'éditeur Cloud Shell en cliquant sur le bouton
en haut à droite de Cloud Shell. Ouvrez src/loadgen/loadgen.py à partir de l'explorateur dans le volet de gauche, puis 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 de Span pour suivre la latence de l'appel de fonction.
Vous devez d'abord créer un Span avec un ID de trace et un ID de span 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 l'instance Tracer qui gère le contexte de trace et les paramètres de l'exportateur.
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}")
Notez qu'il s'agit d'un atelier de programmation visant à comprendre le fonctionnement de l'instrumentation de trace. Nous configurons donc le traceur pour qu'il enregistre chaque requête et l'envoie au backend. (SimpleSpanProcessor()) Cela ne convient pas aux environnements de production. Veillez donc à modifier cette partie lorsque vous instrumentez votre application de production.
Vous pouvez désormais instrumenter les Spans avec le Tracer. L'idée est de générer explicitement un Span, et c'est tout ! Bien que deux lignes ajoutent des métadonnées d'événement à Span, vous n'avez pas besoin de générer manuellement un ID de trace et un ID de span uniques, ni de les intégrer à Span.
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 Docker Build 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 en pyproject.toml.
Service client Instrument
Dans la section précédente, nous avons instrumenté la partie entourée du rectangle rouge dans le schéma ci-dessous. Nous avons instrumenté les informations de portée dans le service de générateur de charge. Comme pour le service de génération de charge, nous devons maintenant instrumenter le service client. La différence avec le service de génération de charge est que le service client doit extraire les informations d'ID de trace propagées à partir du service de génération de charge dans l'en-tête HTTP et utiliser l'ID pour générer des spans.

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 remarquez que vous venez d'importer FlaskInstrumentor, qui permet l'instrumentation automatique pour l'application Flask au nom 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 propose des intégrations utiles similaires avec d'autres bibliothèques majeures. Pour en savoir plus, consultez la documentation officielle.
app = flask.Flask(__name__)
+FlaskInstrumentor().instrument_app(app)
Avant de commencer l'instrumentation, vous devez à nouveau préparer l'instance Tracer de la même manière que dans le 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():
....
Vous pouvez maintenant ajouter l'instrumentation dans le gestionnaire. Recherchez main_handler() et modifiez la partie qui envoie la requête gRPC au service de 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"
Ensuite, essayez de lancer l'application avec la commande skaffold run et vérifiez ce qui s'affiche dans le tableau de bord Cloud Trace :
skaffold run --tail
Après avoir vu des messages de compilation, d'envoi 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ération de charge envoie des requêtes au service client de manière périodique et que vous avez activé les traces pour toutes les requêtes, vous commencez à voir de nombreux points dans la liste des traces.

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" pour afficher les annotations dans le graphique en cascade. Ces annotations sont celles que vous avez instrumentées dans le code par la méthode span.add_event().

Vous remarquerez peut-être que les portées du service de serveur ne s'affichent pas. Bonne réponse. Nous n'avons pas instrumenté les spans dans le service de serveur.
Résumé
Dans 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 de portée des deux services vers Cloud Trace.
Étape suivante
À l'étape suivante, vous instrumenterez le service client et le service serveur pour confirmer comment propager le 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 ces microservices. Dans cette étape, nous allons essayer d'instrumenter la communication gRPC entre le service client et le service serveur. (rectangle vert et violet sur l'image ci-dessous)

Instrumentation automatique pour le client gRPC
L'écosystème OpenTelemetry propose de nombreuses bibliothèques pratiques qui aident les développeurs à instrumenter les applications. À l'étape précédente, nous avons utilisé l'instrumentation automatique pour le module "requests". Dans cette étape, nous essayons de propager le contexte de trace via gRPC. Nous utilisons donc la bibliothèque à cet effet.
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()
Pour le service client, ce que nous devons faire pour l'instrumentation est assez minime. Nous souhaitons propager le contexte de trace, qui est la combinaison de l'ID de trace et de l'ID de span du span actuel via gRPC. Nous appelons donc GrpcInstrumentatorClient.instrument() afin que le client gRPC de la fonction de gestionnaire puisse intégrer le contexte de trace dans l'en-tête HTTP sous-jacent.
Veillez à ajouter de nouvelles dépendances à pyproject.toml avec la commande poetry add :
poetry add "opentelemetry-instrumentation-grpc=^0.20b0"
Instrumentation automatique pour le serveur gRPC
Comme pour le client gRPC, nous appelons l'instrumentation automatique pour le serveur gRPC. Ajoutez des importations comme suit et appelez GrpcInstrumentationServer().instrument() en haut du fichier.
Attention : Assurez-vous d'appeler
GrpcInstrumentationServe()
dans 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 ajouterez ensuite l'exportateur pour envoyer les 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 de 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
Vous voyez à nouveau un certain nombre de traces sur la page "Liste de traces" de Cloud Trace. Cliquez sur l'une des traces. Vous constaterez que les spans couvrent la requête du service de génération de charge au service de serveur.

Résumé
Dans cette étape, vous avez instrumenté la communication basée sur gRPC avec l'aide des 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 de serveur.
6. Félicitations
Vous avez créé des traces distribuées avec OpenTelemetry et confirmé les latences des requêtes dans le microservice sur Google Cloud Trace.
Pour des exercices plus complets, vous pouvez essayer les thèmes suivants par vous-même.
- L'implémentation actuelle envoie toutes les portées générées par la vérification de l'état. Comment filtrer ces spans dans Cloud Trace ? Cliquez ici pour obtenir un indice.
- Corrélez les journaux d'événements avec les spans et découvrez comment cela fonctionne dans Google Cloud Trace et Google Cloud Logging. Cliquez ici pour obtenir un indice.
- Remplacez un service par celui d'une autre langue et essayez de l'instrumenter avec OpenTelemetry pour cette langue.
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 veillez à 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 administration" > "Paramètres", puis cliquez sur le bouton "ARRÊTER".

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