1. Introduzione

Ultimo aggiornamento: 15/07/2022
Osservabilità dell'applicazione
Osservabilità e OpenTelemetry
Osservabilità è il termine utilizzato per descrivere un attributo di un sistema. Un sistema con osservabilità consente ai team di eseguire il debug attivo del sistema. In questo contesto, i tre pilastri dell'osservabilità (log, metriche e tracce) sono l'instrumentazione fondamentale per acquisire l'osservabilità del sistema.
OpenTelemetry è un insieme di specifiche, librerie e agenti che accelerano la strumentazione e l'esportazione dei dati di telemetria (log, metriche e tracce) richiesti dall'osservabilità. OpenTelemetry è un progetto open standard e guidato dalla community nell'ambito della CNCF. Utilizzando le librerie fornite dal progetto e dal suo ecosistema, gli sviluppatori sono in grado di instrumentare le proprie 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 sta ampliando la base utenti del settore. Cloud Profiler è uno degli strumenti di profilazione e fornisce un'interfaccia semplice per visualizzare in dettaglio le metriche delle prestazioni negli stack di chiamate delle applicazioni.
Questo codelab è la prima parte della serie e tratta l'instrumentazione delle tracce distribuite nei microservizi con OpenTelemetry e Cloud Trace. La parte 2 tratterà la profilazione continua con Cloud Profiler.
Trace distribuito
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 tracciamento distribuito è il principale motore per scoprire i colli di bottiglia della latenza nel sistema distribuito complessivo.
Quando analizzi le tracce distribuite, la visualizzazione dei dati di traccia è fondamentale per comprendere a colpo d'occhio le latenze complessive del sistema. Nella traccia distribuita, gestiamo un insieme di chiamate per elaborare una singola richiesta al punto di ingresso del sistema sotto forma di traccia contenente più intervalli.
Lo span rappresenta una singola unità di lavoro eseguita in un sistema distribuito, registrando gli orari di inizio e fine. Gli span spesso hanno relazioni gerarchiche tra loro. Nell'immagine seguente, tutti gli span più piccoli sono span secondari di uno span /messages di grandi dimensioni e vengono assemblati in una traccia che mostra il percorso di lavoro in un sistema.

Google Cloud Trace è una delle opzioni per il backend di tracciamento distribuito ed è ben integrato con altri prodotti in Google Cloud.
Cosa creerai
In questo codelab, strumenterai 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:

- Loadgen invia una stringa di query al client in HTTP
- I client passano la query da loadgen al server in gRPC
- Il server accetta la query dal client, recupera tutte le opere di Shakespeare in formato di testo da Google Cloud Storage, cerca le righe che contengono la query e restituisce al client il numero della riga corrispondente
Strumenterai le informazioni di tracciamento nella richiesta. Dopodiché, incorporerai un agente Profiler nel server e analizzerai il collo di bottiglia.
Cosa imparerai a fare
- Come iniziare a utilizzare le librerie OpenTelemetry Trace nel progetto Go
- Come creare uno span con la libreria
- Come propagare i contesti degli 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 instrumentare i microservizi. Per semplificare la comprensione, questo esempio contiene solo tre componenti (generatore di carico, client e server), ma puoi applicare lo stesso processo spiegato 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 autonomo
Se non hai ancora un Account Google (Gmail o Google Apps), devi crearne uno. Accedi alla console di Google Cloud Platform ( console.cloud.google.com) e crea un nuovo progetto.
Se hai già un progetto, fai clic sul menu a discesa per la selezione dei progetti in alto a sinistra nella console:

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

Se non hai ancora un progetto, dovresti visualizzare una finestra di dialogo come questa per creare il primo:

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

