1. Einführung

Zuletzt aktualisiert: 14.07.2022
Beobachtbarkeit der Anwendung
Beobachtbarkeit und Continuous Profiler
„Beobachtbarkeit“ ist ein Begriff, der ein Attribut eines Systems beschreibt. Ein System mit Beobachtbarkeit ermöglicht es Teams, ihr System aktiv zu debuggen. In diesem Zusammenhang sind die drei Säulen der Beobachtbarkeit (Logs, Messwerte und Traces) die grundlegende Instrumentierung für das System, um Beobachtbarkeit zu erlangen.
Neben den drei Säulen der Observability ist das kontinuierliche Profiling eine weitere wichtige Komponente für die Observability und erweitert die Nutzerbasis in der Branche. Cloud Profiler ist eines der ursprünglichen Tools und bietet eine einfache Oberfläche, um die Leistungsmesswerte in den Anwendungsaufrufstacks zu analysieren.
Dieses Codelab ist Teil 2 der Reihe und behandelt die Instrumentierung eines Continuous Profiler-Agents. Teil 1 behandelt das verteilte Tracing mit OpenTelemetry und Cloud Trace. Sie erfahren, wie Sie den Engpass der Mikrodienste besser identifizieren können.
Umfang
In diesem Codelab instrumentieren Sie den Continuous Profiler-Agent im Serverservice der „Shakespeare-Anwendung“ (auch bekannt als Shakesapp), die in einem Google Kubernetes Engine-Cluster ausgeführt wird. Die Architektur von Shakesapp ist wie unten beschrieben:

- Loadgen sendet einen Abfragestring in HTTP an den Client.
- Clients leiten die Anfrage von Loadgen über gRPC an den Server weiter.
- Der Server akzeptiert die Anfrage vom Client, ruft alle Werke von Shakespeare im Textformat aus Google Cloud Storage ab, sucht nach den Zeilen, die die Anfrage enthalten, und gibt die Nummer der Zeile zurück, die mit dem Client übereinstimmt.
In Teil 1 haben Sie festgestellt, dass der Engpass irgendwo im Serverservice liegt, konnten die genaue Ursache aber nicht ermitteln.
Lerninhalte
- Profiler-Agent einbetten
- Engpässe mit Cloud Profiler untersuchen
In diesem Codelab wird beschrieben, wie Sie einen Continuous Profiler-Agent in Ihrer Anwendung instrumentieren.
Voraussetzungen
- Grundlegende Go-Kenntnisse
- Grundkenntnisse in Kubernetes
2. Einrichtung und Anforderungen
Umgebung zum selbstbestimmten 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 Console ( console.cloud.google.com) an und erstellen Sie ein neues Projekt.
Wenn Sie bereits ein Projekt haben, klicken Sie oben links in der Console auf das Drop-down-Menü zur Projektauswahl:

Klicken Sie im angezeigten Dialogfeld auf die Schaltfläche „NEUES PROJEKT“, um ein neues Projekt zu erstellen:

Wenn Sie noch kein Projekt haben, wird ein Dialogfeld wie das folgende angezeigt, in dem Sie Ihr erstes Projekt erstellen können:

Im nachfolgenden Dialogfeld zum Erstellen von Projekten können Sie die Details Ihres neuen Projekts eingeben:

Merken Sie sich die Projekt-ID. Sie ist für alle Google Cloud-Projekte ein eindeutiger Name. Der Name oben ist bereits vergeben und kann nicht verwendet werden. Sie wird später in diesem Codelab als PROJECT_ID bezeichnet.
Als Nächstes müssen Sie, falls noch nicht geschehen, die Abrechnung in der Entwicklerkonsole aktivieren, um Google Cloud-Ressourcen verwenden zu können, und die Cloud Trace API aktivieren.

