Informationen zu diesem Codelab
1. Einführung
Zuletzt aktualisiert:15.07.2022
Beobachtbarkeit der Anwendung
Beobachtbarkeit und OpenTelemetry
Beobachtbarkeit ist der Begriff, mit dem ein Attribut eines Systems beschrieben wird. Ein System mit Beobachtbarkeit ermöglicht es Teams, ihr System aktiv zu debuggen. In diesem Zusammenhang gibt es drei Säulen der Beobachtbarkeit: Logs, Messwerte und Traces sind die grundlegende Instrumentierung für das System, um Beobachtbarkeit zu erlangen.
OpenTelemetry besteht aus einer Reihe von Spezifikationen, Bibliotheken und Agents, die die Instrumentierung und den Export von Telemetriedaten (Logs, Messwerten und Traces) beschleunigen, die für die Beobachtbarkeit erforderlich sind. OpenTelemetry ist ein Projekt mit offenem Standard und einer Community im Rahmen von CNCF. Mithilfe von Bibliotheken, die im Rahmen des Projekts und seiner Umgebung bereitgestellt werden, können Entwickler ihre Anwendungen anbieterneutral und mit mehreren Architekturen instrumentieren.
Zusätzlich zu den drei Säulen der Beobachtbarkeit ist die kontinuierliche Profilerstellung eine weitere wichtige Komponente für die Beobachtbarkeit und sorgt für eine Erweiterung der Nutzerbasis in der Branche. Cloud Profiler gehört zu den Ursprüngen und bietet eine einfache Benutzeroberfläche, um die Leistungsmesswerte in den Anwendungsaufrufstapeln aufzuschlüsseln.
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.
Verteilter Trace
Bei Logs, Messwerten und Traces ist Trace die Telemetrie, die die Latenz eines bestimmten Prozessteils im System mitteilt. Besonders im Zeitalter der Mikrodienste ist verteiltes Trace der starke Faktor, um Latenzengpässe im gesamten verteilten System zu identifizieren.
Bei der Analyse verteilter Traces ist die Visualisierung von Trace-Daten der Schlüssel, um die Gesamtsystemlatenzen auf einen Blick zu erfassen. Beim verteilten Trace wird eine Reihe von Aufrufen verarbeitet, um eine einzelne Anfrage an den Systemeinstiegspunkt in Form eines Trace mit mehreren Spans zu verarbeiten.
Ein Span stellt eine einzelne Arbeitseinheit in einem verteilten System dar, die Start- und Endzeiten aufzeichnet. Spans haben häufig hierarchische Beziehungen untereinander. Im Bild unten sind alle kleineren Spans untergeordnete Spans eines großen /messages-Spans und werden in einem Trace zusammengefasst, der den Weg der Arbeit durch ein System zeigt.
Google Cloud Trace ist eine der Optionen für verteilte Trace-Back-Ends und ist gut in andere Produkte in Google Cloud eingebunden.
Inhalt
In diesem Codelab instrumentieren Sie Trace-Informationen in den Diensten „Shakespeare-Anwendung“ (auch als Shakesapp bezeichnet), die in einem Google Kubernetes Engine-Cluster ausgeführt wird. Die Architektur von Shakesapp sieht so aus:
- Loadgen sendet in HTTP einen Abfragestring an den Client.
- Clients übergeben die Abfrage von „loadgen“ an den Server in gRPC.
- Der Server akzeptiert die Anfrage vom Client, ruft alle Shakespare-Werke im Textformat aus Google Cloud Storage ab, durchsucht die Zeilen mit der Anfrage und gibt die Nummer der Zeile zurück, die dem Client entspricht
Sie instrumentieren die Trace-Informationen in der gesamten Anfrage. Danach betten Sie einen Profiler-Agent in den Server ein und untersuchen den Engpass.
Aufgaben in diesem Lab
- Erste Schritte mit den OpenTelemetry-Trace-Bibliotheken im Go-Projekt
- Span mit der Bibliothek erstellen
- Span-Kontexte zwischen Anwendungskomponenten auf der Leitungsverbindung 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 Verdeutlichung enthält dieses Beispiel nur drei Komponenten (Load Generator, Client und Server). Du kannst den Prozess, der in diesem Codelab erläutert wird, aber auch auf komplexere und größere Systeme anwenden.
Voraussetzungen
- Sie haben Grundkenntnisse in Go.
- Grundkenntnisse in Kubernetes
2. Einrichtung und Anforderungen
Umgebung für das selbstbestimmte Lernen einrichten
Wenn Sie noch kein Google-Konto (Gmail oder Google Apps) haben, müssen Sie eines 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 auf das Drop-down-Menü für die Projektauswahl oben links in der Konsole:
und klicken Sie auf „NEUES PROJEKT“, Schaltfläche zum Erstellen eines neuen Projekts:
Wenn Sie noch kein Projekt haben, sollten Sie ein Dialogfeld wie dieses sehen, um Ihr erstes zu erstellen:
Im nachfolgenden Dialog zur Projekterstellung können Sie die Details Ihres neuen Projekts eingeben:
Denken Sie an die Projekt-ID. Dies ist ein eindeutiger Name, der in allen Google Cloud-Projekten eindeutig ist. Der oben angegebene Name ist bereits vergeben und funktioniert leider nicht für Sie. Sie wird in diesem Codelab später als PROJECT_ID bezeichnet.
Falls noch nicht geschehen, müssen Sie als Nächstes in der Developers Console die Abrechnung aktivieren, um Google Cloud-Ressourcen nutzen und die Cloud Trace API aktivieren zu können.
Dieses Codelab sollte nicht mehr als ein paar Euro kosten. Wenn Sie sich jedoch dazu entschließen, mehr Ressourcen zu verwenden oder diese weiter auszuführen (siehe Abschnitt „Bereinigen“ am Ende dieses Dokuments), Die Preise für Google Cloud Trace, Google Kubernetes Engine und Google Artifact Registry finden Sie in der offiziellen Dokumentation.
- Preise für die Operations-Suite von Google Cloud | Operations-Suite
- Preise | Kubernetes Engine-Dokumentation
- Artifact Registry – Preise | Artifact Registry-Dokumentation
Neue Google Cloud Platform-Nutzer haben Anspruch auf eine kostenlose Testversion mit 300$Guthaben, wodurch das Codelab in der Regel kostenlos sein sollte.
Google Cloud Shell einrichten
Sie können Google Cloud und Google Cloud Trace per Fernzugriff von Ihrem Laptop aus bedienen. In diesem Codelab verwenden wir jedoch Google Cloud Shell, eine Befehlszeilenumgebung, die in der Cloud ausgeführt wird.
Diese Debian-basierte virtuelle Maschine verfügt über alle erforderlichen Entwicklungstools. Es bietet ein Basisverzeichnis mit 5 GB nichtflüchtigem Speicher und wird in Google Cloud ausgeführt. Dadurch werden die Netzwerkleistung und die Authentifizierung erheblich verbessert. Für dieses Codelab benötigen Sie also nur einen Browser – ja, er funktioniert auf Chromebooks.
Klicken Sie einfach auf „Cloud Shell aktivieren“ , um Cloud Shell über die Cloud Console zu aktivieren. Es dauert nur einen Moment, bis die Umgebung bereitgestellt und eine Verbindung hergestellt werden kann.
Sobald Sie mit Cloud Shell verbunden sind, sollten Sie sehen, dass Sie bereits authentifiziert sind und dass das Projekt bereits auf Ihre PROJECT_ID
eingestellt ist.
gcloud auth list
Befehlsausgabe
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
Befehlsausgabe
[core] project = <PROJECT_ID>
Sollte das Projekt aus irgendeinem Grund nicht eingerichtet sein, geben Sie einfach den folgenden Befehl ein:
gcloud config set project <PROJECT_ID>
Du suchst dein Gerät (PROJECT_ID
)? Sehen Sie nach, welche ID Sie bei den Einrichtungsschritten verwendet haben, oder rufen Sie sie im Dashboard der Cloud Console auf:
Cloud Shell legt außerdem standardmäßig einige Umgebungsvariablen fest, die bei der Ausführung zukünftiger Befehle nützlich sein können.
echo $GOOGLE_CLOUD_PROJECT
Befehlsausgabe
<PROJECT_ID>
Legen Sie schließlich 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 den folgenden Befehl in Cloud Shell 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 Google Kubernetes Engine (GKE) aus. In diesem Codelab werden folgende Schritte ausgeführt:
- Referenzprojekt in Cloud Shell herunterladen
- Mikrodienste in Container umwandeln
- Container in Google Artifact Registry (GAR) hochladen
- Container in GKE bereitstellen
- Quellcode von Diensten für die Trace-Instrumentierung ändern
- Zu Schritt 2
Kubernetes Engine aktivieren
Zuerst richten wir einen Kubernetes-Cluster ein, in dem Shakesapp in der GKE ausgeführt wird. Also müssen wir GKE aktivieren. Rufen Sie das Menü „Kubernetes Engine“ auf. und drücken Sie die Taste 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. Achten Sie darauf, dass sich der Zonenwert unter der Region befindet, die Sie für die Erstellung des Artifact Registry-Repositorys verwenden. Ändern Sie den Zonenwert us-central1-f
, wenn die 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 bereitgestellt werden kann. Als Nächstes bereiten wir eine Container Registry für das Push- und Bereitstellen von Containern vor. Für diese Schritte müssen wir eine Artifact Registry (GAR) und ein Skaffold einrichten, um es verwenden zu können.
Artifact Registry einrichten
Rufen Sie das Menü von „Artifact Registry“ auf und drücken Sie die Taste AKTIVIEREN.
Nach kurzer Zeit wird der Repository-Browser von GAR angezeigt. Klicken Sie auf „REPOSITORY ERSTELLEN“. 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 in der Nähe der Region aus, die Sie für die Google Compute Engine-Standardzone festgelegt haben. In diesem Beispiel ist „us-central1-f“ ausgewählt. Also wählen wir "us-central1 (Iowa)" aus. Klicken Sie dann auf „ERSTELLEN“, Schaltfläche.
Sie sehen jetzt „trace-codelab“ im Repository-Browser.
Wir werden später hierher zurückkehren, um den Registrierungspfad zu überprüfen.
Skaffold-Einrichtung
Skaffold ist ein praktisches Tool, wenn Sie mit der Erstellung von Mikrodiensten arbeiten, die auf Kubernetes ausgeführt werden. Er steuert den Workflow für das Erstellen, Übertragen und Bereitstellen von Anwendungscontainern mit wenigen Befehlen. Skaffold verwendet standardmäßig Docker Registry als Container Registry. Daher müssen Sie Skaffold so konfigurieren, dass GAR beim Hochladen von Containern erkannt wird.
Öffnen Sie Cloud Shell noch einmal und prüfen Sie, ob Skaffold installiert ist. Cloud Shell installiert Skaffold standardmäßig in der Umgebung. Führen Sie den folgenden Befehl aus, um die Skaffold-Version aufzurufen.
skaffold version
Befehlsausgabe
v1.38.0
Jetzt können Sie das Standard-Repository für Skaffold registrieren. Um den Registry-Pfad abzurufen, gehen Sie zum Artifact Registry-Dashboard und klicken Sie auf den Namen des Repositorys, das Sie gerade im vorherigen Schritt eingerichtet haben.
Daraufhin werden oben auf der Seite Navigationspfade angezeigt. Klicken Sie auf das Symbol , um den Registry-Pfad in die Zwischenablage zu kopieren.
Wenn Sie auf die Schaltfläche „Kopieren“ klicken, erscheint ein Dialogfeld unten im Browser mit einer Meldung wie:
"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
Jetzt können Sie einen Kubernetes-Container in GKE einrichten.
Zusammenfassung
In diesem Schritt richten Sie Ihre Codelab-Umgebung ein:
- Cloud Shell einrichten
- Artifact Registry-Repository für die Container Registry erstellt
- Skaffold für die Verwendung der Container Registry einrichten
- Es wurde ein Kubernetes-Cluster erstellt, in dem die Codelab-Mikrodienste ausgeführt werden
Nächstes Thema
Im nächsten Schritt erstellen Sie die Mikrodienste, übertragen sie per Push im Cluster und stellen sie bereit
3. Mikrodienste erstellen, per Push übertragen und bereitstellen
Codelab-Material herunterladen
Im vorherigen Schritt haben wir alle Voraussetzungen für dieses Codelab eingerichtet. Jetzt können Sie ganze Mikrodienste auf ihnen ausführen. Das Codelab-Material wird auf GitHub gehostet. Laden Sie es deshalb 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
- Manifeste: 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
. Sie können die Antworten auch in den folgenden Schritten im Quellcode in step[1-6]
-Ordnern finden. (Teil 1 umfasst Schritt 0 bis 4 und Teil 2 die Schritte 5 und 6.)
skaffold-Befehl ausführen
Schließlich können Sie den gesamten Inhalt erstellen, per Push-Funktion in den soeben erstellten Kubernetes-Cluster übertragen und bereitstellen. Das klingt, als würde er mehrere Schritte umfassen, aber im Prinzip erledigt Skaffold alles für Sie. Versuchen wir das mit dem folgenden Befehl:
cd step0 skaffold dev
Sobald Sie den Befehl ausführen, wird die Logausgabe von docker build
angezeigt und Sie können bestätigen, dass die Dateien erfolgreich in die Registry übertragen 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 der Übertragung aller Dienstcontainer werden Kubernetes-Deployments 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 werden die tatsächlichen Anwendungslogs, die an stdout ausgegeben wurden, in jedem Container so angezeigt:
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
Zu diesem Zeitpunkt möchten Sie alle Nachrichten vom Server sehen. Jetzt können Sie Ihre Anwendung mit OpenTelemetry instrumentieren, um verteiltes Tracing der Dienste zu ermöglichen.
Bevor Sie mit der Instrumentierung des Dienstes beginnen, fahren Sie den 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 die Skaffold-Ausführungen wie erwartet bestätigt.
Nächstes Thema
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 -Weitergabe
Bevor ich den Quellcode bearbeite, möchte ich kurz in einem einfachen Diagramm 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 die Trace-ID und die Span-ID senden, damit Cloud Trace alle Spans mit derselben Trace-ID in einem Trace zusammenfassen kann. Außerdem muss die Anwendung Trace-Kontexte (die Kombination aus Trace-ID und Span-ID des übergeordneten Spans) bei der Anforderung von nachgelagerten Diensten weitergeben, damit sie erkennen können, welchen Trace-Kontext sie verarbeiten.
OpenTelemetry bietet folgende Vorteile:
- zum Generieren einer eindeutigen Trace-ID und Span-ID
- um die Trace-ID und die Span-ID in das Back-End zu exportieren
- zur Weitergabe von Trace-Kontexten an andere Dienste
- zum Einbetten zusätzlicher Metadaten, die bei der Analyse von Traces helfen
Komponenten in OpenTelemetry Trace
So instrumentieren Sie den Anwendungs-Trace mit OpenTelemetry:
- Exporter erstellen
- Erstellen Sie eine TracerProvider-Bindung, die den Exporter in 1 bindet, und legen Sie sie global fest.
- TextMapPropagaror zum Festlegen der Verteilungsmethode festlegen
- Tracer von TracerProvider abrufen
- Span aus Tracer generieren
Derzeit müssen Sie sich nicht mit den detaillierten Eigenschaften der einzelnen Komponenten beschäftigen, aber das Wichtigste ist:
- der Exporter hier ist an TracerProvider anpassbar.
- TracerProvider enthält die gesamte Konfiguration in Bezug auf Trace Sampling und Export.
- Alle Traces sind im Tracer-Objekt gebündelt
Nachdem wir das verstanden haben, machen wir mit dem eigentlichen Programmieren weiter.
Erster Span instrumentieren
Lastgenerator-Dienst für Instrumente
Öffnen Sie den Cloud Shell-Editor, indem Sie oben rechts in Cloud Shell auf die Schaltfläche klicken. Öffnen Sie im Explorer im linken Bereich
step0/src/loadgen/main.go
und suchen Sie die 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, die die Funktion run
darin aufruft. In der aktuellen Implementierung enthält der Abschnitt zwei Logzeilen, in denen der Anfang und das Ende des Funktionsaufrufs aufgezeichnet werden. Instrumentieren wir nun Span-Informationen, um die Latenz des Funktionsaufrufs zu verfolgen.
Richten wir zunächst, wie im vorherigen Abschnitt erwähnt, 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 )
Zur besseren Lesbarkeit erstellen wir eine Einrichtungsfunktion mit dem Namen 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 }
Sie werden vielleicht feststellen, dass das Einrichten von OpenTelemetry so ist wie im vorherigen Abschnitt beschrieben. In dieser Implementierung verwenden wir einen stdout
-Exporter, der alle Trace-Informationen in einem strukturierten Format in die stdout-Datei exportiert.
Dann rufen Sie sie über die Funktion „main“ auf. Rufen Sie die initTracer()
auf und achten Sie darauf, dass die TracerProvider.Shutdown()
aufgerufen wird, 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) ...
Nachdem Sie die Einrichtung abgeschlossen haben, müssen Sie einen Span mit einer eindeutigen Trace-ID und einer Span-ID erstellen. OpenTelemetry bietet dafür eine praktische Bibliothek. Fügen Sie dem HTTP-Client des Zahlungsmittels 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 der Funktion net/http
in der runQuery
-Funktion aufruft, verwenden wir das contrib-Paket für net/http
und aktivieren die Instrumentierung mit der Erweiterung des Pakets httptrace
und otelhttp
.
Zuerst wird die globale Variable "httpClient" des Pakets 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
eine Instrumentierung hinzu, um den benutzerdefinierten Span mithilfe von OpenTelemetry und dem automatisch generierten Span aus dem benutzerdefinierten HTTP-Client zu erstellen. Sie werden Folgendes tun:
- Mit
otel.Tracer()
einen Tracer von der globalenTracerProvider
abrufen - Haupt-Span mit der Methode
Tracer.Start()
erstellen - Beenden Sie den Haupt-Span zu einem beliebigen Zeitpunkt (in diesem Fall das Ende der Funktion
runQuery
).
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
Clientdienst instrumentieren
Im vorherigen Abschnitt haben wir den Teil instrumentiert, der in der Zeichnung unten im roten Rechteck enthalten ist. Wir haben Span-Informationen im Load Generator-Dienst instrumentiert. Ähnlich wie beim Lastgenerator-Dienst müssen wir nun den Client-Dienst instrumentieren. Der Unterschied zum Load Generator-Dienst besteht darin, dass der Clientdienst Trace-ID-Informationen extrahieren muss, die vom Load Generator-Dienst im HTTP-Header weitergegeben werden, und die ID zum Generieren von Spans verwenden muss.
Öffnen Sie den Cloud Shell-Editor und fügen Sie die erforderlichen Pakete wie für den Load Generator-Dienst hinzu.
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 )
Auch hier muss OpenTelemtry eingerichtet werden. Kopieren Sie einfach die Funktion initTracer
aus „loadgen“, fügen Sie sie ein und rufen Sie sie auch in der Funktion main
des Clientdienstes auf.
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 Zeit, Spans zu instrumentieren. Da der Clientdienst HTTP-Anfragen vom Dienst „loadgen“ akzeptieren muss, muss der Handler instrumentiert werden. Der HTTP-Server im Clientdienst ist mit net/http implementiert und du kannst das otelhttp
-Paket wie beim „loadgen“ verwenden.
Zuerst ersetzen wir die Handler-Registrierung durch den Handler otelhttp
. Suchen Sie in der Funktion main
die Zeilen, in denen der HTTP-Handler mit 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 instrumentieren wir die eigentliche Span innerhalb des Handlers. Suchen Sie die Funktion (*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 Spans vom Anfang bis zum Ende der Methode handler
. Fügen Sie zur einfacheren Analyse der Spans ein zusätzliches Attribut hinzu, mit dem die übereinstimmende Anzahl in der Abfrage gespeichert wird. Fügen Sie direkt vor der Protokollzeile den folgenden Code ein.
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 allen oben genannten Instrumentierungen 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 zum Ausführen der Dienste im GKE-Cluster werden die folgenden Logmeldungen angezeigt:
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" ...
Der stdout
-Exporter gibt diese Nachrichten aus. Sie werden feststellen, dass die übergeordneten Spans nach "loadgen" den Wert TraceID: 00000000000000000000000000000000
haben, da dies der Haupt-Span ist, also der erste Span im Trace. Sie finden außerdem, dass das Einbettungsattribut "query"
den Abfragestring enthält, der an den Clientdienst übergeben wird.
Zusammenfassung
In diesem Schritt haben Sie den Load Generator- und den Clientdienst instrumentiert, die über HTTP kommunizieren, und bestätigt, dass Sie Trace Context erfolgreich an alle Dienste weitergeben und Span-Informationen von beiden Diensten nach stdout exportieren können.
Nächstes Thema
Im nächsten Schritt instrumentieren Sie den Client- und Serverdienst, um zu prüfen, wie Trace Context ü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 lila Rechteck im Bild unten)
Instrumentierung für den gRPC-Client vorgefertigt
OpenTelemetry bietet viele praktische Bibliotheken, mit denen Entwickler Anwendungen instrumentieren können. Im vorherigen Schritt haben wir eine vorgefertigte Instrumentierung für das net/http
-Paket verwendet. In diesem Schritt verwenden wir die Bibliothek, um Trace-Kontext über gRPC weiterzugeben.
Zuerst importieren Sie das vorkonfigurierte gRPC-Paket namens 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" )
Dieses Mal ist der Clientdienst ein gRPC-Client für den Serverdienst, sodass Sie den gRPC-Client instrumentieren müssen. Suchen Sie die Funktion mustConnGRPC
und fügen Sie gRPC-Abfangenden hinzu, die jedes Mal neue Spans instrumentieren, wenn der Client Anfragen an den Server stellt.
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, müssen Sie dies nicht tun.
Vordefinierte Instrumentierung für den gRPC-Server
Wie beim gRPC-Client nennen wir die vorgefertigte Instrumentierung für den gRPC-Server. Fügen Sie dem Importabschnitt wie folgt ein neues Paket hinzu:
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 Sie zum ersten Mal einen Server instrumentieren, müssen Sie zuerst OpenTelemetry einrichten, ähnlich wie wir es fürloadgen 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-Abfangenden hinzufügen. Suchen Sie in der main
-Funktion die Stelle, an der grpc.NewServer()
aufgerufen wird, und fügen Sie der Funktion Interceptore 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 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 in 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 mit trace.Start()
oder span.SpanFromContext()
noch keine Span-Namen und manuell erstellten Spans eingebettet haben. Sie erhalten trotzdem eine große Anzahl von Spans, da sie von den gRPC-Interceptoren generiert wurden.
Zusammenfassung
In diesem Schritt haben Sie die gRPC-basierte Kommunikation mit der Unterstützung der Bibliotheken des OpenTelemetry-Ökosystems instrumentiert.
Nächstes Thema
Im nächsten Schritt visualisieren Sie den Trace mit Cloud Trace und lernen, 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 HTTP- und gRPC-Dienste instrumentiert werden. Sie haben zwar gelernt, sie zu instrumentieren, aber immer noch nicht, wie Sie sie analysieren. In diesem Abschnitt ersetzen Sie stdout-Exporter durch Cloud Trace-Exporter und erfahren, wie Sie Ihre Traces analysieren.
Cloud Trace-Exporter verwenden
Eines der leistungsstarken Merkmale von OpenTelemetry ist ihre Plug-in-Fähigkeit. Um alle von Ihrer Instrumentierung erfassten Spans zu visualisieren, müssen Sie einfach den stdout-Exporter durch den Cloud Trace-Exporter ersetzen.
Öffnen Sie main.go
-Dateien jedes Dienstes und suchen Sie die Funktion initTracer()
. 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 Trace prüfen
Führen Sie den Cluster nach der Bearbeitung einfach wie gewohnt mit dem Befehl „skaffold“ aus.
skaffold dev
Dann sehen Sie jetzt nicht mehr viele Span-Informationen im strukturierten Logformat in stdout, da Sie den Exporter durch Cloud Trace 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 ...
Bestätigen Sie nun, dass alle Spans korrekt an Cloud Trace gesendet werden. Rufen Sie die Cloud Console auf und rufen Sie die Traceliste auf. Sie können ganz einfach über das Suchfeld darauf zugreifen. Andernfalls können Sie auf das Menü im linken Bereich klicken.
Dann sind viele blaue Punkte über das Latenzdiagramm verteilt. Jeder Punkt steht für einen einzelnen Trace.
Klicken Sie auf eine davon, um die Details im Trace anzusehen.
Selbst durch diese einfache Übersicht wissen Sie bereits viele Erkenntnisse. Der Wasserfallgrafik können Sie beispielsweise entnehmen, dass die Ursache der Latenz hauptsächlich auf den Span mit dem Namen shakesapp.ShakespeareService/GetMatchCount
zurückzuführen ist. (Siehe 1 in der Abbildung oben). Dies können Sie der Übersichtstabelle entnehmen. In der Spalte ganz rechts sehen Sie die Dauer der einzelnen Spans. Dieser Trace bezog sich auch auf die Abfrage „friend“. (Siehe 2 im Bild oben)
Bei diesen kurzen Analysen stellen Sie möglicherweise fest, dass Sie detailliertere Spannen innerhalb der GetMatchCount
-Methode benötigen. Im Vergleich zu Stdout-Informationen ist die Visualisierung wirkungsvoll. Weitere Informationen zu Cloud Trace finden Sie in der offiziellen Dokumentation.
Zusammenfassung
In diesem Schritt haben Sie den stdout-Exporter durch Cloud Trace 1 ersetzt und Traces in Cloud Trace visualisiert. Außerdem haben Sie gelernt, wie Sie mit der Analyse der Traces beginnen.
Nächstes Thema
Im nächsten Schritt ändern Sie den Quellcode des Serverdienstes, um einen Teil-Span in GetMatchCount hinzuzufügen.
7. Fügen Sie für eine bessere Analyse Teil-Span hinzu
Im vorherigen Schritt haben Sie herausgefunden, dass die von „loadgen“ beobachtete Umlaufzeit hauptsächlich durch den Prozess innerhalb der GetMatchCount-Methode, dem gRPC-Handler, im Serverdienst verursacht wird. Da wir jedoch ausschließlich den Handler instrumentiert haben, können wir in der Wasserfallgrafik keine weiteren Erkenntnisse gewinnen. Das kommt häufig vor, wenn wir mit der Instrumentierung von Mikrodiensten beginnen.
In diesem Abschnitt instrumentieren wir einen Teil-Span, in dem der Server Google Cloud Storage aufruft. Dies liegt daran, dass einige externe Netzwerk-E/A-Vorgänge häufig viel Zeit in Anspruch nehmen und es wichtig ist, herauszufinden, ob der Aufruf die Ursache ist.
Teil-Span im 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 mit Werken von Shakespeare abzurufen. In dieser Funktion können Sie einen Teil-Span erstellen, ähnlich dem, was Sie 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 ...
So viel zum Hinzufügen eines neuen Spans. Sehen wir uns an, wie es funktioniert, indem wir die App ausführen.
Mikrodienst ausführen und Trace prüfen
Führen Sie den Cluster nach der Bearbeitung einfach wie gewohnt mit dem Befehl „skaffold“ aus.
skaffold dev
Wählen Sie einen Trace mit dem Namen query.request
aus der Traceliste aus. Sie sehen ein ähnliches Trace-Wasserfalldiagramm mit Ausnahme eines neuen Spans unter shakesapp.ShakespeareService/GetMatchCount
. (Die Spanne, die unten von einem roten Rechteck eingeschlossen ist.)
An dieser Grafik können Sie jetzt erkennen, dass der externe Aufruf an Google Cloud Storage ein hohes Maß an Latenz beansprucht, aber andere Faktoren einen Großteil der Latenz ausmachen.
Mit ein paar Blicken auf das Trace-Wasserfalldiagramm haben Sie bereits viele Erkenntnisse gewonnen. Wie erhalte ich weitere Details zur Leistung in deiner Bewerbung? Hier kommt der Profiler ins Spiel, aber fürs Erste machen wir es am Ende dieses Codelabs und delegieren alle Profiler-Tutorials an Teil 2.
Zusammenfassung
In diesem Schritt haben Sie einen weiteren Span im Serverdienst instrumentiert und weitere Einblicke in die Systemlatenz erhalten.
8. Glückwunsch
Sie haben verteilte Traces mit OpenTelemery erstellt und die Anfragelatenzen für den Mikrodienst in Google Cloud Trace bestätigt.
Für längere Übungen kannst du 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? Den Hinweis finden Sie hier. - Ereignislogs mit Spans korrelieren und ihre Funktionsweise in Google Cloud Trace und Google Cloud Logging sehen Den Hinweis finden Sie hier.
- Ersetzen Sie einen Dienst durch einen Dienst in einer anderen Sprache und versuchen Sie, für diese Sprache OpenTelemetry zu instrumentieren.
Wenn Sie später 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 keine unerwarteten Kosten für Google Kubernetes Engine, Google Cloud Trace und Google Artifact Registry anfallen.
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
Wählen Sie nach dem Löschen des Clusters im Menübereich die Option „IAM & Admin“ > „Einstellungen“ und dann auf „Beenden“. Schaltfläche.
Geben Sie dann die Projekt-ID (nicht den Projektnamen) in das Formular im Dialogfeld ein und bestätigen Sie das Herunterfahren.