Einfaches OpenTelemetry-Plug-in in gRPC Python einrichten

1. Einführung

In diesem Codelab erstellen Sie mit gRPC einen Client und einen Server, die die Grundlage für eine in Python geschriebene Routenplanungsanwendung bilden.

Am Ende des Tutorials haben Sie eine einfache gRPC-HelloWorld-Anwendung, die mit dem gRPC-OpenTelemetry-Plug-in instrumentiert ist, und können die exportierten Beobachtbarkeitsmesswerte in Prometheus sehen.

Lerninhalte

  • OpenTelemetry-Plug-in für eine vorhandene gRPC-Python-Anwendung einrichten
  • Lokale Prometheus-Instanz ausführen
  • Messwerte nach Prometheus exportieren
  • Messwerte im Prometheus-Dashboard ansehen

2. Hinweis

Voraussetzungen

  • git
  • curl
  • build-essential
  • Python 3.9 oder höher. Eine plattformspezifische Anleitung zur Python-Installation finden Sie unter Python Setup and Usage. Alternativ können Sie eine Nicht-System-Python-Version mit Tools wie uv oder pyenv installieren.
  • pip-Version 9.0.1 oder höher, um Python-Pakete zu installieren.
  • venv zum Erstellen virtueller Python-Umgebungen.

Installieren Sie die notwendigen Komponenten:

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

Code abrufen

Um Ihnen das Lernen zu erleichtern, bietet dieses Codelab ein vorgefertigtes Quellcode-Scaffold, das Ihnen den Einstieg erleichtert. In den folgenden Schritten erfahren Sie, wie Sie das gRPC-OpenTelemetry-Plug-in in einer Anwendung instrumentieren.

grpc-codelabs

Der Quellcode für das Scaffold für dieses Codelab ist in diesem GitHub- Verzeichnis verfügbar. Wenn Sie den Code nicht selbst implementieren möchten, finden Sie den vollständigen Quellcode im Verzeichnis „completed“.

Klonen Sie zuerst das gRPC-Codelab-Repository und wechseln Sie in den Ordner „grpc-python-opentelemetry“:

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

Alternativ können Sie die ZIP-Datei herunterladen, die nur das Codelab-Verzeichnis enthält, und sie manuell entzippen.

Erstellen wir zuerst eine neue virtuelle Python-Umgebung (venv), um die Abhängigkeiten Ihres Projekts von den Systempaketen zu isolieren:

python3 -m venv --upgrade-deps .venv

So aktivieren Sie die virtuelle Umgebung in der Bash-/Zsh-Shell:

source .venv/bin/activate

Informationen zu Windows und Nicht-Standard-Shells finden Sie in der Tabelle unter https://docs.python.org/3/library/venv.html#how-venvs-work.

Installieren Sie als Nächstes die Abhängigkeiten in der Umgebung:

python -m pip install -r requirements.txt

3. OpenTelemetry-Plug-in registrieren

Wir benötigen eine gRPC-Anwendung, um das gRPC-OpenTelemetry-Plug-in hinzuzufügen. In diesem Codelab verwenden wir einen einfachen gRPC-HelloWorld-Client und -Server, die wir mit dem gRPC-OpenTelemetry-Plug-in instrumentieren.

Im ersten Schritt registrieren Sie das OpenTelemetry-Plug-in, das mit einem Prometheus-Exporter im Client konfiguriert ist. Öffnen Sie start_here/observability_greeter_client.py mit Ihrem bevorzugten Editor. Fügen Sie zuerst die zugehörigen Abhängigkeiten und Makros hinzu, damit sie so aussehen:

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

Ändern Sie dann run() so, dass es so aussieht:

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()

Als Nächstes fügen Sie das OpenTelemetry-Plug-in dem Server hinzu. Öffnen Sie start_here/observability_greeter_server.py und fügen Sie die zugehörigen Abhängigkeiten und Makros hinzu, damit sie so aussehen:

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

Ändern Sie dann run() so, dass es so aussieht:

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. Beispiel ausführen und Messwerte ansehen

Führen Sie den folgenden Befehl aus, um den Server auszuführen:

cd start_here
python -m observability_greeter_server

Bei einer erfolgreichen Einrichtung sehen Sie die folgende Ausgabe für den Server:

Server started, listening on 50051

Während der Server ausgeführt wird, führen Sie den Client in einem anderen Terminal aus:

# 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

Eine erfolgreiche Ausführung sieht so aus:

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

Wir haben das gRPC-OpenTelemetry-Plug-in so eingerichtet, dass Messwerte mit Prometheus exportiert werden. Diese Messwerte sind für den Server unter „localhost:9464“ und für den Client unter „localhost:9465“ verfügbar.

So sehen Sie Clientmesswerte:

curl localhost:9465/metrics

Das Ergebnis hat die Form:

# 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

Ähnlich sieht es bei den serverseitigen Messwerten aus:

curl localhost:9464/metrics

5. Messwerte in Prometheus ansehen

Hier richten wir eine Prometheus-Instanz ein, die unseren gRPC-Beispielclient und -Server abruft, die Messwerte mit Prometheus exportieren.

Laden Sie die neueste Version von Prometheus für Ihre Plattform über den angegebenen Link herunter oder verwenden Sie den folgenden Befehl:

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

Extrahieren Sie sie dann und führen Sie sie mit dem folgenden Befehl aus:

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

Erstellen Sie eine Prometheus-Konfigurationsdatei mit dem folgenden Inhalt:

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

Starten Sie Prometheus mit der neuen Konfiguration:

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

Dadurch werden die Messwerte aus den Codelab-Prozessen für Client und Server alle 5 Sekunden abgerufen.

Rufen Sie http://localhost:9090/graph auf, um die Messwerte anzusehen. Beispielabfrage:

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

Dadurch wird ein Diagramm mit der medianen Latenz des Versuchs angezeigt, wobei 1 Minute als Zeitfenster für die Quantilberechnung verwendet wird.

Abfragerate:

increase(grpc_client_attempt_duration_seconds_bucket[1m])

6. (Optional) Übung für Nutzer

In den Prometheus-Dashboards sehen Sie, dass die QPS niedrig ist. Versuchen Sie, verdächtigen Code im Beispiel zu finden, der die QPS begrenzt.

Für Enthusiasten: Der Clientcode ist so begrenzt, dass zu einem bestimmten Zeitpunkt nur ein ausstehender RPC vorhanden ist. Dies kann so geändert werden, dass der Client mehr RPCs sendet, ohne auf den Abschluss der vorherigen zu warten. (Die Lösung dafür wird nicht bereitgestellt.)