Verteiltes Tracing zur Beobachtung der Latenz von Mikrodiensten bereitstellen

Last reviewed 2023-08-11 UTC

In diesem Dokument wird gezeigt, wie Sie die unter Verteiltes Tracing zur Beobachtung der Latenz von Mikrodiensten verwenden beschriebene Referenzarchitektur bereitstellen. Die in diesem Dokument dargestellte Bereitstellung erfasst Trace-Informationen zu Mikrodienstanwendungen mit OpenTelemetry und Cloud Trace.

Die Beispielanwendung in dieser Bereitstellung besteht aus zwei Mikrodiensten, die in Go geschrieben sind.

In diesem Dokument wird davon ausgegangen, dass Sie mit Folgendem vertraut sind:

Ziele

  • GKE-Cluster erstellen und Beispielanwendung bereitstellen
  • OpenTelemetry-Instrumentierungscode überprüfen
  • Von der Instrumentierung generierte Traces und Logs prüfen

Architektur

Das folgende Diagramm zeigt die Architektur, die Sie bereitstellen.

Architektur der Bereitstellung mit zwei GKE-Clustern

Sie verwenden Cloud Build – eine vollständig verwaltete Plattform für Continuous Integration, Continuous Delivery und die kontinuierliche Bereitstellung –, um aus dem Beispielcode Container-Images zu erstellen und in Artifact Registry zu speichern. Zum Zeitpunkt der Bereitstellung werden die Images von den GKE-Clustern aus Artifact Registry abgerufen.

Der Frontend-Dienst akzeptiert HTTP-Anfragen an die URL / und ruft den Backend-Dienst auf. Die Adresse des Backend-Dienstes wird durch eine Umgebungsvariable definiert.

Der Backend-Dienst akzeptiert HTTP-Anfragen an die URL / und ruft eine externe URL auf, die in eine Umgebungsvariablen definiert ist. Nach Abschluss des externen Aufrufs gibt der Backend-Dienst den HTTP-Statuscode, z. B. 200, an den Aufrufer zurück.

Kosten

In diesem Dokument verwenden Sie die folgenden kostenpflichtigen Komponenten von Google Cloud:

Mit dem Preisrechner können Sie eine Kostenschätzung für Ihre voraussichtliche Nutzung vornehmen. Neuen Google Cloud-Nutzern steht möglicherweise eine kostenlose Testversion zur Verfügung.

Nach Abschluss der in diesem Dokument beschriebenen Aufgaben können Sie weitere Kosten vermeiden, indem Sie die erstellten Ressourcen löschen. Weitere Informationen finden Sie unter Bereinigen.

Hinweis

  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

Umgebung einrichten

In diesem Abschnitt richten Sie Ihre Umgebung mit den Tools ein, die Sie während der gesamten Bereitstellung verwenden. Alle Terminalbefehle in dieser Bereitstellung werden über Cloud Shell ausgeführt.

  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. Legen Sie eine Umgebungsvariable auf die ID Ihres Google Cloud-Projekts fest:
    export PROJECT_ID=$(gcloud config list --format 'value(core.project)' 2>/dev/null)
    
  3. Laden Sie die erforderlichen Dateien für diese Bereitstellung herunter. Klonen Sie dazu das zugehörige Git-Repository:
    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples.git
    cd kubernetes-engine-samples/observability/distributed-tracing
    WORKDIR=$(pwd)
    

    Legen Sie den Repository-Ordner als Arbeitsverzeichnis ($WORKDIR) fest, über das Sie alle Aufgaben für diese Bereitstellung ausführen. Wenn Sie die Ressourcen nicht behalten möchten, können Sie den Ordner löschen, wenn die Bereitstellung abgeschlossen ist.

Tools installieren

  1. Installieren Sie in Cloud Shell kubectx und kubens:

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

    Mithilfe dieser Tools können Sie mit mehreren Kubernetes-Clustern, Kontexten und Namespaces arbeiten.

  2. Installieren Sie in Cloud Shell Apache Bench, ein Open-Source-Tool zur Lastgenerierung:

    sudo apt-get install apache2-utils
    

Docker-Repository erstellen

Erstellen Sie ein Docker-Repository zum Speichern des Beispiel-Images für diese Bereitstellung.

Console

  1. Öffnen Sie in der Google Cloud Console die Seite Repositories.

    Zur Seite „Repositories“

  2. Klicken Sie auf Repository erstellen.

  3. Geben Sie distributed-tracing-docker-repo als Repository-Namen an.

  4. Wählen Sie Docker als Format und Standard als Modus aus.

  5. Wählen Sie unter Standorttyp die Option Region und dann den Standort us-west1 aus.

  6. Klicken Sie auf Erstellen.

Das Repository wird der Repository-Liste hinzugefügt.

gcloud

  1. Erstellen Sie in Cloud Shell ein neues Docker-Repository mit dem Namen distributed-tracing-docker-repo am Standort us-west1 mit der Beschreibung docker repository:

    gcloud artifacts repositories create distributed-tracing-docker-repo --repository-format=docker \
    --location=us-west1 --description="Docker repository for distributed tracing deployment"
    
  2. Prüfen Sie, ob das Repository erstellt wurde:

    gcloud artifacts repositories list
    

