Configura el complemento básico de OpenTelemetry en gRPC Python

1. Introducción

En este codelab, usarás gRPC para crear un cliente y un servidor que formen la base de una aplicación de asignación de rutas escrita en Python.

Al final del instructivo, tendrás una aplicación simple de gRPC HelloWorld instrumentada con el complemento de OpenTelemetry de gRPC y podrás ver las métricas de observabilidad exportadas en Prometheus.

Qué aprenderás

  • Cómo configurar el complemento de OpenTelemetry para una aplicación existente de gRPC en Python
  • Ejecuta una instancia local de Prometheus
  • Exporta métricas a Prometheus
  • Visualiza las métricas del panel de Prometheus

2. Antes de comenzar

Requisitos

  • git
  • curl
  • build-essential
  • Python 3.9 o una versión posterior Para obtener instrucciones de instalación de Python específicas de la plataforma, consulta Configuración y uso de Python. Como alternativa, instala un Python que no sea del sistema con herramientas como uv o pyenv.
  • La versión 9.0.1 o posterior de pip para instalar paquetes de Python
  • venv para crear entornos virtuales de Python

Instala los requisitos previos:

sudo apt-get update -y
sudo apt-get upgrade -y
sudo apt-get install -y git curl build-essential clang
sudo apt install python3
sudo apt install python3-pip python3-venv

Obtén el código

Para optimizar tu aprendizaje, este codelab ofrece un código fuente precompilado que te ayudará a comenzar. Los siguientes pasos te guiarán para instrumentar el complemento de OpenTelemetry de gRPC en una aplicación.

grpc-codelabs

El código fuente del andamio para este codelab está disponible en este directorio de GitHub. Si prefieres no implementar el código por tu cuenta, el código fuente completo está disponible en el directorio completed.

Primero, clona el repo del codelab de gRPC y cambia el directorio a la carpeta grpc-python-opentelemetry:

git clone https://github.com/grpc-ecosystem/grpc-codelabs.git
cd grpc-codelabs/codelabs/grpc-python-opentelemetry/

También puedes descargar el archivo .zip que contiene solo el directorio del codelab y descomprimirlo de forma manual.

Primero, crearemos un nuevo entorno virtual de Python (venv) para aislar las dependencias de tu proyecto de los paquetes del sistema:

python3 -m venv --upgrade-deps .venv

Para activar el entorno virtual en el shell de bash/zsh, haz lo siguiente:

source .venv/bin/activate

En el caso de Windows y shells no estándar, consulta la tabla en https://docs.python.org/3/library/venv.html#how-venvs-work.

A continuación, instala las dependencias en el entorno con el siguiente comando:

python -m pip install -r requirements.txt

3. Cómo registrar el complemento de OpenTelemetry

Necesitamos una aplicación de gRPC para agregar el complemento de OpenTelemetry de gRPC. En este codelab, usaremos un cliente y un servidor simples de gRPC HelloWorld que instrumentaremos con el complemento de gRPC OpenTelemetry.

El primer paso es registrar el complemento de OpenTelemetry configurado con un exportador de Prometheus en el cliente. Abre start_here/observability_greeter_client.py con tu editor favorito. Primero, agrega las dependencias y las macros relacionadas para que se vean de la siguiente manera:

import logging
import time

import grpc
import grpc_observability
import helloworld_pb2
import helloworld_pb2_grpc
from opentelemetry.exporter.prometheus import PrometheusMetricReader
from opentelemetry.sdk.metrics import MeterProvider
from prometheus_client import start_http_server

_SERVER_PORT = "50051"
_PROMETHEUS_PORT = 9465

Luego, transforma run() para que se vea así:

def run():
    # Start Prometheus client
    start_http_server(port=_PROMETHEUS_PORT, addr="0.0.0.0")
    meter_provider = MeterProvider(metric_readers=[PrometheusMetricReader()])

    otel_plugin = grpc_observability.OpenTelemetryPlugin(
        meter_provider=meter_provider
    )
    otel_plugin.register_global()

    with grpc.insecure_channel(target=f"localhost:{_SERVER_PORT}") as channel:
        stub = helloworld_pb2_grpc.GreeterStub(channel)
        # Continuously send RPCs every second.
        while True:
            try:
                response = stub.SayHello(helloworld_pb2.HelloRequest(name="You"))
                print(f"Greeter client received: {response.message}")
                time.sleep(1)
            except grpc.RpcError as rpc_error:
                print("Call failed with code: ", rpc_error.code())

    # Deregister is not called in this example, but this is required to clean up.
    otel_plugin.deregister_global()

