1. Einführung
Zuletzt aktualisiert: 15.07.2022
Beobachtbarkeit der Anwendung
Beobachtbarkeit und OpenTelemetry
Mit dem Begriff „Observability“ wird ein Attribut eines Systems beschrieben. Mit einem System mit Beobachtbarkeit können Teams ihr System aktiv debuggen. In diesem Zusammenhang sind Logs, Messwerte und Traces die drei Säulen der Beobachtbarkeit, die für die Beobachtbarkeit des Systems grundlegend sind.
OpenTelemetry ist eine Reihe von Spezifikationen, Bibliotheken und Agenten, die die Instrumentierung und den Export von Telemetriedaten (Protokolle, Messwerte und Traces) beschleunigen, die für die Observability erforderlich sind. OpenTelemetry ist ein offener Standard und ein Community-getriebenes Projekt der CNCF. Durch die Verwendung von Bibliotheken, die vom Projekt und seinem Ökosystem bereitgestellt werden, können Entwickler ihre Anwendungen anbieterneutral und für mehrere Architekturen instrumentieren.
Neben den drei Säulen der Beobachtbarkeit ist das kontinuierliche Profiling eine weitere wichtige Komponente für die Beobachtbarkeit und erweitert die Nutzerbasis in der Branche. Cloud Profiler ist eine der ursprünglichen Lösungen und bietet eine einfache Oberfläche, mit der sich die Leistungsmesswerte in den Aufrufstapeln der Anwendung aufschlüsseln lassen.
Dieses Codelab ist Teil 1 der Reihe und behandelt die Instrumentierung verteilter Traces in Mikrodiensten mit OpenTelemetry und Cloud Trace. In Teil 2 geht es um die kontinuierliche Profilerstellung mit Cloud Profiler.
Verteiltes Tracing
Von Logs, Messwerten und Traces ist der Trace die Telemetrie, die die Latenz eines bestimmten Teils des Prozesses im System angibt. Verteiltes Tracing ist besonders im Zeitalter der Mikrodienste ein wichtiger Faktor, um Latenzengpässe im gesamten verteilten System zu finden.
Bei der Analyse verteilter Traces ist die Visualisierung der Trace-Daten der Schlüssel, um die Gesamtlatenz des Systems auf einen Blick zu erfassen. Bei einem verteilten Trace werden eine Reihe von Aufrufen verarbeitet, um eine einzelne Anfrage an den Systemeingangspunkt in Form eines Tracings mit mehreren Spans zu verarbeiten.
Ein Span stellt eine einzelne Arbeitseinheit in einem verteilten System dar und zeichnet Start- und Endzeiten auf. Spans haben oft hierarchische Beziehungen zueinander. In der Abbildung unten sind alle kleineren Spans untergeordnete Spans einer großen Span „/messages“ und werden in einem Trace zusammengeführt, der den Arbeitspfad durch ein System zeigt.
Google Cloud Trace ist eine der Optionen für das verteilte Trace-Backend und ist gut in andere Produkte in Google Cloud eingebunden.
Umfang
In diesem Codelab erfassen Sie Ablaufinformationen in den Diensten „Shakespeare-Anwendung“ (auch „Shakesapp“) in einem Google Kubernetes Engine-Cluster. Die Architektur von Shakesapp ist wie unten beschrieben:
- Loadgen sendet eine Suchanfrage in HTTP an den Client.
- Clients leiten die Abfrage vom Loadgen an den Server in gRPC weiter
- Der Server nimmt die Abfrage vom Client an, ruft alle Shakespeare-Werke im Textformat aus Google Cloud Storage ab, sucht die Zeilen, die die Abfrage enthalten, und gibt die Nummer der Zeile zurück, die mit dem Client übereinstimmt.
Sie erfassen die Trace-Informationen für die gesamte Anfrage. Anschließend betten Sie einen Profiler-Agent in den Server ein und untersuchen das Nadelöhr.
Aufgaben in diesem Lab
- Erste Schritte mit den OpenTelemetry-Trace-Bibliotheken in einem Go-Projekt
- Span mit der Bibliothek erstellen
- Span-Kontexte über die Verbindung zwischen App-Komponenten weitergeben
- Trace-Daten an Cloud Trace senden
- Trace in Cloud Trace analysieren
In diesem Codelab wird erläutert, wie Sie Ihre Mikrodienste instrumentieren. Zur besseren Verständlichkeit enthält dieses Beispiel nur drei Komponenten (Lastgenerator, Client und Server). Sie können den in diesem Codelab beschriebenen Prozess jedoch auch auf komplexere und größere Systeme anwenden.
Voraussetzungen
- Grundkenntnisse in Go
- Grundkenntnisse in Kubernetes
2. Einrichtung und Anforderungen
Einrichtung der Umgebung im eigenen Tempo
Wenn Sie noch kein Google-Konto (Gmail oder Google Apps) haben, müssen Sie ein Konto erstellen. Melden Sie sich in der Google Cloud Platform Console ( console.cloud.google.com) an und erstellen Sie ein neues Projekt.
Wenn Sie bereits ein Projekt haben, klicken Sie links oben in der Console auf das Drop-down-Menü für die Projektauswahl:
Klicken Sie im angezeigten Dialogfeld auf die Schaltfläche „NEUES PROJEKT“, um ein neues Projekt zu erstellen:
Wenn Sie noch kein Projekt haben, sollte ein Dialogfeld wie dieses angezeigt werden, um Ihr erstes Projekt zu erstellen:
Im folgenden Dialogfeld für die Projekterstellung können Sie die Details Ihres neuen Projekts eingeben:
Notieren Sie sich die Projekt-ID. Diese ist für alle Google Cloud-Projekte eindeutig. Der Name oben ist leider schon vergeben und kann nicht verwendet werden. Sie wird später in diesem Codelab als PROJECT_ID bezeichnet.
Wenn Sie dies noch nicht getan haben, müssen Sie in der Developers Console die Abrechnung aktivieren, um Google Cloud-Ressourcen zu verwenden und die Cloud Trace API zu aktivieren.
Die Ausführung dieses Codelabs sollte Sie nicht mehr als ein paar Dollar kosten, aber es könnte mehr sein, wenn Sie sich für mehr Ressourcen entscheiden oder wenn Sie sie laufen lassen (siehe Abschnitt „Bereinigen“ am Ende dieses Dokuments). Die Preise für Google Cloud Trace, Google Kubernetes Engine und Google Artifact Registry sind in der offiziellen Dokumentation aufgeführt.
- Preise für die Operations-Suite von Google Cloud | Operations Suite
- Preise | Kubernetes Engine-Dokumentation
- Artifact Registry – Preise | Artifact Registry-Dokumentation
Neuen Nutzern der Google Cloud Platform steht eine kostenlose Testversion mit einem Guthaben von 300$ zur Verfügung. Das sollte dieses Codelab vollständig kostenlos machen.
Google Cloud Shell einrichten
Sie können Google Cloud und Google Cloud Trace zwar von Ihrem Laptop aus per Fernzugriff nutzen, in diesem Codelab verwenden wir jedoch Google Cloud Shell, eine Befehlszeilenumgebung, die in der Cloud ausgeführt wird.
Auf dieser Debian-basierten virtuellen Maschine sind alle erforderlichen Entwicklungstools installiert. Sie bietet ein Basisverzeichnis mit 5 GB nichtflüchtigem Speicher und läuft in Google Cloud, was die Netzwerkleistung und Authentifizierung erheblich verbessert. Sie benötigen also nur einen Browser für dieses Codelab (ja, es funktioniert auch auf einem Chromebook).
Klicken Sie in der Cloud Console einfach auf „Cloud Shell aktivieren“ , um Cloud Shell zu aktivieren. Die Bereitstellung und Verbindung mit der Umgebung sollte nur wenige Augenblicke dauern.
Sobald Sie mit Cloud Shell verbunden sind, sollten Sie sehen, dass Sie bereits authentifiziert sind und das Projekt bereits auf Ihre PROJECT_ID
festgelegt ist.
gcloud auth list
Befehlsausgabe
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
Befehlsausgabe
[core] project = <PROJECT_ID>
Wenn das Projekt aus irgendeinem Grund nicht festgelegt ist, geben Sie einfach den folgenden Befehl ein:
gcloud config set project <PROJECT_ID>
Suchen Sie nach Ihrem PROJECT_ID
? Sehen Sie nach, welche ID Sie bei der Einrichtung verwendet haben, oder suchen Sie sie im Cloud Console-Dashboard:
In Cloud Shell werden außerdem einige Umgebungsvariablen standardmäßig festgelegt, die bei der Ausführung zukünftiger Befehle nützlich sein können.
echo $GOOGLE_CLOUD_PROJECT
Befehlsausgabe
<PROJECT_ID>
Legen Sie abschließend die Standardzone und die Projektkonfiguration fest.
gcloud config set compute/zone us-central1-f
Sie können verschiedene Zonen auswählen. Weitere Informationen finden Sie unter Regionen und Zonen.
Go-Sprache einrichten
In diesem Codelab verwenden wir Go für den gesamten Quellcode. Führen Sie in Cloud Shell den folgenden Befehl aus und prüfen Sie, ob die Go-Version 1.17 oder höher ist.
go version
Befehlsausgabe
go version go1.18.3 linux/amd64
Google Kubernetes-Cluster einrichten
In diesem Codelab führen Sie einen Cluster von Mikrodiensten in der Google Kubernetes Engine (GKE) aus. So läuft dieses Codelab ab:
- Referenzprojekt in Cloud Shell herunterladen
- Mikrodienste in Containern erstellen
- Container in die Google Artifact Registry (GAR) hochladen
- Container in GKE bereitstellen
- Quellcode von Diensten für die Ablaufverfolgung ändern
- Zu Schritt 2
Kubernetes Engine aktivieren
Zuerst richten wir einen Kubernetes-Cluster ein, in dem Shakesapp auf GKE ausgeführt wird. Dazu müssen wir GKE aktivieren. Rufen Sie das Menü „Kubernetes Engine“ auf und klicken Sie auf die Schaltfläche AKTIVIEREN.
Jetzt können Sie einen Kubernetes-Cluster erstellen.
Kubernetes-Cluster erstellen
Führen Sie in Cloud Shell den folgenden Befehl aus, um einen Kubernetes-Cluster zu erstellen: Prüfen Sie, ob der Zonenwert zur Region gehört, die Sie für die Erstellung des Artifact Registry-Repositories verwenden. Ändern Sie den Zonenwert us-central1-f
, wenn Ihre Repository-Region die Zone nicht abdeckt.
gcloud container clusters create otel-trace-codelab2 \ --zone us-central1-f \ --release-channel rapid \ --preemptible \ --enable-autoscaling \ --max-nodes 8 \ --no-enable-ip-alias \ --scopes cloud-platform
Befehlsausgabe
Note: Your Pod address range (`--cluster-ipv4-cidr`) can accommodate at most 1008 node(s). Creating cluster otel-trace-codelab2 in us-central1-f... Cluster is being health-checked (master is healthy)...done. Created [https://container.googleapis.com/v1/projects/development-215403/zones/us-central1-f/clusters/otel-trace-codelab2]. To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/workload_/gcloud/us-central1-f/otel-trace-codelab2?project=development-215403 kubeconfig entry generated for otel-trace-codelab2. NAME: otel-trace-codelab2 LOCATION: us-central1-f MASTER_VERSION: 1.23.6-gke.1501 MASTER_IP: 104.154.76.89 MACHINE_TYPE: e2-medium NODE_VERSION: 1.23.6-gke.1501 NUM_NODES: 3 STATUS: RUNNING
Artifact Registry und skaffold einrichten
Jetzt haben wir einen Kubernetes-Cluster, der für die Bereitstellung bereit ist. Als Nächstes bereiten wir eine Container Registry für das Pushen und Bereitstellen von Containern vor. Für diese Schritte müssen wir eine Artifact Registry (GAR) einrichten und skaffold verwenden.
Artifact Registry einrichten
Rufen Sie das Menü „Artifact Registry“ auf und drücken Sie die Schaltfläche AKTIVIEREN.
Nach einigen Augenblicken wird der Repository-Browser von GAR angezeigt. Klicken Sie auf die Schaltfläche „REPOSITORY ERSTELLLEN“ und geben Sie den Namen des Repositorys ein.
In diesem Codelab nenne ich das neue Repository trace-codelab
. Das Format des Artefakts ist „Docker“ und der Standorttyp ist „Region“. Wählen Sie die Region aus, die der von Ihnen für die Google Compute Engine-Standardzone festgelegten Region am nächsten ist. In diesem Beispiel wurde oben „us-central1-f“ ausgewählt. Hier wählen wir also „us-central1 (Iowa)“ aus. Klicken Sie dann auf die Schaltfläche „ERSTELLEN“.
Im Repository-Browser wird jetzt „trace-codelab“ angezeigt.
Wir kommen später noch einmal darauf zurück, um den Registry-Pfad zu prüfen.
Skaffold einrichten
Skaffold ist ein praktisches Tool, wenn Sie Mikrodienste erstellen, die in Kubernetes ausgeführt werden. Es übernimmt den Workflow zum Erstellen, Übertragen und Bereitstellen von Anwendungscontainern mit nur wenigen Befehlen. Skaffold verwendet standardmäßig Docker Registry als Container-Registry. Sie müssen Skaffold also so konfigurieren, dass es GAR beim Pushen von Containern erkennt.
Öffnen Sie Cloud Shell noch einmal und prüfen Sie, ob skaffold installiert ist. (In Cloud Shell wird Skaffold standardmäßig in der Umgebung installiert.) Führen Sie den folgenden Befehl aus, um die skaffold-Version zu sehen.
skaffold version
Befehlsausgabe
v1.38.0
Jetzt können Sie das Standard-Repository für skaffold registrieren. Rufen Sie das Artifact Registry-Dashboard auf und klicken Sie auf den Namen des Repositorys, das Sie im vorherigen Schritt eingerichtet haben.
Oben auf der Seite werden dann Navigationspfade angezeigt. Klicken Sie auf das Symbol , um den Registrypfad in die Zwischenablage zu kopieren.
Wenn Sie auf die Schaltfläche „Kopieren“ klicken, wird unten im Browser ein Dialogfeld mit der Meldung angezeigt:
„us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab“ wurde kopiert
Kehren Sie zu Cloud Shell zurück. Führen Sie den Befehl skaffold config set default-repo
mit dem Wert aus, den Sie gerade aus dem Dashboard kopiert haben.
skaffold config set default-repo us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab
Befehlsausgabe
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
Außerdem müssen Sie die Registry für die Docker-Konfiguration konfigurieren. Führen Sie dazu diesen Befehl aus:
gcloud auth configure-docker us-central1-docker.pkg.dev --quiet
Befehlsausgabe
{ "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
Sie können jetzt mit dem nächsten Schritt fortfahren, um einen Kubernetes-Container in GKE einzurichten.
Zusammenfassung
In diesem Schritt richten Sie Ihre Codelab-Umgebung ein:
- Cloud Shell einrichten
- Artifact Registry-Repository für die Containerregistrierung erstellt
- Skaffold für die Verwendung der Container Registry einrichten
- Kubernetes-Cluster erstellt, in dem die Codelab-Mikrodienste ausgeführt werden
Nächster Artikel
Im nächsten Schritt erstellen, pushen und stellen Sie Ihre Mikrodienste im Cluster bereit.
3. Mikrodienste erstellen, übertragen und bereitstellen
Codelab-Materialien herunterladen
Im vorherigen Schritt haben wir alle Voraussetzungen für dieses Codelab eingerichtet. Jetzt können Sie ganze Mikrodienste darauf ausführen. Das Codelab-Material wird auf GitHub gehostet. Laden Sie es mit dem folgenden Git-Befehl in die Cloud Shell-Umgebung herunter.
cd ~ git clone https://github.com/ymotongpoo/opentelemetry-trace-codelab-go.git cd opentelemetry-trace-codelab-go
Die Verzeichnisstruktur des Projekts sieht so aus:
. ├── README.md ├── step0 │ ├── manifests │ ├── proto │ ├── skaffold.yaml │ └── src ├── step1 │ ├── manifests │ ├── proto │ ├── skaffold.yaml │ └── src ├── step2 │ ├── manifests │ ├── proto │ ├── skaffold.yaml │ └── src ├── step3 │ ├── manifests │ ├── proto │ ├── skaffold.yaml │ └── src ├── step4 │ ├── manifests │ ├── proto │ ├── skaffold.yaml │ └── src ├── step5 │ ├── manifests │ ├── proto │ ├── skaffold.yaml │ └── src └── step6 ├── manifests ├── proto ├── skaffold.yaml └── src
- manifests: Kubernetes-Manifestdateien
- proto: Proto-Definition für die Kommunikation zwischen Client und Server
- src: Verzeichnisse für den Quellcode der einzelnen Dienste
- skaffold.yaml: Konfigurationsdatei für Skaffold
In diesem Codelab aktualisieren Sie den Quellcode im Ordner step0
. Die Antworten auf die folgenden Schritte finden Sie auch im Quellcode in den step[1-6]
-Ordnern. (Teil 1 umfasst die Schritte 0 bis 4 und Teil 2 die Schritte 5 und 6)
Skaffold-Befehl ausführen
Jetzt können Sie Inhalte erstellen, per Push übertragen und im gerade erstellten Kubernetes-Cluster bereitstellen. Das klingt, als würde es mehrere Schritte umfassen, aber in Wirklichkeit erledigt skaffold alles für Sie. Versuchen wir das mit dem folgenden Befehl:
cd step0 skaffold dev
Sobald Sie den Befehl ausführen, sehen Sie die Protokollausgabe von docker build
und können bestätigen, dass die Dateien erfolgreich an die Registry gesendet wurden.
Befehlsausgabe
... ---> 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
Nach dem Push aller Dienstcontainer werden Kubernetes-Bereitstellungen automatisch gestartet.
Befehlsausgabe
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
Nach der Bereitstellung sehen Sie die tatsächlichen Anwendungsprotokolle, die in jedem Container an stdout gesendet wurden, in etwa so:
Befehlsausgabe
[client] 2022/07/14 06:33:15 {"match_count":3040} [loadgen] 2022/07/14 06:33:15 query 'love': matched 3040 [client] 2022/07/14 06:33:15 {"match_count":3040} [loadgen] 2022/07/14 06:33:15 query 'love': matched 3040 [client] 2022/07/14 06:33:16 {"match_count":3040} [loadgen] 2022/07/14 06:33:16 query 'love': matched 3040 [client] 2022/07/14 06:33:19 {"match_count":463} [loadgen] 2022/07/14 06:33:19 query 'tear': matched 463 [loadgen] 2022/07/14 06:33:20 query 'world': matched 728 [client] 2022/07/14 06:33:20 {"match_count":728} [client] 2022/07/14 06:33:22 {"match_count":463} [loadgen] 2022/07/14 06:33:22 query 'tear': matched 463
An dieser Stelle sollten Sie alle Nachrichten vom Server sehen. OK, Sie können jetzt mit der Instrumentierung Ihrer Anwendung mit OpenTelemetry für die verteilte Nachverfolgung der Dienste beginnen.
Bevor Sie mit der Instrumentierung des Dienstes beginnen, fahren Sie Ihren Cluster mit Strg + C herunter.
Befehlsausgabe
... [client] 2022/07/14 06:34:57 {"match_count":1} [loadgen] 2022/07/14 06:34:57 query 'what's past is prologue': matched 1 ^CCleaning up... - W0714 06:34:58.464305 28078 gcp.go:120] WARNING: the gcp auth plugin is deprecated in v1.22+, unavailable in v1.25+; use gcloud instead. - To learn more, consult https://cloud.google.com/blog/products/containers-kubernetes/kubectl-auth-changes-in-gke - deployment.apps "clientservice" deleted - service "clientservice" deleted - deployment.apps "loadgen" deleted - deployment.apps "serverservice" deleted - service "serverservice" deleted
Zusammenfassung
In diesem Schritt haben Sie das Codelab-Material in Ihrer Umgebung vorbereitet und bestätigt, dass skaffold wie erwartet ausgeführt wird.
Nächster Artikel
Im nächsten Schritt ändern Sie den Quellcode des loadgen-Dienstes, um die Trace-Informationen zu instrumentieren.
4. Instrumentierung für HTTP
Konzept der Trace-Instrumentierung und ‑Ausbreitung
Bevor wir den Quellcode bearbeiten, möchte ich Ihnen anhand eines einfachen Diagramms kurz erklären, wie verteilte Traces funktionieren.
In diesem Beispiel instrumentieren wir den Code, um Trace- und Span-Informationen nach Cloud Trace zu exportieren und den Trace-Kontext über die Anfrage vom Loadgen-Dienst an den Serverdienst weiterzugeben.
Anwendungen müssen Trace-Metadaten wie Trace-ID und Span-ID senden, damit Cloud Trace alle Spans mit derselben Trace-ID in einem Trace zusammenführen kann. Außerdem muss die Anwendung Trace-Kontexte (die Kombination aus Trace-ID und Span-ID der übergeordneten Span) bei der Anforderung von Downstream-Diensten weitergeben, damit diese wissen, welchen Trace-Kontext sie verarbeiten.
OpenTelemetry bietet folgende Vorteile:
- um eindeutige Trace-IDs und Span-IDs zu generieren
- Trace-ID und Span-ID in das Backend exportieren
- Trace-Kontexte an andere Dienste weitergeben
- um zusätzliche Metadaten einzubetten, die bei der Analyse von Traces helfen
Komponenten in OpenTelemetry-Traces
So instrumentieren Sie einen Anwendungs-Trace mit OpenTelemetry:
- Exporter erstellen
- Erstellen Sie einen TracerProvider, der den Exporter in 1 bindet, und legen Sie ihn als global fest.
- TextMapPropagaror festlegen, um die Übertragungsmethode festzulegen
- Tracer vom TracerProvider abrufen
- Span aus dem Tracer generieren
Im Moment müssen Sie die detaillierten Eigenschaften der einzelnen Komponenten nicht kennen. Am wichtigsten sind folgende Punkte:
- Der Exporteur hier ist in TracerProvider integrierbar.
- TracerProvider enthält die gesamte Konfiguration für die Stichprobenerhebung und den Export von Traces.
- Alle Traces sind im Tracer-Objekt gebündelt.
Nachdem Sie das verstanden haben, können wir mit dem Codieren beginnen.
Erste Spanne des Instruments
Instrument Load Generator Service
Öffnen Sie den Cloud Shell-Editor, indem Sie rechts oben in Cloud Shell auf die Schaltfläche klicken. Öffnen Sie
step0/src/loadgen/main.go
im Explorer im linken Bereich und suchen Sie nach der Hauptfunktion.
step0/src/loadgen/main.go
func main() { ... for range t.C { log.Printf("simulating client requests, round %d", i) if err := run(numWorkers, numConcurrency); err != nil { log.Printf("aborted round with error: %v", err) } log.Printf("simulated %d requests", numWorkers) if numRounds != 0 && i > numRounds { break } i++ } }
In der Hauptfunktion sehen Sie die Schleife, in der die Funktion run
aufgerufen wird. In der aktuellen Implementierung enthält der Abschnitt zwei Logzeilen, in denen der Beginn und das Ende des Funktionsaufrufs aufgezeichnet werden. Jetzt erfassen wir Informationen zu Intervallen, um die Latenz des Funktionsaufrufs zu erfassen.
Wie im vorherigen Abschnitt erwähnt, richten wir zuerst die gesamten Konfigurationen für OpenTelemetry ein. So fügen Sie OpenTelemetry-Pakete hinzu:
step0/src/loadgen/main.go
import ( "context" // step1. add packages "encoding/json" "fmt" "io" "log" "math/rand" "net/http" "net/url" "time" // step1. add packages "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/propagation" sdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.10.0" "go.opentelemetry.io/otel/trace" // step1. end add packages )
Aus Gründen der besseren Lesbarkeit erstellen wir eine Einrichtungsfunktion namens initTracer
und rufen sie in der Funktion main
auf.
step0/src/loadgen/main.go
// step1. add OpenTelemetry initialization function func initTracer() (*sdktrace.TracerProvider, error) { // create a stdout exporter to show collected spans out to stdout. exporter, err := stdout.New(stdout.WithPrettyPrint()) if err != nil { return nil, err } // for the demonstration, we use AlwaysSmaple sampler to take all spans. // do not use this option in production. tp := sdktrace.NewTracerProvider( sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithBatcher(exporter), ) otel.SetTracerProvider(tp) otel.SetTextMapPropagator(propagation.TraceContext{}) return tp, nil }
Die Einrichtung von OpenTelemetry erfolgt wie im vorherigen Abschnitt beschrieben. In dieser Implementierung verwenden wir einen stdout
-Exporter, der alle Trace-Informationen in einem strukturierten Format in den Standardausgabepuffer exportiert.
Anschließend rufen Sie sie über die Hauptfunktion auf. Rufen Sie die initTracer()
auf und vergessen Sie nicht, die TracerProvider.Shutdown()
aufzurufen, wenn Sie die Anwendung schließen.
step0/src/loadgen/main.go
func main() { // step1. setup OpenTelemetry tp, err := initTracer() if err != nil { log.Fatalf("failed to initialize TracerProvider: %v", err) } defer func() { if err := tp.Shutdown(context.Background()); err != nil { log.Fatalf("error shutting down TracerProvider: %v", err) } }() // step1. end setup log.Printf("starting worder with %d workers in %d concurrency", numWorkers, numConcurrency) log.Printf("number of rounds: %d (0 is inifinite)", numRounds) ...
Nach Abschluss der Einrichtung müssen Sie eine Span mit einer eindeutigen Trace-ID und Span-ID erstellen. OpenTelemetry bietet eine praktische Bibliothek dafür. Fügen Sie dem instrumentierten HTTP-Client zusätzliche neue Pakete hinzu.
step0/src/loadgen/main.go
import ( "context" "encoding/json" "fmt" "io" "log" "math/rand" "net/http" "net/http/httptrace" // step1. add packages "net/url" "time" // step1. add packages "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" // step1. end add packages "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/propagation" sdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.10.0" "go.opentelemetry.io/otel/trace" )
Da der Load-Generator den Clientdienst in HTTP mit net/http
in der Funktion runQuery
aufruft, verwenden wir das contrib-Paket für net/http
und aktivieren die Instrumentierung mit der Erweiterung des httptrace
- und otelhttp
-Pakets.
Zuerst wird die globale Paketvariable „httpClient“ hinzugefügt, um HTTP-Anfragen über den instrumentierten Client aufzurufen.
step0/src/loadgen/main.go
var httpClient = http.Client{ Transport: otelhttp.NewTransport(http.DefaultTransport) }
Fügen Sie als Nächstes in der Funktion runQuery
Instrumentierung hinzu, um den benutzerdefinierten Span mit OpenTelemetry und dem automatisch generierten Span aus dem benutzerdefinierten HTTP-Client zu erstellen. Sie gehen so vor:
- Tracer von globaler
TracerProvider
mitotel.Tracer()
- Stammspanne mit der Methode
Tracer.Start()
erstellen - Die Wurzelspanne zu einem beliebigen Zeitpunkt beenden (in diesem Fall am Ende der
runQuery
-Funktion)
step0/src/loadgen/main.go
reqURL.RawQuery = v.Encode() // step1. replace http.Get() with custom client call // resp, err := http.Get(reqURL.String()) // step1. instrument trace ctx := context.Background() tr := otel.Tracer("loadgen") ctx, span := tr.Start(ctx, "query.request", trace.WithAttributes( semconv.TelemetrySDKLanguageGo, semconv.ServiceNameKey.String("loadgen.runQuery"), attribute.Key("query").String(s), )) defer span.End() ctx = httptrace.WithClientTrace(ctx, otelhttptrace.NewClientTrace(ctx)) req, err := http.NewRequestWithContext(ctx, "GET", reqURL.String(), nil) if err != nil { return -1, fmt.Errorf("error creating HTTP request object: %v", err) } resp, err := httpClient.Do(req) // step1. end instrumentation if err != nil { return -1, fmt.Errorf("error sending request to %v: %v", reqURL.String(), err) }
Jetzt sind Sie mit der Instrumentierung in loadgen (HTTP-Clientanwendung) fertig. Aktualisieren Sie go.mod
und go.sum
mit dem Befehl go mod
.
go mod tidy
Kundenservice für Instrument
Im vorherigen Abschnitt haben wir den Teil, der in der Abbildung unten im roten Rechteck eingeschlossen ist, instrumentiert. Wir haben Informationen zu Zeitspannen im Load-Generatordienst erfasst. Ähnlich wie beim Load-Generatordienst müssen wir jetzt den Clientdienst instrumentieren. Der Unterschied zum Load-Generator-Dienst besteht darin, dass der Clientdienst Informationen zur Trace-ID extrahieren muss, die vom Load-Generator-Dienst im HTTP-Header übertragen wurden, und die ID zum Generieren von Spans verwenden muss.
Öffnen Sie den Cloud Shell-Editor und fügen Sie die erforderlichen Pakete hinzu, wie wir es für den Load-Generatordienst getan haben.
step0/src/client/main.go
import ( "context" "encoding/json" "fmt" "io" "log" "net/http" "net/url" "os" "time" "opentelemetry-trace-codelab-go/client/shakesapp" // step1. add new import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/propagation" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/trace" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" // step1. end new import )
Wir müssen noch einmal OpenTelemetry einrichten. Kopieren Sie einfach die initTracer
-Funktion aus loadgen und fügen Sie sie auch in die main
-Funktion des Client-Dienstes ein.
step0/src/client/main.go
// step1. add OpenTelemetry initialization function func initTracer() (*sdktrace.TracerProvider, error) { // create a stdout exporter to show collected spans out to stdout. exporter, err := stdout.New(stdout.WithPrettyPrint()) if err != nil { return nil, err } // for the demonstration, we use AlwaysSmaple sampler to take all spans. // do not use this option in production. tp := sdktrace.NewTracerProvider( sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithBatcher(exporter), ) otel.SetTracerProvider(tp) otel.SetTextMapPropagator(propagation.TraceContext{}) return tp, nil }
Jetzt ist es an der Zeit, Spannweiten zu erfassen. Da der Clientdienst HTTP-Anfragen vom loadgen-Dienst akzeptieren muss, muss er den Handler instrumentieren. Der HTTP-Server im Clientdienst wird mit net/http implementiert. Sie können das otelhttp
-Paket verwenden, wie wir es in loadgen getan haben.
Ersetzen wir zuerst die Handlerregistrierung durch den otelhttp
-Handler. Suchen Sie in der Funktion main
nach den Zeilen, in denen der HTTP-Handler bei http.HandleFunc()
registriert ist.
step0/src/client/main.go
// step1. change handler to intercept OpenTelemetry related headers // http.HandleFunc("/", svc.handler) otelHandler := otelhttp.NewHandler(http.HandlerFunc(svc.handler), "client.handler") http.Handle("/", otelHandler) // step1. end intercepter setting http.HandleFunc("/_genki", svc.health)
Anschließend erfassen wir die eigentliche Spanne im Handler. Suchen Sie nach „func (*clientService) handler()“ und fügen Sie mit trace.SpanFromContext()
eine Span-Instrumentierung hinzu.
step0/src/client/main.go
func (cs *clientService) handler(w http.ResponseWriter, r *http.Request) { ... ctx := r.Context() ctx, cancel := context.WithCancel(ctx) defer cancel() // step1. instrument trace span := trace.SpanFromContext(ctx) defer span.End() // step1. end instrument ...
Mit dieser Instrumentierung erhalten Sie die Zeitspannen vom Anfang bis zum Ende der handler
-Methode. Damit sich die Spans leichter analysieren lassen, fügen Sie der Abfrage ein zusätzliches Attribut hinzu, in dem die Anzahl der Übereinstimmungen gespeichert wird. Fügen Sie direkt vor der Protokollzeile den folgenden Code hinzu.
func (cs *clientService) handler(w http.ResponseWriter, r *http.Request) { ... // step1. add span specific attribute span.SetAttributes(attribute.Key("matched").Int64(resp.MatchCount)) // step1. end adding attribute log.Println(string(ret)) ...
Mit all den oben genannten Instrumenten haben Sie die Trace-Instrumentierung zwischen loadgen und Client abgeschlossen. In den folgenden Abschnitten erfahren Sie, welche Vorteile Android für Ihr BYOD-Programm bietet. Führen Sie den Code noch einmal mit skaffold aus.
skaffold dev
Nach einiger Zeit, in der die Dienste im GKE-Cluster ausgeführt wurden, sehen Sie eine große Anzahl von Protokollmeldungen wie diese:
Befehlsausgabe
[loadgen] { [loadgen] "Name": "query.request", [loadgen] "SpanContext": { [loadgen] "TraceID": "cfa22247a542beeb55a3434392d46b89", [loadgen] "SpanID": "18b06404b10c418b", [loadgen] "TraceFlags": "01", [loadgen] "TraceState": "", [loadgen] "Remote": false [loadgen] }, [loadgen] "Parent": { [loadgen] "TraceID": "00000000000000000000000000000000", [loadgen] "SpanID": "0000000000000000", [loadgen] "TraceFlags": "00", [loadgen] "TraceState": "", [loadgen] "Remote": false [loadgen] }, [loadgen] "SpanKind": 1, [loadgen] "StartTime": "2022-07-14T13:13:36.686751087Z", [loadgen] "EndTime": "2022-07-14T13:14:31.849601964Z", [loadgen] "Attributes": [ [loadgen] { [loadgen] "Key": "telemetry.sdk.language", [loadgen] "Value": { [loadgen] "Type": "STRING", [loadgen] "Value": "go" [loadgen] } [loadgen] }, [loadgen] { [loadgen] "Key": "service.name", [loadgen] "Value": { [loadgen] "Type": "STRING", [loadgen] "Value": "loadgen.runQuery" [loadgen] } [loadgen] }, [loadgen] { [loadgen] "Key": "query", [loadgen] "Value": { [loadgen] "Type": "STRING", [loadgen] "Value": "faith" [loadgen] } [loadgen] } [loadgen] ], [loadgen] "Events": null, [loadgen] "Links": null, [loadgen] "Status": { [loadgen] "Code": "Unset", [loadgen] "Description": "" [loadgen] }, [loadgen] "DroppedAttributes": 0, [loadgen] "DroppedEvents": 0, [loadgen] "DroppedLinks": 0, [loadgen] "ChildSpanCount": 5, [loadgen] "Resource": [ [loadgen] { [loadgen] "Key": "service.name", [loadgen] "Value": { [loadgen] "Type": "STRING", [loadgen] "Value": "unknown_service:loadgen" ...
Diese Nachrichten werden vom stdout
-Exporteur gesendet. Sie sehen, dass die übergeordneten Elemente aller Spans von loadgen TraceID: 00000000000000000000000000000000
haben, da dies der Stamm-Span ist, also der erste Span im Trace. Außerdem sehen Sie, dass das Embed-Attribut "query"
den Suchstring enthält, der an den Client-Dienst übergeben wird.
Zusammenfassung
In diesem Schritt haben Sie den Load-Generatordienst und den Clientdienst instrumentiert, die über HTTP kommunizieren, und bestätigt, dass Sie den Trace-Kontext erfolgreich über Dienste hinweg weitergeben und Span-Informationen aus beiden Diensten in stdout exportieren konnten.
Nächster Artikel
Im nächsten Schritt instrumentieren Sie den Client- und den Serverdienst, um zu prüfen, wie der Trace-Kontext über gRPC weitergegeben wird.
5. Instrumentierung für gRPC
Im vorherigen Schritt haben wir die erste Hälfte der Anfrage in diesen Mikrodiensten instrumentiert. In diesem Schritt versuchen wir, die gRPC-Kommunikation zwischen Client- und Serverdienst zu instrumentieren. (Grünes und violettes Rechteck in der Abbildung unten)
Vor dem Build ausgeführte Instrumentierung für gRPC-Client
Das OpenTelemetry-Ökosystem bietet viele praktische Bibliotheken, die Entwicklern bei der Instrumentierung von Anwendungen helfen. Im vorherigen Schritt haben wir Pre-Build-Instrumentierung für das net/http
-Paket verwendet. In diesem Schritt versuchen wir, den Trace-Kontext über gRPC zu übertragen, und verwenden dazu die Bibliothek.
Zuerst importieren Sie das vorkonfigurierte gRPC-Paket otelgrpc
.
step0/src/client/main.go
import ( "context" "encoding/json" "fmt" "io" "log" "net/http" "net/url" "os" "time" "opentelemetry-trace-codelab-go/client/shakesapp" // step2. add prebuilt gRPC package (otelgrpc) "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/propagation" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/trace" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" )
Diesmal ist der Clientdienst ein gRPC-Client für den Serverdienst. Sie müssen also den gRPC-Client instrumentieren. Suchen Sie die Funktion mustConnGRPC
und fügen Sie gRPC-Interceptors hinzu, die jedes Mal, wenn der Client Anfragen an den Server sendet, neue span-Elemente instrumentieren.
step0/src/client/main.go
// Helper function for gRPC connections: Dial and create client once, reuse. func mustConnGRPC(ctx context.Context, conn **grpc.ClientConn, addr string) { var err error // step2. add gRPC interceptor interceptorOpt := otelgrpc.WithTracerProvider(otel.GetTracerProvider()) *conn, err = grpc.DialContext(ctx, addr, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor(interceptorOpt)), grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor(interceptorOpt)), grpc.WithTimeout(time.Second*3), ) // step2: end adding interceptor if err != nil { panic(fmt.Sprintf("Error %s grpc: failed to connect %s", err, addr)) } }
Da Sie OpenTelemetry bereits im vorherigen Abschnitt eingerichtet haben, ist das nicht erforderlich.
Vorgefertigte Instrumentierung für gRPC-Server
Wie beim gRPC-Client rufen wir vordefinierte Instrumentierung für den gRPC-Server auf. Fügen Sie dem Importabschnitt ein neues Paket hinzu, z. B.:
step0/src/server/main.go
import ( "context" "fmt" "io/ioutil" "log" "net" "os" "regexp" "strings" "opentelemetry-trace-codelab-go/server/shakesapp" "cloud.google.com/go/storage" // step2. add OpenTelemetry packages including otelgrpc "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" "go.opentelemetry.io/otel" stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/propagation" sdktrace "go.opentelemetry.io/otel/sdk/trace" "google.golang.org/api/iterator" "google.golang.org/api/option" "google.golang.org/grpc" healthpb "google.golang.org/grpc/health/grpc_health_v1" )
Da der Server zum ersten Mal instrumentiert wird, müssen Sie zuerst OpenTelemetry einrichten, ähnlich wie wir es für Loadgen und Clientdienste getan haben.
step0/src/server/main.go
// step2. add OpenTelemetry initialization function func initTracer() (*sdktrace.TracerProvider, error) { // create a stdout exporter to show collected spans out to stdout. exporter, err := stdout.New(stdout.WithPrettyPrint()) if err != nil { return nil, err } // for the demonstration, we use AlwaysSmaple sampler to take all spans. // do not use this option in production. tp := sdktrace.NewTracerProvider( sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithBatcher(exporter), ) otel.SetTracerProvider(tp) otel.SetTextMapPropagator(propagation.TraceContext{}) return tp, nil } func main() { ... // step2. setup OpenTelemetry tp, err := initTracer() if err != nil { log.Fatalf("failed to initialize TracerProvider: %v", err) } defer func() { if err := tp.Shutdown(context.Background()); err != nil { log.Fatalf("error shutting down TracerProvider: %v", err) } }() // step2. end setup ...
Als Nächstes müssen Sie Server-Abfangprogramme hinzufügen. Suchen Sie in der Funktion main
nach der Stelle, an der grpc.NewServer()
aufgerufen wird, und fügen Sie der Funktion Interceptors hinzu.
step0/src/server/main.go
func main() { ... svc := NewServerService() // step2: add interceptor interceptorOpt := otelgrpc.WithTracerProvider(otel.GetTracerProvider()) srv := grpc.NewServer( grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor(interceptorOpt)), grpc.StreamInterceptor(otelgrpc.StreamServerInterceptor(interceptorOpt)), ) // step2: end adding interceptor shakesapp.RegisterShakespeareServiceServer(srv, svc) ...
Mikrodienst ausführen und den Trace prüfen
Führen Sie dann den geänderten Code mit dem skaffold-Befehl aus.
skaffold dev
Jetzt sehen Sie wieder eine Reihe von Span-Informationen auf stdout.
Befehlsausgabe
... [server] { [server] "Name": "shakesapp.ShakespeareService/GetMatchCount", [server] "SpanContext": { [server] "TraceID": "89b472f213a400cf975e0a0041649667", [server] "SpanID": "96030dbad0061b3f", [server] "TraceFlags": "01", [server] "TraceState": "", [server] "Remote": false [server] }, [server] "Parent": { [server] "TraceID": "89b472f213a400cf975e0a0041649667", [server] "SpanID": "cd90cc3859b73890", [server] "TraceFlags": "01", [server] "TraceState": "", [server] "Remote": true [server] }, [server] "SpanKind": 2, [server] "StartTime": "2022-07-14T14:05:55.74822525Z", [server] "EndTime": "2022-07-14T14:06:03.449258891Z", [server] "Attributes": [ ... [server] ], [server] "Events": [ [server] { [server] "Name": "message", [server] "Attributes": [ ... [server] ], [server] "DroppedAttributeCount": 0, [server] "Time": "2022-07-14T14:05:55.748235489Z" [server] }, [server] { [server] "Name": "message", [server] "Attributes": [ ... [server] ], [server] "DroppedAttributeCount": 0, [server] "Time": "2022-07-14T14:06:03.449255889Z" [server] } [server] ], [server] "Links": null, [server] "Status": { [server] "Code": "Unset", [server] "Description": "" [server] }, [server] "DroppedAttributes": 0, [server] "DroppedEvents": 0, [server] "DroppedLinks": 0, [server] "ChildSpanCount": 0, [server] "Resource": [ [server] { ... [server] ], [server] "InstrumentationLibrary": { [server] "Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc", [server] "Version": "semver:0.33.0", [server] "SchemaURL": "" [server] } [server] } ...
Sie stellen fest, dass Sie keine Span-Namen eingebettet und Spans manuell mit trace.Start()
oder span.SpanFromContext()
erstellt haben. Trotzdem erhalten Sie eine große Anzahl von Spans, da sie von den gRPC-Interceptors generiert wurden.
Zusammenfassung
In diesem Schritt haben Sie die gRPC-basierte Kommunikation mithilfe der OpenTelemetry-Bibliotheken instrumentiert.
Nächster Artikel
Im nächsten Schritt visualisieren Sie den Trace mit Cloud Trace und erfahren, wie Sie die erfassten Spans analysieren.
6. Trace mit Cloud Trace visualisieren
Sie haben Traces im gesamten System mit OpenTelemetry instrumentiert. Sie haben bereits gelernt, wie Sie HTTP- und gRPC-Dienste instrumentieren. Sie haben zwar gelernt, wie Sie sie erfassen, aber nicht, wie Sie sie analysieren. In diesem Abschnitt ersetzen Sie stdout-Exporte durch Cloud Trace-Exporte und erfahren, wie Sie Ihre Traces analysieren.
Cloud Trace Exporter verwenden
Eine der leistungsstarken Eigenschaften von OpenTelemetry ist seine Pluggbarkeit. Wenn Sie alle von Ihrer Instrumentierung erfassten Spans visualisieren möchten, müssen Sie lediglich den stdout-Exporter durch den Cloud Trace-Exporter ersetzen.
Öffnen Sie die main.go
-Dateien der einzelnen Dienste und suchen Sie nach der initTracer()
-Funktion. Löschen Sie die Zeile, um einen stdout-Exporter zu generieren, und erstellen Sie stattdessen einen Cloud Trace-Exporter.
step0/src/loadgen/main.go
import ( ... // step3. add OpenTelemetry for Cloud Trace package cloudtrace "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace" ) // step1. add OpenTelemetry initialization function func initTracer() (*sdktrace.TracerProvider, error) { // step3. replace stdout exporter with Cloud Trace exporter // cloudtrace.New() finds the credentials to Cloud Trace automatically following the // rules defined by golang.org/x/oauth2/google.findDefaultCredentailsWithParams. // https://pkg.go.dev/golang.org/x/oauth2/google#FindDefaultCredentialsWithParams exporter, err := cloudtrace.New() // step3. end replacing exporter if err != nil { return nil, err } // for the demonstration, we use AlwaysSmaple sampler to take all spans. // do not use this option in production. tp := sdktrace.NewTracerProvider( sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithBatcher(exporter), ) otel.SetTracerProvider(tp) otel.SetTextMapPropagator(propagation.TraceContext{}) return tp, nil }
Sie müssen dieselbe Funktion auch im Client- und Serverdienst bearbeiten.
Mikrodienst ausführen und den Trace prüfen
Führen Sie den Cluster nach der Bearbeitung einfach wie gewohnt mit dem skaffold-Befehl aus.
skaffold dev
Jetzt sehen Sie nicht mehr viele Span-Informationen im strukturierten Protokollformat auf stdout, weil Sie den Exporter durch den Cloud Trace-Exporter ersetzt haben.
Befehlsausgabe
[loadgen] 2022/07/14 15:01:07 simulated 20 requests [loadgen] 2022/07/14 15:01:07 simulating client requests, round 37 [loadgen] 2022/07/14 15:01:14 query 'sweet': matched 958 [client] 2022/07/14 15:01:14 {"match_count":958} [client] 2022/07/14 15:01:14 {"match_count":3040} [loadgen] 2022/07/14 15:01:14 query 'love': matched 3040 [client] 2022/07/14 15:01:15 {"match_count":349} [loadgen] 2022/07/14 15:01:15 query 'hello': matched 349 [client] 2022/07/14 15:01:15 {"match_count":484} [loadgen] 2022/07/14 15:01:15 query 'faith': matched 484 [loadgen] 2022/07/14 15:01:15 query 'insolence': matched 14 [client] 2022/07/14 15:01:15 {"match_count":14} [client] 2022/07/14 15:01:21 {"match_count":484} [loadgen] 2022/07/14 15:01:21 query 'faith': matched 484 [client] 2022/07/14 15:01:21 {"match_count":728} [loadgen] 2022/07/14 15:01:21 query 'world': matched 728 [client] 2022/07/14 15:01:22 {"match_count":484} [loadgen] 2022/07/14 15:01:22 query 'faith': matched 484 [loadgen] 2022/07/14 15:01:22 query 'hello': matched 349 [client] 2022/07/14 15:01:22 {"match_count":349} [client] 2022/07/14 15:01:23 {"match_count":1036} [loadgen] 2022/07/14 15:01:23 query 'friend': matched 1036 [loadgen] 2022/07/14 15:01:28 query 'tear': matched 463 ...
Prüfen wir nun, ob alle Spans korrekt an Cloud Trace gesendet werden. Rufen Sie die Cloud Console auf und gehen Sie zu „Trace-Liste“. Sie können sie ganz einfach über das Suchfeld aufrufen. Alternativ können Sie auch auf das Menü im linken Bereich klicken.
Sie sehen dann viele blaue Punkte, die über die Latenzgrafik verteilt sind. Jeder Punkt steht für einen einzelnen Trace.
Klicken Sie auf eine davon, um die Details im Trace aufzurufen.
Selbst bei diesem einfachen ersten Blick haben Sie bereits viele Erkenntnisse gewonnen. Im Abfolgediagramm sehen Sie beispielsweise, dass die Ursache für die Latenz hauptsächlich auf die Spanne mit dem Namen shakesapp.ShakespeareService/GetMatchCount
zurückzuführen ist. (Siehe 1 in der Abbildung oben) Sie können das in der Zusammenfassungstabelle prüfen. In der rechten Spalte sehen Sie die Dauer der einzelnen Spannen. Außerdem bezieht sich dieser Trace auf die Suchanfrage „Freund“. (Siehe 2 in der Abbildung oben)
Anhand dieser kurzen Analysen stellen Sie möglicherweise fest, dass Sie detailliertere Zeiträume innerhalb der GetMatchCount
-Methode benötigen. Im Vergleich zu Informationen auf dem Standardausgabe-Gerät ist die Visualisierung leistungsstark. Weitere Informationen zu Cloud Trace finden Sie in der offiziellen Dokumentation.
Zusammenfassung
In diesem Schritt haben Sie den stdout-Exporter durch den Cloud Trace-Exporter ersetzt und Traces in Cloud Trace visualisiert. Außerdem haben Sie gelernt, wie Sie mit der Analyse der Traces beginnen.
Nächster Artikel
Im nächsten Schritt ändern Sie den Quellcode des Serverdiensts, um in „GetMatchCount“ eine untergeordnete Spanne hinzuzufügen.
7. Unterspanne für eine bessere Analyse hinzufügen
Im vorherigen Schritt haben Sie festgestellt, dass die Ursache für die von loadgen beobachtete RTT hauptsächlich der Prozess innerhalb der GetMatchCount-Methode, der gRPC-Handler, im Serverdienst ist. Da wir jedoch nur den Handler instrumentiert haben, können wir keine weiteren Informationen aus dem abfolgebasierten Diagramm ableiten. Das ist ein häufiger Fall, wenn wir mit der Instrumentierung von Mikrodiensten beginnen.
In diesem Abschnitt instrumentieren wir eine untergeordnete Spanne, in der der Server Google Cloud Storage aufruft. Es kommt häufig vor, dass externe Netzwerk-I/Os im Prozess viel Zeit in Anspruch nehmen, und es ist wichtig zu ermitteln, ob der Aufruf die Ursache dafür ist.
Unterspanne auf dem Server instrumentieren
Öffnen Sie main.go
auf dem Server und suchen Sie nach der Funktion readFiles
. Diese Funktion ruft eine Anfrage an Google Cloud Storage auf, um alle Textdateien von Shakespeare-Werken abzurufen. In dieser Funktion können Sie eine untergeordnete Schleife erstellen, ähnlich wie Sie es für die HTTP-Server-Instrumentierung im Clientdienst getan haben.
step0/src/server/main.go
func readFiles(ctx context.Context, bucketName, prefix string) ([]string, error) { type resp struct { s string err error } // step4: add an extra span span := trace.SpanFromContext(ctx) span.SetName("server.readFiles") span.SetAttributes(attribute.Key("bucketname").String(bucketName)) defer span.End() // step4: end add span ...
Das war's zum Hinzufügen einer neuen Span. Sehen wir uns an, wie es funktioniert, indem wir die App ausführen.
Mikrodienst ausführen und den Trace prüfen
Führen Sie den Cluster nach der Bearbeitung einfach wie gewohnt mit dem skaffold-Befehl aus.
skaffold dev
Wählen Sie aus der Liste der Traces einen Trace mit dem Namen query.request
aus. Es wird ein ähnliches Wasserfalldiagramm angezeigt, mit Ausnahme einer neuen Spanne unter shakesapp.ShakespeareService/GetMatchCount
. (Der Bereich, der unten durch das rote Rechteck begrenzt ist)
Aus diesem Diagramm geht hervor, dass der externe Aufruf von Google Cloud Storage einen großen Teil der Latenz verursacht, aber andere Faktoren tragen ebenfalls zum Großteil der Latenz bei.
Sie haben bereits viele Informationen aus nur wenigen Ansichten der Trace-Abfolgegrafik gewonnen. Wie erhalte ich weitere Leistungsdetails in meiner Anwendung? Hier kommt der Profiler ins Spiel. Lassen Sie uns dieses Codelab aber vorerst beenden und alle Profiler-Tutorials auf Teil 2 verschieben.
Zusammenfassung
In diesem Schritt haben Sie eine weitere Span im Serverdienst instrumentiert und weitere Informationen zur Systemlatenz erhalten.
8. Glückwunsch
Sie haben verteilte Traces mit OpenTelemetry erstellt und Anfragelatenzen im gesamten Mikrodienst in Google Cloud Trace bestätigt.
Für erweiterte Übungen können Sie die folgenden Themen selbst ausprobieren.
- Bei der aktuellen Implementierung werden alle von der Systemdiagnose generierten SPANs gesendet. (
grpc.health.v1.Health/Check
) Wie filtern Sie diese Spans aus Cloud Traces heraus? Hier finden Sie einen Tipp. - Ereignisprotokolle mit Spans in Google Cloud Trace und Google Cloud Logging korrelieren Hier finden Sie einen Tipp.
- Ersetzen Sie einen Dienst durch einen Dienst in einer anderen Sprache und versuchen Sie, ihn mit OpenTelemetry für diese Sprache zu instrumentieren.
Wenn Sie mehr über den Profiler erfahren möchten, fahren Sie mit Teil 2 fort. In diesem Fall können Sie den Abschnitt zur Bereinigung unten überspringen.
Aufräumen
Beenden Sie nach diesem Codelab den Kubernetes-Cluster und löschen Sie das Projekt, damit Ihnen keine unerwarteten Kosten für die Google Kubernetes Engine, Google Cloud Trace und Google Artifact Registry in Rechnung gestellt werden.
Löschen Sie zuerst den Cluster. Wenn Sie den Cluster mit skaffold dev
ausführen, müssen Sie nur Strg + C drücken. Wenn Sie den Cluster mit skaffold run
ausführen, führen Sie den folgenden Befehl aus:
skaffold delete
Befehlsausgabe
Cleaning up... - deployment.apps "clientservice" deleted - service "clientservice" deleted - deployment.apps "loadgen" deleted - deployment.apps "serverservice" deleted - service "serverservice" deleted
Nachdem Sie den Cluster gelöscht haben, wählen Sie im Menüband „IAM & Admin“ > „Einstellungen“ aus und klicken Sie dann auf die Schaltfläche „HERUNTERFAHREN“.
Geben Sie dann die Projekt-ID (nicht den Projektnamen) in das Formular im Dialogfeld ein und bestätigen Sie das Herunterfahren.