Ricorda l'ID progetto, che è un nome univoco tra tutti i progetti Google Cloud (il nome sopra è già stato utilizzato e non funzionerà per te, mi dispiace). In questo codelab verrà indicato come PROJECT_ID.
Successivamente, se non l'hai ancora fatto, devi abilitare la fatturazione in Developers Console per utilizzare le risorse Google Cloud e abilitare l'API Cloud Trace.

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 indicati nella documentazione ufficiale.
- Prezzi della suite operativa di Google Cloud | Operations Suite
- Prezzi | Documentazione di Kubernetes Engine
- Prezzi di Artifact Registry | Documentazione di Artifact Registry
I nuovi utenti di Google Cloud Platform 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 gestiti 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 avrai 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 servirà solo un browser (sì, funziona su Chromebook).
Per attivare Cloud Shell dalla console Cloud, fai clic su Attiva Cloud Shell
(bastano pochi istanti per eseguire il provisioning e connettersi all'ambiente).


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 questo comando:
gcloud config set project <PROJECT_ID>
Stai cercando il tuo PROJECT_ID? Controlla l'ID che hai utilizzato nei passaggi di configurazione o cercalo nella dashboard della console Cloud:

Cloud Shell imposta anche alcune variabili di ambiente per impostazione predefinita, che potrebbero essere utili quando esegui i 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 saperne di più, consulta Regioni e zone.
Configurazione della lingua Go
In questo codelab, utilizziamo Go per tutto il codice sorgente. Esegui questo comando su Cloud Shell e verifica se la versione di Go è 1.17 o successive.
go version
Output comando
go version go1.18.3 linux/amd64
Configura un cluster Google Kubernetes
In questo codelab, eseguirai un cluster di microservizi su Google Kubernetes Engine (GKE). La procedura di questo codelab è la seguente:
- Scarica il progetto di base in Cloud Shell
- Creare microservizi in container
- Carica i container in Google Artifact Registry (GAR)
- Esegui il deployment dei container su GKE
- Modificare il codice sorgente dei servizi per l'instrumentazione delle tracce
- Vai al passaggio 2
Abilita Kubernetes Engine
Innanzitutto, configuriamo un cluster Kubernetes in cui Shakesapp viene eseguito su GKE, quindi dobbiamo abilitare GKE. Vai al menu "Kubernetes Engine" e premi il pulsante ATTIVA.

Ora puoi creare un cluster Kubernetes.
Crea un cluster Kubernetes
In Cloud Shell, esegui questo comando per creare un cluster Kubernetes. Conferma che il valore della zona rientra 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. Successivamente, ci prepariamo per un container registry per il push e il deployment dei container. Per questi passaggi, dobbiamo configurare un registro Artifact Registry (GAR) e Skaffold per utilizzarlo.
Configurazione di Artifact Registry
Vai al menu di "Artifact Registry" e premi il pulsante ATTIVA.

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

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

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

Torneremo qui più tardi per controllare il percorso del registro.
Configurazione di Skaffold
Skaffold è uno strumento utile quando lavori alla creazione di microservizi eseguiti su Kubernetes. Gestisce il workflow di creazione, push ed esecuzione del deployment dei container delle applicazioni con un piccolo insieme di comandi. Per impostazione predefinita, Skaffold utilizza Docker Registry come registro dei container, quindi devi configurare Skaffold in modo che riconosca GAR quando esegui il push dei container.
Riapri Cloud Shell e verifica se Skaffold è installato. Cloud Shell installa Skaffold nell'ambiente per impostazione predefinita. Esegui questo comando e visualizza la versione di Skaffold.
skaffold version
Output comando
v1.38.0
Ora puoi registrare il repository predefinito da utilizzare per Skaffold. Per ottenere il percorso del registro, vai alla dashboard di Artifact Registry e fai clic sul nome del repository che hai appena configurato nel passaggio precedente.

Poi vedrai le tracce di breadcrumb nella parte superiore della pagina. Fai clic sull'icona
per copiare il percorso del registro negli appunti.

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 registro nella configurazione 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 al passaggio successivo per configurare un container Kubernetes su GKE.
Riepilogo
In questo passaggio, configurerai l'ambiente del codelab:
- Configura Cloud Shell
- È stato creato un repository Artifact Registry per il registro dei container
- Configura Skaffold per utilizzare Container Registry
- Creazione di un cluster Kubernetes in cui vengono eseguiti i microservizi del codelab
Avanti
Nel passaggio successivo, creerai, invierai 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 microservizi completi sopra. Il materiale del codelab è ospitato su GitHub, quindi scaricalo 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 delle 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 ogni servizio
- skaffold.yaml: file di configurazione per Skaffold
In questo codelab, aggiornerai il codice sorgente che si trova nella cartella step0. Puoi anche fare riferimento al codice sorgente nelle cartelle step[1-6] per le risposte nei passaggi successivi. La parte 1 copre i passaggi da 0 a 4, mentre la parte 2 copre i passaggi 5 e 6.
Esegui il comando skaffold
Infine, puoi creare, eseguire il push e il deployment dell'intero contenuto nel 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 sono stati inviati correttamente al registro.
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 dell'applicazione effettivi emessi in stdout in ogni container nel seguente modo:
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 visualizzare tutti i messaggi del server. Ok, ora puoi iniziare a instrumentare la tua applicazione con OpenTelemetry per il tracciamento distribuito dei servizi.
Prima di iniziare a instrumentare il 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 venga eseguito come previsto.
Avanti
Nel passaggio successivo, modificherai il codice sorgente del servizio loadgen per instrumentare le informazioni di traccia.
4. Strumentazione per HTTP
Concetto di strumentazione e propagazione delle tracce
Prima di modificare il codice sorgente, ti spiego brevemente come funzionano le tracce distribuite in un semplice diagramma.

In questo esempio, strumentiamo il codice per esportare le informazioni su traccia e span in Cloud Trace e propagare il contesto di traccia nella richiesta dal servizio loadgen al servizio server.
Le applicazioni devono inviare metadati di traccia come ID traccia e ID span per consentire a Cloud Trace di assemblare tutti gli span con lo stesso ID traccia in una traccia. Inoltre, l'applicazione deve propagare i contesti di traccia (la combinazione di ID traccia e ID intervallo dell'intervallo principale) quando richiede servizi downstream, in modo che possano essere a conoscenza del contesto di 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 di traccia ad altri servizi
- per incorporare metadati aggiuntivi che aiutano ad analizzare le tracce
Componenti in OpenTelemetry Trace

