Tracciamento distribuito in un'applicazione di microservizi

Last reviewed 2024-06-26 UTC

Questo documento è il quarto di una serie in quattro parti sulla progettazione, sulla creazione e sul deployment dei microservizi. Questa serie descrive i vari elementi di un'architettura di microservizi. La serie include informazioni sui vantaggi e sugli svantaggi del pattern di architettura di microservizi e su come applicarlo.

  1. Introduzione ai microservizi
  2. Eseguire il 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 è rivolta a sviluppatori e architetti di applicazioni che progettano e implementano la migrazione per eseguire il refactoring di un'applicazione monolitica in un'applicazione di microservizi.

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

La pagina dei dettagli del prodotto.

Per eseguire il rendering della pagina dei dettagli del prodotto, il servizio frontend comunica con il servizio di consigli e con il servizio di annunci, come mostrato nel seguente diagramma:

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

Figura 1. Servizi scritti in lingue diverse.

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

Quando crei un sistema distribuito di questo tipo, vuoi che i tuoi strumenti di osservabilità forniscano le seguenti informazioni:

  • I servizi attraverso i quali è passata una richiesta.
  • Se si sono verificati ritardi, una richiesta era lenta.
  • Se si è verificato un errore, la richiesta non è andata a buon fine.
  • In che modo l'esecuzione della richiesta era diversa dal comportamento normale del sistema.
  • Se le differenze nell'esecuzione della richiesta erano correlate al rendimento (se alcune chiamate di servizio hanno richiesto più o meno tempo del solito).

Obiettivi

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

Costi

In questo documento utilizzi i seguenti componenti fatturabili di Google Cloud:

Per generare una stima dei costi basata sull'utilizzo previsto, utilizza il Calcolatore prezzi. I nuovi utenti di Google Cloud potrebbero essere idonei per una prova gratuita.

Al termine di questo documento, puoi evitare la fatturazione continua eliminando le risorse che hai creato. Per ulteriori informazioni, vedi Eseguire la 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 riutilizzarlo. Completa i seguenti passaggi per attivare API aggiuntive e impostare le variabili di ambiente.

  1. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  2. Make sure that billing is enabled for your Google Cloud project.

  3. In the Google Cloud console, activate Cloud Shell.

    Activate 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 monitoraggio distribuito associa metadati contestuali a ogni richiesta e garantisce che i metadati vengano condivisi tra le richieste. Utilizzi i punti traccia per instrument il monitoraggio distribuito. Ad esempio, puoi eseguire l'instrumentazione dei tuoi servizi (frontend, consigli e annunci) con due punti di traccia per gestire una richiesta del client per 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 il funzionamento di questa instrumentation del punto di traccia:

Una misurazione del punto di traccia che ha due punti di traccia.

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

Affinché i punti traccia possano capire quale richiesta eseguire quando viene invocato il servizio, 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 tramite le 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 trasmessi tra i servizi. I metadati includono informazioni come i servizi che chiamano altri servizi e i relativi timestamp.

Nell'esempio della 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 viene decorata con intestazioni contenenti metadati contestuali che rimandano alla richiesta originale.

Ogni singola operazione invocata nell'ambito dell'evasione della richiesta dell'utente finale è chiamata span. Il servizio di origine contrassegna ogni span con il proprio ID univoco e l'ID traccia dello span principale. Il seguente diagramma mostra una visualizzazione del grafico di Gantt di una traccia:

Le singole operazioni sono contrassegnate come intervalli.

Figura 4. Uno span principale include il tempo di risposta degli span secondari.

La Figura 4 mostra un albero di tracce in cui il servizio frontend chiama il servizio di consigli e il servizio di annunci. Il servizio frontend è lo span parent, che descrive il tempo di risposta osservato dall'utente finale. Gli elementi figli descrivono in che modo sono stati chiamati e hanno risposto il servizio di consigli e il servizio pubblicitario, incluse le informazioni sui tempi di risposta.

Un mesh di servizi come Istio consente il monitoraggio 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 dover tracciare il codice che non viene eseguito in un mesh di servizi.

Questo documento utilizza OpenTelemetry per abilitare la misurazione delle applicazioni di microservizi distribuiti per raccogliere tracce e metriche. OpenTelemetry ti consente di raccogliere metriche e tracce, quindi di esportarle in backends come Prometheus, Cloud Monitoring, Datadog, Graphite, Zipkin e Jaeger.

Strumentazione con OpenTelemetry

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

Questo esempio utilizza le librerie OpenTelemetry per JavaScript, Python e Go per instrumentare l'implementazione della traccia per i servizi di pagamento, consigli e frontend. A seconda della modalità di visualizzazione della misurazione, i dati di monitoraggio possono influire sul costo del progetto (fatturazione di Cloud Trace). Per ridurre i problemi di costo, la maggior parte dei sistemi di monitoraggio utilizza varie forme di campionamento per acquisire solo una determinata percentuale delle tracce osservate. Negli ambienti di produzione, la tua organizzazione potrebbe avere motivi per scegliere cosa campionare e perché. Ti consigliamo di personalizzare la strategia di campionamento in base alla gestione dei costi, alla concentrazione su tracce interessanti o all'eliminazione del rumore. Per scoprire di più sul campionamento, consulta Campionamento OpenTelemetry.

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