Dieses Codelab sollte Sie nicht mehr als ein paar Dollar kosten, aber es könnte mehr sein, wenn Sie sich für mehr Ressourcen entscheiden oder wenn Sie sie laufen lassen (siehe Abschnitt „Bereinigen“ am Ende dieses Dokuments). Die Preise für Google Cloud Trace, Google Kubernetes Engine und Google Artifact Registry sind in der offiziellen Dokumentation aufgeführt.
- Preise für die Operations-Suite von Google Cloud | Operations Suite
- Preise | Kubernetes Engine-Dokumentation
- Artifact Registry – Preise | Artifact Registry-Dokumentation
Neuen Nutzern der Google Cloud Platform steht eine kostenlose Testversion mit einem Guthaben von 300$ zur Verfügung. Dieses Codelab sollte damit vollständig kostenlos sein.
Google Cloud Shell einrichten
Während Sie Google Cloud und Google Cloud Trace von Ihrem Laptop aus per Fernzugriff nutzen können, wird in diesem Codelab Google Cloud Shell verwendet, eine Befehlszeilenumgebung, die in der Cloud ausgeführt wird.
Diese Debian-basierte virtuelle Maschine verfügt über alle Entwicklungstools, die Sie benötigen. Sie bietet ein Basisverzeichnis mit 5 GB nichtflüchtigem Speicher und läuft in Google Cloud, was die Netzwerkleistung und Authentifizierung erheblich verbessert. Für dieses Codelab benötigen Sie also nur einen Browser (es funktioniert auch auf einem Chromebook).
Klicken Sie zum Aktivieren von Cloud Shell in der Cloud Console einfach auf „Cloud Shell aktivieren“
. Die Bereitstellung und Verbindung mit der Umgebung sollte nur wenige Augenblicke dauern.


Sobald die Verbindung mit der Cloud Shell hergestellt ist, sehen Sie, dass Sie bereits authentifiziert sind und für das Projekt schon Ihre PROJECT_ID eingestellt ist.
gcloud auth list
Befehlsausgabe
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
Befehlsausgabe
[core] project = <PROJECT_ID>
Wenn das Projekt aus irgendeinem Grund nicht festgelegt ist, führen Sie einfach den folgenden Befehl aus:
gcloud config set project <PROJECT_ID>
Suchst du nach deinem PROJECT_ID? Sehen Sie nach, welche ID Sie in den Einrichtungsschritten verwendet haben, oder suchen Sie sie im Cloud Console-Dashboard:

In Cloud Shell werden auch einige Umgebungsvariablen standardmäßig festgelegt, die für zukünftige Befehle nützlich sein können.
echo $GOOGLE_CLOUD_PROJECT
Befehlsausgabe
<PROJECT_ID>
Legen Sie zum Schluss 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.
Spracheinrichtung
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 gehen wir so vor:
- Baseline-Projekt in Cloud Shell herunterladen
- Mikrodienste in Containern erstellen
- 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 GKE ausgeführt wird. Dazu müssen wir GKE aktivieren. Rufen Sie das Menü „Kubernetes Engine“ auf und klicken Sie auf die Schaltfläche „AKTIVIEREN“.

