Déployer le traçage distribué pour observer la latence des microservices

Last reviewed 2023-08-11 UTC

Ce document explique comment déployer l'architecture de référence décrite sur la page Utiliser le traçage distribué pour observer la latence des microservices. Le déploiement illustré dans ce document capture les informations de trace sur les applications de microservices à l'aide d'OpenTelemetry et de Cloud Trace.

L'exemple d'application de ce déploiement est composé de deux microservices écrits dans le langage Go.

Dans ce document, nous partons du principe que vous connaissez les éléments suivants :

Objectifs

  • Créer un cluster GKE et déployer un exemple d'application
  • Examiner le code d'instrumentation OpenTelemetry
  • Examiner les traces et les journaux générés par l'instrumentation

Architecture

Le schéma suivant illustre l'architecture que vous déployez.

Architecture du déploiement avec deux clusters GKE.

Vous utilisez Cloud Build, une plate-forme entièrement gérée d'intégration, de livraison et de déploiement continus, pour créer des images de conteneurs à partir de l'exemple de code et les stocker dans Artifact Registry. Les clusters GKE extraient les images de Artifact Registry au moment du déploiement.

Le service de frontend accepte les requêtes HTTP sur l'URL / et appelle le service de backend. L'adresse du service de backend est définie par une variable d'environnement.

Le service de backend accepte les requêtes HTTP sur l'URL / et effectue un appel sortant vers une URL externe telle que définie dans une variable d'environnement Une fois l'appel externe terminé, le service de backend renvoie l'appel d'état HTTP (par exemple, 200) à l'appelant.

Coûts

Dans ce document, vous utilisez les composants facturables suivants de Google Cloud :

Obtenez une estimation des coûts en fonction de votre utilisation prévue à l'aide du simulateur de coût. Les nouveaux utilisateurs de Google Cloud peuvent bénéficier d'un essai gratuit.

Une fois que vous avez terminé les tâches décrites dans ce document, vous pouvez éviter de continuer à payer des frais en supprimant les ressources que vous avez créées. Pour en savoir plus, consultez la section Effectuer un nettoyage.

Avant de commencer

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  3. Make sure that billing is enabled for your Google Cloud project.

  4. Enable the GKE, Cloud Trace, Cloud Build, Cloud Storage, and Artifact Registry APIs.

    Enable the APIs

  5. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  6. Make sure that billing is enabled for your Google Cloud project.

  7. Enable the GKE, Cloud Trace, Cloud Build, Cloud Storage, and Artifact Registry APIs.

    Enable the APIs

Configurer votre environnement

Dans cette section, vous allez configurer votre environnement avec les outils que vous utiliserez tout au long du déploiement. Vous exécutez toutes les commandes de terminal de ce déploiement à partir de Cloud Shell.

  1. In the Google Cloud console, activate Cloud Shell.

    Activate Cloud Shell

    At the bottom of the Google Cloud console, a Cloud Shell session starts and displays a command-line prompt. Cloud Shell is a shell environment with the Google Cloud CLI already installed and with values already set for your current project. It can take a few seconds for the session to initialize.

  2. Définissez une variable d'environnement sur l'ID de votre projet Google Cloud:
    export PROJECT_ID=$(gcloud config list --format 'value(core.project)' 2>/dev/null)
    
  3. Téléchargez les fichiers requis pour ce déploiement en clonant le dépôt Git associé:
    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples.git
    cd kubernetes-engine-samples/observability/distributed-tracing
    WORKDIR=$(pwd)
    

    Vous définissez le dossier du dépôt en tant que répertoire de travail ($WORKDIR) à partir duquel vous effectuez toutes les tâches liées à ce déploiement. Ainsi, si vous ne souhaitez pas conserver les ressources, vous pouvez supprimer le dossier une fois le déploiement terminé.

Installer les outils

  1. Dans Cloud Shell, installez kubectx et kubens.

    git clone https://github.com/ahmetb/kubectx $WORKDIR/kubectx
    export PATH=$PATH:$WORKDIR/kubectx
    

    Ces outils permettent de travailler avec plusieurs clusters, contextes et espaces de noms Kubernetes.

  2. Dans Cloud Shell, installez Apache Bench, un outil de génération de charge Open Source :

    sudo apt-get install apache2-utils
    

Créer un dépôt Docker

Créez un dépôt Docker pour stocker l'exemple d'image fourni pour ce déploiement.

