Strumento per migliorare le prestazioni nell'app in Go (parte 1: traccia)

1. Introduzione

505827108874614d.png

Ultimo aggiornamento: 15/07/2022

Osservabilità dell'applicazione

Osservabilità e OpenTelemetry

L'osservabilità è il termine utilizzato per descrivere un attributo di un sistema. Un sistema con l'osservabilità consente ai team di eseguire il debug attivo del sistema. In questo contesto, i tre pilastri dell'osservabilità, ovvero log, metriche e tracce, sono la strumentazione fondamentale per l'acquisizione dell'osservabilità da parte del sistema.

OpenTelemetry è un insieme di specifiche, librerie e agenti che accelerano l'instrumentazione e l'esportazione dei dati di telemetria (log, metriche e tracce) necessari per l'osservabilità. OpenTelemetry è uno standard aperto e un progetto basato sulla community all'interno del CNCF. Utilizzando le librerie fornite dal progetto e dal suo ecosistema, gli sviluppatori sono in grado di eseguire l'instrumentazione delle loro applicazioni in modo indipendente dal fornitore e su più architetture.

Oltre ai tre pilastri dell'osservabilità, il profiling continuo è un altro componente chiave per l'osservabilità e espande la base utenti del settore. Cloud Profiler è uno dei primi e fornisce un'interfaccia semplice per visualizzare in dettaglio le metriche sul rendimento negli stack di chiamate dell'applicazione.

Questo codelab è la parte 1 della serie e tratta della strumentazione delle tracce distribuite nei microservizi con OpenTelemetry e Cloud Trace. La parte 2 riguarderà la profilazione continua con Cloud Profiler.

Trace distribuita

Tra log, metriche e tracce, la traccia è la telemetria che indica la latenza di una parte specifica del processo nel sistema. Soprattutto nell'era dei microservizi, il monitoraggio distribuito è un fattore determinante per individuare i colli di bottiglia della latenza nel sistema distribuito complessivo.

Quando analizzi le tracce distribuite, la visualizzazione dei dati delle tracce è fondamentale per comprendere immediatamente le latenze complessive del sistema. Nella traccia distribuita, gestiamo un insieme di chiamate per elaborare una singola richiesta all'entry point del sistema in una forma di traccia contenente più span.

Un intervallo rappresenta una singola unità di lavoro eseguita in un sistema distribuito, che registra gli orari di inizio e di interruzione. Gli span spesso hanno relazioni gerarchiche tra loro: nell'immagine seguente tutti gli span più piccoli sono span secondari di un grande span /messages e sono assemblati in un'unica traccia che mostra il percorso di lavoro in un sistema.

Una traccia

Google Cloud Trace è una delle opzioni per il backend di traccia distribuito ed è ben integrato con altri prodotti di Google Cloud.

Cosa creerai

In questo codelab, strumentalizzerai le informazioni di traccia nei servizi chiamati "applicazione Shakespeare" (nota anche come Shakesapp) che viene eseguita su un cluster Google Kubernetes Engine. L'architettura di Shakesapp è descritta di seguito:

44e243182ced442f.png

  • Loadgen invia una stringa di query al client in HTTP
  • I client trasmettono la query da LoadGen al server in gRPC
  • Il server accetta la query dal client, recupera tutte le opere di Shakespeare in formato testo da Google Cloud Storage, cerca le righe che contengono la query e restituisce il numero della riga corrispondente al client

Dovrai eseguire l'instrumentazione delle informazioni sulla traccia nella richiesta. Dopodiché, incorporerai un agente di profiler nel server e esaminerai il collo di bottiglia.

Cosa imparerai a fare

  • Come iniziare a utilizzare le librerie di traccia di OpenTelemetry nel progetto Go
  • Come creare un intervallo con la libreria
  • Come propagare i contesti degli elementi span tra i componenti dell'app
  • Come inviare i dati di traccia a Cloud Trace
  • Come analizzare la traccia su Cloud Trace

Questo codelab spiega come eseguire l'instrumentazione dei microservizi. Per semplificare la comprensione, questo esempio contiene solo tre componenti (generatore di carico, client e server), ma puoi applicare la stessa procedura descritta in questo codelab a sistemi più complessi e di grandi dimensioni.

Che cosa ti serve

  • Conoscenza di base di Go
  • Conoscenza di base di Kubernetes

2. Configurazione e requisiti

Configurazione dell'ambiente a tuo ritmo

Se non hai ancora un Account Google (Gmail o Google Apps), devi crearne uno. Accedi alla console della piattaforma Google Cloud ( console.cloud.google.com) e crea un nuovo progetto.

Se hai già un progetto, fai clic sul menu a discesa per la selezione del progetto in alto a sinistra nella console:

7a32e5469db69e9.png

e fai clic sul pulsante "NUOVO PROGETTO" nella finestra di dialogo visualizzata per creare un nuovo progetto:

7136b3ee36ebaf89.png

Se non hai ancora un progetto, dovresti visualizzare una finestra di dialogo come questa per crearne uno:

870a3cbd6541ee86.png

La finestra di dialogo di creazione del progetto successiva ti consente di inserire i dettagli del nuovo progetto:

affdc444517ba805.png

Ricorda l'ID progetto, che è un nome univoco per tutti i progetti Google Cloud (il nome sopra indicato è già stato utilizzato e non funzionerà per te, scusa). Più avanti in questo codelab verrà indicato come PROJECT_ID.

Successivamente, se non l'hai già fatto, devi abilitare la fatturazione in Developers Console per utilizzare le risorse Google Cloud e abilitare l'API Cloud Trace.

15d0ef27a8fbab27.png

L'esecuzione di questo codelab non dovrebbe costarti più di qualche dollaro, ma potrebbe essere più cara se decidi di utilizzare più risorse o se le lasci in esecuzione (vedi la sezione "Pulizia" alla fine di questo documento). I prezzi di Google Cloud Trace, Google Kubernetes Engine e Google Artifact Registry sono riportati nella documentazione ufficiale.

I nuovi utenti della piattaforma Google Cloud possono beneficiare di una prova senza costi di 300$, che dovrebbe rendere questo codelab completamente senza costi.

Configurazione di Google Cloud Shell

Sebbene Google Cloud e Google Cloud Trace possano essere utilizzati da remoto dal tuo laptop, in questo codelab utilizzeremo Google Cloud Shell, un ambiente a riga di comando in esecuzione nel cloud.