Jetzt können Sie einen Kubernetes-Cluster erstellen.
Kubernetes-Cluster erstellen
Führen Sie in Cloud Shell den folgenden Befehl aus, um einen Kubernetes-Cluster zu erstellen. Bestätigen Sie, dass der Zonenwert unter der Region liegt, die Sie zum Erstellen des Artifact Registry-Repositorys verwenden. Ändern Sie den Zonenwert us-central1-f, wenn Ihre Repository-Region die Zone nicht abdeckt.
gcloud container clusters create otel-trace-codelab2 \ --zone us-central1-f \ --release-channel rapid \ --preemptible \ --enable-autoscaling \ --max-nodes 8 \ --no-enable-ip-alias \ --scopes cloud-platform
Befehlsausgabe
Note: Your Pod address range (`--cluster-ipv4-cidr`) can accommodate at most 1008 node(s). Creating cluster otel-trace-codelab2 in us-central1-f... Cluster is being health-checked (master is healthy)...done. Created [https://container.googleapis.com/v1/projects/development-215403/zones/us-central1-f/clusters/otel-trace-codelab2]. To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/workload_/gcloud/us-central1-f/otel-trace-codelab2?project=development-215403 kubeconfig entry generated for otel-trace-codelab2. NAME: otel-trace-codelab2 LOCATION: us-central1-f MASTER_VERSION: 1.23.6-gke.1501 MASTER_IP: 104.154.76.89 MACHINE_TYPE: e2-medium NODE_VERSION: 1.23.6-gke.1501 NUM_NODES: 3 STATUS: RUNNING
Artifact Registry und Skaffold einrichten
Jetzt haben wir einen Kubernetes-Cluster, der für die Bereitstellung bereit ist. Als Nächstes bereiten wir eine Container Registry für das Pushen und Bereitstellen von Containern vor. Für diese Schritte müssen wir eine Artifact Registry (GAR) einrichten und Skaffold verwenden.
Artifact Registry einrichten
Rufen Sie das Menü von „Artifact Registry“ auf und drücken Sie die Schaltfläche „AKTIVIEREN“.

Nach kurzer Zeit wird der Repository-Browser von GAR angezeigt. Klicken Sie auf die Schaltfläche „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 aus, die der Region entspricht, die Sie für die Google Compute Engine-Standardzone festgelegt haben. Im obigen Beispiel wurde „us-central1-f“ ausgewählt. Hier wählen wir also „us-central1 (Iowa)“ aus. Klicken Sie dann auf die Schaltfläche „ERSTELLEN“.

Im Repository-Browser wird jetzt „trace-codelab“ angezeigt.

Wir kehren später hierher zurück, um den Registrierungspfad zu prüfen.
Skaffold einrichten
Skaffold ist ein praktisches Tool, wenn Sie Mikrodienste entwickeln, die in Kubernetes ausgeführt werden. Mit nur wenigen Befehlen können Sie Container von Anwendungen erstellen, übertragen und bereitstellen. Skaffold verwendet standardmäßig Docker Registry als Container-Registry. Sie müssen Skaffold also so konfigurieren, dass GAR beim Übertragen von Containern erkannt wird.
Öffnen Sie Cloud Shell noch einmal und prüfen Sie, ob Skaffold installiert ist. (In Cloud Shell wird Skaffold standardmäßig in der Umgebung installiert.) Führen Sie den folgenden Befehl aus, um die Skaffold-Version zu sehen.
skaffold version
Befehlsausgabe
v1.38.0
Sie können jetzt das Standard-Repository für Skaffold registrieren. Um den Registrierungspfad zu erhalten, rufen Sie das Artifact Registry-Dashboard auf und klicken Sie auf den Namen des Repositorys, das Sie gerade im vorherigen Schritt eingerichtet haben.

Oben auf der Seite sehen Sie dann Navigationspfade. Klicken Sie auf das Symbol
, um den Registrierungspfad in die Zwischenablage zu kopieren.

Wenn Sie auf die Schaltfläche „Kopieren“ klicken, wird unten im Browser ein Dialogfeld mit einer Meldung wie der folgenden angezeigt:
„us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab“ wurde kopiert
Kehren Sie zur 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 mit dem nächsten Schritt fortfahren und einen Kubernetes-Container in GKE einrichten.
Zusammenfassung
In diesem Schritt richten Sie Ihre Codelab-Umgebung ein:
- Cloud Shell einrichten
- Sie haben ein Artifact Registry-Repository für die Containerregistrierung erstellt.
- Skaffold für die Verwendung der Container Registry einrichten
- Sie haben einen Kubernetes-Cluster erstellt, in dem die Mikrodienste des Codelabs ausgeführt werden.
Als Nächstes
Im nächsten Schritt instrumentieren Sie den Continuous Profiler-Agent im Serverservice.
3. Mikrodienste erstellen, übertragen und bereitstellen
Codelab-Material herunterladen
Im vorherigen Schritt haben wir alle Voraussetzungen für dieses Codelab eingerichtet. Jetzt können Sie ganze Microservices darauf ausführen. Die Codelab-Materialien werden auf GitHub gehostet. Laden Sie sie mit dem folgenden Git-Befehl in die Cloud Shell-Umgebung herunter.
cd ~ git clone https://github.com/ymotongpoo/opentelemetry-trace-codelab-go.git cd opentelemetry-trace-codelab-go
Die Verzeichnisstruktur des Projekts sieht so aus:
.
├── README.md
├── step0
│ ├── manifests
│ ├── proto
│ ├── skaffold.yaml
│ └── src
├── step1
│ ├── manifests
│ ├── proto
│ ├── skaffold.yaml
│ └── src
├── step2
│ ├── manifests
│ ├── proto
│ ├── skaffold.yaml
│ └── src
├── step3
│ ├── manifests
│ ├── proto
│ ├── skaffold.yaml
│ └── src
├── step4
│ ├── manifests
│ ├── proto
│ ├── skaffold.yaml
│ └── src
├── step5
│ ├── manifests
│ ├── proto
│ ├── skaffold.yaml
│ └── src
└── step6
├── manifests
├── proto
├── skaffold.yaml
└── src
- manifests: Kubernetes-Manifestdateien
- proto: Proto-Definition für die Kommunikation zwischen Client und Server
- src: Verzeichnisse für den Quellcode der einzelnen Dienste
- skaffold.yaml: Konfigurationsdatei für Skaffold
In diesem Codelab aktualisieren Sie den Quellcode im Ordner step4. Sie können sich auch den Quellcode in den step[1-6]-Ordnern ansehen, um die Änderungen von Anfang an zu verfolgen. (Teil 1 umfasst die Schritte 0 bis 4 und Teil 2 die Schritte 5 und 6.)
Skaffold-Befehl ausführen
Jetzt können Sie die Inhalte erstellen, per Push übertragen und im Kubernetes-Cluster bereitstellen, den Sie gerade erstellt haben. Das klingt nach mehreren Schritten, aber tatsächlich erledigt Skaffold alles für Sie. Versuchen Sie es mit dem folgenden Befehl:
cd step4 skaffold dev
Sobald Sie den Befehl ausführen, sehen Sie die Logausgabe von docker build und können bestätigen, dass die Dateien erfolgreich in das Repository ü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
Nachdem alle Dienstcontainer übertragen wurden, werden Kubernetes-Bereitstellungen automatisch gestartet.
Befehlsausgabe
sha256:b71fce0a96cea08075dc20758ae561cf78c83ff656b04d211ffa00cedb77edf8 size: 1997 Tags used in deployment: - serverservice -> us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/serverservice:step4@sha256:8acdbe3a453001f120fb22c11c4f6d64c2451347732f4f271d746c2e4d193bbe - clientservice -> us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/clientservice:step4@sha256:b71fce0a96cea08075dc20758ae561cf78c83ff656b04d211ffa00cedb77edf8 - loadgen -> us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/loadgen:step4@sha256:eea2e5bc8463ecf886f958a86906cab896e9e2e380a0eb143deaeaca40f7888a Starting deploy... - deployment.apps/clientservice created - service/clientservice created - deployment.apps/loadgen created - deployment.apps/serverservice created - service/serverservice created
Nach dem Deployment werden die tatsächlichen Anwendungslogs, die in stdout ausgegeben werden, 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
An diesem Punkt sollten alle Nachrichten vom Server angezeigt werden. Jetzt können Sie endlich mit der Instrumentierung Ihrer Anwendung mit OpenTelemetry für das verteilte Tracing der Dienste beginnen.
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 bestätigt, dass Skaffold wie erwartet ausgeführt wird.
Als Nächstes
Im nächsten Schritt ändern Sie den Quellcode des Loadgen-Dienstes, um die Trace-Informationen zu instrumentieren.
4. Instrumentierung des Cloud Profiler-Agents
Konzept der kontinuierlichen Profilerstellung
Bevor wir das Konzept des kontinuierlichen Profilings erläutern, müssen wir das Konzept des Profilings selbst verstehen. Die Profilerstellung ist eine der Möglichkeiten, die Anwendung dynamisch zu analysieren (dynamische Programmanalyse). Sie wird in der Regel während der Anwendungsentwicklung im Rahmen von Lasttests usw. durchgeführt. Das ist eine einmalige Aktivität, um die Systemmesswerte wie CPU- und Speicherauslastung während des angegebenen Zeitraums zu messen. Nachdem die Profildaten erfasst wurden, analysieren Entwickler sie außerhalb des Codes.
Das kontinuierliche Profiling ist die erweiterte Version des normalen Profilings: Es werden regelmäßig Profile mit kurzem Zeitfenster für die lang laufende Anwendung ausgeführt und eine Vielzahl von Profildaten erfasst. Anschließend wird die statistische Analyse automatisch anhand eines bestimmten Attributs der Anwendung generiert, z. B. Versionsnummer, Bereitstellungszone oder Messzeit. Weitere Informationen zu diesem Konzept finden Sie in unserer Dokumentation.
Da das Ziel eine laufende Anwendung ist, können Profildaten regelmäßig erfasst und an ein Backend gesendet werden, das die statistischen Daten nachverarbeitet. Das ist der Cloud Profiler-Agent, den Sie bald in den Serverdienst einbetten werden.
Cloud Profiler-Agent einbetten
Öffnen Sie Cloud Shell Editor, indem Sie oben rechts in Cloud Shell auf die Schaltfläche
klicken. Öffnen Sie step4/src/server/main.go über den Explorer im linken Bereich und suchen Sie nach der Hauptfunktion.
step4/src/server/main.go
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
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)
healthpb.RegisterHealthServer(srv, svc)
if err := srv.Serve(lis); err != nil {
log.Fatalf("error serving server: %v", err)
}
}
In der Funktion main sehen Sie einige Einrichtungscodes für OpenTelemetry und gRPC, die in Teil 1 des Codelabs ausgeführt wurden. Fügen Sie nun die Instrumentierung für den Cloud Profiler-Agent hinzu. Wie bei initTracer() können Sie zur besseren Lesbarkeit eine Funktion namens initProfiler() schreiben.
step4/src/server/main.go
import (
...
"cloud.google.com/go/profiler" // step5. add profiler package
"cloud.google.com/go/storage"
...
)
// step5: add Profiler initializer
func initProfiler() {
cfg := profiler.Config{
Service: "server",
ServiceVersion: "1.0.0",
NoHeapProfiling: true,
NoAllocProfiling: true,
NoGoroutineProfiling: true,
NoCPUProfiling: false,
}
if err := profiler.Start(cfg); err != nil {
log.Fatalf("failed to launch profiler agent: %v", err)
}
}
Sehen wir uns die im profiler.Config{}-Objekt angegebenen Optionen genauer an.
- Dienst: Der Dienstname, den Sie im Profiler-Dashboard auswählen und aktivieren können
- ServiceVersion: Der Name der Dienstversion. Sie können Profil-Datasets anhand dieses Werts vergleichen.
- NoHeapProfiling: Profiling des Arbeitsspeicherverbrauchs deaktivieren
- NoAllocProfiling: Profiling der Arbeitsspeicherzuweisung deaktivieren
- NoGoroutineProfiling: Deaktiviert die Goroutine-Profilerstellung.
- NoCPUProfiling: CPU-Profiling deaktivieren
In diesem Codelab aktivieren wir nur das CPU-Profiling.
Jetzt müssen Sie diese Funktion nur noch in der Funktion main aufrufen. Achten Sie darauf, dass Sie das Cloud Profiler-Paket im Importblock importieren.
step4/src/server/main.go
func main() {
...
defer func() {
if err := tp.Shutdown(context.Background()); err != nil {
log.Fatalf("error shutting down TracerProvider: %v", err)
}
}()
// step2. end setup
// step5. start profiler
go initProfiler()
// step5. end
svc := NewServerService()
// step2: add interceptor
...
}
Beachten Sie, dass Sie die Funktion initProfiler() mit dem Keyword go aufrufen. Da profiler.Start() blockiert, müssen Sie es in einer anderen Goroutine ausführen. Jetzt kann es losgehen. Führen Sie go mod tidy vor der Bereitstellung aus.
go mod tidy
Stellen Sie nun Ihren Cluster mit dem neuen Serverservice bereit.
skaffold dev
Es dauert in der Regel einige Minuten, bis das Flammen-Diagramm in Cloud Profiler angezeigt wird. Geben Sie oben in das Suchfeld „Profiler“ ein und klicken Sie auf das Profilersymbol.

