Instrumenta información de seguimiento con OpenTelemetry

1. Introducción

5af4a7e43b0feaab.png

Última actualización: 5/3/2021

Observabilidad de la aplicación

Observabilidad y OpenTelemetry

Observabilidad es el término que se usa para describir un atributo de un sistema. Un sistema con observabilidad permite a los equipos depurar activamente su sistema. En ese contexto, tres pilares de observabilidad: los registros, las métricas y los seguimientos son la instrumentación fundamental para que el sistema adquiera observabilidad.

OpenTelemetry es un conjunto de especificaciones y SDKs que aceleran la instrumentación y la exportación de datos de telemetría (registros, métricas y seguimientos) que requiere la observabilidad. OpenTelemetry es un proyecto de estándar abierto y impulsado por la comunidad bajo la CNCF. Con el uso de las bibliotecas que proporcionan el proyecto y su ecosistema, los desarrolladores pueden instrumentar sus aplicaciones de manera independiente del proveedor y en función de múltiples arquitecturas.

Seguimiento distribuido

Entre registros, métricas y seguimientos, Trace es la telemetría que indica la latencia de una parte específica del proceso en el sistema. Especialmente en la era de los microservicios, el seguimiento distribuido es el fuerte controlador para descubrir cuellos de botella de latencia en el sistema distribuido en general.

Cuando se analizan seguimientos distribuidos, la visualización de datos de seguimiento es la clave para captar las latencias generales del sistema de un vistazo. En el seguimiento distribuido, manejamos un conjunto de llamadas para procesar una sola solicitud al punto de entrada del sistema en forma de seguimiento que contiene varios intervalos.

Un intervalo representa una unidad de trabajo individual que se realiza en un sistema distribuido y que registra los tiempos de inicio y finalización. Los intervalos suelen tener relaciones jerárquicas entre sí. En la imagen que aparece a continuación, todos los intervalos más pequeños son intervalos secundarios de un intervalo /messages grande y se ensamblan en un solo seguimiento que muestra la ruta del trabajo a través de un sistema.

adbd3ecd69d410cb.png

Google Cloud Trace es una de las opciones de backend de seguimiento distribuido y está bien integrado a otros productos de Google Cloud.

Qué compilarás

En este codelab, instrumentarás la información de seguimiento en los servicios llamados "Shakesapp". que se ejecuta en un clúster de Kubernetes que se ejecuta en Google Kubernetes Engine. A continuación, se describe la arquitectura de Shakesapp:

68873c018a7be7de.png

  • Los clientes envían una cadena de consulta al servidor
  • El servidor acepta la consulta del cliente, recupera todos los trabajos de Shakespare en formato de texto de Google Cloud Storage, busca en las líneas que contienen la consulta y muestra el número de la línea que coincidió con el cliente.

instrumentarás la información de seguimiento en toda la solicitud.

Qué aprenderás

  • Cómo comenzar a usar las bibliotecas de seguimiento de OpenTelemetry en un proyecto de Python
  • Cómo crear un intervalo con la biblioteca
  • Cómo propagar contextos de intervalos entre los componentes de una app
  • Cómo enviar datos de seguimiento a Google Cloud Trace
  • Cómo analizar el seguimiento en Google Cloud Trace

En este codelab, se explica cómo instrumentar tus microservicios. Para facilitar la comprensión, este ejemplo solo contiene 3 componentes (generador de cargas, cliente y servidor), pero puedes aplicar el mismo proceso que se explica en este codelab en sistemas más grandes y complejos.

Requisitos

  • Conocimiento de Python 3

2. Configuración y requisitos

Configuración del entorno de autoaprendizaje

Si aún no tienes una Cuenta de Google (Gmail o Google Apps), debes crear una. Accede a Google Cloud Platform Console (console.cloud.google.com) y crea un proyecto nuevo.

Si ya tienes un proyecto, haz clic en el menú desplegable de selección de proyectos en la parte superior izquierda de la Console:

15b8b6ac4d917005.png

y haz clic en el botón “PROYECTO NUEVO” en el diálogo resultante para crear un proyecto nuevo:

O

Si aún no tienes un proyecto, deberías ver un cuadro de diálogo como este para crear el primero:

