Verteiltes Tracing in Mikrodienstanwendungen

Last reviewed 2024-06-26 UTC

Dieses Dokument ist der vierte Teil einer vierteiligen Reihe zum Entwerfen, Erstellen und Bereitstellen von Mikrodiensten. In dieser Reihe werden die verschiedenen Elemente einer Mikrodienstarchitektur beschrieben. Die Reihe enthält Informationen zu den Vor- und Nachteilen des Mikrodienstarchitekturmusters und zu ihrer Anwendung.

  1. Einführung in Mikrodienste
  2. Monolithische Anwendung in Mikrodienste refaktorieren
  3. Kommunikation zwischen Diensten in einem Mikrodienst-Setup
  4. Verteiltes Tracing in einer Mikrodienstanwendung (dieses Dokument)

Diese Serie richtet sich an Anwendungsentwickler und Architekten, die Migrationen entwerfen und implementieren, um eine monolithische Anwendung in eine Mikrodienstanwendung zu refaktorieren.

In einem verteilten System ist es wichtig zu wissen, wie eine Anfrage von einem Dienst zu einem anderen fließt und wie lange es dauert, eine Aufgabe in jedem Dienst auszuführen. Betrachten Sie die auf Mikrodiensten basierende Online Boutique-Anwendung, die Sie im vorherigen Dokument Monolithische Anwendung in Mikrodienste umwandeln bereitgestellt haben. Die Anwendung besteht aus mehreren Diensten. Der folgende Screenshot zeigt beispielsweise die Seite mit den Produktdetails, mit der Informationen von den Frontend-, Empfehlungs- und Anzeigendiensten abgerufen werden.

Die Seite mit den Produktdetails.

Um die Seite mit den Produktdetails zu rendern, kommuniziert der Frontend-Dienst mit dem Empfehlungsdienst und dem Anzeigendienst, wie im folgenden Diagramm dargestellt:

Der Frontend-Dienst kommuniziert mit dem Empfehlungsdienst, dem Produktkatalog und dem Anzeigendienst.

Abbildung 1. Dienste, die in verschiedenen Sprachen geschrieben sind.

In Abbildung 1 ist der Frontend-Dienst in Go geschrieben. Der in Python geschriebene Empfehlungsdienst verwendet gRPC, um mit dem Frontend-Dienst zu kommunizieren. Der in Java geschriebene Anzeigendienst verwendet ebenfalls gRPC, um mit dem Frontend-Dienst zu kommunizieren. Neben gRPC kann die dienstübergreifende Kommunikationsmethode auch in REST HTTP sein.

Wenn Sie ein solches verteiltes System aufbauen, sollten Ihre Beobachtbarkeits-Werkzeuge die folgenden Erkenntnisse liefern:

  • Die Dienste, die eine Anfrage durchlaufen hat.
  • Wo Verzögerungen auftraten, wenn eine Anfrage langsam war.
  • Wo ein Fehler aufgetreten ist, wenn die Anfrage fehlgeschlagen ist.
  • Wie sich die Ausführung der Anfrage vom normalen Verhalten des Systems unterschied.
  • Ob Unterschiede bei der Ausführung der Anfrage auf die Leistung zurückzuführen sind (ob einige Dienstaufrufe länger oder kürzer als gewöhnlich gedauert haben).

Lernziele

  • Verwenden Sie Kustomize-Manifestdateien, um die Infrastruktur einzurichten.
  • Beispielanwendung „Online Boutique“ in Google Kubernetes Engine (GKE) bereitstellen.
  • Verwenden Sie Cloud Trace, um den Weg eines Nutzers in der Beispielanwendung zu prüfen.

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 dieses Dokuments können Sie weitere Kosten vermeiden, indem Sie die erstellten Ressourcen löschen. Weitere Informationen finden Sie unter Bereinigen.

Hinweis

Wenn Sie bereits ein Projekt eingerichtet haben, indem Sie das vorherige Dokument dieser Reihe, Interservice-Kommunikation in einer Mikrodiensteinrichtung, abschließen, können Sie das Projekt wiederverwenden. Führen Sie die folgenden Schritte aus, um zusätzliche APIs zu aktivieren und Umgebungsvariablen festzulegen.

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

    Go to project selector

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

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

    Activate Cloud Shell

  4. Aktivieren Sie die APIs für Compute Engine, GKE, Cloud SQL, Artefaktanalyse, Trace und Container Registry:

     gcloud services enable \
       compute.googleapis.com \
       sql-component.googleapis.com \
       servicenetworking.googleapis.com\
       container.googleapis.com \
       containeranalysis.googleapis.com \
       containerregistry.googleapis.com \
       sqladmin.googleapis.com
    

