1. Einführung
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:
- 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:
und klicken Sie auf „NEUES PROJEKT“, Schaltfläche zum Erstellen eines neuen Projekts:
Wenn Sie noch kein Projekt haben, sollten Sie ein Dialogfeld wie dieses sehen, um Ihr erstes zu erstellen:
Im nachfolgenden Dialog zur Projekterstellung können Sie die Details Ihres neuen Projekts eingeben:
Denken Sie an die Projekt-ID. Dies ist ein eindeutiger Name, der in allen Google Cloud-Projekten eindeutig ist. Der oben angegebene Name ist bereits vergeben und funktioniert leider nicht für Sie. Sie wird in diesem Codelab später als PROJECT_ID bezeichnet.
Falls noch nicht geschehen, müssen Sie als Nächstes in der Developers Console die Abrechnung aktivieren, um Google Cloud-Ressourcen nutzen und die Cloud Trace API aktivieren zu können.
Dieses Codelab sollte nicht mehr als ein paar Euro kosten. Wenn Sie sich jedoch dazu entschließen, mehr Ressourcen zu verwenden oder diese weiter auszuführen (siehe Abschnitt „Bereinigen“ am Ende dieses Dokuments), Die Preise für Google Cloud Trace, Google Kubernetes Engine und Google Artifact Registry finden Sie in der offiziellen Dokumentation.
- Preise für die Operations-Suite von Google Cloud | Operations-Suite
- Preise | Kubernetes Engine-Dokumentation
- Artifact Registry – Preise | Artifact Registry-Dokumentation
Neue Google Cloud Platform-Nutzer haben Anspruch auf eine kostenlose Testversion mit 300$Guthaben, wodurch das Codelab in der Regel kostenlos sein sollte.
Google Cloud Shell einrichten
Sie können Google Cloud und Google Cloud Trace per Fernzugriff von Ihrem Laptop aus bedienen. In diesem Codelab verwenden wir jedoch Google Cloud Shell, eine Befehlszeilenumgebung, die in der Cloud ausgeführt wird.
Diese Debian-basierte virtuelle Maschine verfügt über alle erforderlichen Entwicklungstools. Es bietet ein Basisverzeichnis mit 5 GB nichtflüchtigem Speicher und wird in Google Cloud ausgeführt. Dadurch werden die Netzwerkleistung und die Authentifizierung erheblich verbessert. Für dieses Codelab benötigen Sie also nur einen Browser – ja, er funktioniert auf Chromebooks.
Klicken Sie einfach auf „Cloud Shell aktivieren“ , um Cloud Shell über die Cloud Console zu aktivieren. Es dauert nur einen Moment, bis die Umgebung bereitgestellt und eine Verbindung hergestellt werden kann.
Sobald Sie mit Cloud Shell verbunden sind, sollten Sie sehen, dass Sie bereits authentifiziert sind und dass das Projekt bereits auf Ihre PROJECT_ID
eingestellt ist.
gcloud auth list
Befehlsausgabe
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
Befehlsausgabe
[core] project = <PROJECT_ID>
Sollte das Projekt aus irgendeinem Grund nicht eingerichtet sein, geben Sie einfach den folgenden Befehl ein:
gcloud config set project <PROJECT_ID>
Du suchst dein Gerät (PROJECT_ID
)? Sehen Sie nach, welche ID Sie bei den Einrichtungsschritten verwendet haben, oder rufen Sie sie im Dashboard der Cloud Console auf:
Cloud Shell legt außerdem standardmäßig einige Umgebungsvariablen fest, die bei der Ausführung zukünftiger Befehle nützlich sein können.
echo $GOOGLE_CLOUD_PROJECT
Befehlsausgabe
<PROJECT_ID>
Legen Sie schließlich die Standardzone und die Projektkonfiguration fest.
gcloud config set compute/zone us-central1-f
Sie können verschiedene Zonen auswählen. Weitere Informationen finden Sie unter Regionen und Zonen.
Go-Sprache einrichten
In diesem Codelab verwenden wir Go für den gesamten Quellcode. Führen Sie den folgenden Befehl in Cloud Shell aus und prüfen Sie, ob die Go-Version 1.17 oder höher ist
go version
Befehlsausgabe
go version go1.18.3 linux/amd64
Google Kubernetes-Cluster einrichten
In diesem Codelab führen Sie einen Cluster von Mikrodiensten in Google Kubernetes Engine (GKE) aus. In diesem Codelab werden folgende Schritte ausgeführt:
- Referenzprojekt in Cloud Shell herunterladen
- Mikrodienste in Container umwandeln
- Container in Google Artifact Registry (GAR) hochladen
- Container in GKE bereitstellen
- Quellcode von Diensten für die Trace-Instrumentierung ändern
- Zu Schritt 2
Kubernetes Engine aktivieren
Zuerst richten wir einen Kubernetes-Cluster ein, in dem Shakesapp in der GKE ausgeführt wird. Also müssen wir GKE aktivieren. Rufen Sie das Menü „Kubernetes Engine“ auf. und drücken Sie die Taste AKTIVIEREN.
Jetzt können Sie einen Kubernetes-Cluster erstellen.
Kubernetes-Cluster erstellen
Führen Sie in Cloud Shell den folgenden Befehl aus, um einen Kubernetes-Cluster zu erstellen. Achten Sie darauf, dass sich der Zonenwert unter der Region befindet, die Sie für die Erstellung des Artifact Registry-Repositorys verwenden. Ändern Sie den Zonenwert us-central1-f
, wenn die Repository-Region die Zone nicht abdeckt.
gcloud container clusters create otel-trace-codelab2 \ --zone us-central1-f \ --release-channel rapid \ --preemptible \ --enable-autoscaling \ --max-nodes 8 \ --no-enable-ip-alias \ --scopes cloud-platform
Befehlsausgabe
Note: Your Pod address range (`--cluster-ipv4-cidr`) can accommodate at most 1008 node(s). Creating cluster otel-trace-codelab2 in us-central1-f... Cluster is being health-checked (master is healthy)...done. Created [https://container.googleapis.com/v1/projects/development-215403/zones/us-central1-f/clusters/otel-trace-codelab2]. To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/workload_/gcloud/us-central1-f/otel-trace-codelab2?project=development-215403 kubeconfig entry generated for otel-trace-codelab2. NAME: otel-trace-codelab2 LOCATION: us-central1-f MASTER_VERSION: 1.23.6-gke.1501 MASTER_IP: 104.154.76.89 MACHINE_TYPE: e2-medium NODE_VERSION: 1.23.6-gke.1501 NUM_NODES: 3 STATUS: RUNNING
Artifact Registry und Skaffold einrichten
Jetzt haben wir einen Kubernetes-Cluster, der bereitgestellt werden kann. Als Nächstes bereiten wir eine Container Registry für das Push- und Bereitstellen von Containern vor. Für diese Schritte müssen wir eine Artifact Registry (GAR) und ein Skaffold einrichten, um es verwenden zu können.
Artifact Registry einrichten
Rufen Sie das Menü von „Artifact Registry“ auf und drücken Sie die Taste AKTIVIEREN.
Nach kurzer Zeit wird der Repository-Browser von GAR angezeigt. Klicken Sie auf „REPOSITORY ERSTELLEN“. und geben Sie den Namen des Repositorys ein.
In diesem Codelab nenne ich das neue Repository trace-codelab
. Das Format des Artefakts ist „Docker“ und der Standorttyp ist „Region“. Wählen Sie die Region in der Nähe der Region aus, die Sie für die Google Compute Engine-Standardzone festgelegt haben. In diesem Beispiel ist „us-central1-f“ ausgewählt. Also wählen wir "us-central1 (Iowa)" aus. Klicken Sie dann auf „ERSTELLEN“, Schaltfläche.
Sie sehen jetzt „trace-codelab“ im Repository-Browser.
Wir werden später hierher zurückkehren, um den Registrierungspfad zu überprüfen.
Skaffold-Einrichtung
Skaffold ist ein praktisches Tool, wenn Sie mit der Erstellung von Mikrodiensten arbeiten, die auf Kubernetes ausgeführt werden. Er steuert den Workflow für das Erstellen, Übertragen und Bereitstellen von Anwendungscontainern mit wenigen Befehlen. Skaffold verwendet standardmäßig Docker Registry als Container Registry. Daher müssen Sie Skaffold so konfigurieren, dass GAR beim Hochladen von Containern erkannt wird.
Öffnen Sie Cloud Shell noch einmal und prüfen Sie, ob Skaffold installiert ist. Cloud Shell installiert Skaffold standardmäßig in der Umgebung. Führen Sie den folgenden Befehl aus, um die Skaffold-Version aufzurufen.
skaffold version
Befehlsausgabe
v1.38.0
Jetzt können Sie das Standard-Repository für Skaffold registrieren. Um den Registry-Pfad abzurufen, gehen Sie zum Artifact Registry-Dashboard und klicken Sie auf den Namen des Repositorys, das Sie gerade im vorherigen Schritt eingerichtet haben.
Dann sehen Sie oben auf der Seite Navigationspfade. Klicken Sie auf das Symbol , um den Registry-Pfad in die Zwischenablage zu kopieren.
Wenn Sie auf die Schaltfläche „Kopieren“ klicken, erscheint ein Dialogfeld unten im Browser mit einer Meldung wie:
"us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab" wurde kopiert
Kehren Sie zu Cloud Shell zurück. Führen Sie den Befehl skaffold config set default-repo
mit dem Wert aus, den Sie gerade aus dem Dashboard kopiert haben.
skaffold config set default-repo us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab
Befehlsausgabe
set value default-repo to us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab for context gke_stackdriver-sandbox-3438851889_us-central1-b_stackdriver-sandbox
Außerdem müssen Sie die Registry für die Docker-Konfiguration konfigurieren. Führen Sie dazu diesen Befehl aus:
gcloud auth configure-docker us-central1-docker.pkg.dev --quiet
Befehlsausgabe
{ "credHelpers": { "gcr.io": "gcloud", "us.gcr.io": "gcloud", "eu.gcr.io": "gcloud", "asia.gcr.io": "gcloud", "staging-k8s.gcr.io": "gcloud", "marketplace.gcr.io": "gcloud", "us-central1-docker.pkg.dev": "gcloud" } } Adding credentials for: us-central1-docker.pkg.dev
Jetzt können Sie einen Kubernetes-Container in GKE einrichten.
Zusammenfassung
In diesem Schritt richten Sie Ihre Codelab-Umgebung ein:
- Cloud Shell einrichten
- Artifact Registry-Repository für die Container Registry erstellt
- Skaffold für die Verwendung der Container Registry einrichten
- Es wurde ein Kubernetes-Cluster erstellt, in dem die Codelab-Mikrodienste ausgeführt werden
Nächstes Thema
Im nächsten Schritt 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 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.
Dann sehen Sie das folgende Flame-Diagramm.
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.
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.
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.
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.
Ä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).
Sie können nicht nur das Flame-Diagramm einer einzelnen Version betrachten, sondern auch die Unterschiede zwischen zwei Versionen vergleichen.
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“.
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.
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).
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.
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“ > „Einstellungen“ und dann auf „Beenden“. Schaltfläche.
Geben Sie dann die Projekt-ID (nicht den Projektnamen) in das Formular im Dialogfeld ein und bestätigen Sie das Herunterfahren.