Dann wird die folgende Flammengrafik angezeigt.

Zusammenfassung
In diesem Schritt haben Sie den Cloud Profiler-Agent in den Serverservice eingebettet und bestätigt, dass er ein Flammen-Diagramm generiert.
Als Nächstes
Im nächsten Schritt untersuchen Sie die Ursache des Engpasses in der Anwendung mit dem Flammen-Diagramm.
5. Cloud Profiler-Flame-Diagramm analysieren
Was ist ein Flame-Diagramm?
Das Flame-Diagramm ist eine der Möglichkeiten, Profildaten zu visualisieren. Eine ausführliche Erklärung finden Sie in diesem Dokument. Hier eine kurze Zusammenfassung:
- Jeder Balken stellt den Methoden-/Funktionsaufruf in der Anwendung dar.
- Die vertikale Richtung ist der Aufrufstack. Der Aufrufstack wird von oben nach unten erweitert.
- Die horizontale Richtung gibt die Ressourcennutzung an. Je länger, desto schlechter.
Sehen wir uns das erstellte Flame-Diagramm an.

Flame-Diagramm analysieren
Im vorherigen Abschnitt haben Sie erfahren, dass jeder Balken in der Flammengrafik den Funktions-/Methodenaufruf darstellt und die Länge des Balkens die Ressourcennutzung in der Funktion/Methode angibt. Im Flammen-Diagramm von Cloud Profiler wird der Balken in absteigender Reihenfolge der Länge von links nach rechts sortiert. Sie können also zuerst oben links im Diagramm nachsehen.

