Tracciamento distribuito in un'applicazione di microservizi

Data di aggiornamento: 24-10-2023

Questo è il quarto di una serie di quattro parti sulla progettazione, la creazione e il deployment di microservizi. Questa serie descrive i vari elementi dell'architettura dei microservizi. La serie include informazioni sui vantaggi e sugli svantaggi dei pattern di architettura basate su microservizi e su come applicarli.

  1. Introduzione ai microservizi
  2. Refactoring di un monolite in microservizi
  3. Comunicazione tra servizi in una configurazione di microservizi
  4. Tracciamento distribuito in un'applicazione di microservizi (questo documento)

Questa serie è destinata agli sviluppatori di applicazioni e agli architetti che progettano e implementano la migrazione per il refactoring di un'applicazione monolitica a un'applicazione di microservizi.

In un sistema distribuito, è importante sapere come passa una richiesta da un servizio a un altro e il tempo necessario per eseguire un'attività in ciascun servizio. Considera l'applicazione Online Boutique basata su microservizi di cui hai eseguito il deployment nel documento precedente, Esegui il refactoring di un monolite in microservizi. L'applicazione è composta da più servizi. Ad esempio, il seguente screenshot mostra la pagina dei dettagli del prodotto, che recupera le informazioni dal frontend, dai suggerimenti e dai servizi pubblicitari.

La pagina dei dettagli del prodotto.

Per visualizzare la pagina dei dettagli del prodotto, il servizio frontend comunica con il servizio di suggerimenti e con il servizio di annunci, come illustrato nel seguente diagramma:

Il servizio di frontend comunica con il servizio di suggerimenti, il catalogo dei prodotti e il servizio di annunci.

Figura 1. Servizi scritti in diverse lingue.

Nella figura 1, il servizio frontend è scritto in Go. Il servizio di suggerimenti, scritto in Python, utilizza gRPC per comunicare con il servizio frontend. Anche il servizio di annunci, scritto in Java, utilizza gRPC per comunicare con il servizio frontend. Oltre a gRPC, il metodo di comunicazione tra i servizi può essere anche in HTTP REST.

Quando crei un sistema così distribuito, vuoi che gli strumenti di osservabilità forniscano i seguenti insight:

  • I servizi oggetto di una richiesta.
  • Dove si sono verificati ritardi se una richiesta era lenta.
  • Dove si è verificato un errore se la richiesta non è andata a buon fine.
  • Il modo in cui l'esecuzione della richiesta è diversa dal normale comportamento del sistema.
  • Il fatto che le differenze nell'esecuzione della richiesta fossero correlate alle prestazioni (a prescindere dal fatto che alcune chiamate di servizio richiedessero un tempo più lungo o più breve rispetto al solito).

Obiettivi

  • Utilizza i file manifest kustomize per configurare l'infrastruttura.
  • Esegui il deployment dell'applicazione di esempio Online Boutique in Google Kubernetes Engine (GKE).
  • Utilizza Cloud Trace per rivedere il percorso di un utente nell'applicazione di esempio.

Costi

In questo documento vengono utilizzati i seguenti componenti fatturabili di Google Cloud:

Per generare una stima dei costi in base all'utilizzo previsto, utilizza il Calcolatore prezzi. I nuovi utenti di Google Cloud possono essere idonei a una prova senza costi aggiuntivi.

Una volta completato questo documento, puoi evitare la prosecuzione della fatturazione eliminando le risorse che hai creato. Per ulteriori informazioni, consulta la sezione Pulizia.

Prima di iniziare

Se hai già configurato un progetto completando il documento precedente di questa serie, Comunicazione tra servizi in una configurazione di microservizi, puoi riutilizzare il progetto. Completa i passaggi seguenti per abilitare API aggiuntive e impostare variabili di ambiente.

  1. Nella pagina del selettore di progetti della console Google Cloud, seleziona o crea un progetto Google Cloud.

    Vai al selettore progetti

  2. Assicurati che la fatturazione sia attivata per il tuo progetto Google Cloud.

  3. Nella console Google Cloud, attiva Cloud Shell.

    Attiva Cloud Shell

  4. Abilita le API per Compute Engine, GKE, Cloud SQL, Artifact Analysis, Trace e Container Registry:

     gcloud services enable \
       compute.googleapis.com \
       sql-component.googleapis.com \
       servicenetworking.googleapis.com\
       container.googleapis.com \
       containeranalysis.googleapis.com \
       containerregistry.googleapis.com \
       sqladmin.googleapis.com
    

