Einfaches OpenTelemetry-Plug-in in gRPC Python einrichten

1. Einführung

In diesem Codelab verwenden Sie gRPC, um einen Client und einen Server zu erstellen, die die Grundlage einer in Python geschriebenen Anwendung zur Routenplanung 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 Plattformspezifische Installationsanleitungen für Python finden Sie unter Python Setup and Usage. Alternativ können Sie mit Tools wie uv oder pyenv eine Python-Version installieren, die nicht zum System gehört.
  • pip-Version 9.0.1 oder höher zum Installieren von Python-Paketen.
  • 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 den Einstieg zu erleichtern, wird in diesem Codelab ein vorgefertigtes Quellcode-Gerüst bereitgestellt. In den folgenden Schritten wird beschrieben, wie Sie das gRPC OpenTelemetry-Plug-in in einer Anwendung instrumentieren.

grpc-codelabs

Der Gerüstquellcode 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 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 entpacken.

Erstellen Sie 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 standardmäßigen 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 mit:

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.

Als Erstes müssen Sie das OpenTelemetry-Plug-in, das mit einem Prometheus-Exporter konfiguriert ist, im Client registrieren. Ö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

Transformieren Sie run() dann 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()

Im nächsten Schritt fügen Sie dem Server das OpenTelemetry-Plug-in hinzu. Öffnen Sie start_here/observability_greeter_server.py und fügen Sie die entsprechenden Abhängigkeiten und Makros hinzu, sodass es so aussieht:

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

Transformieren Sie run() dann 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

So führen Sie den Server aus:

cd start_here
python -m observability_greeter_server

Bei einer erfolgreichen Einrichtung wird für den Server die folgende Ausgabe angezeigt:

Server started, listening on 50051

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

# 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

Ein erfolgreicher Lauf 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 auf localhost:9464 für den Server und auf localhost:9465 für den Client verfügbar.

So rufen Sie Clientmesswerte auf:

curl localhost:9465/metrics

Das Ergebnis würde so aussehen:

# 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 verhält es sich bei den serverseitigen Messwerten:

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 aktuelle 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 es dann und führen Sie es mit dem folgenden Befehl aus:

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

Erstellen Sie eine Prometheus-Konfigurationsdatei mit den folgenden Inhalten:

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 der Client- und Server-Codelab-Prozesse alle 5 Sekunden erfasst.

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

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

wird ein Diagramm mit der medianen Latenz für Versuche angezeigt, wobei für die Quantilberechnung ein Zeitfenster von einer Minute verwendet wird.

Anzahl der Anfragen –

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. Sehen Sie nach, ob Sie im Beispielcode verdächtigen Code finden, der die QPS begrenzt.

Für den Enthusiasten beschränkt sich der Clientcode darauf, zu einem bestimmten Zeitpunkt nur einen ausstehenden RPC zu haben. Dies kann so geändert werden, dass der Client mehr RPCs sendet, ohne auf den Abschluss der vorherigen zu warten. (Die Lösung hierfür wurde nicht bereitgestellt.)