הגדרה של פלאגין OpenTelemetry בסיסי ב-gRPC Python

1. מבוא

ב-codelab הזה תשתמשו ב-gRPC כדי ליצור לקוח ושרת שיהוו את הבסיס לאפליקציה למיפוי מסלולים שנכתבה ב-Python.

בסוף המדריך תהיה לכם אפליקציית gRPC HelloWorld פשוטה עם מכשור באמצעות הפלאגין gRPC OpenTelemetry, ותוכלו לראות את מדדי יכולת הצפייה המיוצאים ב-Prometheus.

מה תלמדו

  • איך מגדירים את הפלאגין OpenTelemetry לאפליקציית Python gRPC קיימת
  • הפעלת מופע מקומי של Prometheus
  • ייצוא מדדים ל-Prometheus
  • הצגת מדדים ממרכז הבקרה של Prometheus

‫2. לפני שמתחילים

הדרישות

  • git
  • curl
  • build-essential
  • ‫Python 3.9 ואילך. הוראות להתקנת Python ספציפיות לפלטפורמה מופיעות במאמר הגדרה ושימוש ב-Python. אפשר גם להתקין Python שאינו מערכת באמצעות כלים כמו uv או pyenv.
  • pip בגרסה 9.0.1 ואילך להתקנת חבילות Python.
  • venv כדי ליצור סביבות וירטואליות של Python.

מתקינים את הדרישות המוקדמות:

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

קבל את הקוד

כדי לייעל את הלמידה, ב-Codelab הזה מוצעת מסגרת מוכנה מראש של קוד מקור שתעזור לכם להתחיל. השלבים הבאים יעזרו לכם להטמיע את הפלאגין gRPC OpenTelemetry באפליקציה.

grpc-codelabs

קוד המקור של ה-scaffold ל-codelab הזה זמין בספרייה הזו ב-GitHub. אם אתם מעדיפים לא להטמיע את הקוד בעצמכם, קוד המקור המלא זמין בספרייה completed.

קודם כול, משכפלים את מאגר ה-codelab של grpc ועוברים לתיקייה grpc-python-opentelemetry:

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

אפשרות אחרת היא להוריד את קובץ ה-‎ .zip שמכיל רק את ספריית ה-codelab ולבטל את הדחיסה שלו באופן ידני.

קודם ניצור סביבה וירטואלית של Python (venv) כדי לבודד את התלות של הפרויקט בחבילות המערכת:

python3 -m venv --upgrade-deps .venv

כדי להפעיל את הסביבה הווירטואלית במעטפת bash/zsh:

source .venv/bin/activate

בטבלה שבכתובת https://docs.python.org/3/library/venv.html#how-venvs-work מוסבר איך פועלים סביבות וירטואליות ב-Windows ובמעטפות לא סטנדרטיות.

לאחר מכן, מתקינים את יחסי התלות בסביבה באמצעות הפקודה:

python -m pip install -r requirements.txt

3. רישום הפלאגין OpenTelemetry

כדי להוסיף את הפלאגין gRPC OpenTelemetry, צריך אפליקציית gRPC. ב-codelab הזה נשתמש בלקוח ובשרת פשוטים של gRPC HelloWorld, שבהם נשתמש בפלאגין gRPC OpenTelemetry.

השלב הראשון הוא לרשום את OpenTelemetry Plugin שהוגדר עם Prometheus exporter בלקוח. פותחים את start_here/observability_greeter_client.py בעורך המועדף. קודם מוסיפים את התלות והפקודות המקרו הרלוונטיות כך:

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

אחר כך משנים את הצורה של run() כך שתיראה כמו –

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

השלב הבא הוא להוסיף את הפלאגין OpenTelemetry לשרת. פותחים את start_here/observability_greeter_server.py ומוסיפים פקודות מאקרו ויחסי תלות קשורים כך:

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

אחר כך משנים את הצורה של run() כך שתיראה כמו –

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. הרצת הדוגמה והצגת המדדים

כדי להריץ את השרת, מריצים את הפקודה –

cd start_here
python -m observability_greeter_server

אם ההגדרה הושלמה בהצלחה, הפלט של השרת ייראה כך:

Server started, listening on 50051

בזמן שהשרת פועל, במסוף אחר, מריצים את הלקוח –

# 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

הרצה מוצלחת תיראה כך:

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

הגדרנו את הפלאגין gRPC OpenTelemetry לייצוא מדדים באמצעות Prometheus. המדדים האלה יהיו זמינים בכתובת localhost:9464 לשרת ובכתובת localhost:9465 ללקוח.

כדי לראות את מדדי הלקוח –

curl localhost:9465/metrics

התוצאה תהיה מהצורה –

# 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

באופן דומה, לגבי המדדים בצד השרת –

curl localhost:9464/metrics

5. הצגת מדדים ב-Prometheus

במאמר הזה נגדיר מופע של Prometheus שיבצע scraping של הלקוח והשרת לדוגמה של gRPC שמייצאים מדדים באמצעות Prometheus.

מורידים את הגרסה האחרונה של Prometheus לפלטפורמה שלכם באמצעות הקישור שמופיע, או משתמשים בפקודה הבאה:

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

לאחר מכן מחלצים ומריצים אותו באמצעות הפקודה הבאה:

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

יוצרים קובץ תצורה של Prometheus עם הפרטים הבאים:

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

מפעילים את Prometheus עם ההגדרה החדשה –

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

הפעולה הזו תגדיר את המדדים מתהליכי ה-codelab של הלקוח והשרת כך שהם ייגרדו כל 5 שניות.

כדי לראות את המדדים, עוברים אל http://localhost:9090/graph. לדוגמה, השאילתה –

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

יוצג תרשים עם חביון הניסיון הממוצע, כשדקה אחת מוגדרת כחלון הזמן לחישוב הכמותי.

שיעור השאילתות –

increase(grpc_client_attempt_duration_seconds_bucket[1m])

6. (אופציונלי) תרגיל למשתמש

במרכזי הבקרה של Prometheus, תראו שערך השאילתות לשנייה נמוך. בודקים אם אפשר לזהות בקוד לדוגמה קוד חשוד שמגביל את מספר השאילתות לשנייה.

למי שרוצה להעמיק, קוד הלקוח מוגבל כך שיכולה להיות רק קריאת RPC אחת בהמתנה בכל רגע נתון. אפשר לשנות את ההגדרה כך שהלקוח ישלח עוד בקשות RPC בלי לחכות להשלמת הבקשות הקודמות. (הפתרון לבעיה הזו לא סופק).