90977ce514204b51.png

El cuadro de diálogo de creación posterior del proyecto te permite ingresar los detalles de tu proyecto nuevo:

6d9573e346e930b4.png

Recuerda el ID del proyecto, que es un nombre único en todos los proyectos de Google Cloud (el nombre anterior ya se encuentra en uso y no lo podrá usar). Se mencionará más adelante en este codelab como PROJECT_ID.

A continuación, si aún no lo hiciste, deberás habilitar la facturación en Developers Console para usar los recursos de Google Cloud y habilitar la API de Cloud Trace.

eb5325f65619ad6a.png

Ejecutar este codelab debería costar solo unos pocos dólares, pero su costo podría aumentar si decides usar más recursos o si los dejas en ejecución (consulta la sección “Limpiar” al final de este documento). Los precios de Google Cloud Trace, Google Kubernetes Engine y Google Artifacat Registry se indican en la documentación oficial.

Los usuarios nuevos de Google Cloud Platform están aptas para obtener una prueba gratuita de $300, por lo que este codelab es completamente gratuito.

Configuración de Google Cloud Shell

Si bien Google Cloud y Google Cloud Trace se pueden operar de forma remota desde tu laptop, en este codelab usaremos Google Cloud Shell, un entorno de línea de comandos que se ejecuta en la nube.

Esta máquina virtual basada en Debian está cargada con todas las herramientas de desarrollo que necesitarás. Ofrece un directorio principal persistente de 5 GB y se ejecuta en Google Cloud, lo que permite mejorar considerablemente el rendimiento de la red y la autenticación. Esto significa que todo lo que necesitarás para este Codelab es un navegador (sí, funciona en una Chromebook).

Para activar Cloud Shell desde la consola de Cloud, simplemente haz clic en Activar Cloud Shell gcLMt5IuEcJJNnMId-Bcz3sxCd0rZn7IzT_r95C8UZeqML68Y1efBG_B0VRp7hc7qiZTLAF-TXD7SsOadxn8uadgHhaLeASnVS3ZHK39eOlKJOgj9SJua_oeGhMxRrbOg3qigddS2A (el aprovisionamiento y la conexión al entorno debería tardar solo unos momentos).

ff81d016724c4f67.png

fbe156ee6edfbb2e.png

Una vez conectado a Cloud Shell, debería ver que ya se autenticó y que el proyecto ya se configuró con tu PROJECT_ID:

gcloud auth list

Resultado del comando

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

Resultado del comando

[core]
project = <PROJECT_ID>

Si, por algún motivo, el proyecto no está configurado, solo emite el siguiente comando:

gcloud config set project <PROJECT_ID>

Si no conoce su PROJECT_ID, Observa el ID que usaste en los pasos de configuración o búscalo en el panel de la consola de Cloud:

a3e716fc9e7454e9.png

Cloud Shell también configura algunas variables de entorno de forma predeterminada, lo que puede resultar útil cuando ejecutas comandos futuros.

echo $GOOGLE_CLOUD_PROJECT

Salida de comando

<PROJECT_ID>

Establece la zona predeterminada y la configuración del proyecto.

gcloud config set compute/zone us-central1-f

Puedes elegir una variedad de zonas diferentes. Para obtener más información, consulta Regiones y zonas.

Configuración de Python

En este codelab, usamos "poesía" de administrar estrictamente las versiones de paquetes. Ejecuta el siguiente comando en Cloud Shell:

curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python3 -
source $HOME/.poetry/env

Configura un clúster de Google Kubernetes

En este codelab, ejecutarás un clúster de microservicios en Google Kubernetes Engine (GKE). El proceso de este codelab es el siguiente:

  1. Descarga el proyecto de referencia en Cloud Shell
  2. Compila microservicios en contenedores
  3. Sube contenedores a Google Artifact Registry (GAR)
  4. Implementa contenedores en GKE
  5. Modifica el código fuente de los servicios para la instrumentación de seguimiento
  6. Ir al paso 2

Habilitar Kubernetes Engine

Primero, configuramos un clúster de Kubernetes en el que Shakesapp se ejecuta en GKE, así que debemos habilitar GKE. Navega al menú “Kubernetes Engine”. y presiona el botón HABILITAR.