Console

  1. Dans la console Google Cloud, ouvrez la page Dépôts.

    Ouvrir la page "Dépôts"

  2. Cliquez sur Créer un dépôt.

  3. Spécifiez distributed-tracing-docker-repo comme nom de dépôt.

  4. Choisissez Docker comme format et Standard comme mode.

  5. Sous Type d'emplacement, sélectionnez Région, puis l'emplacement us-west1.

  6. Cliquez sur Créer.

Le dépôt est ajouté à la liste des dépôts.

gcloud

  1. Dans Cloud Shell, créez un dépôt Docker nommé distributed-tracing-docker-repo à l'emplacement us-west1 avec la description docker repository:

    gcloud artifacts repositories create distributed-tracing-docker-repo --repository-format=docker \
    --location=us-west1 --description="Docker repository for distributed tracing deployment"
    
  2. Vérifiez que le dépôt a bien été créé:

    gcloud artifacts repositories list
    

Créer des clusters GKE

Dans cette section, vous allez créer deux clusters GKE dans lesquels vous allez déployer l'exemple d'application. Par défaut, les clusters GKE sont créés avec un accès en écriture seule à l'API Cloud Trace. Par conséquent, vous n'avez pas besoin de définir un accès lorsque vous créez les clusters.

  1. Dans Cloud Shell, créez les clusters :

    gcloud container clusters create backend-cluster \
        --zone=us-west1-a \
        --verbosity=none --async
    
    gcloud container clusters create frontend-cluster \
        --zone=us-west1-a \
        --verbosity=none
    

    Dans cet exemple, les clusters se trouvent dans la zone us-west1-a. Pour plus d'informations, consultez la page Zones géographiques et régions.

  2. Récupérez les identifiants de cluster et stockez-les localement :

    gcloud container clusters get-credentials backend-cluster --zone=us-west1-a
    gcloud container clusters get-credentials frontend-cluster --zone=us-west1-a
    
  3. Renommez les contextes des clusters afin de pouvoir y accéder plus facilement plus tard lors du déploiement:

    kubectx backend=gke_${PROJECT_ID}_us-west1-a_backend-cluster
    kubectx frontend=gke_${PROJECT_ID}_us-west1-a_frontend-cluster
    

Examiner l'instrumentation OpenTelementry

Dans les sections suivantes, vous allez examiner le code du fichier main.go dans l'exemple d'application. Cela vous aide à utiliser la propagation de contexte pour permettre l'ajout de segments à une seule trace parente à partir de plusieurs requêtes.

Examiner les importations dans le code de l'application

import (
	"context"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"

	cloudtrace "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace"
	"github.com/gorilla/mux"
	"go.opentelemetry.io/contrib/detectors/gcp"
	"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
	"go.opentelemetry.io/contrib/propagators/autoprop"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/sdk/resource"
	"go.opentelemetry.io/otel/sdk/trace"
)

Notez les points suivants concernant les importations:

  • Le package go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp contient le plug-in otelhttp, qui peut instrumenter un serveur ou un client HTTP. L'instrumentation du serveur récupère le contexte de segment de la requête HTTP et enregistre un segment pour le traitement de la requête par le serveur. L'instrumentation du client injecte le contexte de segment dans la requête HTTP sortante et enregistre un segment pour le temps passé à attendre une réponse.
  • Le package go.opentelemetry.io/contrib/propagators/autoprop fournit une mise en œuvre de l'interface TextMapPropagator d'OpenTelemetry, qui est utilisée par otelhttp pour gérer la propagation. Les outils de propagation déterminent le format et les clés utilisés pour stocker le contexte de trace dans les transports tels que HTTP. Plus précisément, otelhttp transmet les en-têtes HTTP à l'outil de propagation. L'outil de propagation extrait un contexte de segment dans un contexte Go à partir des en-têtes ou il encode et injecte un contexte de segment dans le contexte Go dans les en-têtes (selon qu'il s'agisse d'un client ou d'un serveur). Par défaut, le package autoprop injecte et extrait le contexte de segment à l'aide du format de propagation du contexte de trace W3C.
  • L'importation github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace exporte des traces vers Cloud Trace.
  • L'importation github.com/gorilla/mux est la bibliothèque utilisée par l'exemple d'application pour le traitement des requêtes.
  • L'importation go.opentelemetry.io/contrib/detectors/gcp ajoute des attributs aux segments, tels que cloud.availability_zone, qui identifient l'emplacement d'exécution de votre application dans Google Cloud.
  • Les importations go.opentelemetry.io/otel, go.opentelemetry.io/otel/sdk/trace et go.opentelemetry.io/otel/sdk/resource utilisées pour configurer OpenTelemetry.

Examinez la fonction main