Registra gli esportatori di traccia

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

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 di consigli (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), il seguente esempio di codice registra l'esportatore:

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

Configurare la propagazione del contesto

Il sistema di monitoraggio deve rispettare una specifica del contesto traccia che definisce il formato per la propagazione del contesto traccia tra i servizi. Alcuni esempi di formato di propagazione includono il formato B3 di Zipkin e X-Google-Cloud-Trace.

OpenTelemetry propaga il contesto utilizzando il parametro TextMapPropagator globale. Questo esempio utilizza il propagatore del contesto traccia, che utilizza il formato traceparent del W3C. Le librerie di strumenti, come le librerie HTTP e gRPC di OpenTelemetry, utilizzano il propagatore globale per aggiungere il contesto traccia come metadati alle richieste HTTP o gRPC. Affinché la propagazione del contesto vada a buon fine, il client e il server devono utilizzare lo stesso formato di propagazione.

Propagazione del contesto tramite HTTP

Il servizio frontend inserisce un contesto traccia nelle intestazioni della richiesta HTTP. I servizi di backend estraggono il contesto della traccia. Il seguente esempio di codice mostra come viene eseguito il monitoraggio del servizio frontend per configurare il contesto della 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 tramite 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 intercettatore di chiamate gRPC che intercetta le chiamate in uscita e inietta 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 pagamento o del catalogo dei prodotti (ListProducts) estrae il contesto dalle intestazioni della richiesta e utilizza i metadati della traccia principale per generare uno span secondario.

Le sezioni che seguono forniscono dettagli su come configurare e esaminare il monitoraggio distribuito per l'applicazione di esempio Online Boutique.

Esegui il deployment dell'applicazione

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

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

    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 PROJECT_ID con l'ID progetto Google Cloud che vuoi utilizzare.

  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 all'account di servizio i ruoli richiesti per Cloud Trace:

    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. Aggiungi un'annotazione all'account di servizio Kubernetes (default/default per lo spazio dei nomi predefinito) per utilizzare l'account di servizio Identity and Access Management (IAM):

    kubectl annotate serviceaccount default \
        iam.gke.io/gcp-service-account=${GSA_EMAIL}
    
  8. Attiva la configurazione di Google Cloud Observability per GKE, che abilita il monitoraggio:

    cd ~/microservices-demo/kustomize && \
    kustomize edit add component components/google-cloud-operations
    

    Il comando precedente aggiorna il file kustomize/kustomization.yaml, che è simile al seguente:

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

    kubectl apply -k .
    
  10. 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 ciascun comando è simile al seguente:

    Waiting for deployment "" rollout to finish: 0 of 1 updated replicas are available...
    deployment "" successfully rolled out
    
  11. Ottieni 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, premere Ctrl+C. Prendi nota dell'indirizzo IP del bilanciatore del carico e accedi all'applicazione all'URL http://IP_ADDRESS. Potrebbe essere necessario un po' di tempo prima che il bilanciatore del carico diventi stabile e inizi a inoltrare il traffico.

Esaminare le tracce utilizzando Cloud Trace

Il percorso di acquisto di un utente nell'applicazione Online Boutique ha il seguente flusso:

  • L'utente vede un catalogo di 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 ai 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 la latenza elevata, puoi visualizzare i grafici di monitoraggio distribuito per esaminare le prestazioni dell'intera richiesta nei diversi servizi.

Per esaminare i grafici di monitoraggio distribuito:

  1. Accedi all'applicazione e fai clic su un prodotto qualsiasi. Viene visualizzata la pagina dei dettagli del prodotto.
  2. Nella console Google Cloud, vai alla pagina Elenco di tracciamenti e controlla la sequenza temporale.
  3. Per visualizzare i risultati della traccia distribuita, fai clic su Frontend nella colonna URI.
  4. La visualizzazione Cascata della traccia mostra gli intervalli associati all'URI:

    La visualizzazione della traccia a cascata mostra gli intervalli.

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

    • L'intervallo Frontend acquisisce la latenza end-to-end (150.349 ms) rilevata dal client durante il caricamento della pagina dei dettagli del prodotto.
    • L'intervallo Servizio di consigli acquisisce la latenza delle chiamate di backend per il recupero dei consigli (4.246 ms) correlati al prodotto.
    • L'intervallo Servizio annunci acquisisce la latenza delle chiamate di backend per recuperare gli annunci (4.511 ms) pertinenti alla pagina del prodotto.

Per risolvere i problemi relativi ai tempi di risposta elevati, puoi esaminare gli approfondimenti che includono i 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 approfondimenti sul rendimento e creare report di analisi 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 ha accesso per esportare le tracce. Esamina i passaggi per concedere i ruoli richiesti 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. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

Elimina le risorse

Se vuoi mantenere il progetto Google Cloud 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