La procedura per instrumentare la traccia dell'applicazione con OpenTelemetry è la seguente:
- Crea un esportatore
- Crea un TracerProvider associando l'esportatore in 1 e impostalo come globale.
- Imposta TextMapPropagaror per impostare il metodo di propagazione
- Recupera il tracer da TracerProvider
- Genera span dal tracer
Al momento non è necessario comprendere le proprietà dettagliate di ogni componente, ma la cosa più importante da ricordare è:
- 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
Ora che abbiamo capito questo, passiamo al lavoro di codifica vero e proprio.
Intervallo del primo strumento
Servizio di generazione del carico dello strumento
Apri l'editor di Cloud Shell premendo il pulsante
in alto a destra di Cloud Shell. Apri step0/src/loadgen/main.go dall'explorer 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, vedi il ciclo che chiama la funzione run. Nell'implementazione attuale, la sezione contiene due righe di log che registrano l'inizio e la fine della chiamata di funzione. Ora strumentiamo le informazioni sullo span per monitorare la latenza della chiamata di funzione.
Innanzitutto, come indicato nella sezione precedente, configuriamo tutte le impostazioni per OpenTelemetry. Aggiungi i pacchetti OpenTelemetry nel seguente modo:
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 è quella 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 il initTracer() e assicurati di chiamare il TracerProvider.Shutdown() quando chiudi la richiesta.
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)
...
Una volta completata la configurazione, devi creare uno span con un ID traccia e un ID span univoci. OpenTelemetry fornisce una libreria utile per questo scopo. Aggiungi nuovi pacchetti aggiuntivi 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 abilitiamo la strumentazione con l'estensione del pacchetto httptrace e otelhttp.
Innanzitutto, aggiungi una variabile globale del pacchetto httpClient per chiamare le richieste HTTP tramite il client instrumentato.
step0/src/loadgen/main.go
var httpClient = http.Client{
Transport: otelhttp.NewTransport(http.DefaultTransport)
}
Aggiungi poi la strumentazione nella funzione runQuery per creare lo span personalizzato utilizzando OpenTelemetry e lo span generato automaticamente dal client HTTP personalizzato. Ecco cosa devi fare:
- Ricevi un Tracer da
TracerProviderglobale conotel.Tracer() - Crea uno span radice con il metodo
Tracer.Start() - Termina lo span 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 l'instrumentazione in loadgen (applicazione client HTTP). Assicurati di aggiornare go.mod e go.sum con il comando go mod.
go mod tidy
Servizio clienti dello strumento
Nella sezione precedente, abbiamo instrumentato la parte racchiusa nel rettangolo rosso nel disegno riportato di seguito. Abbiamo instrumentato le informazioni sugli span nel servizio di generazione del carico. Analogamente al servizio di generazione del carico, ora dobbiamo instrumentare il servizio client. La differenza rispetto al servizio di generazione del carico è che il servizio client deve estrarre le informazioni sull'ID traccia propagate dal servizio di generazione del carico nell'intestazione HTTP e utilizzare l'ID per generare gli intervalli.