In unserem Fall ist explizit zu sehen, dass grpc.(*Server).serveStreams.func1.2 den größten Teil der CPU-Zeit beansprucht. Wenn wir den Aufrufstack von oben nach unten durchgehen, sehen wir, dass die meiste Zeit in main.(*serverService).GetMatchCount verbracht wird, dem gRPC-Server-Handler im Serverservice.
Unter „GetMatchCount“ sehen Sie eine Reihe von regexp-Funktionen: regexp.MatchString und regexp.Compile. Sie stammen aus dem Standardpaket und sollten aus vielen Blickwinkeln, einschließlich der Leistung, gut getestet sein. Das Ergebnis zeigt jedoch, dass die Ressourcennutzung der CPU-Zeit in regexp.MatchString und regexp.Compile hoch ist. Angesichts dieser Fakten wird davon ausgegangen, dass die Verwendung von regexp.MatchString mit Leistungsproblemen zusammenhängt. Sehen wir uns also den Quellcode an, in dem die Funktion verwendet wird.
step4/src/server/main.go
func (s *serverService) GetMatchCount(ctx context.Context, req *shakesapp.ShakespeareRequest) (*shakesapp.ShakespeareResponse, error) {
resp := &shakesapp.ShakespeareResponse{}
texts, err := readFiles(ctx, bucketName, bucketPrefix)
if err != nil {
return resp, fmt.Errorf("fails to read files: %s", err)
}
for _, text := range texts {
for _, line := range strings.Split(text, "\n") {
line, query := strings.ToLower(line), strings.ToLower(req.Query)
isMatch, err := regexp.MatchString(query, line)
if err != nil {
return resp, err
}
if isMatch {
resp.MatchCount++
}
}
}
return resp, nil
}
Hier wird regexp.MatchString aufgerufen. Wenn Sie den Quellcode lesen, werden Sie feststellen, dass die Funktion im verschachtelten for-Loop aufgerufen wird. Die Verwendung dieser Funktion ist also möglicherweise nicht korrekt. Sehen wir uns die GoDoc von regexp an.