Verteiltes Tracing

Bei der verteilten Tracing-Funktion werden jeder Anfrage kontextbezogene Metadaten hinzugefügt und es wird sichergestellt, dass die Metadaten zwischen den Anfragen geteilt werden. Sie verwenden Trace-Punkte für die instrumentierte verteilte Tracing-Funktion. Sie können beispielsweise Ihre Dienste (Frontend, Empfehlung und Anzeigen) mit zwei Trace-Punkten instrumentieren, um eine Clientanfrage zum Anzeigen der Produktdetails zu verarbeiten: einen Trace-Punkt zum Senden der Anfrage und einen weiteren Trace, um die Antwort zu erhalten. Das folgende Diagramm zeigt, wie diese Trace-Punkt-Instrumentierung funktioniert:

Eine Trace-Punkt-Instrumentierung mit zwei Trace-Punkten.

Abbildung 2. Jeder dienstübergreifende Aufruf hat zwei Trace-Punkte, die aus einem Anfrage-Antwort-Paar bestehen.

Damit Trace-Punkte feststellen können, welche Anfrage ausgeführt werden soll, wenn der Dienst aufgerufen wird, übergibt der Ursprungsdienst eine Trace-ID entlang des Ausführungsablaufs. Der Prozess, der die Trace-ID übergibt, wird als Metadatenverteilung oder verteilte Kontextverteilung bezeichnet. Die Kontextverteilung überträgt Metadaten über Netzwerkaufrufe, wenn Dienste einer verteilten Anwendung während der Ausführung einer bestimmten Anfrage miteinander kommunizieren. Das folgende Diagramm zeigt die Weitergabe von Metadaten:

Die Metadaten-Verteilung übergibt die Trace-ID.

Abbildung 3. Trace-Metadaten werden zwischen Diensten übergeben. Die Metadaten enthalten Informationen wie welcher Dienst ruft welche Zeitstempel auf.

Im Beispiel „Online Boutique“ beginnt ein Trace, wenn ein Nutzer eine erste Anfrage zum Abrufen von Produktdetails sendet. Eine neue Trace-ID wird generiert und jede nachfolgende Anfrage wird mit Headern versehen, die kontextbezogene Metadaten enthalten, die an die ursprüngliche Anfrage zurückgegeben werden.

Jeder einzelne Vorgang, der als Teil der Anfrage des Endnutzers aufgerufen wird, wird als Span bezeichnet. Der ursprüngliche Dienst-Tag hat jeweils eine eigene eindeutige ID und die Trace-ID des übergeordneten Spans. Das folgende Diagramm zeigt eine Trace-Diagrammvisualisierung eines Trace:

Einzelne Vorgänge werden als Spans getaggt.

Abbildung 4. Ein übergeordneter Span enthält die Antwortzeit untergeordneter Spans.

Abbildung 4 zeigt eine Trace-Struktur, in der der Frontend-Dienst den Empfehlungsdienst und den Anzeigendienst aufruft. Der Frontend-Dienst ist der übergeordnete Span, der die vom Endnutzer beobachtete Antwortzeit beschreibt. Die untergeordneten Spans beschreiben, wie der Empfehlungsdienst und der Anzeigendienst aufgerufen und beantwortet wurden, einschließlich Informationen zur Antwortzeit.

Ein Service Mesh wie Istio ermöglicht das verteilte Tracing von Dienst-zu-Dienst-Traffic ohne spezielle Instrumentierung. Es kann jedoch Situationen geben, in denen Sie mehr Kontrolle über die Traces haben möchten, oder Sie müssen Trace-Code erstellen, der nicht in einem Service Mesh ausgeführt wird.

In diesem Dokument wird OpenTelemetry verwendet, um die Instrumentierung verteilter Mikrodienstanwendungen zu ermöglichen, um Traces und Messwerte zu erfassen. Mit OpenTelemetry können Sie Messwerte und Traces erfassen und dann in Back-Ends exportieren, z. B. Prometheus, Cloud Monitoring, Datadog, Graphite, Zipkin und Jaeger.

Instrumentierung mit OpenTelemetry

In den folgenden Abschnitten wird gezeigt, wie mit der Kontextweitergabe Spans aus mehreren Anfragen an einen einzelnen übergeordneten Trace angehängt werden können.

