Instrument zur Steigerung der Leistung Ihrer App in Go (Teil 2: Profiler)

1. Einführung

e0509e8a07ad5537.png

Zuletzt aktualisiert:14. Juli 2022

Beobachtbarkeit der Anwendung

Beobachtbarkeit und Continuous Profiler

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.

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 2 der Reihe und behandelt die Instrumentierung eines kontinuierlichen Profiler-Agents. Teil 1 behandelt verteiltes Tracing mit OpenTelemetry und Cloud Trace. In Teil 1 erfahren Sie, wie Sie Engpässe bei Mikrodiensten besser erkennen.

Inhalt

In diesem Codelab instrumentieren Sie den kontinuierlichen Profiler-Agent im Serverdienst der „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

In Teil 1 haben Sie festgestellt, dass der Engpass irgendwo im Serverdienst vorliegt, aber Sie konnten die genaue Ursache nicht ermitteln.

Aufgaben in diesem Lab

  • Profiler-Agent einbetten
  • So untersuchen Sie den Engpass in Cloud Profiler

In diesem Codelab wird erläutert, wie Sie einen Continuous-Profiler-Agent in Ihrer Anwendung instrumentieren.

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

Dann sehen Sie oben auf der Seite Navigationspfade. 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 instrumentieren Sie den kontinuierlichen Profiler-Agent im Serverdienst.

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 step4. Sie können auch im Quellcode in step[1-6]-Ordnern auf die Änderungen von Anfang an verweisen. (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 step4
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 des Cloud Profiler-Agents

Konzept der kontinuierlichen Profilerstellung

Bevor wir das Konzept der kontinuierlichen Profilerstellung erklären, müssen wir zuerst verstehen, was die Profilerstellung ist. Die Profilerstellung ist eine der Möglichkeiten, die Anwendung dynamisch zu analysieren (dynamische Programmanalyse), und wird normalerweise während der Anwendungsentwicklung im Rahmen von Belastungstests usw. durchgeführt. Dabei handelt es sich um eine einmalige Aktivität, mit der die Systemmesswerte wie CPU- und Speichernutzung in einem bestimmten Zeitraum gemessen werden. Nachdem die Profildaten erfasst wurden, analysieren die Entwickler sie mithilfe des Codes.

Die kontinuierliche Profilerstellung ist der erweiterte Ansatz der normalen Profilerstellung: Dabei werden Kurzfensterprofile regelmäßig für die lange laufende Anwendung ausgeführt und eine Reihe von Profildaten erfasst. Anschließend wird die statistische Analyse basierend auf einem bestimmten Attribut der Anwendung wie Versionsnummer, Bereitstellungszone, Messzeit usw. automatisch generiert. Weitere Informationen zu diesem Konzept finden Sie in unserer Dokumentation.

Da es sich bei dem Ziel um eine laufende Anwendung handelt, können Profildaten regelmäßig erfasst und an ein Back-End gesendet werden, das die statistischen Daten nachbearbeitet. Das ist der Cloud Profiler-Agent. Sie werden ihn bald in den Serverdienst einbetten.

Cloud Profiler-Agent einbetten

Ö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 step4/src/server/main.go und suchen Sie die 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 Einrichtungscode für OpenTelemetry und gRPC. Diesen Code finden Sie in Teil 1 des Codelab. Jetzt fügen Sie hier die Instrumentierung für den Cloud Profiler-Agent hinzu. Wie bei initTracer() können Sie eine Funktion namens initProfiler() zur besseren Lesbarkeit 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 Objekt profiler.Config{} angegebenen Optionen genauer an.

  • Dienst: Der Dienstname, den Sie auswählen und auf dem Profiler-Dashboard aktivieren können.
  • ServiceVersion: Der Name der Dienstversion. Sie können Profildatensätze basierend auf diesem Wert vergleichen.
  • NoHeapProfiling: Arbeitsspeicherverbrauchs-Profiling deaktivieren
  • NoAllocProfiling: Arbeitsspeicherzuweisungs-Profiling deaktivieren
  • NoGoroutineProfiling: Goroutine-Profilerstellung deaktivieren
  • NoCPUProfiling: CPU-Profilerstellung deaktivieren

In diesem Codelab aktivieren wir nur die CPU-Profilerstellung.

Jetzt müssen Sie diese Funktion einfach in der Funktion main aufrufen. Achten Sie darauf, das Cloud Profiler-Paket in den Importblock zu 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 Schlüsselwort go aufrufen. Weil profiler.Start() blockiert wird, musst du ihn daher in einer anderen Goroutine ausführen. Jetzt kann es erstellt werden. Führen Sie go mod tidy vor der Bereitstellung aus.

go mod tidy

Stellen Sie jetzt den Cluster mit dem neuen Serverdienst bereit.

skaffold dev

In der Regel dauert es einige Minuten, bis das Flame-Diagramm in Cloud Profiler angezeigt wird. Geben Sie „profiler“ ein oben in das Suchfeld und klicken Sie auf das Profiler-Symbol.

3d8ca8a64b267a40.png

Dann sehen Sie das folgende Flame-Diagramm.

7f80797dddc0128d.png

Zusammenfassung

In diesem Schritt haben Sie den Cloud Profiler-Agent in den Serverdienst eingebettet und überprüft, ob er ein Flame-Diagramm generiert.

Nächstes Thema

Im nächsten Schritt untersuchen Sie die Ursache des Engpasses in der Anwendung mithilfe des Flame-Diagramms.

5. Cloud Profiler-Flame-Diagramm analysieren

Was ist ein Flame-Diagramm?

Das Flame-Diagramm ist eine der Möglichkeiten zur Visualisierung der Profildaten. Eine ausführliche Erläuterung finden Sie in unserem Dokument. Hier eine kurze Zusammenfassung:

  • Jeder Balken drückt den Methoden-/Funktionsaufruf in der Anwendung aus.
  • „Vertikale Richtung“ ist der Aufrufstack. Aufrufstack wächst von oben nach unten
  • „Horizontale Richtung“ bezieht sich auf die Ressourcennutzung. desto länger, desto schlimmer.

Sehen wir uns daher das erhaltene Flame-Diagramm an.

7f80797dddc0128d.png

Flame-Diagramm analysieren

Im vorherigen Abschnitt haben Sie gelernt, dass jeder Balken im Flame-Diagramm den Funktions-/Methodenaufruf ausdrückt, und die Länge des Balkens steht für die Ressourcennutzung in der Funktion/Methode. Das Flame-Diagramm von Cloud Profiler sortiert den Balken in absteigender Reihenfolge oder nach Länge von links nach rechts. Sehen Sie sich zuerst den linken oberen Bereich des Diagramms an.

6d90760c6c1183cd.png

In unserem Fall ist es explizit, dass grpc.(*Server).serveStreams.func1.2 die meiste CPU-Zeit beansprucht. Wenn Sie den Aufrufstack von oben nach unten durchsehen, wird dies die meiste Zeit in main.(*serverService).GetMatchCount verbracht, dem gRPC-Server-Handler im Serverdienst.

Unter „GetMatchCount“ sehen Sie eine Reihe von Regexp-Funktionen: regexp.MatchString und regexp.Compile. Sie sind aus dem Standardpaket, das heißt, sie sollten in vielerlei Hinsicht getestet werden, auch in Bezug auf die Leistung. Das Ergebnis hier zeigt jedoch, dass die Ressourcennutzung für die CPU-Zeit in regexp.MatchString und regexp.Compile hoch ist. In Anbetracht dieser Fakten wird hier angenommen, dass die Verwendung von regexp.MatchString etwas mit Leistungsproblemen zu tun hat. Lesen wir also den Quellcode, 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
}