Laut dem Dokument wird das Muster des regulären Ausdrucks bei jedem Aufruf von regexp.MatchString kompiliert. Das ist der Grund für den hohen Ressourcenverbrauch.
Zusammenfassung
In diesem Schritt haben Sie die Ursache des Ressourcenverbrauchs durch Analyse des Flame-Diagramms angenommen.
Als Nächstes
Im nächsten Schritt aktualisieren Sie den Quellcode des Serverdienstes und bestätigen die Änderung gegenüber Version 1.0.0.
6. Quellcode aktualisieren und Flame-Graphen vergleichen
Quellcode aktualisieren
Im vorherigen Schritt sind Sie davon ausgegangen, dass die Verwendung von regexp.MatchString etwas mit dem hohen Ressourcenverbrauch zu tun hat. Lass uns das Problem also beheben. Öffnen Sie den Code und ändern Sie diesen Teil.
step4/src/server/main.go
func (s *serverService) GetMatchCount(ctx context.Context, req *shakesapp.ShakespeareRequest) (*shakesapp.ShakespeareResponse, error) {
resp := &shakesapp.ShakespeareResponse{}
texts, err := readFiles(ctx, bucketName, bucketPrefix)
if err != nil {
return resp, fmt.Errorf("fails to read files: %s", err)
}
// step6. considered the process carefully and naively tuned up by extracting
// regexp pattern compile process out of for loop.
query := strings.ToLower(req.Query)
re := regexp.MustCompile(query)
for _, text := range texts {
for _, line := range strings.Split(text, "\n") {
line = strings.ToLower(line)
isMatch := re.MatchString(line)
// step6. done replacing regexp with strings
if isMatch {
resp.MatchCount++
}
}
}
return resp, nil
}
Wie Sie sehen, wird die Kompilierung des regulären Ausdrucks jetzt aus der regexp.MatchString extrahiert und aus der verschachtelten for-Schleife verschoben.
Aktualisieren Sie vor der Bereitstellung dieses Codes den Versionsstring in der Funktion initProfiler().
step4/src/server/main.go
func initProfiler() {
cfg := profiler.Config{
Service: "server",
ServiceVersion: "1.1.0", // step6. update version
NoHeapProfiling: true,
NoAllocProfiling: true,
NoGoroutineProfiling: true,
NoCPUProfiling: false,
}
if err := profiler.Start(cfg); err != nil {
log.Fatalf("failed to launch profiler agent: %v", err)
}
}
Sehen wir uns an, wie das funktioniert. Stellen Sie den Cluster mit dem Skaffold-Befehl bereit.
skaffold dev
Laden Sie das Cloud Profiler-Dashboard nach einer Weile neu und sehen Sie sich das Ergebnis an.