Apri l'editor di Cloud Shell e aggiungi i pacchetti richiesti come abbiamo fatto per il servizio di generazione del 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
)
Anche in questo caso, dobbiamo configurare OpenTelemetry. 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 instrumentare gli span. Poiché il servizio client deve accettare le richieste HTTP dal servizio loadgen, deve instrumentare il 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 del gestore con il gestore otelhttp. 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, strumentiamo l'intervallo effettivo all'interno del gestore. Trova func (*clientService) handler() e aggiungi la strumentazione dello 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 strumentazione, ottieni gli span dall'inizio alla fine del metodo handler. Per semplificare l'analisi degli intervalli, aggiungi alla query un attributo aggiuntivo che memorizzi il conteggio delle corrispondenze. Subito prima della riga di log, aggiungi il seguente codice.
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 precedente, hai completato la strumentazione di traccia tra loadgen e 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 una grande 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 genera questi messaggi. Noterai che i genitori di tutti gli intervalli di loadgen hanno TraceID: 00000000000000000000000000000000, perché questo è l'intervallo radice, ovvero il primo intervallo nella traccia. Inoltre, l'attributo di incorporamento "query" contiene la stringa di query passata al servizio client.
Riepilogo
In questo passaggio, hai instrumentato il servizio di generazione del carico e il servizio client che comunicano in HTTP e hai verificato di poter propagare correttamente il contesto di traccia tra i servizi ed esportare le informazioni sugli intervalli da entrambi i servizi in stdout.
Avanti
Nel passaggio successivo, strumenterai il servizio client e il servizio server per verificare come propagare il contesto di traccia tramite gRPC.
5. Strumentazione per gRPC
Nel passaggio precedente, abbiamo instrumentato la prima metà della richiesta in questi microservizi. In questo passaggio, proviamo a instrumentare la comunicazione gRPC tra il servizio client e il servizio server. (Rettangolo verde e viola nell'immagine qui sotto)

Strumentazione pre-build per il client gRPC
L'ecosistema di OpenTelemetry offre molte librerie utili che aiutano gli sviluppatori a instrumentare le applicazioni. Nel passaggio precedente, abbiamo utilizzato la strumentazione predefinita per il pacchetto net/http. In questo passaggio, poiché stiamo tentando di propagare il contesto di 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 rispetto al servizio server, quindi devi instrumentare il client gRPC. Trova la funzione mustConnGRPC e aggiungi intercettori gRPC che strumentano nuovi intervalli ogni volta che il client effettua 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 strumentazione predefinita per il server gRPC. Aggiungi il nuovo pacchetto alla sezione di importazione come segue:
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 instrumenti il server, devi prima configurare OpenTelemetry, in modo simile a quanto fatto per i servizi loadgen e 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
...
Successivamente, devi aggiungere gli intercettori del server. Nella funzione main, trova il punto in cui viene chiamato grpc.NewServer() e aggiungi gli intercettori 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
Poi esegui il codice modificato con il comando skaffold.
skaffold dev
Ora, di nuovo, vedi 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] }
...
Noti di non aver incorporato nomi di intervalli e di aver creato manualmente intervalli con trace.Start() o span.SpanFromContext(). Tuttavia, ottieni un numero elevato di intervalli perché sono stati generati dagli intercettori gRPC.
Riepilogo
In questo passaggio, hai instrumentato la comunicazione basata su gRPC con il supporto delle librerie dell'ecosistema OpenTelemetry.
Avanti
Nel passaggio successivo, visualizzerai finalmente la traccia con Cloud Trace e imparerai ad analizzare gli intervalli raccolti.
6. Visualizzare la traccia con Cloud Trace
Hai instrumentato le tracce in tutto il sistema con OpenTelemetry. Finora hai imparato a instrumentare i servizi HTTP e gRPC. Anche se hai imparato a strumentarli, non hai ancora imparato ad analizzarli. In questa sezione, sostituirai gli esportatori stdout con gli esportatori Cloud Trace e imparerai ad analizzare le tracce.
Utilizzare l'esportatore Cloud Trace
Una delle caratteristiche più potenti di OpenTelemetry è la sua pluggabilità. Per visualizzare tutti gli span raccolti dalla strumentazione, devi solo sostituire l'esportatore stdout con l'esportatore Cloud Trace.
Apri i file main.go di ogni servizio e trova la funzione initTracer(). Elimina la riga per generare un esportatore stdout e crea invece l'esportatore 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 al solito con il comando skaffold.
skaffold dev
Ora non vedi molte informazioni sugli intervalli nel formato dei log strutturati 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 alternativa, puoi fare clic sul menu nel riquadro a sinistra. 
Poi vedi molti punti blu distribuiti nel grafico della latenza. Ogni punto rappresenta una singola traccia.