Questa macchina virtuale basata su Debian viene caricata con tutti gli strumenti di sviluppo di cui hai bisogno. Offre una home directory permanente da 5 GB e viene eseguita in Google Cloud, migliorando notevolmente le prestazioni e l'autenticazione della rete. Ciò significa che per questo codelab ti serve solo un browser (sì, funziona su Chromebook).

Per attivare Cloud Shell dalla console Cloud, fai clic su Attiva Cloud Shell gcLMt5IuEcJJNnMId-Bcz3sxCd0rZn7IzT_r95C8UZeqML68Y1efBG_B0VRp7hc7qiZTLAF-TXD7SsOadxn8uadgHhaLeASnVS3ZHK39eOlKJOgj9SJua_oeGhMxRrbOg3qigddS2A (dovrebbero bastare pochi istanti per eseguire il provisioning e connettersi all'ambiente).

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

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

Una volta eseguita la connessione a Cloud Shell, dovresti vedere che il tuo account è già autenticato e il progetto è già impostato sul tuo PROJECT_ID.

gcloud auth list

Output comando

Credentialed accounts:
 - <myaccount>@<mydomain>.com (active)
gcloud config list project

Output comando

[core]
project = <PROJECT_ID>

Se, per qualche motivo, il progetto non è impostato, esegui semplicemente il seguente comando:

gcloud config set project <PROJECT_ID>

Stai cercando il tuo PROJECT_ID? Controlla quale ID hai utilizzato nei passaggi di configurazione o cercalo nella dashboard della console Cloud:

158fNPfwSxsFqz9YbtJVZes8viTS3d1bV4CVhij3XPxuzVFOtTObnwsphlm6lYGmgdMFwBJtc-FaLrZU7XHAg_ZYoCrgombMRR3h-eolLPcvO351c5iBv506B3ZwghZoiRg6cz23Qw

Cloud Shell imposta anche alcune variabili di ambiente per impostazione predefinita, che potrebbero essere utili per l'esecuzione di comandi futuri.

echo $GOOGLE_CLOUD_PROJECT

Output comando

<PROJECT_ID>

Infine, imposta la zona e la configurazione del progetto predefinite.

gcloud config set compute/zone us-central1-f

Puoi scegliere una serie di zone diverse. Per ulteriori informazioni, consulta Regioni e zone.

Vai alla configurazione della lingua

In questo codelab utilizziamo Go per tutto il codice sorgente. Esegui il seguente comando su Cloud Shell e verifica se la versione di Go è 1.17 o successiva

go version

Output comando

go version go1.18.3 linux/amd64

Configurare un cluster Google Kubernetes

In questo codelab eseguirai un cluster di microservizi su Google Kubernetes Engine (GKE). La procedura di questo codelab è la seguente:

  1. Scarica il progetto di riferimento in Cloud Shell
  2. Creare microservizi in container
  3. Carica i container in Google Artifact Registry (GAR)
  4. Esegui il deployment dei container su GKE
  5. Modificare il codice sorgente dei servizi per la misurazione delle tracce
  6. Vai al passaggio 2

Abilita Kubernetes Engine

Innanzitutto, abbiamo configurato un cluster Kubernetes in cui Shakesapp viene eseguito su GKE, quindi dobbiamo abilitare GKE. Vai al menu "Kubernetes Engine" e premi il pulsante ATTIVA.

548cfd95bc6d344d.png

Ora è tutto pronto per la creazione di un cluster Kubernetes.

Crea un cluster Kubernetes

In Cloud Shell, esegui il comando seguente per creare un cluster Kubernetes. Verifica che il valore della zona rientri nella regione che utilizzerai per la creazione del repository Artifact Registry. Modifica il valore della zona us-central1-f se la regione del repository non copre la zona.

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

Output comando

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

Configurazione di Artifact Registry e Skaffold

Ora abbiamo un cluster Kubernetes pronto per il deployment. A questo punto prepariamo un registry dei container per eseguire il push e il deployment dei container. Per questi passaggi, dobbiamo configurare un Artifact Registry (GAR) e skaffold per utilizzarlo.

Configurazione di Artifact Registry

Vai al menu "Artifact Registry" (Registro degli elementi) e premi il pulsante ATTIVA.

45e384b87f7cf0db.png

Dopo alcuni istanti, vedrai il browser del repository di GAR. Fai clic sul pulsante "CREA REPOSITORY" e inserisci il nome del repository.

d6a70f4cb4ebcbe3.png

In questo codelab, chiamo il nuovo repository trace-codelab. Il formato dell'elemento è "Docker" e il tipo di località è "Regione". Scegli la regione vicina a quella impostata per la zona predefinita di Google Compute Engine. Ad esempio, in questo esempio è stato scelto "us-central1-f" sopra, quindi qui scegliamo "us-central1 (Iowa)". Poi fai clic sul pulsante "CREA".

9c2d1ce65258ef70.png

Ora vedrai "trace-codelab" nel browser del repository.

7a3c1f47346bea15.png

Torneremo qui più tardi per controllare il percorso del registro.

Configurazione di Skaffold

Skaffold è uno strumento utile per la creazione di microservizi in esecuzione su Kubernetes. Gestisce il flusso di lavoro di creazione, push ed esecuzione del deployment di container di applicazioni con un piccolo insieme di comandi. Per impostazione predefinita, Skaffold utilizza Docker Registry come registry dei container, quindi devi configurare skaffold in modo che riconosca il repository GAR in cui eseguire il push dei container.

Riapri Cloud Shell e verifica se skaffold è installato. (Cloud Shell installa skaffold nell'ambiente per impostazione predefinita). Esegui il seguente comando e controlla la versione di Skaffold.

skaffold version

Output comando

v1.38.0

Ora puoi registrare il repository predefinito da utilizzare con skaffold. Per ottenere il percorso del registry, vai alla dashboard di Artifact Registry e fai clic sul nome del repository appena configurato nel passaggio precedente.

7a3c1f47346bea15.png

Nella parte superiore della pagina vedrai le tracce dei breadcrumb. Fai clic sull'icona e157b1359c3edc06.png per copiare il percorso del registro negli appunti.

e0f2ae2144880b8b.png

Se fai clic sul pulsante di copia, nella parte inferiore del browser viene visualizzata la finestra di dialogo con il messaggio:

"us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab" è stato copiato

Torna a Cloud Shell. Esegui il comando skaffold config set default-repo con il valore che hai appena copiato dalla dashboard.

skaffold config set default-repo us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab

Output comando

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

Inoltre, devi configurare il registry per la configurazione di Docker. Esegui questo comando:

gcloud auth configure-docker us-central1-docker.pkg.dev --quiet

Output comando

{
  "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

Ora puoi procedere con il passaggio successivo per configurare un contenitore Kubernetes su GKE.

Riepilogo

In questo passaggio, configura l'ambiente del codelab:

  • Configura Cloud Shell
  • È stato creato un repository Artifact Registry per il registry dei container
  • Configura skaffold per utilizzare il registry dei container
  • Hai creato un cluster Kubernetes in cui vengono eseguiti i microservizi del codelab

Prossimi video

Nel passaggio successivo, creerai, spingerai ed eseguirai il deployment dei microservizi nel cluster

3. Crea, esegui il push e il deployment dei microservizi

Scarica il materiale del codelab

Nel passaggio precedente abbiamo configurato tutti i prerequisiti per questo codelab. Ora puoi eseguire interi microservizi su di essi. I materiali del codelab sono ospitati su GitHub, quindi scaricali nell'ambiente Cloud Shell con il seguente comando git.

cd ~
git clone https://github.com/ymotongpoo/opentelemetry-trace-codelab-go.git
cd opentelemetry-trace-codelab-go

La struttura di directory del progetto è la seguente:

.
├── 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: file manifest Kubernetes
  • proto: definizione del protocollo per la comunicazione tra client e server
  • src: directory per il codice sorgente di ciascun servizio
  • skaffold.yaml: file di configurazione per Skaffold

In questo codelab aggiornerai il codice sorgente nella cartella step0. Per le risposte nei passaggi successivi, puoi anche fare riferimento al codice sorgente nelle cartelle step[1-6]. (la Parte 1 copre i passaggi da 0 a 4 e la Parte 2 i passaggi 5 e 6)

Esegui il comando skaffold

Finalmente, puoi creare, eseguire il push e il deployment di interi contenuti sul cluster Kubernetes che hai appena creato. Sembra che contenga più passaggi, ma in realtà skaffold fa tutto per te. Proviamo con il seguente comando:

cd step0
skaffold dev

Non appena esegui il comando, visualizzi l'output del log di docker build e puoi confermare che i dati sono stati inviati correttamente al registry.

Output comando

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

Dopo il push di tutti i container di servizio, i deployment Kubernetes vengono avviati automaticamente.

Output comando

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

Dopo il deployment, vedrai i log effettivi dell'applicazione emessi in stdout in ogni contenitore come segue:

Output comando

[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

Tieni presente che a questo punto vuoi vedere tutti i messaggi del server. Ottimo, finalmente puoi iniziare a eseguire l'instrumentazione della tua applicazione con OpenTelemetry per il monitoraggio distribuito dei servizi.

Prima di iniziare a eseguire l'instrumentazione del servizio, arresta il cluster con Ctrl-C.

Output comando

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

Riepilogo

In questo passaggio, hai preparato il materiale del codelab nel tuo ambiente e hai verificato che skaffold funzioni come previsto.

Prossimi video

Nel passaggio successivo, modificherai il codice sorgente del servizio loadgen per eseguire l'instrumentazione delle informazioni sulla traccia.

4. Strumentazione per HTTP

Concetto di strumenti e propagazione delle tracce

Prima di modificare il codice sorgente, ti spiegherò brevemente come funzionano le tracce distribuite in un semplice diagramma.

6be42e353b9bfd1d.png

In questo esempio, strumentiamo il codice per esportare le informazioni su tracce e intervalli in Cloud Trace e per propagare il contesto della traccia nella richiesta dal servizio loadgen al servizio del server.

Le applicazioni devono inviare metadati di traccia, come l'ID traccia e l'ID intervallo, affinché Cloud Trace possa assemblare tutti gli intervalli con lo stesso ID traccia in un'unica traccia. Inoltre, l'applicazione deve propagare i contesti traccia (la combinazione di ID traccia e ID span dello span principale) ai servizi downstream che effettuano la richiesta, in modo che siano a conoscenza del contesto traccia che stanno gestendo.

OpenTelemetry ti aiuta a:

  • per generare ID traccia e ID intervallo univoci
  • per esportare l'ID traccia e l'ID intervallo nel backend
  • per propagare i contesti traccia ad altri servizi
  • per incorporare metadati aggiuntivi che aiutano ad analizzare le tracce

Componenti in OpenTelemetry Trace

b01f7bb90188db0d.png

La procedura per instrumentare la traccia dell'applicazione con OpenTelemetry è la seguente:

  1. Crea un esportatore
  2. Crea un TracerProvider che leghi l'esportatore in 1 e impostalo come globale.
  3. Imposta TextMapPropagaror per impostare il metodo di propagazione
  4. Recupera il Tracer dal TracerProvider
  5. Genera uno span dal tracer

Al momento, non è necessario comprendere le proprietà dettagliate di ogni componente, ma le cose più importanti da ricordare sono:

  • l'esportatore qui è collegabile a TracerProvider
  • TracerProvider contiene tutta la configurazione relativa al campionamento e all'esportazione delle tracce
  • tutte le tracce sono raggruppate nell'oggetto Tracer

Dopo aver compreso questo concetto, passiamo al lavoro di programmazione effettivo.

Primo intervallo dello strumento

Strumento per il servizio Generatore di carico

Apri l'editor di Cloud Shell premendo il pulsante 776a11bfb2122549.png in alto a destra in Cloud Shell. Apri step0/src/loadgen/main.go dall'esploratore nel riquadro a sinistra e trova la funzione principale.

step0/src/loadgen/main.go

func main() {
        ...
        for range t.C {
                log.Printf("simulating client requests, round %d", i)
                if err := run(numWorkers, numConcurrency); err != nil {
                        log.Printf("aborted round with error: %v", err)
                }
                log.Printf("simulated %d requests", numWorkers)
                if numRounds != 0 && i > numRounds {
                        break
                }
                i++
        }
}

Nella funzione principale, vedrai il loop che chiama la funzione run. Nell'implementazione attuale, la sezione contiene due righe di log che registrano l'inizio e la fine della chiamata della funzione. Ora esaminiamo le informazioni di Span per monitorare la latenza della chiamata di funzione.

Innanzitutto, come indicato nella sezione precedente, impostiamo tutte le configurazioni per OpenTelemetry. Aggiungi i pacchetti OpenTelemetry come segue:

step0/src/loadgen/main.go

import (
        "context" // step1. add packages
        "encoding/json"
        "fmt"
        "io"
        "log"
        "math/rand"
        "net/http"
        "net/url"
        "time"
        // step1. add packages
        "go.opentelemetry.io/otel"
        "go.opentelemetry.io/otel/attribute"
        stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
        "go.opentelemetry.io/otel/propagation"
        sdktrace "go.opentelemetry.io/otel/sdk/trace"
        semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
        "go.opentelemetry.io/otel/trace"
        // step1. end add packages
)

Per una maggiore leggibilità, creiamo una funzione di configurazione chiamata initTracer e la chiamiamo nella funzione main.

step0/src/loadgen/main.go

// step1. add OpenTelemetry initialization function
func initTracer() (*sdktrace.TracerProvider, error) {
        // create a stdout exporter to show collected spans out to stdout.
        exporter, err := stdout.New(stdout.WithPrettyPrint())
        if err != nil {
                return nil, err
        }

        // for the demonstration, we use AlwaysSmaple sampler to take all spans.
        // do not use this option in production.
        tp := sdktrace.NewTracerProvider(
                sdktrace.WithSampler(sdktrace.AlwaysSample()),
                sdktrace.WithBatcher(exporter),
        )
        otel.SetTracerProvider(tp)
        otel.SetTextMapPropagator(propagation.TraceContext{})
        return tp, nil
}

Potresti notare che la procedura per configurare OpenTelemetry è descritta nella sezione precedente. In questa implementazione, utilizziamo un esportatore stdout che esporta tutte le informazioni sulle tracce in stdout in un formato strutturato.

Poi la chiami dalla funzione principale. Chiama initTracer() e assicurati di chiamare TracerProvider.Shutdown() quando chiudi l'applicazione.

step0/src/loadgen/main.go

func main() {
        // step1. setup OpenTelemetry
        tp, err := initTracer()
        if err != nil {
                log.Fatalf("failed to initialize TracerProvider: %v", err)
        }
        defer func() {
                if err := tp.Shutdown(context.Background()); err != nil {
                        log.Fatalf("error shutting down TracerProvider: %v", err)
                }
        }()
        // step1. end setup

        log.Printf("starting worder with %d workers in %d concurrency", numWorkers, numConcurrency)
        log.Printf("number of rounds: %d (0 is inifinite)", numRounds)
        ...

Al termine della configurazione, devi creare uno Span con un ID traccia e un ID Span univoci. OpenTelemetry fornisce una comoda libreria per questo scopo. Aggiungi nuovi pacchetti al client HTTP dello strumento.

step0/src/loadgen/main.go

import (
        "context"
        "encoding/json"
        "fmt"
        "io"
        "log"
        "math/rand"
        "net/http"
        "net/http/httptrace" // step1. add packages
        "net/url"
        "time"
        // step1. add packages
        "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace"
        "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
        // step1. end add packages
        "go.opentelemetry.io/otel"
        "go.opentelemetry.io/otel/attribute"
        stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
        "go.opentelemetry.io/otel/propagation"
        sdktrace "go.opentelemetry.io/otel/sdk/trace"
        semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
        "go.opentelemetry.io/otel/trace"
)

Poiché il generatore di carico chiama il servizio client in HTTP con net/http nella funzione runQuery, utilizziamo il pacchetto contrib per net/http e attiviamo la misurazione con l'estensione del pacchetto httptrace e otelhttp.

Innanzitutto, aggiungi una variabile globale del pacchetto httpClient per chiamare le richieste HTTP tramite il client sottoposto a ispezione.

step0/src/loadgen/main.go

var httpClient = http.Client{
        Transport: otelhttp.NewTransport(http.DefaultTransport)
}

Aggiungi poi la misurazione nella funzione runQuery per creare lo span personalizzato utilizzando OpenTelemetry e lo span generato automaticamente dal client HTTP personalizzato. Dovrai:

  1. Ricevi un Tracer da TracerProvider globale con otel.Tracer()
  2. Creare uno spazio principale con il metodo Tracer.Start()
  3. Termina lo spazio radice in un momento arbitrario (in questo caso, alla fine della funzione runQuery)

step0/src/loadgen/main.go

        reqURL.RawQuery = v.Encode()
        // step1. replace http.Get() with custom client call
        // resp, err := http.Get(reqURL.String())

        // step1. instrument trace
        ctx := context.Background()
        tr := otel.Tracer("loadgen")
        ctx, span := tr.Start(ctx, "query.request", trace.WithAttributes(
                semconv.TelemetrySDKLanguageGo,
                semconv.ServiceNameKey.String("loadgen.runQuery"),
                attribute.Key("query").String(s),
        ))
        defer span.End()
        ctx = httptrace.WithClientTrace(ctx, otelhttptrace.NewClientTrace(ctx))
        req, err := http.NewRequestWithContext(ctx, "GET", reqURL.String(), nil)
        if err != nil {
                return -1, fmt.Errorf("error creating HTTP request object: %v", err)
        }
        resp, err := httpClient.Do(req)
        // step1. end instrumentation
        if err != nil {
                return -1, fmt.Errorf("error sending request to %v: %v", reqURL.String(), err)
        }

Ora hai completato la misurazione in loadgen (applicazione client HTTP). Assicurati di aggiornare go.mod e go.sum con il comando go mod.

go mod tidy

Assistenza clienti per gli strumenti

Nella sezione precedente abbiamo strumentato la parte racchiusa nel rettangolo rosso nel disegno di seguito. Abbiamo eseguito l'instrumentazione delle informazioni sugli intervalli nel servizio di generazione di carico. Analogamente al servizio del generatore di carico, ora dobbiamo eseguire l'instrumentazione del servizio client. La differenza rispetto al servizio di generazione di carico è che il servizio client deve estrarre le informazioni sull'ID traccia propagate dal servizio di generazione di carico nell'intestazione HTTP e utilizzare l'ID per generare gli span.

bcaccd06691269f8.png

Apri l'editor di Cloud Shell e aggiungi i pacchetti richiesti come abbiamo fatto per il servizio di generatore di carico.

step0/src/client/main.go

import (
        "context"
        "encoding/json"
        "fmt"
        "io"
        "log"
        "net/http"
        "net/url"
        "os"
        "time"

        "opentelemetry-trace-codelab-go/client/shakesapp"
        // step1. add new import
        "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
        "go.opentelemetry.io/otel"
        "go.opentelemetry.io/otel/attribute"
        stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
        "go.opentelemetry.io/otel/propagation"
        sdktrace "go.opentelemetry.io/otel/sdk/trace"
        "go.opentelemetry.io/otel/trace"
        "google.golang.org/grpc"
        "google.golang.org/grpc/credentials/insecure"
        // step1. end new import
)

Ancora una volta, dobbiamo configurare OpenTelemtry. Basta copiare e incollare la funzione initTracer da loadgen e chiamarla anche nella funzione main del servizio client.

step0/src/client/main.go

// step1. add OpenTelemetry initialization function
func initTracer() (*sdktrace.TracerProvider, error) {
        // create a stdout exporter to show collected spans out to stdout.
        exporter, err := stdout.New(stdout.WithPrettyPrint())
        if err != nil {
                return nil, err
        }

        // for the demonstration, we use AlwaysSmaple sampler to take all spans.
        // do not use this option in production.
        tp := sdktrace.NewTracerProvider(
                sdktrace.WithSampler(sdktrace.AlwaysSample()),
                sdktrace.WithBatcher(exporter),
        )
        otel.SetTracerProvider(tp)
        otel.SetTextMapPropagator(propagation.TraceContext{})
        return tp, nil
}

Ora è il momento di eseguire l'instrumentazione degli intervalli. Poiché il servizio client deve accettare le richieste HTTP dal servizio loadgen, deve eseguire l'instrumentazione del gestore. Il server HTTP nel servizio client è implementato con net/http e puoi utilizzare il pacchetto otelhttp come abbiamo fatto in loadgen.

Innanzitutto, sostituiamo la registrazione dell'handler con otelhttp Handler. Nella funzione main, trova le righe in cui il gestore HTTP è registrato con http.HandleFunc().

step0/src/client/main.go

        // step1. change handler to intercept OpenTelemetry related headers
        // http.HandleFunc("/", svc.handler)
        otelHandler := otelhttp.NewHandler(http.HandlerFunc(svc.handler), "client.handler")
        http.Handle("/", otelHandler)
        // step1. end intercepter setting
        http.HandleFunc("/_genki", svc.health)

Poi, misuriamo lo spazio effettivo all'interno del gestore. Trova func (*clientService) handler() e aggiungi la misurazione degli span con trace.SpanFromContext().

step0/src/client/main.go

func (cs *clientService) handler(w http.ResponseWriter, r *http.Request) {
        ...
        ctx := r.Context()
        ctx, cancel := context.WithCancel(ctx)
        defer cancel()
        // step1. instrument trace
        span := trace.SpanFromContext(ctx)
        defer span.End()
        // step1. end instrument
        ...

Con questa misurazione, ottieni gli intervalli dall'inizio alla fine del metodo handler. Per semplificare l'analisi degli intervalli, aggiungi un attributo aggiuntivo che memorizzi il conteggio delle corrispondenze nella query. Subito prima della riga di log, aggiungi il seguente codice.

step0/src/client/main.go

func (cs *clientService) handler(w http.ResponseWriter, r *http.Request) {
        ...
        // step1. add span specific attribute
        span.SetAttributes(attribute.Key("matched").Int64(resp.MatchCount))
        // step1. end adding attribute
        log.Println(string(ret))
        ...

Con tutta la strumentazione sopra indicata, hai completato la strumentazione della traccia tra loadgen e il client. Vediamo come funziona. Esegui di nuovo il codice con skaffold.

skaffold dev

Dopo un po' di tempo per l'esecuzione dei servizi sul cluster GKE, vedrai un'enorme quantità di messaggi di log come questo:

Output comando

[loadgen] {
[loadgen]       "Name": "query.request",
[loadgen]       "SpanContext": {
[loadgen]               "TraceID": "cfa22247a542beeb55a3434392d46b89",
[loadgen]               "SpanID": "18b06404b10c418b",
[loadgen]               "TraceFlags": "01",
[loadgen]               "TraceState": "",
[loadgen]               "Remote": false
[loadgen]       },
[loadgen]       "Parent": {
[loadgen]               "TraceID": "00000000000000000000000000000000",
[loadgen]               "SpanID": "0000000000000000",
[loadgen]               "TraceFlags": "00",
[loadgen]               "TraceState": "",
[loadgen]               "Remote": false
[loadgen]       },
[loadgen]       "SpanKind": 1,
[loadgen]       "StartTime": "2022-07-14T13:13:36.686751087Z",
[loadgen]       "EndTime": "2022-07-14T13:14:31.849601964Z",
[loadgen]       "Attributes": [
[loadgen]               {
[loadgen]                       "Key": "telemetry.sdk.language",
[loadgen]                       "Value": {
[loadgen]                               "Type": "STRING",
[loadgen]                               "Value": "go"
[loadgen]                       }
[loadgen]               },
[loadgen]               {
[loadgen]                       "Key": "service.name",
[loadgen]                       "Value": {
[loadgen]                               "Type": "STRING",
[loadgen]                               "Value": "loadgen.runQuery"
[loadgen]                       }
[loadgen]               },
[loadgen]               {
[loadgen]                       "Key": "query",
[loadgen]                       "Value": {
[loadgen]                               "Type": "STRING",
[loadgen]                               "Value": "faith"
[loadgen]                       }
[loadgen]               }
[loadgen]       ],
[loadgen]       "Events": null,
[loadgen]       "Links": null,
[loadgen]       "Status": {
[loadgen]               "Code": "Unset",
[loadgen]               "Description": ""
[loadgen]       },
[loadgen]       "DroppedAttributes": 0,
[loadgen]       "DroppedEvents": 0,
[loadgen]       "DroppedLinks": 0,
[loadgen]       "ChildSpanCount": 5,
[loadgen]       "Resource": [
[loadgen]               {
[loadgen]                       "Key": "service.name",
[loadgen]                       "Value": {
[loadgen]                               "Type": "STRING",
[loadgen]                               "Value": "unknown_service:loadgen"
...

L'esportatore stdout emette questi messaggi. Noterai che gli elementi principali di tutti gli elementi di loadgen hanno TraceID: 00000000000000000000000000000000, perché si tratta dell'elemento principale, ovvero il primo elemento della traccia. Inoltre, noti che l'attributo di incorporamento "query" contiene la stringa di query passata al servizio clienti.

Riepilogo

In questo passaggio, hai strumentato il servizio di generazione di carico e il servizio client che comunicano in HTTP e hai verificato di poter propagare correttamente il contesto traccia tra i servizi ed esportare le informazioni sugli intervalli da entrambi i servizi in stdout.

Prossimi video

Nel passaggio successivo, strumenti il servizio client e il servizio server per verificare come propagare il contesto traccia tramite gRPC.

5. Strumentazione per gRPC

Nel passaggio precedente abbiamo strumentato la prima metà della richiesta in questi microservizi. In questo passaggio, cerchiamo di eseguire l'instrumentazione della comunicazione gRPC tra il servizio client e il servizio server. (rettangolo verde e viola nell'immagine di seguito)

75310d8e0e3b1a30.png

Strumentazione di precompilazione per il client gRPC

L'ecosistema di OpenTelemetry offre molte librerie utili che aiutano gli sviluppatori a eseguire l'instrumentazione delle applicazioni. Nel passaggio precedente abbiamo utilizzato la misurazione pre-build per il pacchetto net/http. In questo passaggio, poiché stiamo tentando di propagare il contesto traccia tramite gRPC, utilizziamo la libreria.

Per prima cosa, importa il pacchetto gRPC precompilato denominato otelgrpc.

step0/src/client/main.go

import (
        "context"
        "encoding/json"
        "fmt"
        "io"
        "log"
        "net/http"
        "net/url"
        "os"
        "time"

        "opentelemetry-trace-codelab-go/client/shakesapp"
        // step2. add prebuilt gRPC package (otelgrpc) 
        "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
        "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
        "go.opentelemetry.io/otel"
        "go.opentelemetry.io/otel/attribute"
        stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
        "go.opentelemetry.io/otel/propagation"
        sdktrace "go.opentelemetry.io/otel/sdk/trace"
        "go.opentelemetry.io/otel/trace"
        "google.golang.org/grpc"
        "google.golang.org/grpc/credentials/insecure"
)

Questa volta, il servizio client è un client gRPC per il servizio server, quindi devi eseguire l'instrumentazione del client gRPC. Trova la funzione mustConnGRPC e aggiungi intercettatori gRPC che strumentino nuovi span ogni volta che il client invia richieste al server.

step0/src/client/main.go

// Helper function for gRPC connections: Dial and create client once, reuse.
func mustConnGRPC(ctx context.Context, conn **grpc.ClientConn, addr string) {
        var err error
        // step2. add gRPC interceptor
        interceptorOpt := otelgrpc.WithTracerProvider(otel.GetTracerProvider())
        *conn, err = grpc.DialContext(ctx, addr,
                grpc.WithTransportCredentials(insecure.NewCredentials()),
                grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor(interceptorOpt)),
                grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor(interceptorOpt)),
                grpc.WithTimeout(time.Second*3),
        )
        // step2: end adding interceptor
        if err != nil {
                panic(fmt.Sprintf("Error %s grpc: failed to connect %s", err, addr))
        }
}

Poiché hai già configurato OpenTelemetry nella sezione precedente, non devi farlo.

Strumentazione predefinita per il server gRPC

Come abbiamo fatto per il client gRPC, chiamiamo la misurazione predefinita per il server gRPC. Aggiungi un nuovo pacchetto alla sezione di importazione, ad esempio:

step0/src/server/main.go

import (
        "context"
        "fmt"
        "io/ioutil"
        "log"
        "net"
        "os"
        "regexp"
        "strings"

        "opentelemetry-trace-codelab-go/server/shakesapp"

        "cloud.google.com/go/storage"
        // step2. add OpenTelemetry packages including otelgrpc
        "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
        "go.opentelemetry.io/otel"
        stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
        "go.opentelemetry.io/otel/propagation"
        sdktrace "go.opentelemetry.io/otel/sdk/trace"
        "google.golang.org/api/iterator"
        "google.golang.org/api/option"
        "google.golang.org/grpc"
        healthpb "google.golang.org/grpc/health/grpc_health_v1"
)

Poiché è la prima volta che esegui la strumentazione del server, devi prima configurare OpenTelemetry, in modo simile a quanto abbiamo fatto per loadgen e i servizi client.

step0/src/server/main.go

// step2. add OpenTelemetry initialization function
func initTracer() (*sdktrace.TracerProvider, error) {
        // create a stdout exporter to show collected spans out to stdout.
        exporter, err := stdout.New(stdout.WithPrettyPrint())
        if err != nil {
                return nil, err
        }
        // for the demonstration, we use AlwaysSmaple sampler to take all spans.
        // do not use this option in production.
        tp := sdktrace.NewTracerProvider(
                sdktrace.WithSampler(sdktrace.AlwaysSample()),
                sdktrace.WithBatcher(exporter),
        )
        otel.SetTracerProvider(tp)
        otel.SetTextMapPropagator(propagation.TraceContext{})
        return tp, nil
}

func main() {
        ...

        // step2. setup OpenTelemetry
        tp, err := initTracer()
        if err != nil {
                log.Fatalf("failed to initialize TracerProvider: %v", err)
        }
        defer func() {
                if err := tp.Shutdown(context.Background()); err != nil {
                        log.Fatalf("error shutting down TracerProvider: %v", err)
                }
        }()
        // step2. end setup
        ...

Poi devi aggiungere gli intercettatori del server. Nella funzione main, individua il punto in cui viene chiamata grpc.NewServer() e aggiungi gli intercettatori alla funzione.

step0/src/server/main.go

func main() {
        ...
        svc := NewServerService()
        // step2: add interceptor
        interceptorOpt := otelgrpc.WithTracerProvider(otel.GetTracerProvider())
        srv := grpc.NewServer(
                grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor(interceptorOpt)),
                grpc.StreamInterceptor(otelgrpc.StreamServerInterceptor(interceptorOpt)),
        )
        // step2: end adding interceptor
        shakesapp.RegisterShakespeareServiceServer(srv, svc)
        ...

Esegui il microservizio e conferma la traccia

Quindi esegui il codice modificato con il comando skaffold.

skaffold dev

Ancora una volta, visualizzi una serie di informazioni sugli intervalli in stdout.

Output comando

...
[server] {
[server]        "Name": "shakesapp.ShakespeareService/GetMatchCount",
[server]        "SpanContext": {
[server]                "TraceID": "89b472f213a400cf975e0a0041649667",
[server]                "SpanID": "96030dbad0061b3f",
[server]                "TraceFlags": "01",
[server]                "TraceState": "",
[server]                "Remote": false
[server]        },
[server]        "Parent": {
[server]                "TraceID": "89b472f213a400cf975e0a0041649667",
[server]                "SpanID": "cd90cc3859b73890",
[server]                "TraceFlags": "01",
[server]                "TraceState": "",
[server]                "Remote": true
[server]        },
[server]        "SpanKind": 2,
[server]        "StartTime": "2022-07-14T14:05:55.74822525Z",
[server]        "EndTime": "2022-07-14T14:06:03.449258891Z",
[server]        "Attributes": [
...
[server]        ],
[server]        "Events": [
[server]                {
[server]                        "Name": "message",
[server]                        "Attributes": [
...
[server]                        ],
[server]                        "DroppedAttributeCount": 0,
[server]                        "Time": "2022-07-14T14:05:55.748235489Z"
[server]                },
[server]                {
[server]                        "Name": "message",
[server]                        "Attributes": [
...
[server]                        ],
[server]                        "DroppedAttributeCount": 0,
[server]                        "Time": "2022-07-14T14:06:03.449255889Z"
[server]                }
[server]        ],
[server]        "Links": null,
[server]        "Status": {
[server]                "Code": "Unset",
[server]                "Description": ""
[server]        },
[server]        "DroppedAttributes": 0,
[server]        "DroppedEvents": 0,
[server]        "DroppedLinks": 0,
[server]        "ChildSpanCount": 0,
[server]        "Resource": [
[server]                {
...
[server]        ],
[server]        "InstrumentationLibrary": {
[server]                "Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
[server]                "Version": "semver:0.33.0",
[server]                "SchemaURL": ""
[server]        }
[server] }
...

Ti accorgi di non aver incorporato nomi di span e di aver creato manualmente span con trace.Start() o span.SpanFromContext(). Tuttavia, ottieni un numero elevato di span perché sono stati generati dagli intercettatori gRPC.

Riepilogo

In questo passaggio hai strumentato la comunicazione basata su gRPC con il supporto delle librerie dell'ecosistema OpenTelemetry.

Prossimi video

Nel passaggio successivo, visualizzerai finalmente la traccia con Cloud Trace e imparerai ad analizzare gli span raccolti.

6. Visualizzare la traccia con Cloud Trace

Hai eseguito l'instrumentazione delle tracce nell'intero sistema con OpenTelemetry. Finora hai imparato a eseguire l'instrumentazione dei servizi HTTP e gRPC. Anche se hai imparato a eseguirne l'instrumentazione, non hai ancora imparato ad analizzarli. In questa sezione sostituirai gli esportatori di output standard con gli esportatori di Cloud Trace e imparerai ad analizzare le tue tracce.

Utilizzare l'esportatore Cloud Trace

Una delle caratteristiche più potenti di OpenTelemetry è la sua pluggabilità. Per visualizzare tutti gli span raccolti dalla tua strumentazione, devi solo sostituire l'esportatore di output standard con l'esportatore di Cloud Trace.

Apri i file main.go di ogni servizio e trova la funzione initTracer(). Elimina la riga per generare un esportatore di output standard e crea invece l'esportatore di Cloud Trace.

step0/src/loadgen/main.go

import (
        ...
        // step3. add OpenTelemetry for Cloud Trace package
        cloudtrace "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace"
)

// step1. add OpenTelemetry initialization function
func initTracer() (*sdktrace.TracerProvider, error) {
        // step3. replace stdout exporter with Cloud Trace exporter
        // cloudtrace.New() finds the credentials to Cloud Trace automatically following the
        // rules defined by golang.org/x/oauth2/google.findDefaultCredentailsWithParams.
        // https://pkg.go.dev/golang.org/x/oauth2/google#FindDefaultCredentialsWithParams
        exporter, err := cloudtrace.New()
        // step3. end replacing exporter
        if err != nil {
                return nil, err
        }

        // for the demonstration, we use AlwaysSmaple sampler to take all spans.
        // do not use this option in production.
        tp := sdktrace.NewTracerProvider(
                sdktrace.WithSampler(sdktrace.AlwaysSample()),
                sdktrace.WithBatcher(exporter),
        )
        otel.SetTracerProvider(tp)
        otel.SetTextMapPropagator(propagation.TraceContext{})
        return tp, nil
}

Devi modificare la stessa funzione anche nel servizio client e server.

Esegui il microservizio e conferma la traccia

Dopo la modifica, esegui il cluster come di consueto con il comando skaffold.

skaffold dev

Ora non vedi molte informazioni sugli intervalli in formato di log strutturato su stdout, perché hai sostituito l'esportatore con quello di Cloud Trace.

Output comando

[loadgen] 2022/07/14 15:01:07 simulated 20 requests
[loadgen] 2022/07/14 15:01:07 simulating client requests, round 37
[loadgen] 2022/07/14 15:01:14 query 'sweet': matched 958
[client] 2022/07/14 15:01:14 {"match_count":958}
[client] 2022/07/14 15:01:14 {"match_count":3040}
[loadgen] 2022/07/14 15:01:14 query 'love': matched 3040
[client] 2022/07/14 15:01:15 {"match_count":349}
[loadgen] 2022/07/14 15:01:15 query 'hello': matched 349
[client] 2022/07/14 15:01:15 {"match_count":484}
[loadgen] 2022/07/14 15:01:15 query 'faith': matched 484
[loadgen] 2022/07/14 15:01:15 query 'insolence': matched 14
[client] 2022/07/14 15:01:15 {"match_count":14}
[client] 2022/07/14 15:01:21 {"match_count":484}
[loadgen] 2022/07/14 15:01:21 query 'faith': matched 484
[client] 2022/07/14 15:01:21 {"match_count":728}
[loadgen] 2022/07/14 15:01:21 query 'world': matched 728
[client] 2022/07/14 15:01:22 {"match_count":484}
[loadgen] 2022/07/14 15:01:22 query 'faith': matched 484
[loadgen] 2022/07/14 15:01:22 query 'hello': matched 349
[client] 2022/07/14 15:01:22 {"match_count":349}
[client] 2022/07/14 15:01:23 {"match_count":1036}
[loadgen] 2022/07/14 15:01:23 query 'friend': matched 1036
[loadgen] 2022/07/14 15:01:28 query 'tear': matched 463
...

Ora verifichiamo se tutti gli span vengono inviati correttamente a Cloud Trace. Accedi alla console Cloud e vai a "Elenco trace". È facile accedervi dalla casella di ricerca. In caso contrario, puoi fare clic sul menu nel riquadro a sinistra. 8b3f8411bd737e06.png

Vedrai molti punti blu distribuiti nel grafico della latenza. Ogni spot rappresenta una singola traccia.

3ecf131423fc4c40.png

Fai clic su una di queste per visualizzare i dettagli all'interno della traccia. 4fd10960c6648a03.png

Anche da questo semplice sguardo veloce, puoi già conoscere molti approfondimenti. Ad esempio, dal grafico a cascata, puoi vedere che la causa della latenza è dovuta principalmente all'intervallo denominato shakesapp.ShakespeareService/GetMatchCount. (vedi 1 nell'immagine sopra). Puoi confermarlo dalla tabella di riepilogo. La colonna più a destra mostra la durata di ogni intervallo. Inoltre, questa traccia è relativa alla query "amico". (vedi 2 nell'immagine sopra)

Da queste brevi analisi, potresti capire che devi conoscere intervalli più granulari all'interno del metodo GetMatchCount. Rispetto alle informazioni di stdout, la visualizzazione è molto efficace. Per scoprire di più sui dettagli di Cloud Trace, consulta la nostra documentazione ufficiale.

Riepilogo

In questo passaggio, hai sostituito l'esportatore di output standard con quello di Cloud Trace e hai visualizzato le tracce su Cloud Trace. Inoltre, hai imparato come iniziare ad analizzare le tracce.

Prossimi video

Nel passaggio successivo, modificherai il codice sorgente del servizio del server per aggiungere un sottosegmento in GetMatchCount.

7. Aggiungi un intervallo secondario per un'analisi migliore

Nel passaggio precedente hai scoperto che la causa del tempo di percorrenza osservato da loadgen è principalmente il processo all'interno del metodo GetMatchCount, l'handler gRPC, nel servizio del server. Tuttavia, poiché non abbiamo eseguito l'instrumentazione di altro che dell'handler, non siamo in grado di trovare ulteriori informazioni dal grafico a cascata. Questo è un caso comune quando iniziamo a eseguire l'instrumentazione dei microservizi.

3b63a1e471dddb8c.png

In questa sezione, strumentalizzeremo un sottointervallo in cui il server chiama Google Cloud Storage, perché è comune che alcune operazioni di I/O di rete esterne richiedano molto tempo durante il processo ed è importante identificare se la causa è la chiamata.

Strumentare uno span secondario nel server

Apri main.go nel server e trova la funzione readFiles. Questa funzione chiama una richiesta a Google Cloud Storage per recuperare tutti i file di testo delle opere di Shakespeare. In questa funzione, puoi creare uno span secondario, come hai fatto per la misurazione del server HTTP nel servizio client.

step0/src/server/main.go

func readFiles(ctx context.Context, bucketName, prefix string) ([]string, error) {
        type resp struct {
                s   string
                err error
        }

        // step4: add an extra span
        span := trace.SpanFromContext(ctx)
        span.SetName("server.readFiles")
        span.SetAttributes(attribute.Key("bucketname").String(bucketName))
        defer span.End()
        // step4: end add span
        ...

È tutto per quanto riguarda l'aggiunta di un nuovo span. Vediamo come va eseguendo l'app.

Esegui il microservizio e conferma la traccia

Dopo la modifica, esegui il cluster come di consueto con il comando skaffold.

skaffold dev

Scegli una traccia denominata query.request dall'elenco delle tracce. Vedrai un grafico a cascata della traccia simile, tranne per un nuovo intervallo sotto shakesapp.ShakespeareService/GetMatchCount. (L'intervallo racchiuso dal rettangolo rosso di seguito)

3d4a891aa30d7a32.png

Ora puoi capire da questo grafico che la chiamata esterna a Google Cloud Storage occupa una grande quantità di latenza, ma che la maggior parte della latenza è causata da altri fattori.

Hai già ottenuto molti approfondimenti solo da un paio di visualizzazioni del grafico a cascata della traccia. Come faccio a ottenere ulteriori dettagli sul rendimento nella mia applicazione? È qui che entra in gioco Profiler, ma per il momento interrompiamo questo codelab e rimandiamo tutti i tutorial su Profiler alla parte 2.

Riepilogo

In questo passaggio hai eseguito l'instrumentazione di un altro span nel servizio del server e hai ottenuto ulteriori informazioni sulla latenza del sistema.

8. Complimenti

Hai creato tracce distribuite con OpenTelemetry e hai confermato le latenze delle richieste nel microservizio su Google Cloud Trace.

Per esercizi più approfonditi, puoi provare autonomamente i seguenti argomenti.

  • L'implementazione attuale invia tutti gli span generati dal controllo di integrità. (grpc.health.v1.Health/Check) Come filtri questi intervalli da Cloud Trace? Il suggerimento è qui.
  • Correla i log eventi con gli span e scopri come funzionano in Google Cloud Trace e Google Cloud Logging. Il suggerimento è qui.
  • Sostituisci un servizio con quello in un'altra lingua e prova a eseguirlo con OpenTelemetry per quella lingua.

Inoltre, se vuoi saperne di più sul profiler, vai alla parte 2. In questo caso, puoi saltare la sezione di pulizia di seguito.

Pulizia

Al termine di questo codelab, interrompi il cluster Kubernetes e assicurati di eliminare il progetto per evitare addebiti imprevisti su Google Kubernetes Engine, Google Cloud Trace e Google Artifact Registry.

Per prima cosa, elimina il cluster. Se esegui il cluster con skaffold dev, devi solo premere Ctrl-C. Se esegui il cluster con skaffold run, esegui il seguente comando:

skaffold delete

Output comando

Cleaning up...
 - deployment.apps "clientservice" deleted
 - service "clientservice" deleted
 - deployment.apps "loadgen" deleted
 - deployment.apps "serverservice" deleted
 - service "serverservice" deleted

Dopo aver eliminato il cluster, seleziona "IAM e amministrazione" > "Impostazioni" nel riquadro del menu e poi fai clic sul pulsante "SCONOSCI".

45aa37b7d5e1ddd1.png

Poi inserisci l'ID progetto (non il nome del progetto) nel modulo nella finestra di dialogo e conferma l'arresto.