GKE-Cluster erstellen

In diesem Abschnitt erstellen Sie zwei GKE-Cluster, in denen Sie die Beispielanwendung bereitstellen. GKE-Cluster werden standardmäßig nur mit Schreibzugriff auf die Cloud Trace API erstellt, sodass Sie den Zugriff beim Erstellen der Cluster nicht definieren müssen.

  1. Erstellen Sie die Cluster in Cloud Shell:

    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
    

    In diesem Beispiel befinden sich die Cluster in der Zone us-west1-a. Weitere Informationen hierzu finden Sie unter Geografie und Regionen.

  2. Rufen Sie die Anmeldedaten für die Cluster ab und speichern Sie sie lokal:

    gcloud container clusters get-credentials backend-cluster --zone=us-west1-a
    gcloud container clusters get-credentials frontend-cluster --zone=us-west1-a
    
  3. Benennen Sie die Kontexte der Cluster um, damit Sie später in der Bereitstellung leichter darauf zugreifen können:

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

OpenTelemetry-Instrumentierung überprüfen

In den folgenden Abschnitten überprüfen Sie den Code aus der Datei main.go in der Beispielanwendung. So erfahren Sie, wie Sie mit der Kontextweitergabe Spans aus mehreren Anfragen an einen einzelnen übergeordneten Trace anhängen können.

Importe im Anwendungscode überprüfen

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"
)

Beachten Sie bei den Importen Folgendes:

  • Das Paket go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp enthält das Plug-in otelhttp, mit dem ein HTTP-Server oder HTTP-Client instrumentiert werden kann. Die Serverinstrumentierung ruft den Span-Kontext aus der HTTP-Anfrage ab und zeichnet einen Span für die Verarbeitung der Anfrage durch den Server auf. Die Clientinstrumentierung fügt den Span-Kontext in die ausgehende HTTP-Anfrage ein und zeichnet einen Span für die Zeit auf, die für das Warten auf eine Antwort aufgewendet wird.
  • Das Paket go.opentelemetry.io/contrib/propagators/autoprop bietet eine Implementierung der OpenTelemetry-Schnittstelle TextMapPropagator, die von otelhttp zur Verarbeitung der Weitergabe verwendet wird. Propagatoren bestimmen das Format und die Schlüssel, die zum Speichern des Trace-Kontexts in Transporten wie HTTP verwendet werden. Insbesondere übergibt otelhttp HTTP-Header an den Propagator. Der Propagator extrahiert einen Span-Kontext aus den Headern in einen Go-Kontext oder codiert einen Span-Kontext und fügt ihn in den Go-Kontext in Header ein (je nachdem, ob es sich um einen Client oder Server handelt). Das Paket autoprop fügt den Span-Kontext standardmäßig mithilfe des W3C-Trace-Kontext-Weitergabeformats ein und extrahiert ihn.
  • Der Import github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace exportiert Traces in Cloud Trace.
  • Der Import github.com/gorilla/mux ist die Bibliothek, die die Beispielanwendung für die Anfrageverarbeitung verwendet.
  • Der Import go.opentelemetry.io/contrib/detectors/gcp fügt Spans Attribute hinzu, z. B. cloud.availability_zone, die angeben, wo Ihre Anwendung in Google Cloud ausgeführt wird.
  • Die Importe go.opentelemetry.io/otel, go.opentelemetry.io/otel/sdk/trace und go.opentelemetry.io/otel/sdk/resource werden zum Einrichten von OpenTelemetry verwendet.

main-Funktion überprüfen

Die Funktion main richtet den Trace-Export nach Cloud Trace ein und verwendet einen Mux-Router, um Anfragen an die URL / zu verarbeiten.

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))
}

Beachten Sie bei diesem Code Folgendes:

  • Sie konfigurieren einen OpenTelemetry-TracerProvider, der Attribute erkennt, wenn er in Google Cloud ausgeführt wird, und der Traces in Cloud Trace exportiert.
  • Mit den Funktionen otel.SetTracerProvider und otel.SetTextMapPropagators können Sie die globalen Einstellungen TracerProvider und Propagator festlegen. Standardmäßig verwenden Instrumentierungsbibliotheken wie otelhttp den global registrierten TracerProvider, um Spans zu erstellen, und den Propagator, um Kontext weiterzugeben.
  • Sie verpacken den HTTP-Server mit otelhttp.NewHandler, um den HTTP-Server zu instrumentieren.

mainHandler-Funktion überprüfen

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))
}

Zum Erfassen der Latenz ausgehender Anfragen an das Ziel verwenden Sie das Plug-in otelhttp, um eine HTTP-Anfrage zu senden. Sie verwenden die Funktion r.Context auch, um die eingehende Anfrage mit der ausgehenden Anfrage zu verknüpfen, wie im folgenden Beispiel gezeigt:

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

Anwendung bereitstellen

