إعداد المكوّن الإضافي الأساسي OpenTelemetry في gRPC Python

1. مقدمة

في هذا الدرس التطبيقي حول الترميز، ستستخدم gRPC لإنشاء عميل وخادم يشكّلان الأساس لتطبيق يحدّد المسارات مكتوب بلغة Python.

في نهاية هذا البرنامج التعليمي، سيكون لديك تطبيق gRPC HelloWorld بسيط مزوّد بالمكوّن الإضافي gRPC OpenTelemetry، وستتمكّن من الاطّلاع على مقاييس قابلية المراقبة التي تم تصديرها في Prometheus.

أهداف الدورة التعليمية

  • كيفية إعداد المكوّن الإضافي OpenTelemetry لتطبيق gRPC python حالي
  • تشغيل نسخة محلية من Prometheus
  • تصدير المقاييس إلى Prometheus
  • عرض المقاييس من لوحة بيانات Prometheus

2. قبل البدء

المتطلبات

  • git
  • curl
  • build-essential
  • الإصدار 3.9 من Python أو إصدار أحدث للحصول على تعليمات تثبيت Python خاصة بنظام التشغيل، يُرجى الاطّلاع على إعداد Python واستخدامه. بدلاً من ذلك، يمكنك تثبيت إصدار غير تابع للنظام من Python باستخدام أدوات مثل uv أو pyenv.
  • الإصدار 9.0.1 أو إصدار أحدث من pip لتثبيت حِزم 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

الحصول على الشفرة‏

لتبسيط عملية التعلّم، يوفّر لك هذا الدرس التطبيقي حول الترميز بنية أساسية مُعدّة مسبقًا لرمز المصدر لمساعدتك على البدء. ستوضّح لك الخطوات التالية كيفية إعداد إضافة gRPC OpenTelemetry في أحد التطبيقات.

grpc-codelabs

يتوفّر رمز المصدر الخاص بهذا الدرس التطبيقي حول الترميز في دليل github هذا. إذا كنت تفضّل عدم تنفيذ الرمز بنفسك، يتوفّر الرمز المصدر المكتمل في الدليل المكتمل.

أولاً، استنسِخ مستودع grpc codelab وانتقِل إلى المجلد grpc-python-opentelemetry:

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

بدلاً من ذلك، يمكنك تنزيل ملف ‎ .zip الذي يحتوي على دليل الدرس العملي فقط وفك ضغطه يدويًا.

لننشئ أولاً بيئة افتراضية بلغة Python (venv) جديدة لعزل تبعيات مشروعك عن حِزم النظام:

python3 -m venv --upgrade-deps .venv

لتفعيل البيئة الافتراضية في shell bash/zsh، اتّبِع الخطوات التالية:

source .venv/bin/activate

بالنسبة إلى Windows والأصداف غير العادية، يمكنك الاطّلاع على الجدول في https://docs.python.org/3/library/venv.html#how-venvs-work.

بعد ذلك، ثبِّت التبعيات في البيئة باستخدام:

python -m pip install -r requirements.txt

3- تسجيل OpenTelemetry Plugin

نحتاج إلى تطبيق gRPC لإضافة المكوّن الإضافي gRPC OpenTelemetry. في هذا الدرس التطبيقي حول الترميز، سنستخدم عميل وخادم بسيطَين من 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()

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 الذي سيزحف إلى مثال عميل وخادم 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 بدون انتظار اكتمال الطلبات السابقة. (لم يتم تقديم الحلّ لهذه المشكلة).