Fai clic su uno di questi e potrai visualizzare i dettagli all'interno della traccia. 
Anche da questa semplice occhiata, puoi già ricavare molte informazioni. Ad esempio, dal grafico a cascata puoi vedere che la causa della latenza è principalmente lo span denominato shakesapp.ShakespeareService/GetMatchCount. (Vedi 1 nell'immagine sopra) Puoi verificarlo nella tabella di riepilogo. L'ultima colonna a destra mostra la durata di ogni intervallo. Inoltre, questa traccia riguardava la query "amico". (vedi il punto 2 nell'immagine sopra)
Da queste brevi analisi, potresti renderti conto di dover conoscere intervalli più granulari all'interno del metodo GetMatchCount. Rispetto alle informazioni stdout, la visualizzazione è potente. Per saperne di più sui dettagli di Cloud Trace, visita la nostra documentazione ufficiale.
Riepilogo
In questo passaggio, hai sostituito l'esportatore stdout con quello di Cloud Trace e hai visualizzato le tracce su Cloud Trace. Inoltre, hai imparato a iniziare ad analizzare le tracce.
Avanti
Nel passaggio successivo, modificherai il codice sorgente del servizio server per aggiungere un intervallo secondario in GetMatchCount.
7. Aggiungi un intervallo secondario per un'analisi migliore
Nel passaggio precedente, hai scoperto che la causa del tempo di andata e ritorno osservato da loadgen è principalmente il processo all'interno del metodo GetMatchCount, il gestore gRPC, nel servizio server. Tuttavia, poiché non abbiamo strumentato nient'altro oltre al gestore, non siamo in grado di trovare ulteriori approfondimenti dal grafico a cascata. Questo è un caso comune quando iniziamo a strumentare i microservizi.

In questa sezione, strumenteremo un intervallo secondario in cui il server chiama Google Cloud Storage, perché è comune che alcune operazioni di I/O di rete esterna richiedano molto tempo nel processo ed è importante identificare se la chiamata è la causa.
Strumentare un intervallo 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 un intervallo secondario, come hai fatto per la strumentazione 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 l'aggiunta di un nuovo intervallo. Vediamo come va eseguendo l'app.
Esegui il microservizio e conferma la traccia
Dopo la modifica, esegui il cluster come al solito con il comando skaffold.
skaffold dev
e scegli una traccia denominata query.request dall'elenco delle tracce. Vedrai un grafico a cascata di traccia simile, ad eccezione di un nuovo intervallo in shakesapp.ShakespeareService/GetMatchCount. (Il periodo racchiuso nel rettangolo rosso qui sotto)

Da questo grafico puoi notare che la chiamata esterna a Google Cloud Storage occupa una grande quantità di latenza, ma anche altri fattori contribuiscono alla maggior parte della latenza.
Hai già ottenuto molte informazioni utili solo da un paio di occhiate al grafico a cascata della traccia. Come si ottengono ulteriori dettagli sul rendimento nella tua applicazione? Qui entra in gioco Profiler, ma per ora concludiamo questo codelab e deleghiamo tutti i tutorial su Profiler alla parte 2.
Riepilogo
In questo passaggio, hai instrumentato un altro span nel servizio server e hai ottenuto ulteriori informazioni sulla latenza del sistema.
8. Complimenti
Hai creato correttamente tracce distribuite con OpenTelemetry e hai confermato le latenze delle richieste nel microservizio su Google Cloud Trace.
Per esercizi più lunghi, puoi provare i seguenti argomenti da solo.
- L'implementazione attuale invia tutti gli span generati dal controllo di integrità. (
grpc.health.v1.Health/Check) Come si filtrano questi intervalli da Cloud Trace? Il suggerimento è qui. - Metti in correlazione i log eventi con gli intervalli e scopri come funziona su Google Cloud Trace e Google Cloud Logging. Il suggerimento è qui.
- Sostituisci un servizio con quello in un'altra lingua e prova a strumentarlo con OpenTelemetry per quella lingua.
Inoltre, se vuoi saperne di più sul Profiler, passa alla parte 2. In questo caso, puoi saltare la sezione per liberare spazio riportata di seguito.
Pulizia
Al termine di questo codelab, arresta il cluster Kubernetes e assicurati di eliminare il progetto in modo da non ricevere addebiti imprevisti in Google Kubernetes Engine, Google Cloud Trace e Google Artifact Registry.
Innanzitutto, elimina il cluster. Se esegui il cluster con skaffold dev, devi solo premere Ctrl+C. Se esegui il cluster con skaffold run, esegui questo 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" dal riquadro del menu e poi fai clic sul pulsante "ARRESTA".

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