Instrument zur Steigerung der Leistung Ihrer App in Go (Teil 1: Nachverfolgen)

Instrument zur Steigerung der Leistung Ihrer App in Go (Teil 1:
Nachverfolgen)

Informationen zu diesem Codelab

subjectZuletzt aktualisiert: Juli 25, 2022
account_circleVerfasst von Yoshi Yamaguchi

1. Einführung

505827108874614d.png

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.

Einen Trace

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:

44e243182ced442f.png

  • 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:

7a32e5469db69e9.png

und klicken Sie auf „NEUES PROJEKT“, Schaltfläche zum Erstellen eines neuen Projekts:

7136b3ee36ebaf89.png

Wenn Sie noch kein Projekt haben, sollten Sie ein Dialogfeld wie dieses sehen, um Ihr erstes zu erstellen:

870a3cbd6541ee86.png

Im nachfolgenden Dialog zur Projekterstellung können Sie die Details Ihres neuen Projekts eingeben:

affdc444517ba805.png

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.

15d0ef27a8fbab27.png

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.

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“ gcLMt5IuEcJJNnMId-Bcz3sxCd0rZn7IzT_r95C8UZeqML68Y1efBG_B0VRp7hc7qiZTLAF-TXD7SsOadxn8uadgHhaLeASnVS3ZHK39eOlKJOgj9SJua_oeGhMxRrbOg3qigddS2A, um Cloud Shell über die Cloud Console zu aktivieren. Es dauert nur einen Moment, bis die Umgebung bereitgestellt und eine Verbindung hergestellt werden kann.

JjEuRXGg0AYYIY6QZ8d-66gx_Mtc-_jDE9ijmbXLJSAXFvJt-qUpNtsBsYjNpv2W6BQSrDc1D-ARINNQ-1EkwUhz-iUK-FUCZhJ-NtjvIEx9pIkE-246DomWuCfiGHK78DgoeWkHRw

Screen Shot 2017-06-14 at 10.13.43 PM.png

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:

158fNPfwSxsFqz9YbtJVZes8viTS3d1bV4CVhij3XPxuzVFOtTObnwsphlm6lYGmgdMFwBJtc-FaLrZU7XHAg_ZYoCrgombMRR3h-eolLPcvO351c5iBv506B3ZwghZoiRg6cz23Qw

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:

  1. Referenzprojekt in Cloud Shell herunterladen
  2. Mikrodienste in Container umwandeln
  3. Container in Google Artifact Registry (GAR) hochladen
  4. Container in GKE bereitstellen
  5. Quellcode von Diensten für die Trace-Instrumentierung ändern
  6. 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.

548cfd95bc6d344d.png

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.

45e384b87f7cf0db.png

Nach kurzer Zeit wird der Repository-Browser von GAR angezeigt. Klicken Sie auf „REPOSITORY ERSTELLEN“. und geben Sie den Namen des Repositorys ein.

d6a70f4cb4ebcbe3.png

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.

9c2d1ce65258ef70.png

Sie sehen jetzt „trace-codelab“ im Repository-Browser.

7a3c1f47346bea15.png

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.

7a3c1f47346bea15.png

Daraufhin werden oben auf der Seite Navigationspfade angezeigt. Klicken Sie auf das Symbol e157b1359c3edc06.png, um den Registry-Pfad in die Zwischenablage zu kopieren.

e0f2ae2144880b8b.png

Wenn Sie auf die Schaltfläche „Kopieren“ klicken, erscheint ein Dialogfeld unten im Browser mit einer Meldung wie:

&quot;us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab&quot; 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.

6be42e353b9bfd1d.png

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

b01f7bb90188db0d.png

So instrumentieren Sie den Anwendungs-Trace mit OpenTelemetry:

  1. Exporter erstellen
  2. Erstellen Sie eine TracerProvider-Bindung, die den Exporter in 1 bindet, und legen Sie sie global fest.
  3. TextMapPropagaror zum Festlegen der Verteilungsmethode festlegen
  4. Tracer von TracerProvider abrufen
  5. 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 776a11bfb2122549.png 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:

  1. Mit otel.Tracer() einen Tracer von der globalen TracerProvider abrufen
  2. Haupt-Span mit der Methode Tracer.Start() erstellen
  3. 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.

bcaccd06691269f8.png

Ö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.

step0/src/client/main.go

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)

75310d8e0e3b1a30.png

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. 8b3f8411bd737e06.png

Dann sind viele blaue Punkte über das Latenzdiagramm verteilt. Jeder Punkt steht für einen einzelnen Trace.

831423fc4c40.png

Klicken Sie auf eine davon, um die Details im Trace anzusehen. 4fd10960c6648a03.png

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.

3b63a1e471dddb8c.png

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

3d4a891aa30d7a32.png

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“ &gt; „Einstellungen“ und dann auf „Beenden“. Schaltfläche.

45aa37b7d5e1ddd1.png

Geben Sie dann die Projekt-ID (nicht den Projektnamen) in das Formular im Dialogfeld ein und bestätigen Sie das Herunterfahren.