راه اندازی افزونه OpenTelemetry پایه در gRPC پایتون

۱. مقدمه

در این آزمایشگاه کد، شما از gRPC برای ایجاد یک کلاینت و سرور استفاده خواهید کرد که پایه و اساس یک برنامه مسیریابی نوشته شده در پایتون را تشکیل می‌دهند.

در پایان این آموزش، شما یک برنامه ساده gRPC HelloWorld مجهز به افزونه gRPC OpenTelemetry خواهید داشت و می‌توانید معیارهای مشاهده‌پذیری خروجی گرفته شده را در Prometheus مشاهده کنید.

آنچه یاد خواهید گرفت

  • نحوه تنظیم افزونه OpenTelemetry برای برنامه پایتون gRPC موجود
  • اجرای یک نمونه محلی پرومتئوس
  • خروجی گرفتن از معیارها به پرومتئوس
  • مشاهده معیارها از داشبورد پرومتئوس

۲. قبل از شروع

آنچه نیاز دارید

  • گیت
  • حلقه زدن
  • ساخت ضروری
  • پایتون ۳.۹ یا بالاتر. برای دستورالعمل‌های نصب پایتون مخصوص هر پلتفرم، به بخش «راه‌اندازی و کاربرد پایتون» مراجعه کنید. روش دیگر، نصب یک پایتون غیرسیستمی با استفاده از ابزارهایی مانند uv یا pyenv است.
  • برای نصب بسته‌های پایتون، از pip نسخه ۹.۰.۱ یا بالاتر استفاده کنید.
  • venv برای ایجاد محیط‌های مجازی پایتون.

پیش‌نیازها را نصب کنید:

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 موجود است.

ابتدا، مخزن grpc codelab را کپی کنید و با دستور cd به پوشه grpc-python-opentelemetry منتقل شوید:

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

روش دیگر این است که فایل .zip که فقط شامل دایرکتوری codelab است را دانلود کرده و به صورت دستی آن را از حالت فشرده خارج کنید.

بیایید ابتدا یک محیط مجازی پایتون جدید (venv) ایجاد کنیم تا وابستگی‌های پروژه شما از بسته‌های سیستمی جدا شود:

python3 -m venv --upgrade-deps .venv

برای فعال کردن محیط مجازی در شل bash/zsh:

source .venv/bin/activate

برای پوسته‌های ویندوز و غیر استاندارد، به جدول https://docs.python.org/3/library/venv.html#how-venvs-work مراجعه کنید.

در مرحله بعد، وابستگی‌ها را با استفاده از دستور زیر در محیط نصب کنید:

python -m pip install -r requirements.txt

۳. افزونه OpenTelemetry را ثبت کنید

برای اضافه کردن افزونه gRPC OpenTelemetry به یک برنامه gRPC نیاز داریم. در این آزمایشگاه کد، از یک کلاینت و سرور ساده gRPC HelloWorld استفاده خواهیم کرد که آن را با افزونه gRPC OpenTelemetry تجهیز خواهیم کرد.

اولین قدم شما ثبت افزونه OpenTelemetry پیکربندی شده با یک صادرکننده Prometheus در کلاینت است. 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()

۴. اجرای مثال و مشاهده معیارها

برای اجرای سرور، دستور زیر را اجرا کنید -

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

۵. مشاهده معیارها در پرومتئوس

در اینجا، ما یک نمونه پرومتئوس راه‌اندازی خواهیم کرد که کلاینت و سرور نمونه gRPC ما را که معیارها را با استفاده از پرومتئوس صادر می‌کنند، scrap می‌کند.

آخرین نسخه 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-*

یک فایل پیکربندی پرومتئوس با موارد زیر ایجاد کنید -

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 --config.file=grpc_otel_python_prometheus.yml

این کار باعث می‌شود معیارهای مربوط به فرآیندهای codelab کلاینت و سرور هر ۵ ثانیه یکبار scrap شوند.

برای مشاهده معیارها به آدرس http://localhost:9090/graph بروید. برای مثال، عبارت زیر

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

نموداری با میانه تأخیر تلاش با استفاده از ۱ دقیقه به عنوان بازه زمانی برای محاسبه چندک نشان می‌دهد.

نرخ درخواست‌ها -

increase(grpc_client_attempt_duration_seconds_bucket[1m])

۶. (اختیاری) تمرین برای کاربر

در داشبوردهای پرومتئوس، متوجه خواهید شد که QPS پایین است. ببینید آیا می‌توانید کد مشکوکی را در مثال شناسایی کنید که QPS را محدود می‌کند یا خیر.

برای علاقه‌مندان، کد کلاینت خود را به داشتن تنها یک RPC در حال انتظار در یک لحظه معین محدود می‌کند. این می‌تواند به گونه‌ای اصلاح شود که کلاینت RPC های بیشتری را بدون انتظار برای تکمیل RPC های قبلی ارسال کند. (راه حلی برای این مشکل ارائه نشده است.)