لماذا اختيار OpenTelemetry بدلًا من وكيل خاص
OpenTelemetry مشروع متخرّج من CNCF يوحّد ركائز المراقبة الثلاث — التتبعات والمقاييس والسجلات — تحت معيار محايد للمورّدين. على عكس وكلاء Datadog أو New Relic الذين يقيّدونك بعقد وصيغة خاصة، يستقبل OTel Collector البيانات ويحوّلها ويصدّرها إلى أي خادم متوافق. يمكنك تغيير التخزين دون تعديل سطر واحد في كود تطبيقك.
تغطّي حزم SDK لغات Node.js وPython وGo وJava وPHP وRuby ومعظم اللغات الحديثة. منتشرات السياق W3C TraceContext وB3 مدمجة: تمرّر trace-id تلقائيًا بين خدماتك الصغيرة عبر رؤوس HTTP دون إعداد إضافي.
ما توفّره لك مكدّسة OTel + Grafana Tempo
- تتبع موزع بدون فهرس خارجي — يخزّن Grafana Tempo v2.x التتبعات في كتل مضغوطة (parquet) على القرص المحلي أو تخزين الكائنات، بدون Cassandra أو Elasticsearch.
- استعلامات TraceQL — تتيح لغة Tempo المخصصة تصفية التتبعات حسب المدة والخدمة وحالة الخطأ أو السمات المخصصة في ثوانٍ.
- توافق متعدد البروتوكولات — يقبل Tempo بروتوكولات OTLP وZipkin وJaeger: تتصل تطبيقاتك الحالية دون إعادة تجهيز كاملة.
- خفيف على VPS — 2 جيجابايت RAM تكفي لبيئة التطوير أو الاختبار؛ في الإنتاج تحت الحمل، يوفّر 4 جيجابايت هامشًا مريحًا.
- تكلفة محكومة — VPS بسعر 99 درهم/شهر استضافة ذاتية يحلّ محل اشتراك Datadog APM بحوالي 23 دولارًا لكل مضيف شهريًا.
- ربط التتبعات بالسجلات والمقاييس — بربط Tempo بـ Loki وPrometheus في Grafana، تنتقل من تتبع إلى سجلاته ومقاييسه بنقرة واحدة.
- لا قيود على المورّد — إذا انتقلت إلى Jaeger غدًا، تعيد تهيئة مُصدِّر واحد في Collector؛ حزم SDK والكود لا تتغير.
متطلبات VPS
يحتاج VPS إلى 2 جيجابايت RAM على الأقل (يُنصح بـ 4 في الإنتاج) وصلاحية root أو sudo. يجب تثبيت Docker Engine ≥ 24 وDocker Compose v2.
خطّط لنطاق فرعي مخصص — مثلًا grafana.your-domain.com — لعرض واجهة Grafana خلف وكيل عكسي TLS. يجب فتح المنفذَين 4317 (OTLP/gRPC) و4318 (OTLP/HTTP) في جدار الحماية على الجانب الخاص لاستقبال البيانات من تطبيقاتك؛ لا تعرّضهما على الواجهة العامة. تبقى المنافذ 3200 (Tempo) و3000 (Grafana) داخلية في شبكة Docker.
للتخزين، خطّط لـ 10 جيجابايت على الأقل لكتل التتبع. الوحدة الافتراضية في /tmp/tempo لا تبقى بعد إعادة التشغيل: استبدلها بـ Docker volume أو قرص مخصص قبل الإنتاج.
بنية المكدّس: من span إلى التصوير
يتبع تدفق البيانات أربع خطوات:
1. تطبيقك يجهّز طلباته بحزمة OTel SDK ويصدّر الـ spans إلى OTel Collector (OTLP gRPC منفذ 4317 أو HTTP منفذ 4318).
2. OTel Collector يستقبل ويجمّع في دُفعات ثم يصدّر إلى Tempo عبر OTLP gRPC داخلي. يمكنه في آنٍ واحد إرسال المقاييس إلى Prometheus والسجلات إلى Loki.
3. Grafana Tempo يخزّن كتل التتبع المضغوطة على القرص (أو S3). يعرض API لـ TraceQL على المنفذ 3200.
4. Grafana يستعلم Tempo بـ TraceQL، ويعرض flamegraphs ورسوم بيانية للخدمات وSpan metrics.
TraceQL هي لغة استعلام Tempo الأصلية. استعلام نموذجي: {service.name="api" && status=error && duration>500ms}.
Span Metrics خط أنابيب في Tempo يستخرج تلقائيًا مقاييس RED من التتبعات الواردة دون إضافة عدّاد واحد في كودك.
نشر مكدّس OTel + Tempo في سبع خطوات
إنشاء هيكل المجلدات
أنشئ مجلد
otel-stack/على VPS:mkdir -p otel-stack/config cd otel-stackستضع هنا ثلاثة ملفات إعداد:
tempo.yamlوotel-collector-config.yamlوdocker-compose.yml.كتابة إعداد Tempo (tempo.yaml)
أنشئ
config/tempo.yamlبخادم تخزين محلي ودعم TraceQL:server: http_listen_port: 3200 distributor: receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 ingester: max_block_bytes: 1_000_000 max_block_duration: 5m compactor: compaction: block_retention: 168h # 7 أيام storage: trace: backend: local wal: path: /tmp/tempo/wal local: path: /tmp/tempo/blocksكتابة إعداد OTel Collector
أنشئ
config/otel-collector-config.yaml:receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 http: endpoint: 0.0.0.0:4318 processors: batch: timeout: 5s send_batch_size: 1000 exporters: otlp: endpoint: tempo:4317 tls: insecure: true service: pipelines: traces: receivers: [otlp] processors: [batch] exporters: [otlp]كتابة ملف docker-compose.yml
أنشئ
docker-compose.ymlفي جذرotel-stack/:services: otel-collector: image: otel/opentelemetry-collector-contrib:latest volumes: - ./config/otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml ports: - "4317:4317" - "4318:4318" networks: - observability depends_on: - tempo tempo: image: grafana/tempo:latest command: ["-config.file=/etc/tempo.yaml"] volumes: - ./config/tempo.yaml:/etc/tempo.yaml - tempo-data:/tmp/tempo ports: - "3200:3200" networks: - observability grafana: image: grafana/grafana:latest ports: - "3000:3000" environment: - GF_AUTH_ANONYMOUS_ENABLED=true - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin networks: - observability depends_on: - tempo volumes: tempo-data: networks: observability:شغّل المكدّس:
docker compose up -dثم تحقق:docker compose ps.إعداد Grafana Tempo كمصدر بيانات
افتح Grafana على
http://vps-الخاص-بك:3000. انتقل إلى Connections → Data sources → Add data source واختر Tempo. اضبط URL علىhttp://tempo:3200. فعّل Service graph وSpan metrics. إذا كانت مكدّستك تشمل Loki، اربط مصدرَي البيانات في حقل Linked data sources للتنقل من span إلى سجلاته بنقرة واحدة.إرسال أول تتبع تجريبي
اختبر استقبال التتبعات باستخدام
telemetrygen:docker run --rm --network otel-stack_observability \ ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:latest \ traces \ --otlp-endpoint otel-collector:4317 \ --otlp-insecure \ --duration 5s \ --rate 10في Grafana، افتح Explore، اختر مصدر بيانات Tempo، ونفّذ الاستعلام
{}لرؤية جميع التتبعات المستقبَلة.استعلام التتبعات بـ TraceQL
في لوحة Explore بـ Grafana، أدخل استعلام TraceQL لتصفية تتبعاتك:
-
{service.name="my-api"}— جميع تتبعات الخدمة
-{status=error}— جميع التتبعات الخاطئة
-{duration>1s && service.name="my-api"}— التتبعات البطيئة
-{span.http.route="/api/orders" && status=error}— أخطاء مسار محددانقر على أي تتبع لفتح flamegraph التفصيلي.
تهيئة الاحتفاظ بالتتبعات والتخزين
يوفّر Tempo خيارَي تخزين رئيسيَّين.
التخزين المحلي على Docker volume: الخيار الموصى به لـ VPS مستقل. اضبط block_retention في tempo.yaml حسب الحاجة: 72 ساعة لبيئة الاختبار، 168 إلى 720 ساعة للإنتاج. كل ساعة من التتبعات المضغوطة تشغل حوالي 100–300 ميجابايت.
تخزين كائنات متوافق مع S3: لمكدّس متعدد العقد أو تخزين غير محدود. Tempo متوافق مع AWS S3 وMinIO وScaleway Object Storage. تبقى WAL دائمًا على القرص المحلي لضمان متانة التتبعات قيد الاستيعاب.
يدمج المضغّط كتلًا صغيرة ويطبّق سياسة الاحتفاظ تلقائيًا كل 5 دقائق.
تجهيز تطبيق Node.js
يلتقط التجهيز التلقائي لـ OTel في Node.js استدعاءات HTTP والطلبات وقواعد البيانات دون تعديل منطق الأعمال.
ثبّت الحزم:
npm install @opentelemetry/sdk-node \
@opentelemetry/auto-instrumentations-node \
@opentelemetry/exporter-trace-otlp-grpcأنشئ tracing.js:
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc');
const sdk = new NodeSDK({
traceExporter: new OTLPTraceExporter({
url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'grpc://localhost:4317',
}),
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();شغّل التطبيق:
export OTEL_SERVICE_NAME=my-api
export OTEL_EXPORTER_OTLP_ENDPOINT=http://your-vps:4317
node --require ./tracing.js app.jsتجهيز تطبيق Python
تثبيت وإعداد:
pip install opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap -a installتشغيل التطبيق:
export OTEL_SERVICE_NAME=my-python-service
export OTEL_EXPORTER_OTLP_ENDPOINT=http://your-vps:4317
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
opentelemetry-instrument python app.pyيتم تجهيز Django وFlask وFastAPI وSQLAlchemy وعملاء HTTP تلقائيًا. للتجهيز اليدوي:
from opentelemetry import trace
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("process-order") as span:
span.set_attribute("order.id", order_id)OTel + Tempo مقابل Jaeger مقابل Zipkin مقابل Datadog APM
مرّر الجدول أفقيًا
| المعيار | OTel + Grafana Tempo | Jaeger | Datadog APM |
|---|---|---|---|
| التكلفة الشهرية | الاستضافة فقط (99 درهم/شهر VPS) | الاستضافة فقط (مماثل) | ~23 دولار لكل مضيف شهريًا |
| خادم التخزين | محلي، S3، GCS، Azure Blob | Cassandra، Elasticsearch، Badger | SaaS مُدار (مغلق) |
| لغة الاستعلام | TraceQL (أصلية، قوية) | Jaeger Query UI (أساسية) | لغة خاصة بـ Datadog |
| التجهيز التلقائي | حزمة OTel SDK عالمية | مكتبات Jaeger أو OTel | وكيل Datadog الخاص |
| ربط التتبعات بالسجلات | نعم (Loki + Grafana) | محدود | نعم (مدمج، مدفوع) |
| Span Metrics (RED) | نعم (خط أنابيب Tempo أصلي) | غير أصلي | نعم (يحسبه Datadog) |
| القيود على المورّد | لا شيء (معيار CNCF) | منخفض (متوافق مع OTel) | قوي (وكيل وصيغة خاصة) |
| تعقيد التثبيت | متوسط (3 حاويات) | منخفض إلى متوسط | منخفض (وكيل تلقائي) |
التنبيهات وـ SLOs في Grafana
يتيح Grafana 10+ تعريف تنبيهات على المقاييس المشتقة من التتبعات. تولّد Span Metrics في Tempo ثلاثة مقاييس تلقائيًا لكل خدمة: معدل الطلبات وتوزيع زمن الاستجابة وعدّاد الأخطاء.
لإنشاء SLO على معدل الخطأ، اذهب إلى Alerting → Alert rules → New alert rule في Grafana، اختر مصدر بيانات Prometheus، وأدخل استعلامًا يحسب نسبة الأخطاء. اضبط الحد (مثلًا > 0.05 لمعدل خطأ فوق 5%) ونافذة التقييم. هيّئ نقطة اتصال: Alertmanager أو PagerDuty أو Slack أو webhook. التنبيهات المبنية على التتبعات أدق بكثير من التنبيهات على مقاييس اصطناعية.
الجمع بين التتبعات والسجلات والمقاييس في Collector واحد
OTel Collector ليس حصرًا للتتبعات. بإضافة filelog receiver، تجمع سجلات حاويات Docker وترسلها إلى Loki. وprometheus receiver يجمع نقاط /metrics ويصدّرها إلى Prometheus. عملية واحدة تدير كل بيانات المراقبة على VPS.
في Grafana، اربط Tempo وLoki عبر derived fields: عندما يحتوي سجل على trace_id، رابط قابل للنقر يأخذك مباشرة إلى flamegraph المقابل. هذا التنقل المتقاطع يقلّص وقت التشخيص بشكل ملحوظ.
حل المشكلات الشائعة
لا spans تصل إلى Tempo. تحقق أولًا من أن OTel Collector قابل للوصول من تطبيقك (telnet your-vps 4317). راجع سجلات Collector: خطأ connection refused على tempo:4317 يعني أن Tempo لم يبدأ بعد أو منفذه الداخلي غير مكشوف على شبكة Docker.
رفض الاتصال من التطبيق إلى Collector. في UFW أو firewalld، اسمح بالمنفذَين 4317 و4318 لعناوين IP تطبيقاتك فقط. في الإنتاج، ضع التطبيقات والـ Collector على نفس الشبكة الخاصة.
تتبعات مقطوعة أو spans مفقودة. قد يستغرق الـ batch processor حتى 5 ثوانٍ قبل الإرسال. للتطوير، قلّل timeout: 1s. إذا كانت spans غائبة باستمرار، تحقق من تفعيل منتشر السياق W3CTraceContextPropagator في SDK.
Tempo يستهلك مساحة كبيرة. قلّل block_retention وأعد تشغيل Tempo. سيطبّق المضغّط السياسة الجديدة في الدورة القادمة.
Grafana لا يجد تتبعات من سجلات Loki. تحقق من أن تطبيقاتك تحقن trace_id في السجلات وأن Derived fields في مصدر بيانات Loki يشير إلى النمط الصحيح.