Tracciamento distribuito

Il tracciamento distribuito allega i metadati contestuali a ogni richiesta e garantisce che i metadati vengano condivisi tra le richieste. I punti di traccia vengono usati per il tracciamento distribuito. Ad esempio, puoi instrumentare i tuoi servizi (frontend, suggerimenti e annunci) con due punti di traccia per gestire la richiesta di un client e visualizzare i dettagli di un prodotto: un punto di traccia per inviare la richiesta e un altro punto di traccia per ricevere la risposta. Il seguente diagramma mostra come funziona questa strumentazione dei punti traccia:

Uno strumentazione punto traccia con due punti traccia.

Figura 2. Ogni chiamata tra servizi ha due punti di traccia costituiti da una coppia richiesta-risposta.

Per consentire ai punti di traccia di capire quale richiesta eseguire quando il servizio viene richiamato, il servizio di origine passa un ID traccia lungo il flusso di esecuzione. Il processo che passa l'ID traccia è chiamato propagazione dei metadati o propagazione del contesto distribuito. La propagazione del contesto trasferisce i metadati sulle chiamate di rete quando i servizi di un'applicazione distribuita comunicano tra loro durante l'esecuzione di una determinata richiesta. Il seguente diagramma mostra la propagazione dei metadati:

La propagazione dei metadati passa l'ID traccia.

Figura 3. I metadati Trace vengono passati da un servizio all'altro. I metadati includono informazioni come quale servizio chiama quale e i relativi timestamp.

Nell'esempio Boutique online, una traccia inizia quando un utente invia una richiesta iniziale per recuperare i dettagli del prodotto. Viene generato un nuovo ID traccia e ogni richiesta successiva è decorata con intestazioni contenenti metadati contestuali che rimandano alla richiesta originale.

Ogni singola operazione richiamata nell'ambito del completamento della richiesta dell'utente finale è denominata span. I tag di servizio di origine hanno ciascun intervallo con il proprio ID univoco e l'ID traccia dell'intervallo padre. Il seguente diagramma mostra una visualizzazione di una traccia nel grafico di Gantt:

Le singole operazioni sono contrassegnate come intervalli.

Figura 4. Un intervallo padre include il tempo di risposta degli intervalli figlio.

La Figura 4 mostra un albero di traccia in cui il servizio frontend chiama il servizio di suggerimenti e il servizio di annunci. Il servizio frontend è l'span padre, che descrive il tempo di risposta osservato dall'utente finale. Gli intervalli secondari descrivono come sono stati chiamati e hanno risposto il servizio di suggerimenti e quello di annunci, incluse le informazioni sui tempi di risposta.

Un mesh di servizi come Istio consente il tracciamento distribuito del traffico service-to-service senza bisogno di strumentazione dedicata. Tuttavia, potrebbero verificarsi situazioni in cui vuoi avere un maggiore controllo sulle tracce o potresti avere la necessità di tracciare il codice che non è in esecuzione all'interno di un mesh di servizi.

Questo documento utilizza OpenTelemetry per consentire la strumentazione di applicazioni di microservizi distribuiti al fine di raccogliere tracce e metriche. OpenTelemetry consente di raccogliere metriche e tracce per poi esportarle in backend come Prometheus, Cloud Monitoring, Datadog, Graphite, Zipkin e Jaeger.

Strumentazione con OpenTelemetry

Le seguenti sezioni mostrano come utilizzare la propagazione del contesto per consentire l'aggiunta di intervalli di più richieste a una singola traccia padre.