El siguiente paso es agregar el complemento de OpenTelemetry al servidor. Abre start_here/observability_greeter_server.py y agrega las dependencias y macros relacionadas para que se vea de la siguiente manera:

from concurrent import futures
import logging
import time

import grpc
import grpc_observability
import helloworld_pb2
import helloworld_pb2_grpc
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.exporter.prometheus import PrometheusMetricReader
from prometheus_client import start_http_server

_SERVER_PORT = "50051"
_PROMETHEUS_PORT = 9464

Luego, transforma run() para que se vea así:

def serve():
    # Start Prometheus client
    start_http_server(port=_PROMETHEUS_PORT, addr="0.0.0.0")

    meter_provider = MeterProvider(metric_readers=[PrometheusMetricReader()])

    otel_plugin = grpc_observability.OpenTelemetryPlugin(
        meter_provider=meter_provider
    )
    otel_plugin.register_global()

    server = grpc.server(
        thread_pool=futures.ThreadPoolExecutor(max_workers=10),
    )
    helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
    server.add_insecure_port("[::]:" + _SERVER_PORT)
    server.start()
    print("Server started, listening on " + _SERVER_PORT)

    server.wait_for_termination()

    # Deregister is not called in this example, but this is required to clean up.
    otel_plugin.deregister_global()

4. Cómo ejecutar el ejemplo y ver las métricas

Para ejecutar el servidor, ejecuta el siguiente comando:

cd start_here
python -m observability_greeter_server

Si la configuración se realiza correctamente, verás el siguiente resultado para el servidor:

Server started, listening on 50051

Mientras el servidor se ejecuta, en otra terminal, ejecuta el cliente:

# Run the below commands to cd to the working directory and activate virtual environment in the new terminal
cd grpc-codelabs/codelabs/grpc-python-opentelemetry/
source .venv/bin/activate

cd start_here
python -m observability_greeter_client

Una ejecución correcta se verá de la siguiente manera:

Greeter client received: Hello You
Greeter client received: Hello You
Greeter client received: Hello You

Dado que configuramos el complemento de OpenTelemetry de gRPC para exportar métricas con Prometheus. Esas métricas estarán disponibles en localhost:9464 para el servidor y en localhost:9465 para el cliente.

Para ver las métricas del cliente, haz lo siguiente:

curl localhost:9465/metrics

El resultado tendría la siguiente forma:

# HELP python_gc_objects_collected_total Objects collected during gc
# TYPE python_gc_objects_collected_total counter
python_gc_objects_collected_total{generation="0"} 241.0
python_gc_objects_collected_total{generation="1"} 163.0
python_gc_objects_collected_total{generation="2"} 0.0
# HELP python_gc_objects_uncollectable_total Uncollectable objects found during GC
# TYPE python_gc_objects_uncollectable_total counter
python_gc_objects_uncollectable_total{generation="0"} 0.0
python_gc_objects_uncollectable_total{generation="1"} 0.0
python_gc_objects_uncollectable_total{generation="2"} 0.0
# HELP python_gc_collections_total Number of times this generation was collected
# TYPE python_gc_collections_total counter
python_gc_collections_total{generation="0"} 78.0
python_gc_collections_total{generation="1"} 7.0
python_gc_collections_total{generation="2"} 0.0
# HELP python_info Python platform information
# TYPE python_info gauge
python_info{implementation="CPython",major="3",minor="10",patchlevel="9",version="3.10.9"} 1.0
# HELP process_virtual_memory_bytes Virtual memory size in bytes.
# TYPE process_virtual_memory_bytes gauge
process_virtual_memory_bytes 1.868988416e+09
# HELP process_resident_memory_bytes Resident memory size in bytes.
# TYPE process_resident_memory_bytes gauge
process_resident_memory_bytes 4.1680896e+07
# TYPE process_resident_memory_bytes gauge                                                                                                                                                                                                                                                                21:20:16 [154/966]
process_resident_memory_bytes 4.1680896e+07
# HELP process_start_time_seconds Start time of the process since unix epoch in seconds.
# TYPE process_start_time_seconds gauge
process_start_time_seconds 1.72375679833e+09
# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds.
# TYPE process_cpu_seconds_total counter
process_cpu_seconds_total 0.38
# HELP process_open_fds Number of open file descriptors.
# TYPE process_open_fds gauge
process_open_fds 9.0
# HELP process_max_fds Maximum number of open file descriptors.
# TYPE process_max_fds gauge
process_max_fds 4096.0
# HELP target_info Target metadata
# TYPE target_info gauge
target_info{service_name="unknown_service",telemetry_sdk_language="python",telemetry_sdk_name="opentelemetry",telemetry_sdk_version="1.26.0"} 1.0
# HELP grpc_client_attempt_started_total Number of client call attempts started
# TYPE grpc_client_attempt_started_total counter
grpc_client_attempt_started_total{grpc_method="other",grpc_target="localhost:50051"} 18.0
# HELP grpc_client_attempt_sent_total_compressed_message_size_bytes Compressed message bytes sent per client call attempt
# TYPE grpc_client_attempt_sent_total_compressed_message_size_bytes histogram
grpc_client_attempt_sent_total_compressed_message_size_bytes_bucket{grpc_method="other",grpc_status="OK",grpc_target="localhost:50051",le="0.0"} 0.0
grpc_client_attempt_sent_total_compressed_message_size_bytes_bucket{grpc_method="other",grpc_status="OK",grpc_target="localhost:50051",le="5.0"} 18.0
grpc_client_attempt_sent_total_compressed_message_size_bytes_bucket{grpc_method="other",grpc_status="OK",grpc_target="localhost:50051",le="10.0"} 18.0
grpc_client_attempt_sent_total_compressed_message_size_bytes_bucket{grpc_method="other",grpc_status="OK",grpc_target="localhost:50051",le="25.0"} 18.0
grpc_client_attempt_sent_total_compressed_message_size_bytes_bucket{grpc_method="other",grpc_status="OK",grpc_target="localhost:50051",le="50.0"} 18.0
grpc_client_attempt_sent_total_compressed_message_size_bytes_bucket{grpc_method="other",grpc_status="OK",grpc_target="localhost:50051",le="75.0"} 18.0
grpc_client_attempt_sent_total_compressed_message_size_bytes_bucket{grpc_method="other",grpc_status="OK",grpc_target="localhost:50051",le="100.0"} 18.0
grpc_client_attempt_sent_total_compressed_message_size_bytes_bucket{grpc_method="other",grpc_status="OK",grpc_target="localhost:50051",le="250.0"} 18.0

Del mismo modo, para las métricas del servidor, haz lo siguiente:

curl localhost:9464/metrics

5. Visualiza métricas en Prometheus

Aquí, configuraremos una instancia de Prometheus que extraerá nuestro cliente y servidor de ejemplo de gRPC que exportan métricas con Prometheus.

Descarga la versión más reciente de Prometheus para tu plataforma con el vínculo proporcionado o usa el siguiente comando:

curl -sLO https://github.com/prometheus/prometheus/releases/download/v3.7.3/prometheus-3.7.3.linux-amd64.tar.gz

Luego, extráelo y ejecútalo con el siguiente comando:

tar xvfz prometheus-*.tar.gz
cd prometheus-*

Crea un archivo de configuración de Prometheus con lo siguiente:

cat > grpc_otel_python_prometheus.yml <<EOF
scrape_configs:
  - job_name: "prometheus"
    scrape_interval: 5s
    static_configs:
      - targets: ["localhost:9090"]
  - job_name: "grpc-otel-python"
    scrape_interval: 5s
    static_configs:
      - targets: ["localhost:9464", "localhost:9465"]
EOF

Inicia Prometheus con la nueva configuración:

./prometheus --config.file=grpc_otel_python_prometheus.yml

Esto configurará las métricas de los procesos del codelab del cliente y del servidor para que se extraigan cada 5 segundos.

Ve a http://localhost:9090/graph para ver las métricas. Por ejemplo, la siguiente consulta:

histogram_quantile(0.5, rate(grpc_client_attempt_duration_seconds_bucket[1m]))

mostrará un gráfico con la latencia mediana de intentos usando 1 minuto como período para el cálculo del cuantil.

Tasa de consultas:

increase(grpc_client_attempt_duration_seconds_bucket[1m])

6. Ejercicio opcional para el usuario

En los paneles de Prometheus, observarás que los QPS son bajos. Intenta identificar código sospechoso en el ejemplo que limita las QPS.

Para los entusiastas, el código del cliente se limita a tener solo un RPC pendiente en un momento determinado. Esto se puede modificar para que el cliente envíe más RPC sin esperar a que se completen las anteriores. (No se proporcionó la solución para este problema).