In diesem Beispiel werden die OpenTelemetry-JavaScript-, Python- und Go-Bibliotheken zur Instrumentierung der Trace-Implementierung für die Zahlungs-, Empfehlungs- und Frontend-Dienste verwendet. Je nach Ausführlichkeit der Instrumentierung können sich Tracing-Daten auf die Kosten des Projekts auswirken (Cloud Trace-Abrechnung). Um die Kosten zu verringern, verwenden die meisten Tracing-Systeme verschiedene Arten der Stichprobenerfassung, um nur einen bestimmten Prozentsatz der beobachteten Traces zu erfassen. In Produktionsumgebungen hat Ihre Organisation möglicherweise Gründe dafür, welche Beispiele sie ausführen möchte und warum. Sie können Ihre Stichprobenstrategie anhand der Kostenverwaltung, der Konzentration auf interessante Spuren oder der Filterung von Rauschen anpassen. Weitere Informationen zur Stichprobenerhebung finden Sie unter OpenTelemetry-Stichprobenerhebung.

In diesem Dokument wird Trace zur Visualisierung verteilter Traces verwendet. Sie verwenden einen OpenTelemetry-Exporter, um Traces an Trace zu senden.

Trace-Exporter registrieren

In diesem Abschnitt wird beschrieben, wie Sie den Trace-Exporter in jedem Dienst registrieren, indem Sie dem Mikrodienstcode Zeilen hinzufügen.

Für den in Go geschriebenen Frontend-Dienst wird im folgenden Codebeispiel der Exporter registriert:

[...]
exporter, err := otlptracegrpc.New(
        ctx,
        otlptracegrpc.WithGRPCConn(svc.collectorConn))
    if err != nil {
        log.Warnf("warn: Failed to create trace exporter: %v", err)
    }
tp := sdktrace.NewTracerProvider(
        sdktrace.WithBatcher(exporter),
        sdktrace.WithSampler(sdktrace.AlwaysSample()))
    otel.SetTracerProvider(tp)

Für den Empfehlungsdienst (in Python geschrieben) wird im folgenden Codebeispiel der Exporter registriert:

if os.environ["ENABLE_TRACING"] == "1":
    trace.set_tracer_provider(TracerProvider())
    otel_endpoint = os.getenv("COLLECTOR_SERVICE_ADDR", "localhost:4317")
    trace.get_tracer_provider().add_span_processor(
        BatchSpanProcessor(
            OTLPSpanExporter(
            endpoint = otel_endpoint,
            insecure = True
            )
        )
    )

Für den Zahlungsdienst (in JavaScript geschrieben) wird im folgenden Codebeispiel der Exporter registriert:

provider.addSpanProcessor(new SimpleSpanProcessor(new OTLPTraceExporter({url: collectorUrl})));
provider.register();

Kontextverteilung einrichten

Das Tracing-System muss einer Trace-Kontextspezifikation folgen, die das Format zum Verteilen des Tracing-Kontexts zwischen Diensten definiert. Beispiele für das Verteilungsformat sind Zipkins B3-Format und X-Google-Cloud-Trace.

OpenTelemetry überträgt den Kontext mithilfe des globalen TextMapPropagator. In diesem Beispiel wird der Trace Context-Propagator verwendet, der das W3C-Traceparent-Format verwendet. Instrumentierungsbibliotheken wie die HTTP- und gRPC-Bibliotheken von OpenTelemetry verwenden den globalen Propagator, um HTTP- oder gRPC-Anfragen den Trace-Kontext als Metadaten hinzuzufügen. Damit die Kontextweitergabe erfolgreich ist, müssen Client und Server dasselbe Weitergabeformat verwenden.

Kontextweitergabe über HTTP

Der Frontend-Dienst fügt einen Trace-Kontext in die HTTP-Anfrageheader ein. Die Back-End-Dienste extrahieren den Trace-Kontext. Das folgende Codebeispiel zeigt, wie der Frontend-Dienst instrumentiert wird, um den Trace-Kontext zu konfigurieren:

otel.SetTextMapPropagator(
    propagation.NewCompositeTextMapPropagator(
        propagation.TraceContext{}, propagation.Baggage{}))

if os.Getenv("ENABLE_TRACING") == "1" {
    log.Info("Tracing enabled.")
    initTracing(log, ctx, svc)
} else {
    log.Info("Tracing disabled.")
}

...

var handler http.Handler = r
handler = &logHandler{log: log, next: handler}     // add logging
handler = ensureSessionID(handler)                 // add session ID
handler = otelhttp.NewHandler(handler, "frontend") // add OpenTelemetry tracing

Kontextweitergabe über gRPC

Betrachten Sie den Ablauf, in dem der Bezahldienst die Bestellung basierend auf dem Produkt aufgibt, das ein Nutzer auswählt. Diese Dienste kommunizieren über gRPC.