56c680e93e169731.png

Ya está todo listo para crear un clúster de Kubernetes.

Crea un clúster de Kubernetes

En Cloud Shell, ejecuta el siguiente comando para crear un clúster de Kubernetes. Confirma que el valor de la zona esté debajo de la región que usaste para crear el repositorio de Artifact Registry. Cambia el valor de zona us-central1-f si la región de tu repositorio no cubre la zona.

gcloud container clusters create otel-trace-codelab --zone us-central1-f \
--num-nodes 1 \
--machine-type e2-highcpu-4

Resultado del comando

Creating cluster otel-trace-codelab in us-central1-f... Cluster is being health-checked (master is healthy)...done.
Created [https://container.googleapis.com/v1/projects/psychic-order-307806/zones/us-central1-f/clusters/otel-trace-codelab].
To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/workload_/gcloud/us-central1-f/otel-trace-codelab?project=psychic-order-307806
kubeconfig entry generated for otel-trace-codelab.
NAME                LOCATION       MASTER_VERSION    MASTER_IP        MACHINE_TYPE  NODE_VERSION      NUM_NODES  STATUS
otel-trace-codelab  us-central1-f  1.18.12-gke.1210  104.154.162.176  e2-medium     1.18.12-gke.1210  3          RUNNING

Configuración de Artifact Registry y Skaffold

Ahora tenemos un clúster de Kubernetes listo para implementar. A continuación, nos prepararemos para un registro de contenedores para implementar y enviar contenedores. En este paso, debemos configurar GAR y Skaffold para usarlos.

Configuración de Artifact Registry

Navega al menú de “Artifact Registry” y presiona el botón HABILITAR.

f7493243bae0cdf7.png

Después de un momento, verás el navegador del repositorio de GAR. Haz clic en “CREAR REPOSITORIO”. e ingresa el nombre del repositorio.

f97f337f5476651.png

En este codelab, asignaré el nombre trace-codelab al nuevo repositorio. El formato del artefacto es “Docker” y el tipo de ubicación es "Región". Elige una región cercana a la que estableciste para la zona predeterminada de Google Compute Engine. Por ejemplo, este ejemplo eligió “us-central1-f” arriba, así que aquí elegimos "us-central1 (Iowa)". Luego, haz clic en "CREATE" .

2f04143077ca56db.png

Ahora verás "trace-codelab" en el navegador del repositorio.

7a3c1f47346bea15.png

Regresaremos más adelante para verificar la ruta de registro.

Configuración de Skaffold

Skaffold es una herramienta útil cuando trabajas en la compilación de microservicios y ejecución en Kubernetes. Controla el flujo de trabajo de compilación, envío e implementación de contenedores de aplicaciones con un conjunto pequeño de comandos. De forma predeterminada, Skaffold usa Docker Registry como registro de contenedores, por lo que debes configurar Skaffold para que reconozca GAR cuando se envían contenedores.

Vuelve a abrir Cloud Shell y confirma si Skaffold está instalado. (Cloud Shell instala Skaffold en el entorno de forma predeterminada). Ejecuta el siguiente comando y consulta la versión de Skaffold.

skaffold version

Resultado del comando

v1.20.0

Ahora, puedes registrar el repositorio predeterminado para que lo use Skaffold. Para obtener la ruta de acceso del registro, navega hasta el panel de Artifact Registry y haz clic en el nombre del repositorio que acabas de configurar en el paso anterior.

55173fe922f40327.png

Luego, verás los recorridos de las rutas de navegación en la parte superior de la página. Haz clic en el ícono e157b1359c3edc06.png para copiar la ruta de registro en el portapapeles.

a9b0fa44c37e0178.png

Cuando hagas clic en el botón de copiar, verás un diálogo en la parte inferior del navegador con el siguiente mensaje:

&quot;us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab&quot; se copió

Regresa a Cloud Shell. Ejecuta el comando skaffold config set default-repo con el valor que acabas de copiar del panel.

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

Resultado del 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

Además, debes establecer el registro en la configuración de Docker. Ejecuta el siguiente comando:

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

Resultado del 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

Ya está todo listo para el próximo paso que te permitirá configurar un contenedor de Kubernetes en GKE.

Resumen

En este paso, configurarás el entorno de tu codelab:

  • Configura Cloud Shell
  • Creó un repositorio de Artifact Registy para Container Registry
  • Configura Skaffold para usar Container Registry
  • Creaste un clúster de Kubernetes en el que se ejecutan los microservicios del codelab.

Cuál es el próximo paso

En el siguiente paso, compilarás, enviarás y, luego, implementarás tus microservicios en el clúster

3. Compila, envía e implementa los microservicios

Descarga el material del codelab

En el paso anterior, configuramos todos los requisitos previos para este codelab. Ahora está todo listo para ejecutar microservicios completos sobre ellos. El material del codelab está alojado en GitHub, así que descárgalo en el entorno de Cloud Shell con el siguiente comando de Git.

cd ~
git clone https://github.com/GoogleCloudPlatform/opentelemetry-trace-codelab-python.git

La estructura de directorios del proyecto es la siguiente:

shakesapp-python
├── LICENSE
├── manifests
│   ├── client.yaml
│   ├── loadgen.yaml
│   └── server.yaml
├── proto
│   └── shakesapp.proto
├── skaffold.yaml
└── src
    ├── client
    ├── loadgen
    └── server
  • Manifiestos: Archivos de manifiesto de Kubernetes
  • proto: definición de proto para la comunicación entre el cliente y el servidor
  • src: Directorios para el código fuente de cada servicio
  • skaffold.yaml: Archivo de configuración de Skaffold

Ejecuta el comando de Skaffold

Por último, estarás listo para compilar, enviar e implementar todo el contenido en el clúster de Kubernetes que acabas de crear. Parece que contiene varios pasos, pero Skaffold hace todo por ti. Probemos eso con el siguiente comando:

cd shakesapp-python
skaffold run --tail

En cuanto ejecutes el comando, verás el resultado del registro de docker build y podrás confirmar que se enviaron correctamente al registro.

Resultado del 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

Después de enviar todos los contenedores de servicio, las implementaciones de Kubernetes se inician automáticamente.

Resultado del 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

Precaución: Si ves el error “No push access to specified image repository”, verifica si el comando de skaffold está intentando enviar imágenes a Docker Hub (docker.io), independientemente de tu configuración en el repositorio predeterminado de Skaffold. En ese caso, intenta agregar “–default-repo”. la opción para ejecutar Skaffold como se muestra a continuación.

$ Skaffold run –tail –default-repo=us-central1-docker.pkg.dev/[ID del proyecto]/[nombre del repositorio]

Después de la implementación, verás los registros de aplicación reales emitidos a stdout en cada contenedor de la siguiente manera:

Resultado del comando

[server] {"event": "starting server: 0.0.0.0:5050", "severity": "info", "timestamp": "2021-03-17T05:25:56.758575Z"}
[client] [2021-03-17 05:25:54 +0000] [1] [INFO] Starting gunicorn 20.0.4
[client] [2021-03-17 05:25:54 +0000] [1] [INFO] Listening at: http://0.0.0.0:8080 (1)
[client] [2021-03-17 05:25:54 +0000] [1] [INFO] Using worker: threads
[client] [2021-03-17 05:25:54 +0000] [7] [INFO] Booting worker with pid: 7
[client] {"event": "server address is serverservice:5050", "severity": "info", "timestamp": "2021-03-17T05:25:54.888627Z"}
[client] {"event": "request to server with query: world", "severity": "info", "timestamp": "2021-03-17T05:26:11.550923Z"}
[server] {"event": "query: world", "severity": "info", "timestamp": "2021-03-17T05:26:11.567048Z"}
[loadgen] {"event": "check connectivity: http://clientservice:8080/_healthz", "severity": "info", "timestamp": "2021-03-17T05:26:11.533605Z"}
[loadgen] {"event": "/_healthz response: ok", "severity": "info", "timestamp": "2021-03-17T05:26:11.544267Z"}
[loadgen] {"event": "confirmed connection ot clientservice", "severity": "info", "timestamp": "2021-03-17T05:26:11.544527Z"}

Por último, está todo listo para comenzar a instrumentar tu aplicación con OpenTelemetry a fin de realizar un seguimiento distribuido de los servicios.

Resumen

En este paso, preparaste el material del codelab en tu entorno y confirmaste que Skaffold se ejecuta como se esperaba.

Cuál es el próximo paso

En el siguiente paso, modificarás el código fuente del servicio loadgen para instrumentar la información de seguimiento.

4. Instrumentación para HTTP

Concepto de instrumentación y propagación de seguimiento

Antes de editar el código fuente, permíteme explicarte brevemente cómo funcionan los seguimientos distribuidos en un diagrama sencillo.

c8c659deaa9c9091.png

En este ejemplo, instrumentamos el código para exportar información de Trace y Span a Cloud Trace y propagar el contexto de seguimiento en la solicitud desde el servicio loadgen hasta el servicio del servidor.

La aplicación debe enviar metadatos de Trace, como el ID de seguimiento y el ID de intervalo, para que Cloud Trace ensambla todos los intervalos que tienen el mismo ID de Trace en un solo seguimiento. Además, la aplicación debe propagar contextos de seguimiento (la combinación del ID de seguimiento y el ID de intervalo del intervalo superior) cuando se solicitan servicios downstream, para que puedan saber qué contexto de seguimiento están manejando.

OpenTelemetry te ayuda a hacer lo siguiente:

  • para generar un ID de seguimiento y un ID de intervalo únicos
  • para exportar el ID de seguimiento y el ID de intervalo al backend
  • para propagar contextos de seguimiento a otros servicios

Primer intervalo del instrumento

Servicio de generador de cargas de instrumentos

Para abrir el editor de Cloud Shell, presiona el botón 776a11bfb2122549.png ubicado en la parte superior derecha de Cloud Shell. Abre src/loadgen/loadgen.py desde el explorador en el panel izquierdo y busca la función main.

src/loadgen/loadgen.py

def main():
    ...
    # start request loop to client service
    logger.info("start client request loop")
    addr = f"http://{target}"
    while True:
        logger.info("start request to client")
        call_client(addr)
        logger.info("end request to client")
        time.sleep(2.0)

En la función main, verás el bucle que llama a la función call_client. En la implementación actual, la sección tiene 2 líneas de registro que registran el comienzo y el final de la llamada a función. Ahora instrumentaremos la información del intervalo para hacer un seguimiento de la latencia de la llamada a función.

Primero, debes crear un intervalo con un ID de seguimiento y un ID de intervalo únicos. OpenTelemetry proporciona una biblioteca práctica para ello. Agrega las siguientes líneas para importar bibliotecas de OpenTelemetry a tu código.

 import structlog
+from opentelemetry import propagate, trace
+from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter
+from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.instrumentation.requests import RequestsInstrumentor
+from opentelemetry.sdk.trace.export import SimpleSpanProcessor
+from opentelemetry.propagators.cloud_trace_propagator import CloudTraceFormatPropagator

Debido a que el generador de cargas llama a la aplicación cliente en HTTP a través del módulo requests, usamos el paquete de extensión para requests y habilitamos la instrumentación.

 from opentelemetry.propagators.cloud_trace_propagator import CloudTraceFormatPropagator
+
+RequestsInstrumentor().instrument()

Luego, configura la instancia de Tracer que se encarga de la configuración de Trace Contenxt y del exportador.

     target = os.environ.get("CLIENT_ADDR", "0.0.0.0:8080")

+    exporter = CloudTraceSpanExporter()
+    trace.get_tracer_provider().add_span_processor(SimpleSpanProcessor(exporter))
+    tracer = trace.get_tracer(__name__)
+    propagate.set_global_textmap(CloudTraceFormatPropagator())
+    trace.set_tracer_provider(TracerProvider())
+
     # connectivity check to client service
     healthz = f"http://{target}/_healthz"
     logger.info(f"check connectivity: {healthz}")

Ten en cuenta que, como este es un codelab para comprender cómo funciona la instrumentación de seguimiento, configuramos Tracer para que registre cada solicitud y las envíe al backend. (SimpleSpanProcessor()) Esto no es adecuado para entornos de producción, así que asegúrate de cambiar esta parte cuando instrumentes tu aplicación de producción.

Ahora puedes instrumentar intervalos con el rastreador. El punto aquí es que lo que debes hacer es generar un Span de manera explícita, ¡y eso es todo! Aunque existen dos líneas que agregan metadatos de eventos al intervalo, no es necesario generar un ID de seguimiento y un ID de intervalo únicos de forma manual y, luego, incorporarlos al intervalo.

     logger.info("start client request loop")
     addr = f"http://{target}"
     while True:
-        logger.info("start request to client")
-        call_client(addr)
-        logger.info("end request to client")
+        with tracer.start_as_current_span("loadgen") as root_span:
+            root_span.add_event(name="request_start")
+            logger.info("start request to client")
+            call_client(addr)
+            root_span.add_event(name="request_end")
+            logger.info("end request to client")
         time.sleep(2.0)

Para que la compilación de Docker recupere los paquetes necesarios de OpenTelemetry, ejecuta el siguiente comando:

poetry add "opentelemetry-exporter-gcp-trace=^1.0.0rc0"
poetry add "opentelemetry-propagator-gcp=^1.0.0rc0"
poetry add "opentelemetry-instrumentation-requests=^0.20b0"

Puedes confirmar que la descripción de la dependencia correspondiente esté escrita en pyproject.toml.

Instrumenta el servicio de cliente

En la sección anterior, instrumentamos la parte delimitada en el rectángulo rojo en el siguiente dibujo. Implementamos la información de intervalo en el servicio del generador de cargas. De manera similar al servicio del generador de cargas, ahora debemos instrumentar el servicio del cliente. La diferencia del servicio de generador de cargas es que el servicio de cliente debe extraer la información del ID de Trace que se propaga del servicio de generador de cargas en el encabezado HTTP y usar el ID para generar intervalos.

ae074d4513c9931f.png

Abre el editor de Cloud Shell y agrega los módulos requeridos, como hicimos con el servicio del generador de cargas.

src/client/client.py

 import flask
 import grpc
 import structlog
+from opentelemetry import propagate, trace
+from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter
+from opentelemetry.instrumentation.flask import FlaskInstrumentor
+from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.sdk.trace.export import SimpleSpanProcessor
+from opentelemetry.propagators.cloud_trace_propagator import \
+    CloudTraceFormatPropagator

 import shakesapp_pb2
 import shakesapp_pb2_grpc

Notaste que acabas de importar FlaskInstrumentor, que habilita la instrumentación automática de la aplicación Flask, en nombre de los usuarios, para extraer encabezados HTTP y obtener contextos de seguimiento con una sola línea de código. La comunidad de OpenTelemetry proporciona integraciones útiles similares con otras bibliotecas importantes. Para obtener más información, puedes consultar la documentación oficial.

 app = flask.Flask(__name__)
+FlaskInstrumentor().instrument_app(app)

Antes de comenzar con la instrumentación, debe preparar la instancia Tracer de manera similar a como lo hicimos en el servicio del generador de cargas.

 logger.info(f"server address is {SERVER_ADDR}")

+exporter = CloudTraceSpanExporter()
+trace.get_tracer_provider().add_span_processor(SimpleSpanProcessor(exporter))
+propagate.set_global_textmap(CloudTraceFormatPropagator())
+trace.set_tracer_provider(TracerProvider())

 @app.route("/")
 def main_handler():
    ....

Ahora está listo para agregar instrumentación en el controlador. Busca main_handler() y modifica la parte que muestra la solicitud de gRPC al servicio del servidor.

@app.route("/")
def main_handler():
    q, count = random.choice(list(queries.items()))

    # get Tracer
    tracer = trace.get_tracer(__name__)

    with tracer.start_as_current_span("client") as cur_span:
        channel = grpc.insecure_channel(SERVER_ADDR)
        stub = shakesapp_pb2_grpc.ShakespeareServiceStub(channel)
        logger.info(f"request to server with query: {q}")
        cur_span.add_event("server_call_start")
        resp = stub.GetMatchCount(shakesapp_pb2.ShakespeareRequest(query=q))
        cur_span.add_event("server_call_end")
        if count != resp.match_count:
            raise UnexpectedResultError(
                f"The expected count for '{q}' was {count}, but result was {resp.match_count } obtained"
            )
        result = str(resp.match_count)
        logger.info(f"matched count for '{q}' is {result}")
    return result

De manera similar al servicio de generador de cargas, agrega los paquetes requeridos a pyproject.toml con el siguiente comando.

poetry add "opentelemetry-exporter-gcp-trace=^1.0.0rc0"
poetry add "opentelemetry-propagator-gcp=^1.0.0rc0"
poetry add "opentelemetry-instrumentation-flask=^0.20b0"

Luego, intenta iniciar la aplicación con el comando skaffold run y observa lo que muestra el panel de Cloud Trace:

skaffold run --tail

Después de ver algunos mensajes de compilación, implementación y envío, verás registros de aplicaciones en formatos JSON. Navega a Cloud Trace > Lista de seguimientos para verificar si obtienes la información de seguimiento. Debido a que el servicio de generador de cargas envía solicitudes al servicio de cliente de forma periódica y tú habilitaste los seguimientos para todas las solicitudes, comenzarás a ver muchos puntos en la lista de seguimiento.

f7440360551980e.png

Si haces clic en una de ellas, verás un gráfico de cascada como el de abajo que explica la latencia de cada parte durante el proceso de solicitud y respuesta. Busca la casilla de verificación junto a "Mostrar eventos" y, luego, obtendrás las anotaciones dentro del gráfico de cascada. Estas anotaciones son aquellas que instrumentaste en el código con el método span.add_event().

67596a4a313738.png

Es posible que notes que no ves los intervalos del servicio del servidor. Es correcta, ya que no instrumentamos los intervalos en el servicio del servidor.

Resumen

En este paso, instrumentaste el servicio del generador de cargas y el servicio al cliente, y confirmaste que pudiste propagar correctamente el contexto de seguimiento en todos los servicios y exportar la información del intervalo de ambos servicios a Cloud Trace.

Cuál es el próximo paso

En el siguiente paso, instrumentarás el servicio de cliente y el servicio del servidor para confirmar cómo propagar Trace Context a través de gRPC.

5. Instrumentación para gRPC

En el paso anterior, instrumentamos la primera mitad de la solicitud en estos microservicios. En este paso, intentamos instrumentar la comunicación de gRPC entre el servicio del cliente y el servicio del servidor. (Rectángulos verde y púrpura en la imagen de abajo)

c4dec3e741c3ab4f.png

Instrumentación automática para el cliente de gRPC

El ecosistema de OpenTelemetry ofrece una gran cantidad de bibliotecas prácticas que ayudan a los desarrolladores a instrumentar aplicaciones. En el paso anterior, usamos instrumentación automática para "solicitudes" módulo. En este paso, ya que intentamos propagar Trace Context a través de gRPC, usamos la biblioteca para ello.

src/client/client.py

 import flask
 import grpc
 import structlog
 from opentelemetry import propagate, trace
 from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter
 from opentelemetry.instrumentation.flask import FlaskInstrumentor
+from opentelemetry.instrumentation.grpc import GrpcInstrumentorClient
 from opentelemetry.sdk.trace import TracerProvider
 from opentelemetry.sdk.trace.export import SimpleSpanProcessor
 from opentelemetry.propagators.cloud_trace_propagator import \
     CloudTraceFormatPropagator
 import shakesapp_pb2
 import shakesapp_pb2_grpc


 app = flask.Flask(__name__)
 FlaskInstrumentor().instrument_app(app)
+GrpcInstrumentorClient().instrument()

En cuanto al servicio al cliente, la instrumentación es bastante pequeña. Lo que queremos hacer es propagar el Contexto de seguimiento, que es la combinación del ID de seguimiento y el ID de intervalo del intervalo actual a través de gRPC. Por lo tanto, llamamos a GrpcInstrumentatorClient.instrument() para que el cliente de gRPC en la función de traspaso pueda incorporar el contexto de seguimiento en el encabezado HTTP que está debajo.

Asegúrate de agregar dependencias nuevas a pyproject.toml con el comando poetry add:

poetry add "opentelemetry-instrumentation-grpc=^0.20b0"

Instrumentación automática para el servidor gRPC

Al igual que con el cliente de gRPC, llamamos instrumentación automática para el servidor de gRPC. Agrega importaciones, como seguimientos, y llama a GrpcInstrumentationServer().instrument() en la parte superior del archivo.

Precaución: Asegúrate de llamar

GrpcInstrumentationServe() 

en este paso, no

GrpcInstrumentationClient()

.

src/server/server.py

 import grpc
 import structlog
 from google.cloud import storage
 from grpc_health.v1 import health_pb2, health_pb2_grpc
+from opentelemetry import propagate, trace
+from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter
+from opentelemetry.instrumentation.grpc import GrpcInstrumentorServer
+from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.sdk.trace.export import SimpleSpanProcessor
+from opentelemetry.propagators.cloud_trace_propagator import CloudTraceFormatPropagator

 import shakesapp_pb2
 import shakesapp_pb2_grpc


 BUCKET_NAME = "dataflow-samples"
 BUCKET_PREFIX = "shakespeare/"

+# enable auto gRPC server trace instrumentation
+GrpcInstrumentorServer().instrument()
+

A continuación, agregarás el exportador para enviar información de seguimiento al backend de Cloud Trace. Agrega el siguiente código a la función serve().

def serve():
+    # start trace exporter
+    trace.set_tracer_provider(TracerProvider())
+    trace.get_tracer_provider().add_span_processor(
+        SimpleSpanProcessor(CloudTraceSpanExporter())
+    )
+    propagators.set_global_textmap(CloudTraceFormatPropagator())
+
+    # add gRPC services to server
     server = grpc.server(futures.ThreadPoolExecutor(max_workers=4))
     service = ShakesappService()
     shakesapp_pb2_grpc.add_ShakespeareServiceServicer_to_server(service, server)
     health_pb2_grpc.add_HealthServicer_to_server(service, server)

Asegúrate de agregar los paquetes agregados recientemente en el servicio de servidor.

poetry add "opentelemetry-exporter-gcp-trace=^1.0.0rc0"
poetry add "opentelemetry-instrumentation-grpc=^0.20b0"
poetry add "opentelemetry-propagator-gcp=^1.0.0rc0"
poetry add "opentelemetry-instrumentation=^0.20b0"

Ejecuta el microservicio y confirma el seguimiento

Luego, ejecuta el código modificado con el comando de Skaffold.

skaffold run --tail

Ahora, de nuevo, verás un montón de seguimientos en la página de lista de Trace de Cloud Trace. Haz clic en uno de los seguimientos y ahora verás que abarca la solicitud, desde el servicio del generador de cargas hasta el servicio del servidor.

141cb620245b689d.png

Resumen

En este paso, instrumentaste la comunicación basada en gRPC con la compatibilidad de las bibliotecas del ecosistema de OpenTelemetry. Además, confirmaste que el contexto de seguimiento generado en el servicio de generador de cargas se entregó correctamente al servicio del servidor.

6. Felicitaciones

Creaste correctamente seguimientos distribuidos con OpenTelemery y confirmaste latencias de solicitud en el microservicio de Google Cloud Trace.

Para los ejercicios extendidos, puedes probar los siguientes temas por tu cuenta.

  • La implementación actual envía todos los intervalos que genera la verificación de estado. ¿Cómo se filtran esos intervalos de Cloud Trace? Puedes ver una pista aquí.
  • Correlaciona los registros de eventos con los intervalos y descubre cómo funcionan en Google Cloud Trace y Google Cloud Logging. Puedes ver una pista aquí.
  • Reemplaza algún servicio por el que está en otro idioma y prueba instrumentarlo con OpenTelemetry para ese idioma

Precaución: Google Kubernetes Engine y Google Artifact Registry consumen el recurso constantemente.

Realiza una limpieza

Después de este codelab, detén el clúster de Kubernetes y asegúrate de borrar el proyecto para no recibir cargos inesperados de Google Kubernetes Engine, Google Cloud Trace o Google Artifact Registry.

Primero, borra el clúster con el siguiente comando:

skaffold delete

Resultado del comando

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

Después de borrar el clúster, en el panel de menú, selecciona “IAM & Administrador &gt; “Configuración” y, luego, haz clic en “APAGAR” .

578ca2b72a161e9d.png

Luego, ingresa el ID del proyecto (no el nombre del proyecto) en el formulario del diálogo y confirma el cierre.