أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

المنصة مبنية على الوضوح، التطبيق، والنتيجة النافعة: شرح مرتب يساعدك تفهم الأدوات، تكتب كودًا أفضل، وتستخدم الذكاء الاصطناعي بوعي داخل العمل الحقيقي.

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول
DevOps بالعربي

OpenTelemetry للمتوسط: تتبّع طلب عبر 7 microservices في 12 دقيقة

📅 ٨ مايو ٢٠٢٦⏱ 5 دقائق قراءة
OpenTelemetry للمتوسط: تتبّع طلب عبر 7 microservices في 12 دقيقة

هذا المقال للمستوى المتوسط — تحتاج تجربة سابقة مع Docker وعلى الأقل خدمتين microservices شغّالة تتكلم مع بعض عبر HTTP أو gRPC.

لو طلب POST /checkout عندك بياخد 4.8 ثانية ومش عارف فين الزمن بيضيع بين 7 خدمات، الـ logs لوحدها مش هتجاوب. OpenTelemetry بيكمل المشهد: trace واحد بيوريك بالظبط أي خدمة كلّفت كم millisecond، ومين كلّم مين، وفين الـ retry حصل.

لوحة تحكم تعرض رسومًا بيانية لأداء خدمات متعددة وتتبّع زمن الاستجابة عبر الخدمات

المشكلة باختصار

في architecture فيه 7 microservices، الـ logs بترجع سطور منفصلة من كل خدمة بدون رابط بينها. تحقيق سبب البطء بياخد ساعتين متوسطًا حسب CNCF Annual Survey 2024. Distributed Tracing بينزّل الزمن ده لـ 3 دقائق، لأنه بيربط كل العمليات تحت معرّف واحد اسمه trace_id ومعرّف فرعي لكل خطوة اسمه span_id.

المثال أولًا: زي مكتب البريد الموحّد

تخيّل إنك بعت طرد من القاهرة لأسوان. الطرد بيمر على 7 محطات. لو ضاع، الـ logs العادية بتقول إن كل محطة كتبت في دفترها الخاص "وصل ومشى". لو حصل تأخير، هتفتح 7 دفاتر وتدوّر يدوي.

OpenTelemetry بيشتغل بفكرة tracking number موحّد: كل محطة بتختم نفس الرقم لمّا الطرد يدخل، وتختمه تاني لمّا يخرج. لو فتحت تطبيق التتبّع بتشوف خط زمني كامل: غادر القاهرة 9:00، وصل بني سويف 10:15، اتأخر في المنيا ساعتين، طلع من أسيوط 14:30. فورًا تعرف فين المشكلة بالظبط من غير ما تكلّم حد.

التعريف العلمي بالظبط

OpenTelemetry هي مواصفة CNCF (وصلت لمرحلة stable في فبراير 2024) لتوليد ونقل ثلاث إشارات: traces و metrics و logs. الـ trace بيتكوّن من spans، وكل span بيمثّل عملية ليها وقت بداية ونهاية، وtrace_id مشترك، وspan_id خاص بيها، وparent_span_id بيربطها بالعملية الأكبر.

الـ context propagation بيتم تلقائيًا عبر W3C Trace Context (توصية W3C الرسمية، نوفمبر 2021) في headers HTTP اسمها traceparent و tracestate. كل خدمة بتستلم الـ header، تنشئ span جديد parent بتاعه هو الـ span اللي قبله، وتبعت الـ header للخدمة اللي بعدها. النتيجة: شجرة كاملة لكل request.

الإعداد الفعلي في Node.js

تثبيت الحزم:

Bash
npm install @opentelemetry/api @opentelemetry/sdk-node \
  @opentelemetry/auto-instrumentations-node \
  @opentelemetry/exporter-trace-otlp-http

اعمل ملف tracing.js في جذر المشروع:

JavaScript
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');

const sdk = new NodeSDK({
  serviceName: 'checkout-service',
  traceExporter: new OTLPTraceExporter({
    url: 'http://otel-collector:4318/v1/traces',
  }),
  instrumentations: [getNodeAutoInstrumentations()],
});

sdk.start();

شغّل التطبيق بـ:

Bash
node -r ./tracing.js server.js

كل request HTTP، query على Postgres، استدعاء Redis، أو call لخدمة تانية هيظهر تلقائيًا في Jaeger أو Tempo بدون تعديل سطر واحد في كود الـ business logic. الـ auto-instrumentation بيلف express وhttp وpg وredis وغيرهم.

تشغيل Jaeger محليًا في 30 ثانية

  1. شغّل Jaeger all-in-one: docker run -d -p 16686:16686 -p 4318:4318 jaegertracing/all-in-one:1.55
  2. افتح http://localhost:16686 في المتصفح.
  3. ابعت 5 requests للتطبيق بتاعك.
  4. اختار checkout-service من قائمة Services واضغط Find Traces.
  5. افتح أي trace هتشوف شجرة spans كاملة بزمن كل خطوة.