La fonction main configure l'exportation de la trace vers Cloud Trace et utilise un routeur mux pour gérer les requêtes envoyées à l'URL /.

func main() {
	ctx := context.Background()
	// Set up the Cloud Trace exporter.
	exporter, err := cloudtrace.New()
	if err != nil {
		log.Fatalf("cloudtrace.New: %v", err)
	}
	// Identify your application using resource detection.
	res, err := resource.New(ctx,
		// Use the GCP resource detector to detect information about the GKE Cluster.
		resource.WithDetectors(gcp.NewDetector()),
		resource.WithTelemetrySDK(),
	)
	if err != nil {
		log.Fatalf("resource.New: %v", err)
	}
	tp := trace.NewTracerProvider(
		trace.WithBatcher(exporter),
		trace.WithResource(res),
	)
	// Set the global TracerProvider which is used by otelhttp to record spans.
	otel.SetTracerProvider(tp)
	// Flush any pending spans on shutdown.
	defer tp.ForceFlush(ctx)

	// Set the global Propagators which is used by otelhttp to propagate
	// context using the w3c traceparent and baggage formats.
	otel.SetTextMapPropagator(autoprop.NewTextMapPropagator())

	// Handle incoming request.
	r := mux.NewRouter()
	r.HandleFunc("/", mainHandler)
	var handler http.Handler = r

	// Use otelhttp to create spans and extract context for incoming http
	// requests.
	handler = otelhttp.NewHandler(handler, "server")
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", os.Getenv("PORT")), handler))
}

Notez les points suivants concernant ce code:

  • Vous configurez un fournisseur TracerProvider OpenTelemetry, qui détecte les attributs lors de son exécution sur Google Cloud et exporte les traces vers Cloud Trace.
  • Vous utilisez les fonctions otel.SetTracerProvider et otel.SetTextMapPropagators pour définir les paramètres TracerProvider et Propagator globaux. Par défaut, les bibliothèques d'instrumentation telles que otelhttp utilisent le TracerProvider enregistré globalement pour créer des segments et Propagator pour propager le contexte.
  • Vous encapsulez le serveur HTTP avec otelhttp.NewHandler pour l'instrumenter.

Examinez la fonction mainHandler

func mainHandler(w http.ResponseWriter, r *http.Request) {
	// Use otelhttp to record a span for the outgoing call, and propagate
	// context to the destination.
	destination := os.Getenv("DESTINATION_URL")
	resp, err := otelhttp.Get(r.Context(), destination)
	if err != nil {
		log.Fatal("could not fetch remote endpoint")
	}
	defer resp.Body.Close()
	_, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatalf("could not read response from %v", destination)
	}

	fmt.Fprint(w, strconv.Itoa(resp.StatusCode))
}

Pour capturer la latence des requêtes sortantes adressées à la destination, vous utilisez le plug-in otelhttp pour effectuer une requête HTTP. Vous utilisez également la fonction r.Context pour associer la requête entrante à la requête sortante, comme indiqué dans la liste suivante:

// Use otelhttp to record a span for the outgoing call, and propagate
// context to the destination.
resp, err := otelhttp.Get(r.Context(), destination)

Déployer l'application

Dans cette section, vous allez créer des images de conteneur pour les services de backend et frontend à l'aide de Cloud Build, puis les déployer sur leurs clusters GKE.

Créer le conteneur Docker

  1. Dans Cloud Shell, envoyez la compilation à partir du répertoire de travail:

    cd $WORKDIR
    gcloud builds submit . --tag us-west1-docker.pkg.dev/$PROJECT_ID/distributed-tracing-docker-repo/backend:latest
    
  2. Vérifiez que l'image du conteneur a bien été créée et qu'elle est disponible dans Artifact Registry :

    gcloud artifacts docker images list us-west1-docker.pkg.dev/$PROJECT_ID/distributed-tracing-docker-repo
    

    L'image du conteneur a bien été créée lorsque le résultat ressemble à ce qui suit, où PROJECT_ID correspond à l'ID de votre projet Google Cloud:

    NAME
    us-west1-docker.pkg.dev/PROJECT_ID/distributed-tracing-docker-repo/backend
    