In diesem Abschnitt verwenden Sie Cloud Build, um Container-Images für die Backend- und Frontend-Dienste zu erstellen und dann in ihren GKE-Clustern bereitzustellen.

Docker-Container erstellen

  1. Senden Sie den Build in Cloud Shell aus dem Arbeitsverzeichnis:

    cd $WORKDIR
    gcloud builds submit . --tag us-west1-docker.pkg.dev/$PROJECT_ID/distributed-tracing-docker-repo/backend:latest
    
  2. Prüfen Sie, ob das Container-Image erfolgreich erstellt wurde und in Artifact Registry verfügbar ist:

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

    Das Container-Image wurde erfolgreich erstellt, wenn die Ausgabe der folgenden ähnelt, wobei PROJECT_ID die ID Ihres Google Cloud-Projekts ist:

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

Backend-Dienst bereitstellen

  1. Legen Sie in Cloud Shell den kubectx-Kontext auf den backend-Cluster fest:

    kubectx backend
    
  2. Erstellen Sie die YAML-Datei für die backend-Bereitstellung:

    export PROJECT_ID=$(gcloud info --format='value(config.project)')
    envsubst < backend-deployment.yaml | kubectl apply -f -
    
  3. Prüfen Sie, ob die Pods ausgeführt werden.

    kubectl get pods
    

    Die Ausgabe zeigt für Status den Wert Running an:

    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. Machen Sie die backend-Bereitstellung mithilfe eines Load-Balancers verfügbar:

    kubectl expose deployment backend --type=LoadBalancer
    
  5. Rufen Sie die IP-Adresse des backend-Dienstes ab:

    kubectl get services backend
    

    Die Ausgabe sieht in etwa so aus:

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

    Wenn das Feld EXTERNAL-IP den Wert <pending> hat, wiederholen Sie den Befehl, bis der Wert eine IP-Adresse ist.

  6. Erfassen Sie die IP-Adresse aus dem vorherigen Schritt in einer Variable:

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

Frontend-Dienst bereitstellen

  1. Legen Sie in Cloud Shell den kubectx-Kontext auf den Backend-Cluster fest:

    kubectx frontend
    
  2. Erstellen Sie die YAML-Datei für die frontend-Bereitstellung:

    export PROJECT_ID=$(gcloud info --format='value(config.project)')
    envsubst < frontend-deployment.yaml | kubectl apply -f -
    
  3. Prüfen Sie, ob die Pods ausgeführt werden.

    kubectl get pods
    

    Die Ausgabe zeigt für Status den Wert Running an:

    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. Machen Sie die frontend-Bereitstellung mithilfe eines Load-Balancers verfügbar:

    kubectl expose deployment frontend --type=LoadBalancer
    
  5. Rufen Sie die IP-Adresse des frontend-Dienstes ab:

    kubectl get services frontend
    

    Die Ausgabe sieht in etwa so aus:

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

    Wenn das Feld EXTERNAL-IP den Wert <pending> hat, wiederholen Sie den Befehl, bis der Wert eine IP-Adresse ist.

  6. Erfassen Sie die IP-Adresse aus dem vorherigen Schritt in einer Variable:

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

Anwendung laden und Traces überprüfen

In diesem Abschnitt verwenden Sie das Apache Bench-Dienstprogramm, um Anfragen für Ihre Anwendung zu erstellen. Anschließend überprüfen Sie die resultierenden Traces in Cloud Trace.

  1. Verwenden Sie Apache Bench in Cloud Shell, um 1.000 Anfragen mit 3 gleichzeitigen Threads zu generieren:

    ab -c 3 -n 1000 http://${FRONTEND_IP}:8081/
    
  2. Rufen Sie in der Google Cloud Console die Seite Trace-Liste auf:

    Zur Trace-Liste

  3. Klicken Sie zum Prüfen der Zeitachse auf einen der URIs mit der Bezeichnung server.

    Streudiagramm der Traces

    Dieser Trace enthält vier Spans mit den folgenden Namen:

    • Der erste Span server erfasst die End-to-End-Latenz der Verarbeitung der HTTP-Anfrage auf dem Frontend-Server.
    • Der erste Span HTTP GET erfasst die Latenz des GET-Aufrufs, der vom Client des Frontends an das Backend vorgenommen wird.
    • Der zweite Span server erfasst die End-to-End-Latenz der Verarbeitung der HTTP-Anfrage auf dem Backend-Server.
    • Der zweite HTTP GET-Span erfasst die Latenz des GET-Aufrufs, der vom Backend-Client an google.com gesendet wird.

    Balkendiagramm der Spans

Bereinigen

Am einfachsten können Sie weitere Kosten vermeiden, wenn Sie das Google Cloud-Projekt löschen, das Sie für die Bereitstellung erstellt haben. Alternativ haben Sie die Möglichkeit, die einzelnen Ressourcen zu löschen.

Projekt löschen

  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.

Einzelne Ressourcen löschen

Führen Sie die folgenden Befehle in Cloud Shell aus, um einzelne Ressourcen statt des gesamten Projekts zu löschen:

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

Nächste Schritte