Questo esempio utilizza le librerie OpenTelemetry JavaScript, Python e Go per instrumentare l'implementazione delle tracce per i servizi di pagamento, suggerimento e frontend. A seconda del livello di dettaglio della strumentazione, il tracciamento dei dati può influire sul costo del progetto (fatturazione di Cloud Trace). Per ridurre le preoccupazioni relative ai costi, la maggior parte dei sistemi di tracciamento utilizza varie forme di campionamento per acquisire solo una determinata percentuale delle tracce osservate. Nei tuoi ambienti di produzione, la tua organizzazione potrebbe avere delle ragioni sia per gli elementi che vogliono campionare, sia per il motivo. Potresti voler personalizzare la tua strategia di campionamento in base alla gestione dei costi, concentrandoti su tracce interessanti o escludendo il rumore. Per scoprire di più sul campionamento, consulta la pagina Campionamento di OpenTelemetry.

Questo documento utilizza Trace per visualizzare le tracce distribuite. Utilizzi un esportatore OpenTelemetry per inviare tracce a Trace.

Registra esportatori di tracce

Questa sezione mostra come registrare l'esportatore di tracce in ogni servizio aggiungendo righe al codice dei microservizi.

Per il servizio frontend (scritto in Go), il seguente esempio di codice registra l'esportatore:

[...]
exporter, err := otlptracegrpc.New(
        ctx,
        otlptracegrpc.WithGRPCConn(svc.collectorConn))
    if err != nil {
        log.Warnf("warn: Failed to create trace exporter: %v", err)
    }
tp := sdktrace.NewTracerProvider(
        sdktrace.WithBatcher(exporter),
        sdktrace.WithSampler(sdktrace.AlwaysSample()))
    otel.SetTracerProvider(tp)

Per il servizio per suggerimenti (scritto in Python), il seguente esempio di codice registra l'esportatore:

if os.environ["ENABLE_TRACING"] == "1":
    trace.set_tracer_provider(TracerProvider())
    otel_endpoint = os.getenv("COLLECTOR_SERVICE_ADDR", "localhost:4317")
    trace.get_tracer_provider().add_span_processor(
        BatchSpanProcessor(
            OTLPSpanExporter(
            endpoint = otel_endpoint,
            insecure = True
            )
        )
    )

Per il servizio di pagamento (scritto in JavaScript), l'esportatore viene registrato dal seguente esempio di codice:

provider.addSpanProcessor(new SimpleSpanProcessor(new OTLPTraceExporter({url: collectorUrl})));
provider.register();

Configura la propagazione del contesto

Il sistema di tracciamento deve seguire una specifica del contesto di traccia che definisca il formato per propagare il contesto di tracciamento tra i servizi. Esempi di formati di propagazione includono il formato B3 di Zipkin e X-Google-Cloud-Trace.

OpenTelemetry propaga il contesto utilizzando l'TextMapPropagator globale. In questo esempio viene utilizzato il propagatore del contesto di traccia, che usa il formato traceparent W3C. Le librerie di strumentazione, come le librerie HTTP e gRPC di OpenTelemetry, utilizzano il propagatore globale per aggiungere un contesto di traccia come metadati alle richieste HTTP o gRPC. Affinché la propagazione del contesto abbia esito positivo, il client e il server devono utilizzare lo stesso formato di propagazione.

Propagazione del contesto su HTTP

Il servizio frontend inserisce un contesto di traccia nelle intestazioni delle richieste HTTP. I servizi di backend estrae il contesto della traccia. Il seguente esempio di codice mostra in che modo il servizio frontend è instrumentato per configurare il contesto di traccia:

otel.SetTextMapPropagator(
    propagation.NewCompositeTextMapPropagator(
        propagation.TraceContext{}, propagation.Baggage{}))

if os.Getenv("ENABLE_TRACING") == "1" {
    log.Info("Tracing enabled.")
    initTracing(log, ctx, svc)
} else {
    log.Info("Tracing disabled.")
}

...

var handler http.Handler = r
handler = &logHandler{log: log, next: handler}     // add logging
handler = ensureSessionID(handler)                 // add session ID
handler = otelhttp.NewHandler(handler, "frontend") // add OpenTelemetry tracing

Propagazione del contesto su gRPC

Considera il flusso in cui il servizio di pagamento effettua l'ordine in base al prodotto selezionato da un utente. Questi servizi comunicano tramite gRPC.

Il seguente esempio di codice utilizza un intercettore di chiamata gRPC che intercetta le chiamate in uscita e inserisce il contesto della traccia:

var srv *grpc.Server

// Propagate trace context always
otel.SetTextMapPropagator(
    propagation.NewCompositeTextMapPropagator(
        propagation.TraceContext{}, propagation.Baggage{}))
