Verteiltes Tracing in Mikrodienstanwendungen

Refresh_date: 2023-10-24

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 auch 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. Wählen Sie in der Google Cloud Console auf der Seite der Projektauswahl ein Google Cloud-Projekt aus oder erstellen Sie eines.

    Zur Projektauswahl

  2. Die Abrechnung für das Google Cloud-Projekt muss aktiviert sein.

  3. Aktivieren Sie Cloud Shell in der Google Cloud Console.

    Cloud Shell aktivieren

  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 hinsichtlich Kosten anpassen, sich auf interessante Traces konzentrieren oder Rauschen herausfiltern. Weitere Informationen zur Stichprobenerhebung finden sich unter OpenTelemetry-Sampling.

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 leitet Kontext über den globalen TextMapPropagator weiter. 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 Trace-Kontext als Metadaten hinzuzufügen. Damit die Kontextverteilung erfolgreich ist, müssen Client und Server das gleiche Verteilungsformat 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 für die Konfiguration des Trace-Kontexts instrumentiert wird:

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, mit dem der Checkout-Dienst die Bestellung basierend auf dem Produkt ausführt, 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()),
)

Nach dem Erhalt der Anfrage extrahiert der Zahlungs- oder Produktkatalogdienst (ListProducts) 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 Dienstübergreifende Kommunikation in einem Mikrodienst-Setup 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 für die neue Bereitstellung die Umgebungsvariablen zurück:

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

    Ersetzen Sie Folgendes:

    • PROJECT_ID: die Kennzeichnung für Ihre Projekt-ID
  3. Optional: Erstellen Sie einen neuen Cluster oder verwenden Sie einen vorhandenen Cluster, 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 GSA die für Cloud Tracing 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 Google IAM-Dienstkonto zu verwenden:

    kubectl annotate serviceaccount default \
        iam.gke.io/gcp-service-account=${GSA_EMAIL}
    
  8. Aktivieren Sie die Cloud Operations for GKE-Konfiguration, die das Tracing aktiviert:

    cd ~/microservices-demo/kustomize && \
    kustomize edit add component components/google-cloud-operations
    
  9. Dadurch wird die Datei kustomize/kustomization.yaml aktualisiert, die etwa so aussehen könnte:

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

    kubectl apply -k .
    
  11. 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
    
  12. 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 lange Antwortzeiten 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 verteilten Trace-Ergebnisse 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 Frontend-Span 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 Application Performance Management nicht angezeigt werden, prüfen Sie den Log-Explorer auf einen Fehler "Berechtigung verweigert". Die Berechtigung wird verweigert, 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 annotieren Sie das Dienstkonto mit dem richtigen Namespace. Starten Sie dann 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. Wechseln Sie in der Google Cloud Console zur Seite Ressourcen verwalten.

    Zur Seite „Ressourcen verwalten“

  2. Wählen Sie in der Projektliste das Projekt aus, das Sie löschen möchten, und klicken Sie dann auf Löschen.
  3. Geben Sie im Dialogfeld die Projekt-ID ein und klicken Sie auf Shut down (Beenden), um das Projekt zu löschen.

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