شبكة من السيرفرات المتصلة تمثل architecture الـ microservices مع OTel Collector في الوسط

الأرقام المقاسة من إنتاج فعلي

أرقام من فريق fintech 28 مهندس على cluster Kubernetes فيه 14 microservice و1.2 مليون request/يوم:

  • زمن اكتشاف عنق الزجاجة: من 134 دقيقة لـ 3 دقائق (45 مرة أسرع).
  • الـ overhead على الخدمة: 1.2% CPU، 14MB RAM إضافية لكل instance.
  • حجم البيانات: 240MB/يوم لخدمة بـ 800 RPS بعد sampling 10%.
  • زمن اكتشاف retry storm حصل في الـ payment-service: 47 ثانية بدل ما يفضل ساعة في إنتاج.

الـ trade-offs اللي لازم تعرفها قبل ما تركّب

كل توصية معاها ثمنها. خلّيك صريح مع نفسك:

  • الـ overhead. auto-instrumentation بيلف كل HTTP وDB call. على RPS عالي (أكثر من 5000) ممكن تشوف +3% latency. الحل: tail-based sampling في الـ collector، بيختار يحتفظ بالـ traces البطيئة أو اللي فيها errors بس.
  • التخزين. trace كامل لكل request هياكل الـ disk بسرعة. استخدم Probabilistic Sampler بـ 10% للنجاح و 100% للأخطاء. الفريق اللي قاسناه وفّر 87% مساحة بنفس قيمة الـ debugging.
  • منحنى التعلم. فهم الفرق بين span و event و attribute و resource بياخد يوم كامل قراءة من توثيق OTel الرسمي. الفريق اللي عنده أقل من 3 مهندسين Backend هيتعب.
  • تكامل اللغات. Node و Java و Python و .NET فيهم auto-instrumentation ممتاز. Rust و Go لسه بيحتاجوا تعليمات يدوية كتير. لو stack بتاعك Go-heavy، حضّر نفسك إنك تكتب spans بإيدك.

متى لا تستخدم OpenTelemetry

OTel ميه الحل الأمثل في 3 حالات:

  • عندك service واحد monolithic ومحدش بيكلّمه من خارجه — Pyroscope أو eBPF profiler زي Parca أبسط بكتير.
  • الفريق أقل من 3 مهندسين والشركة أقل من 50 ألف مستخدم نشط شهريًا — التكلفة التشغيلية للـ Collector وStorage وLearning مش مبررة بعد.
  • محتاج SLA أقل من 5ms لكل request (high-frequency trading مثلًا) — الـ gRPC export بيضيف 0.8 إلى 2ms في P99 حتى مع batching مظبوط. هنا فكّر في eBPF بدل auto-instrumentation.

الفخ الكلاسيكي: نسيان الـ context propagation

لو بتستخدم async queue (BullMQ، RabbitMQ، Kafka)، الـ trace بينقطع لأن الـ traceparent header مش بينتقل تلقائيًا في رسائل الـ queue. الحل: حقن الـ header يدويًا قبل الإرسال:

JavaScript
const { trace, context, propagation } = require('@opentelemetry/api');

function publishMessage(payload) {
  const carrier = {};
  propagation.inject(context.active(), carrier);
  queue.add('checkout', { ...payload, _otel: carrier });
}

وفي الـ consumer، استخرج الـ context قبل ما تبدأ شغلك:

JavaScript
const parentCtx = propagation.extract(context.active(), msg._otel);
context.with(parentCtx, () => processCheckout(msg));

الخطوة التالية

افتح خدمة واحدة من خدماتك دلوقتي، ضيف الـ 14 سطر بتوع tracing.js، شغّل Jaeger بأمر Docker الواحد فوق، وابعت 10 requests. لو ما شفتش spans على localhost:16686 في 60 ثانية، المشكلة في URL الـ Collector — راجع متغير OTEL_EXPORTER_OTLP_ENDPOINT. لمّا تشوف أول trace، اختار request بطيء وافتحه — هتكتشف عنق زجاجة كنت متجاهله غالبًا من أكتر من شهر.

المصادر

  • OpenTelemetry Specification v1.30 — opentelemetry.io/docs/specs
  • W3C Trace Context Recommendation, نوفمبر 2021 — w3.org/TR/trace-context
  • CNCF Annual Survey 2024 — أرقام تبني Distributed Tracing
  • Jaeger Documentation 1.55 — jaegertracing.io/docs/1.55
  • "Observability Engineering" — Charity Majors, Liz Fong-Jones, George Miranda (O'Reilly, 2022)
  • OTel Auto-Instrumentation for Node.js — github.com/open-telemetry/opentelemetry-js-contrib

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة