Implementa el seguimiento distribuido para observar la latencia de los microservicios

Last reviewed 2023-08-11 UTC

En este documento, se muestra cómo implementar la arquitectura de referencia que se describe en Usa el seguimiento distribuido para observar la latencia de los microservicios. La implementación que se ilustra en este documento captura información de seguimiento en aplicaciones de microservicios mediante OpenTelemetry y Cloud Trace.

La aplicación de muestra en esta implementación consta de dos microservicios escritos en Go.

En este documento, se supone que estás familiarizado con los siguientes productos:

Objetivos

  • Crear un clúster de GKE y, luego, implementar una app de muestra
  • Revisar el código de instrumentación de OpenTelemetry
  • Revisar los seguimientos y los registros que generó la instrumentación

Arquitectura

En el siguiente diagrama, se muestra la arquitectura que implementas.

Arquitectura de la implementación con dos clústeres de GKE

Usa Cloud Build, una plataforma de implementación, integración y entrega continuas completamente administrada, para compilar imágenes de contenedor a partir del código de muestra y almacenarlas en Artifact Registry. Los clústeres de GKE extraen las imágenes de Artifact Registry durante la implementación.

El servicio de frontend acepta solicitudes HTTP en la URL / y llama al servicio de backend. La variable de entorno de define la dirección del servicio de backend.

El servicio de backend acepta solicitudes HTTP en la URL / y realiza una llamada saliente a una URL externa como se define en la variable de entorno de . Una vez que se completa la llamada externa, el servicio de backend le muestra la llamada de estado HTTP (por ejemplo, 200) al emisor.

Costos

En este documento, usarás los siguientes componentes facturables de Google Cloud:

Para generar una estimación de costos en función del uso previsto, usa la calculadora de precios. Es posible que los usuarios nuevos de Google Cloud califiquen para obtener una prueba gratuita.

Cuando finalices las tareas que se describen en este documento, puedes borrar los recursos que creaste para evitar que continúe la facturación. Para obtener más información, consulta Cómo realizar una limpieza.

Antes de comenzar

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

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

  4. Enable the GKE, Cloud Trace, Cloud Build, Cloud Storage, and Artifact Registry APIs.

    Enable the APIs

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

    Go to project selector

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

  7. Enable the GKE, Cloud Trace, Cloud Build, Cloud Storage, and Artifact Registry APIs.

    Enable the APIs

Configure su entorno

En esta sección, debes configurar el entorno con las herramientas que se usan durante la implementación. Ejecuta todos los comandos de terminal en esta implementación desde Cloud Shell.

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

    Activate Cloud Shell

    At the bottom of the Google Cloud console, a Cloud Shell session starts and displays a command-line prompt. Cloud Shell is a shell environment with the Google Cloud CLI already installed and with values already set for your current project. It can take a few seconds for the session to initialize.

  2. Establece una variable de entorno para el ID de tu proyecto de Google Cloud:
    export PROJECT_ID=$(gcloud config list --format 'value(core.project)' 2>/dev/null)
    
  3. Puedes descargar los archivos necesarios para esta implementación si clonas el repositorio de Git asociado:
    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples.git
    cd kubernetes-engine-samples/observability/distributed-tracing
    WORKDIR=$(pwd)
    

    Haz que la carpeta del repositorio sea el directorio de trabajo ($WORKDIR) desde el que realizas todas las tareas relacionadas con esta implementación. De esa manera, si no deseas conservar los recursos, puedes borrar la carpeta cuando termines la implementación.

Instala herramientas

  1. En Cloud Shell, instala kubectx y kubens:

    git clone https://github.com/ahmetb/kubectx $WORKDIR/kubectx
    export PATH=$PATH:$WORKDIR/kubectx
    

    Usa estas herramientas para trabajar con varios clústeres, contextos y espacios de nombres de Kubernetes.

  2. En Cloud Shell, instala Apache Bench, una herramienta de generación de carga de código abierto:

    sudo apt-get install apache2-utils
    

Crea un repositorio de Docker

Crea un repositorio de Docker para almacenar la imagen de muestra de esta implementación.

Consola

  1. En la consola de Google Cloud, abre la página Repositorios.

    Abrir la página Repositorios

  2. Haz clic en Crear repositorio.

  3. Especifica distributed-tracing-docker-repo como el nombre del repositorio.

  4. Selecciona Docker como el formato y Estándar como el modo.

  5. En Tipo de ubicación, selecciona Región y, luego, elige la ubicación us-west1.

  6. Haga clic en Crear.

El repositorio se agrega a la lista de repositorios.

gcloud

  1. En Cloud Shell, crea un nuevo repositorio de Docker llamado distributed-tracing-docker-repo en la ubicación us-west1 con la descripción docker repository:

    gcloud artifacts repositories create distributed-tracing-docker-repo --repository-format=docker \
    --location=us-west1 --description="Docker repository for distributed tracing deployment"
    
  2. Verifica si se creó el repositorio:

    gcloud artifacts repositories list
    

Crea clústeres de GKE

En esta sección, debes crear dos clústeres de GKE en los que implementarás la app de muestra. Los clústeres de GKE se crean con acceso de solo escritura a la API de Cloud Trace de forma predeterminada, por lo que no necesitas definir el acceso cuando los creas.

  1. En Cloud Shell, crea los clústeres:

    gcloud container clusters create backend-cluster \
        --zone=us-west1-a \
        --verbosity=none --async
    
    gcloud container clusters create frontend-cluster \
        --zone=us-west1-a \
        --verbosity=none
    

    En este ejemplo, los clústeres están en la zona us-west1-a. Para obtener más información, consulta Geografía y regiones.

  2. Obtén las credenciales del clúster y almacénalas de forma local:

    gcloud container clusters get-credentials backend-cluster --zone=us-west1-a
    gcloud container clusters get-credentials frontend-cluster --zone=us-west1-a
    
  3. Cambia el nombre de los contextos de los clústeres para que sea más fácil acceder a ellos más adelante en la implementación:

    kubectx backend=gke_${PROJECT_ID}_us-west1-a_backend-cluster
    kubectx frontend=gke_${PROJECT_ID}_us-west1-a_frontend-cluster
    

Revisa la instrumentación de OpenTelemetry

En las siguientes secciones, deberás revisar el código del archivo main.go en la aplicación de muestra. Esto te ayuda a aprender a usar la propagación de contexto para permitir que se agreguen intervalos de varias solicitudes a un solo seguimiento superior.

Revisa las importaciones en el código de la aplicación

import (
	"context"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"

	cloudtrace "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace"
	"github.com/gorilla/mux"
	"go.opentelemetry.io/contrib/detectors/gcp"
	"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
	"go.opentelemetry.io/contrib/propagators/autoprop"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/sdk/resource"
	"go.opentelemetry.io/otel/sdk/trace"
)

Ten en cuenta lo siguiente sobre las importaciones:

  • El paquete go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp contiene el complemento otelhttp, que puede instrumentar un servidor HTTP o un cliente HTTP. La instrumentación del servidor recupera el contexto del intervalo de la solicitud HTTP y registra un intervalo para el manejo de la solicitud del servidor. La instrumentación del cliente inserta el contexto de intervalo en la solicitud HTTP saliente y registra un intervalo para el tiempo que se espera una respuesta.
  • El paquete go.opentelemetry.io/contrib/propagators/autoprop proporciona una implementación de la interfaz TextMapPropagator de OpenTelemetry, que usa otelhttp para manejar la propagación. Los propagadores determinan el formato y las claves que se usan para almacenar el contexto de seguimiento en transportes como HTTP. En particular, otelhttp pasa encabezados HTTP al propagador. El propagador extrae un contexto de intervalo en un contexto de Go de los encabezados, o codifica y, luego, incorpora un contexto de intervalo en el contexto de Go en los encabezados (según si es un cliente o un servidor). De forma predeterminada, el paquete autoprop inserta y extrae el contexto de intervalo mediante el formato de propagación contexto de seguimiento W3C.
  • La importación github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace exporta seguimientos a Cloud Trace.
  • La importación github.com/gorilla/mux es la biblioteca que usa la app de muestra para controlar las solicitudes.
  • La importación go.opentelemetry.io/contrib/detectors/gcp agrega atributos a los intervalos, como cloud.availability_zone, que identifican dónde se ejecuta tu aplicación en Google Cloud.
  • Las importaciones go.opentelemetry.io/otel, go.opentelemetry.io/otel/sdk/trace y go.opentelemetry.io/otel/sdk/resource que se usan para configurar OpenTelemetry

Revisa la función main

La función main configura la exportación de seguimiento a Cloud Trace y usa un router mux para controlar las solicitudes que se realizan a la URL /.

func main() {
	ctx := context.Background()
	// Set up the Cloud Trace exporter.
	exporter, err := cloudtrace.New()
	if err != nil {
		log.Fatalf("cloudtrace.New: %v", err)
	}
	// Identify your application using resource detection.
	res, err := resource.New(ctx,
		// Use the GCP resource detector to detect information about the GKE Cluster.
		resource.WithDetectors(gcp.NewDetector()),
		resource.WithTelemetrySDK(),
	)
	if err != nil {
		log.Fatalf("resource.New: %v", err)
	}
	tp := trace.NewTracerProvider(
		trace.WithBatcher(exporter),
		trace.WithResource(res),
	)
	// Set the global TracerProvider which is used by otelhttp to record spans.
	otel.SetTracerProvider(tp)
	// Flush any pending spans on shutdown.
	defer tp.ForceFlush(ctx)

	// Set the global Propagators which is used by otelhttp to propagate
	// context using the w3c traceparent and baggage formats.
	otel.SetTextMapPropagator(autoprop.NewTextMapPropagator())

	// Handle incoming request.
	r := mux.NewRouter()
	r.HandleFunc("/", mainHandler)
	var handler http.Handler = r

	// Use otelhttp to create spans and extract context for incoming http
	// requests.
	handler = otelhttp.NewHandler(handler, "server")
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", os.Getenv("PORT")), handler))
}

Ten en cuenta lo siguiente sobre este código:

  • Configura un TracerProvider de OpenTelemetry, que detecta atributos cuando se ejecuta en Google Cloud y que exporta seguimientos a Cloud Trace.
  • Usa las funciones otel.SetTracerProvider y otel.SetTextMapPropagators para establecer la configuración global de TracerProvider y Propagator. De forma predeterminada, las bibliotecas de instrumentación, como otelhttp, usan el TracerProvider registrado de forma global para crear intervalos y Propagator para propagar el contexto.
  • Debes unir el servidor HTTP con otelhttp.NewHandler para instrumentar el servidor HTTP.

Revisa la función mainHandler

func mainHandler(w http.ResponseWriter, r *http.Request) {
	// Use otelhttp to record a span for the outgoing call, and propagate
	// context to the destination.
	destination := os.Getenv("DESTINATION_URL")
	resp, err := otelhttp.Get(r.Context(), destination)
	if err != nil {
		log.Fatal("could not fetch remote endpoint")
	}
	defer resp.Body.Close()
	_, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatalf("could not read response from %v", destination)
	}

	fmt.Fprint(w, strconv.Itoa(resp.StatusCode))
}

Para capturar la latencia de las solicitudes salientes que se realizan al destino, usa el complemento otelhttp para realizar una solicitud HTTP. También puedes usar la función r.Context para vincular la solicitud entrante con la saliente, como se muestra en la siguiente lista:

// Use otelhttp to record a span for the outgoing call, and propagate
// context to the destination.
resp, err := otelhttp.Get(r.Context(), destination)

Implemente la aplicación

En esta sección, debes usar Cloud Build para compilar imágenes de contenedor para los servicios de backend y frontend. Debes implementarlos en sus clústeres de GKE.

Compila el contenedor de Docker

  1. En Cloud Shell, envía la compilación desde el directorio de trabajo:

    cd $WORKDIR
    gcloud builds submit . --tag us-west1-docker.pkg.dev/$PROJECT_ID/distributed-tracing-docker-repo/backend:latest
    
  2. Confirma que la imagen de contenedor se creó de manera correcta y que está disponible en Artifact Registry:

    gcloud artifacts docker images list us-west1-docker.pkg.dev/$PROJECT_ID/distributed-tracing-docker-repo
    

    La imagen de contenedor se creó de manera correcta si el resultado es similar al siguiente, en el que PROJECT_ID es el ID de tu proyecto de Google Cloud:

    NAME
    us-west1-docker.pkg.dev/PROJECT_ID/distributed-tracing-docker-repo/backend
    