Ändern Sie die Version zu "1.1.0", damit nur die Profile aus Version 1.1.0 angezeigt werden. Wie Sie sehen, ist die Länge des Balkens für „GetMatchCount“ kürzer geworden und das Nutzungsverhältnis der CPU-Zeit hat sich verringert.

Sie können nicht nur das Flammen-Diagramm einer einzelnen Version ansehen, sondern auch die Unterschiede zwischen zwei Versionen vergleichen.

Ändern Sie den Wert der Drop-down-Liste „Vergleichen mit“ in „Version“ und den Wert von „Vergleichsversion“ in „1.0.0“, die Originalversion.

Sie sehen eine Flammengrafik wie diese. Die Form des Diagramms ist dieselbe wie in 1.1.0, aber die Farben sind anders. Im Vergleichsmodus haben die Farben folgende Bedeutung:
- Blau: Der Wert (Ressourcenverbrauch) wurde reduziert.
- Orange: Der gewonnene Wert (Ressourcenverbrauch)
- Grau: neutral
Sehen wir uns die Funktion anhand der Legende genauer an. Wenn Sie auf den Balken klicken, den Sie vergrößern möchten, sehen Sie weitere Details im Stapel. Klicken Sie auf die Leiste main.(*serverService).GetMatchCount. Wenn Sie den Mauszeiger auf die Spalte bewegen, werden die Details des Vergleichs angezeigt.