Dies ist der Ort, an dem regexp.MatchString aufgerufen wird. Beim Lesen des Quellcodes stellen Sie möglicherweise fest, dass die Funktion innerhalb der verschachtelten for-Schleife aufgerufen wird. Daher wird diese Funktion möglicherweise falsch verwendet. Sehen wir uns das GoDoc für regexp an.

80b8a4ba1931ff7b.png

Gemäß dem Dokument kompiliert regexp.MatchString das Muster des regulären Ausdrucks bei jedem Aufruf. Die Ursache für den hohen Ressourcenverbrauch klingt also so:

Zusammenfassung

In diesem Schritt haben Sie das Flame-Diagramm analysiert und so die Ursache des Ressourcenverbrauchs angenommen.

Nächstes Thema

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-Diagramme vergleichen

Quellcode aktualisieren

Im vorherigen Schritt sind Sie davon ausgegangen, dass die Nutzung von regexp.MatchString mit dem hohen Ressourcenverbrauch zu tun hat. Lassen Sie uns dieses Problem lösen. Öffnen Sie den Code und ändern Sie diesen Teil ein wenig.

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 jetzt der Kompilierungsprozess für das Regex-Muster aus regexp.MatchString extrahiert und aus der verschachtelten For-Schleife verschoben.

Bevor Sie diesen Code bereitstellen, müssen Sie den Versionsstring in der Funktion initProfiler() aktualisieren.

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 nun an, wie das funktioniert. Stellen Sie den Cluster mit dem Skaffold-Befehl bereit.