Im folgenden Codebeispiel wird ein gRPC-Aufruf-Interceptor verwendet, der die ausgehenden Anrufe abfängt und den Trace-Kontext einfügt:

var srv *grpc.Server

// Propagate trace context always
otel.SetTextMapPropagator(
    propagation.NewCompositeTextMapPropagator(
        propagation.TraceContext{}, propagation.Baggage{}))
srv = grpc.NewServer(
    grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()),
    grpc.StreamInterceptor(otelgrpc.StreamServerInterceptor()),
)

Nachdem der Zahlungs- oder Produktkatalogdienst (ListProducts) die Anfrage erhalten hat, extrahiert er den Kontext aus den Anfrageheadern und verwendet die übergeordneten Trace-Metadaten, um einen untergeordneten Span zu erzeugen.

In den folgenden Abschnitten wird gezeigt, wie Sie verteiltes Tracing für die Beispielanwendung „Online Boutique“ einrichten und prüfen.

Anwendung bereitstellen

Wenn Sie bereits eine laufende Anwendung aus dem vorherigen Dokument dieser Reihe Zwischen-Dienst-Kommunikation in einer Mikrodienst-Einrichtung haben, können Sie mit dem nächsten Abschnitt Traces prüfen fortfahren. Führen Sie andernfalls die folgenden Schritte aus, um das Beispiel „Online Boutique“ bereitzustellen:

  1. Klonen Sie in der Cloud Shell das GitHub-Repository:

    git clone https://github.com/GoogleCloudPlatform/microservices-demo.git
    
  2. Setzen Sie die Umgebungsvariablen für die neue Bereitstellung zurück:

    PROJECT_ID=PROJECT_ID
    REGION=us-central1
    GSA_NAME=microservices-sa
    GSA_EMAIL=$GSA_NAME@$PROJECT_ID.iam.gserviceaccount.com
    

    Ersetzen Sie PROJECT_ID durch die Google Cloud-Projekt-ID, die Sie verwenden möchten.

  3. Optional: Erstellen Sie einen neuen Cluster oder verwenden Sie einen vorhandenen, falls vorhanden:

    gcloud container clusters create-auto online-boutique --project=${PROJECT_ID}
      --region=${REGION}
    
  4. Erstellen Sie ein Google-Dienstkonto:

    gcloud iam service-accounts create $GSA_NAME \
      --project=$PROJECT_ID
    
  5. APIs aktivieren:

    gcloud services enable \
    monitoring.googleapis.com \
    cloudtrace.googleapis.com \
    cloudprofiler.googleapis.com \
      --project ${PROJECT_ID}
    
  6. Weisen Sie dem Dienstkonto die für Cloud Trace erforderlichen Rollen zu:

    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
    --member "serviceAccount:${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role roles/cloudtrace.agent
    
    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
    --member "serviceAccount:${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role roles/monitoring.metricWriter
    
    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
    --member "serviceAccount:${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role roles/cloudprofiler.agent
    
    gcloud iam service-accounts add-iam-policy-binding ${GSA_EMAIL} \
    --role roles/iam.workloadIdentityUser \
    --member "serviceAccount:${PROJECT_ID}.svc.id.goog[default/default]"
    
  7. Annotieren Sie Ihr Kubernetes-Dienstkonto (default/default für den Standard-Namespace), um das IAM-Dienstkonto (Identity and Access Management) zu verwenden:

    kubectl annotate serviceaccount default \
        iam.gke.io/gcp-service-account=${GSA_EMAIL}
    
  8. Aktivieren Sie die Google Cloud-Beobachtbarkeit für die GKE-Konfiguration, um das Tracing zu aktivieren:

    cd ~/microservices-demo/kustomize && \
    kustomize edit add component components/google-cloud-operations
    

    Mit dem vorherigen Befehl wird die Datei kustomize/kustomization.yaml aktualisiert. Sie sieht in etwa so aus:

    apiVersion: kustomize.config.k8s.io/v1beta1
    kind: Kustomization
    resources:
    - base
    components:
    - components/google-cloud-operations
    [...]
    
  9. Mikrodienste bereitstellen

    kubectl apply -k .
    
  10. Prüfen Sie den Status der Bereitstellung:

    kubectl rollout status deployment/frontend
    kubectl rollout status deployment/paymentservice
    kubectl rollout status deployment/recommendationservice
    kubectl rollout status deployment/adservice
    

    Die Ausgabe für die verschiedenen Befehle sieht so aus:

    Waiting for deployment "" rollout to finish: 0 of 1 updated replicas are available...
    deployment "" successfully rolled out
    
  11. Rufen Sie die IP-Adresse der bereitgestellten Anwendung ab:

    kubectl get service frontend-external | awk '{print $4}'
    

    Warten Sie, bis die IP-Adresse des Load Balancers veröffentlicht wurde. Zum Beenden des Befehls drücken Sie Ctrl+C. Notieren Sie sich die IP-Adresse des Load-Balancers und greifen Sie dann unter der URL http://IP_ADDRESS auf die Anwendung zu. Es kann einige Zeit dauern, bis der Load-Balancer fehlerfrei ist und Traffic weiterleitet.