Die gesamte CPU-Zeit wird von 5,26 Sekunden auf 2,88 Sekunden reduziert (insgesamt 10 Sekunden = Stichprobenerfassungszeitraum). Das ist eine enorme Verbesserung.
Jetzt können Sie die Leistung Ihrer Anwendung anhand der Analyse von Profildaten verbessern.
Zusammenfassung
In diesem Schritt haben Sie eine Änderung am Serverservice vorgenommen und die Verbesserung im Vergleichsmodus von Cloud Profiler bestätigt.
Als Nächstes
Im nächsten Schritt aktualisieren Sie den Quellcode des Serverdienstes und bestätigen die Änderung gegenüber Version 1.0.0.
7. Zusätzlicher Schritt: Verbesserung im Trace-Wasserfall bestätigen
Unterschied zwischen verteiltem Trace und kontinuierlichem Profiling
Im ersten Teil des Codelabs haben Sie bestätigt, dass Sie den Engpassdienst für einen Anfragepfad über die Mikrodienste hinweg ermitteln können, aber nicht die genaue Ursache des Engpasses im jeweiligen Dienst. In diesem Teil 2 des Codelabs haben Sie gelernt, dass Sie mit kontinuierlichem Profiling den Engpass im einzelnen Dienst anhand von Callstacks ermitteln können.
In diesem Schritt sehen wir uns das Wasserfalldiagramm aus dem verteilten Trace (Cloud Trace) an und vergleichen es mit dem kontinuierlichen Profiling.
Dieses Wasserfalldiagramm gehört zu den Traces mit der Anfrage „love“. Das dauert insgesamt etwa 6,7 Sekunden (6.700 Millisekunden).

Das ist das Ergebnis nach der Verbesserung für dieselbe Abfrage. Wie Sie sehen, beträgt die Gesamtlatenz jetzt 1,5 Sekunden (1.500 ms). Das ist eine enorme Verbesserung gegenüber der vorherigen Implementierung.

Wichtig ist hier, dass die Callstack-Informationen im Wasserfalldiagramm für verteilte Traces nur verfügbar sind, wenn Sie Spans überall instrumentieren. Verteilte Traces konzentrieren sich nur auf die Latenz zwischen den Diensten, während sich das kontinuierliche Profiling auf die Computerressourcen (CPU, Arbeitsspeicher, Betriebssystem-Threads) eines einzelnen Dienstes konzentriert.
In einem anderen Aspekt ist der verteilte Trace die Ereignisbasis, das kontinuierliche Profil ist statistisch. Jeder Trace hat ein anderes Latenzdiagramm. Sie benötigen ein anderes Format wie Verteilung, um den Trend der Latenzänderungen zu sehen.
Zusammenfassung
In diesem Schritt haben Sie sich den Unterschied zwischen verteiltem Tracing und kontinuierlichem Profiling angesehen.
8. Glückwunsch
Sie haben mit OpenTelemetry verteilte Traces erstellt und die Anfragelatenzen für den Mikrodienst in Google Cloud Trace bestätigt.
Bei den erweiterten Übungen können Sie die folgenden Themen selbst ausprobieren.
- Bei der aktuellen Implementierung werden alle Spans gesendet, die von der Systemdiagnose generiert werden. (
grpc.health.v1.Health/Check) Wie filtere ich diese Spans aus Cloud Trace heraus? Hier finden Sie einen Hinweis. - Ereignislogs mit Spans in Beziehung setzen und sehen, wie das in Google Cloud Trace und Google Cloud Logging funktioniert. Hier finden Sie einen Hinweis.
- Ersetzen Sie einen Dienst durch den Dienst in einer anderen Sprache und versuchen Sie, ihn mit OpenTelemetry für diese Sprache zu instrumentieren.
Wenn Sie danach mehr über den Profiler erfahren möchten, fahren Sie mit Teil 2 fort. In diesem Fall können Sie den Abschnitt „Bereinigen“ unten überspringen.
Aufräumen
Nach dieser Codelab-Anleitung sollten Sie den Kubernetes-Cluster beenden und das Projekt löschen, damit Ihnen keine unerwarteten Gebühren für Google Kubernetes Engine, Google Cloud Trace und Google Artifact Registry in Rechnung gestellt werden.
Löschen Sie zuerst den Cluster. Wenn Sie den Cluster mit skaffold dev ausführen, müssen Sie nur Strg + C drücken. Wenn Sie den Cluster mit skaffold run ausführen, führen Sie den folgenden Befehl aus:
skaffold delete
Befehlsausgabe
Cleaning up... - deployment.apps "clientservice" deleted - service "clientservice" deleted - deployment.apps "loadgen" deleted - deployment.apps "serverservice" deleted - service "serverservice" deleted
Nachdem Sie den Cluster gelöscht haben, wählen Sie im Menübereich „IAM & Admin“ > „Einstellungen“ aus und klicken Sie dann auf die Schaltfläche „HERUNTERFAHREN“.

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