Implementa el servicio de backend

  1. En Cloud Shell, establece el contexto de kubectx en el clúster de backend:

    kubectx backend
    
  2. Crea el archivo YAML para la implementación backend:

    export PROJECT_ID=$(gcloud info --format='value(config.project)')
    envsubst < backend-deployment.yaml | kubectl apply -f -
    
  3. Confirma que los pods se estén ejecutando:

    kubectl get pods
    

    El resultado muestra un valor Status de Running:

    NAME                       READY   STATUS    RESTARTS   AGE
    backend-645859d95b-7mx95   1/1     Running   0          52s
    backend-645859d95b-qfdnc   1/1     Running   0          52s
    backend-645859d95b-zsj5m   1/1     Running   0          52s
    
  4. Expón la implementación de backend mediante un balanceador de cargas:

    kubectl expose deployment backend --type=LoadBalancer
    
  5. Obtén la dirección IP del servicio de backend:

    kubectl get services backend
    

    El resultado es similar a este:

    NAME      TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)          AGE
    backend   LoadBalancer   10.11.247.58   34.83.88.143   8080:30714/TCP   70s
    

    Si el valor del campo EXTERNAL-IP es <pending>, repite el comando hasta que el valor sea una dirección IP.

  6. Captura la dirección IP del paso anterior en una variable:

    export BACKEND_IP=$(kubectl get svc backend -ojson | jq -r '.status.loadBalancer.ingress[].ip')
    

Implementa el servicio de frontend

  1. En Cloud Shell, establece tu contexto de kubectx en el clúster de backend:

    kubectx frontend
    
  2. Crea el archivo YAML para la implementación frontend:

    export PROJECT_ID=$(gcloud info --format='value(config.project)')
    envsubst < frontend-deployment.yaml | kubectl apply -f -
    
  3. Confirma que los pods se estén ejecutando:

    kubectl get pods
    

    El resultado muestra un valor Status de Running:

    NAME                        READY   STATUS    RESTARTS   AGE
    frontend-747b445499-v7x2w   1/1     Running   0          57s
    frontend-747b445499-vwtmg   1/1     Running   0          57s
    frontend-747b445499-w47pf   1/1     Running   0          57s
    
  4. Expón la implementación de frontend mediante un balanceador de cargas:

    kubectl expose deployment frontend --type=LoadBalancer
    
  5. Obtén la dirección IP del servicio de frontend:

    kubectl get services frontend
    

    El resultado es similar a este:

    NAME       TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)         AGE
    frontend   LoadBalancer   10.27.241.93   34.83.111.232   8081:31382/TCP  70s
    

    Si el valor del campo EXTERNAL-IP es <pending>, repite el comando hasta que el valor sea una dirección IP.

  6. Captura la dirección IP del paso anterior en una variable:

    export FRONTEND_IP=$(kubectl get svc frontend -ojson | jq -r '.status.loadBalancer.ingress[].ip')
    

Carga la aplicación y revisa los seguimientos

En esta sección, debes usar la herramienta Apache Bench para crear solicitudes para la aplicación. Luego, revisa los seguimientos resultantes en Cloud Trace.

  1. En Cloud Shell, usa Apache Bench para generar 1,000 solicitudes mediante 3 subprocesos simultáneos:

    ab -c 3 -n 1000 http://${FRONTEND_IP}:8081/
    
  2. En la consola de Google Cloud, ve a la página Lista de seguimiento.

    Ir a la Lista de Trace

  3. Para revisar el cronograma, haz clic en uno de los URI etiquetados como server.

    Gráfico de dispersión de los seguimientos

    Este seguimiento contiene cuatro intervalos que tienen los siguientes nombres:

    • El primer intervalo server captura la latencia de extremo a extremo para controlar la solicitud HTTP en el servidor de frontend.
    • El primer intervalo HTTP GET captura la latencia de la llamada GET que realiza el cliente del frontend al backend.
    • El segundo intervalo server captura la latencia de extremo a extremo para controlar la solicitud HTTP en el servidor de backend.
    • El segundo intervalo HTTP GET captura la latencia de la llamada GET que realiza el cliente del backend a google.com.

    Gráfico de barras de los intervalos

Limpia

La manera más fácil de eliminar la facturación es borrar el proyecto de Google Cloud que creaste para la implementación. Como alternativa, puedes borrar los recursos individuales.

Borra el proyecto

  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.

Borra los recursos individuales

Para borrar recursos individuales en lugar de borrar todo el proyecto, ejecuta los siguientes comandos en Cloud Shell:

gcloud container clusters delete frontend-cluster --zone=us-west1-a
gcloud container clusters delete backend-cluster --zone=us-west1-a
gcloud artifacts repositories delete distributed-tracing-docker-repo --location us-west1

¿Qué sigue?

  • Obtén información sobre OpenTelemetry.
  • Para obtener más información sobre las arquitecturas de referencia, los diagramas y las prácticas recomendadas, explora Cloud Architecture Center.