skaffold dev

Nach einiger Zeit laden Sie das Cloud Profiler-Dashboard neu, um zu sehen, wie es aussieht.

283cfcd4c13716ad.png

Ändern Sie die Version in "1.1.0", damit nur die Profile von Version 1.1.0 angezeigt werden. Wie Sie sehen, verringerte sich die Länge des Balkens von GetMatchCount und das Nutzungsverhältnis der CPU-Zeit (d.h. der Balken wurde kürzer).

e3a1456b4aada9a5.png

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

841dec77d8ba5595.png

Wert von „Vergleichen mit“ ändern Drop-down-Liste auf "Version" und ändern Sie den Wert für „Verglichene Version“ auf die Originalversion „1.0.0“.

5553844292d6a537.png

Sie sehen diese Art von Flame-Diagramm. Die Form des Diagramms entspricht der bei 1.1.0, aber die Farbe ist anders. Im Vergleichsmodus bedeutet die Farbe:

  • Blau: reduzierter Wert (Ressourcenverbrauch)
  • Orange: der gewonnene Wert (Ressourcenverbrauch)
  • Grau: neutral

Sehen wir uns die Funktion anhand der Legende genauer an. Wenn Sie auf den Balken klicken, um heranzuzoomen, sehen Sie weitere Details im Stapel. Klicken Sie auf den Balken main.(*serverService).GetMatchCount. Wenn Sie den Mauszeiger auf die Leiste bewegen, werden die Details des Vergleichs angezeigt.

ca08d942dc1e2502.png

Dort steht, dass die CPU-Gesamtzeit von 5,26 s auf 2,88 s reduziert wird (insgesamt 10 s = Stichprobenfenster). Das ist eine enorme Verbesserung!

Jetzt können Sie die Anwendungsleistung mithilfe der Analyse von Profildaten verbessern.

Zusammenfassung

In diesem Schritt haben Sie eine Änderung im Serverdienst vorgenommen und die Verbesserung des Vergleichsmodus von Cloud Profiler bestätigt.

Nächstes Thema

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 des Trace-Wasserfalls bestätigen

Unterschied zwischen verteiltem Trace und kontinuierlicher Profilerstellung

In Teil 1 des Codelabs haben Sie bestätigt, dass Sie den Engpassdienst in den Mikrodiensten für einen Anfragepfad ermitteln und die genaue Ursache des Engpasses bei dem jeweiligen Dienst nicht ermitteln konnten. In diesem Codelab im zweiten Teil haben Sie gelernt, dass Sie mithilfe der kontinuierlichen Profilerstellung den Engpass in einem einzelnen Dienst anhand von Aufrufstacks identifizieren können.

Sehen wir uns in diesem Schritt das Wasserfalldiagramm des verteilten Trace (Cloud Trace) an und sehen den Unterschied zur kontinuierlichen Profilerstellung.

Dieses Wasserfalldiagramm ist einer der Traces mit der Abfrage „love“. Das Ganze dauert etwa 6,7 Sekunden (6.700 ms).

e2b7dec25926ee51.png

Dies geschieht nach der Verbesserung derselben Abfrage. Wie Sie sehen, beträgt die Gesamtlatenz jetzt 1,5 s (1.500 ms), was eine enorme Verbesserung im Vergleich zur vorherigen Implementierung darstellt.

feeb7207f36c7e5e.png

Wichtig ist hier, dass die Aufrufstack-Informationen im verteilten Trace-Wasserfalldiagramm nur verfügbar sind, wenn Sie übergreifende Spannen instrumentieren. Verteilte Traces konzentrieren sich außerdem nur auf die Latenz der Dienste, während sich die kontinuierliche Profilerstellung auf die Computerressourcen (CPU, Arbeitsspeicher, Betriebssystem-Threads) eines einzelnen Dienstes bezieht.

Ein weiterer Aspekt, dass der verteilte Trace die Ereignisbasis ist, das kontinuierliche Profil ist statistisch. Jeder Trace hat ein anderes Latenzdiagramm und Sie benötigen ein anderes Format, z. B. Verteilung, um den Trend der Latenzänderungen zu erkennen.

Zusammenfassung

In diesem Schritt haben Sie den Unterschied zwischen verteiltem Trace und kontinuierlicher Profilerstellung geprüft.

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.