srv = grpc.NewServer(
    grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()),
    grpc.StreamInterceptor(otelgrpc.StreamServerInterceptor()),
)

Dopo aver ricevuto la richiesta, il servizio di cataloghi dei prodotti o dei pagamenti (ListProducts) estrae il contesto dalle intestazioni della richiesta e utilizza i metadati della traccia padre per generare un intervallo figlio.

Le seguenti sezioni forniscono dettagli su come configurare ed esaminare il tracciamento distribuito per l'applicazione Boutique online di esempio.

Implementazione dell'applicazione

Se hai già un'applicazione in esecuzione che ha completato il documento precedente di questa serie, Comunicazione tra servizi in una configurazione di microservizi, puoi passare alla sezione successiva, Revisione delle tracce. In caso contrario, completa i seguenti passaggi per eseguire il deployment dell'esempio di Boutique online di esempio:

  1. Per configurare l'infrastruttura, in Cloud Shell clona il repository GitHub:

    git clone https://github.com/GoogleCloudPlatform/microservices-demo.git
    
  2. Per il nuovo deployment, reimposta le variabili di ambiente:

    PROJECT_ID=PROJECT_ID
    REGION=us-central1
    GSA_NAME=microservices-sa
    GSA_EMAIL=$GSA_NAME@$PROJECT_ID.iam.gserviceaccount.com
    

    Sostituisci quanto segue:

    • PROJECT_ID: l'identificatore dell'ID progetto.
  3. (Facoltativo) Crea un nuovo cluster o riutilizzane uno esistente, se esistente:

    gcloud container clusters create-auto online-boutique --project=${PROJECT_ID}
      --region=${REGION}
    
  4. Crea un account di servizio Google:

    gcloud iam service-accounts create $GSA_NAME \
      --project=$PROJECT_ID
    
  5. Abilita le API:

    gcloud services enable \
    monitoring.googleapis.com \
    cloudtrace.googleapis.com \
    cloudprofiler.googleapis.com \
      --project ${PROJECT_ID}
    
  6. Concedi i ruoli richiesti per Cloud Tracciamento a Google Search Ads:

    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
    --member "serviceAccount:${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role roles/cloudtrace.agent
    
    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
    --member "serviceAccount:${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role roles/monitoring.metricWriter
    
    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
    --member "serviceAccount:${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role roles/cloudprofiler.agent
    
    gcloud iam service-accounts add-iam-policy-binding ${GSA_EMAIL} \
    --role roles/iam.workloadIdentityUser \
    --member "serviceAccount:${PROJECT_ID}.svc.id.goog[default/default]"
    
  7. Annota il tuo account di servizio Kubernetes (default/default per lo spazio dei nomi predefinito) in modo da utilizzare l'account di servizio Google IAM:

    kubectl annotate serviceaccount default \
        iam.gke.io/gcp-service-account=${GSA_EMAIL}
    
  8. Abilita la configurazione della Suite operativa di Google Cloud per GKE, che consente il tracciamento di:

    cd ~/microservices-demo/kustomize && \
    kustomize edit add component components/google-cloud-operations
    
  9. Il file kustomize/kustomization.yaml verrà aggiornato, che potrebbe essere simile a:

    apiVersion: kustomize.config.k8s.io/v1beta1
    kind: Kustomization
    resources:
    - base
    components:
    - components/google-cloud-operations
    [...]
    
  10. Esegui il deployment dei microservizi:

    kubectl apply -k .
    
  11. Controlla lo stato del deployment:

    kubectl rollout status deployment/frontend
    kubectl rollout status deployment/paymentservice
    kubectl rollout status deployment/recommendationservice
    kubectl rollout status deployment/adservice
    

    L'output di ogni comando è simile al seguente:

    Waiting for deployment "" rollout to finish: 0 of 1 updated replicas are available...
    deployment "" successfully rolled out
    
  12. Recupera l'indirizzo IP dell'applicazione di cui è stato eseguito il deployment:

    kubectl get service frontend-external | awk '{print $4}'
    

    Attendi che l'indirizzo IP del bilanciatore del carico venga pubblicato. Per uscire dal comando, premi Ctrl+C. Prendi nota dell'indirizzo IP del bilanciatore del carico e quindi accedi all'applicazione all'URL http://IP_ADDRESS. Potrebbe essere necessario un po' di tempo prima che il bilanciatore del carico sia in stato integro e inizi a trasmettere il traffico.