Déployer le service de backend

  1. Dans Cloud Shell, définissez le contexte kubectx sur le cluster backend:

    kubectx backend
    
  2. Créez le fichier YAML pour le déploiement backend:

    export PROJECT_ID=$(gcloud info --format='value(config.project)')
    envsubst < backend-deployment.yaml | kubectl apply -f -
    
  3. Vérifiez que les pods sont en cours d'exécution :

    kubectl get pods
    

    Le résultat affiche la valeur Status de Running:

    NAME                       READY   STATUS    RESTARTS   AGE
    backend-645859d95b-7mx95   1/1     Running   0          52s
    backend-645859d95b-qfdnc   1/1     Running   0          52s
    backend-645859d95b-zsj5m   1/1     Running   0          52s
    
  4. Exposez le déploiement backend à l'aide d'un équilibreur de charge :

    kubectl expose deployment backend --type=LoadBalancer
    
  5. Récupérez l'adresse IP du service backend :

    kubectl get services backend
    

    Le résultat ressemble à ce qui suit :

    NAME      TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)          AGE
    backend   LoadBalancer   10.11.247.58   34.83.88.143   8080:30714/TCP   70s
    

    Si la valeur du champ EXTERNAL-IP est <pending>, répétez la commande jusqu'à ce que la valeur soit une adresse IP.

  6. Collectez l'adresse IP de l'étape précédente dans une variable :

    export BACKEND_IP=$(kubectl get svc backend -ojson | jq -r '.status.loadBalancer.ingress[].ip')
    

Déployer le service de frontend

  1. Dans Cloud Shell, définissez votre contexte kubectx sur le cluster de backend :

    kubectx frontend
    
  2. Créez le fichier YAML pour le déploiement frontend:

    export PROJECT_ID=$(gcloud info --format='value(config.project)')
    envsubst < frontend-deployment.yaml | kubectl apply -f -
    
  3. Vérifiez que les pods sont en cours d'exécution :

    kubectl get pods
    

    Le résultat affiche la valeur Status de Running:

    NAME                        READY   STATUS    RESTARTS   AGE
    frontend-747b445499-v7x2w   1/1     Running   0          57s
    frontend-747b445499-vwtmg   1/1     Running   0          57s
    frontend-747b445499-w47pf   1/1     Running   0          57s
    
  4. Exposez le déploiement frontend à l'aide d'un équilibreur de charge :

    kubectl expose deployment frontend --type=LoadBalancer
    
  5. Récupérez l'adresse IP du service frontend :

    kubectl get services frontend
    

    Le résultat ressemble à ce qui suit :

    NAME       TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)         AGE
    frontend   LoadBalancer   10.27.241.93   34.83.111.232   8081:31382/TCP  70s
    

    Si la valeur du champ EXTERNAL-IP est <pending>, répétez la commande jusqu'à ce que la valeur soit une adresse IP.

  6. Collectez l'adresse IP de l'étape précédente dans une variable :

    export FRONTEND_IP=$(kubectl get svc frontend -ojson | jq -r '.status.loadBalancer.ingress[].ip')
    

Charger l'application et examiner les traces

Dans cette section, vous allez créer des requêtes pour votre application à l'aide de l'utilitaire Apache Bench. Vous pouvez ensuite examiner les traces obtenues dans Cloud Trace.

  1. Dans Cloud Shell, utilisez Apache Bench pour générer 1 000 requêtes à l'aide de trois threads simultanés:

    ab -c 3 -n 1000 http://${FRONTEND_IP}:8081/
    
  2. Dans la console Google Cloud, accédez à la page Liste de traces.

    Accéder à la liste de traces

  3. Pour consulter la chronologie, cliquez sur l'un des URI libellés server.

    Graphique en nuage de points représentant les traces.

    Cette trace contient quatre segments portant les noms suivants:

    • Le premier segment server collecte la latence de bout en bout du traitement de la requête HTTP sur le serveur frontend.
    • Le premier segment HTTP GET collecte la latence de l'appel GET effectué par le client de l'interface vers le backend.
    • Le deuxième segment server collecte la latence de bout en bout du traitement de la requête HTTP sur le serveur backend.
    • Le second segment HTTP GET collecte la latence de l'appel GET effectué par le client du backend à google.com.

    Graphique à barres des délais.

Effectuer un nettoyage

Le moyen le plus simple d'éviter la facturation consiste à supprimer le projet Google Cloud que vous avez créé pour le déploiement. Vous pouvez également supprimer les différentes ressources.

Supprimer le projet

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

Supprimer des ressources individuelles

Pour supprimer des ressources individuelles au lieu de supprimer l'ensemble du projet, exécutez les commandes suivantes dans Cloud Shell:

gcloud container clusters delete frontend-cluster --zone=us-west1-a
gcloud container clusters delete backend-cluster --zone=us-west1-a
gcloud artifacts repositories delete distributed-tracing-docker-repo --location us-west1

Étapes suivantes