Traces mit Cloud Trace prüfen

Der Kaufprozess eines Nutzers in der Anwendung „Online Boutique“ hat folgenden Ablauf:

  • Der Nutzer sieht einen Produktkatalog auf der Landingpage.
  • Für einen Kauf klickt der Nutzer auf Kaufen.
  • Der Nutzer wird auf eine Produktdetailseite weitergeleitet, auf der er den Artikel dem Einkaufswagen hinzufügt.
  • Der Nutzer wird zu einer Zahlungsseite weitergeleitet, auf der er eine Zahlung vornehmen kann, um die Bestellung abzuschließen.

Stellen Sie sich ein Szenario vor, in dem Sie eine lange Antwortzeit beim Laden der Seite mit den Produktdetails beheben müssen. Wie bereits beschrieben, besteht die Seite mit den Produktdetails aus mehreren Mikrodiensten. Um zu ermitteln, wo und warum die hohe Latenz auftritt, können Sie verteilte Tracing-Diagramme aufrufen, um die Leistung der gesamten Anfrage für die verschiedenen Dienste zu prüfen.

So prüfen Sie die Grafiken für verteilte Tracing:

  1. Rufen Sie die Anwendung auf und klicken Sie auf ein beliebiges Produkt. Die Seite mit den Produktdetails wird angezeigt.
  2. Rufen Sie in der Google Cloud Console die Seite Trace-Liste auf und prüfen Sie die Zeitachse.
  3. Klicken Sie in der URI-Spalte auf Frontend, um die Ergebnisse des verteilten Traces anzusehen.
  4. In der Trace-Wasserfallansicht werden die Spans angezeigt, die mit dem URI verknüpft sind:

    Die Trace-Wasserfallansicht zeigt Spans an.

    Im vorherigen Screenshot enthält der Trace für ein Produkt die folgenden Spans:

    • Der Span Frontend erfasst die End-to-End-Latenz (150.349 ms), die der Client beim Laden der Seite mit den Produktdetails beobachtet.
    • Der Span Empfehlungdienst erfasst die Latenz der Back-End-Aufrufe beim Abrufen von Empfehlungen (4,246 ms), die sich auf das Produkt beziehen.
    • Der Anzeigendienst-Span erfasst die Latenz der Back-End-Aufrufe beim Abrufen von Anzeigen (4,511 ms), die für die Produktseite relevant sind.

Zur Fehlersuche bei hohen Antwortzeiten können Sie sich Insights ansehen, einschließlich Latenzverteilungsdiagrammen aller Ausreißeranfragen, wenn die Abhängigkeiten des Dienstes ihre Service Level Objectives (SLOs) nicht erfüllen. Sie können außerdem Cloud Trace verwenden, um anhand der Stichproben Leistungsstatistiken zu erhalten und Analyseberichte zu erstellen.

Fehlerbehebung

Wenn die Traces in der Application Performance Management nicht angezeigt werden, prüfen Sie im Log-Explorer, ob der Fehler „Zugriff verweigert“ auftritt. Dieser Fehler tritt auf, wenn das Dienstkonto keinen Zugriff zum Exportieren der Traces hat. Sehen Sie sich die Schritte zum Zuweisen der für Cloud Trace erforderlichen Rollen an und achten Sie darauf, das Dienstkonto mit dem richtigen Namespace zu annotieren. Starte danach die opentelemetrycollector neu:

  kubectl rollout restart deployment opentelemetrycollector

Bereinigen

Damit Ihrem Google Cloud-Konto die in dieser Anleitung verwendeten Ressourcen nicht in Rechnung gestellt werden, löschen Sie entweder das Projekt, das die Ressourcen enthält, oder Sie behalten das Projekt und löschen die einzelnen Ressourcen.

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.

Ressourcen löschen

Wenn Sie das in diesem Dokument verwendete Google Cloud-Projekt beibehalten möchten, löschen Sie die einzelnen Ressourcen:

  • Löschen Sie in Cloud Shell die Ressourcen:

    gcloud container clusters delete online-boutique --project=${PROJECT_ID} --region=${REGION}
    

Nächste Schritte