Revisione delle tracce con Cloud Trace

Il percorso di acquisto di un utente nell'applicazione Boutique online prevede il seguente flusso:

  • L'utente visualizza un catalogo dei prodotti nella pagina di destinazione.
  • Per effettuare un acquisto, l'utente fa clic su Acquista.
  • L'utente viene reindirizzato a una pagina dei dettagli del prodotto, dove aggiunge l'articolo al carrello.
  • L'utente viene reindirizzato a una pagina di pagamento, dove può effettuare un pagamento per completare l'ordine.

Considera uno scenario in cui devi risolvere i problemi relativi a tempi di risposta elevati durante il caricamento della pagina dei dettagli del prodotto. Come descritto in precedenza, la pagina dei dettagli del prodotto è composta da più microservizi. Per determinare dove e perché si verifica l'alta latenza, puoi visualizzare grafici di tracciamento distribuito per esaminare le prestazioni dell'intera richiesta nei diversi servizi.

Per esaminare i grafici di tracciamento distribuiti, procedi nel seguente modo:

  1. Accedi all'applicazione e fai clic su un prodotto. Viene visualizzata la pagina dei dettagli del prodotto.
  2. Nella console Google Cloud, vai alla pagina Elenco di tracce ed esamina la sequenza temporale.
  3. Per vedere i risultati delle tracce distribuite, fai clic su Frontend nella colonna URI.
  4. La visualizzazione a cascata della traccia mostra gli intervalli associati all'URI:

    La visualizzazione a cascata di Trace mostra gli intervalli.

    Nello screenshot precedente, la traccia di un prodotto contiene i seguenti intervalli:

    • L'intervallo di frontend acquisisce la latenza end-to-end (150,349 ms) che il client osserva durante il caricamento della pagina dei dettagli del prodotto.
    • L'intervallo del servizio di suggerimenti acquisisce la latenza delle chiamate di backend nel recupero dei suggerimenti (4,246 ms) correlati al prodotto.
    • L'intervallo del servizio degli annunci acquisisce la latenza delle chiamate di backend nel recupero degli annunci (4,511 ms) pertinenti alla pagina del prodotto.

Per risolvere tempi di risposta elevati, puoi esaminare insight che includono grafici di distribuzione della latenza di eventuali richieste outlier quando le dipendenze del servizio non soddisfano gli obiettivi del livello di servizio (SLO). Puoi anche utilizzare Cloud Trace per ottenere insight sulle prestazioni e creare report di analisi a partire dai dati campionati.

Risoluzione dei problemi

Se le tracce in Application Performance Management non vengono visualizzate, controlla in Esplora log se è presente un errore di autorizzazione negata. L'autorizzazione negata si verifica quando l'account di servizio non dispone dell'accesso per esportare le tracce. Esamina i passaggi per concedere i ruoli necessari per Cloud Trace e assicurati di annotare l'account di servizio con lo spazio dei nomi corretto. Dopodiché, riavvia opentelemetrycollector:

```
kubectl rollout restart deployment opentelemetrycollector
```

Esegui la pulizia

Per evitare che al tuo Account Google Cloud vengano addebitati costi relativi alle risorse utilizzate in questo tutorial, elimina il progetto che contiene le risorse oppure mantieni il progetto ed elimina le singole risorse.

Elimina il progetto

  1. Nella console Google Cloud, vai alla pagina Gestisci risorse.

    Vai a Gestisci risorse

  2. Nell'elenco dei progetti, seleziona il progetto che vuoi eliminare, quindi fai clic su Elimina.
  3. Nella finestra di dialogo, digita l'ID del progetto e fai clic su Chiudi per eliminare il progetto.

Elimina le risorse

Se vuoi conservare il progetto Google Cloud che hai utilizzato in questo documento, elimina le singole risorse:

  • In Cloud Shell, elimina le risorse:

    gcloud container clusters delete online-boutique --project=${PROJECT_ID} --region=${REGION}
    

Passaggi successivi