تحكم في خدماتك مع OTEL و Jaeger و Prometheus

لنناقش سؤالًا مهمًا: كيف نراقب خدماتنا في حال حدوث خطأ؟

من جهة، لدينا Prometheus مع التنبيهات و Kibana للوحات المعلوماتية وميزات مفيدة أخرى. كما نعرف كيف نجمع السجلات – حزمة ELK هي الحل الأمثل لدينا. ومع ذلك، السجل المبسط لا يكون دائمًا كافيًا: فهو لا يوفر رؤية شاملة لرحلة الطلب عبر النظام البيئي بأكمله من المكونات.

يمكنك العثور على مزيد من المعلومات حول ELK هنا.

ولكن ماذا إذا أردنا تصور الطلبات؟ ماذا لو أردنا ربط الطلبات التي تسافر بين الأنظمة؟ ينطبق هذا على كل من الخدمات الميكروية والأنظمة الضخمة – لا يهم كم عدد الخدمات لدينا؛ المهم هو كيفية إدارة تأخرها.

في الواقع، يمكن أن يمر كل طلب مستخدم عبر سلسلة كاملة من الخدمات المستقلة وقواعد البيانات وطوابير الرسائل وواجهات برمجة تطبيقات خارجية.

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

للتعامل مع هذه التحديات بفعالية، نحتاج إلى نظام مركزي ومتسق لجمع بيانات التلميم – مثل الأثر، والمقاييس، والسجلات. هنا حيث تأتي OpenTelemetry وJaeger للإنقاذ.

لنلق نظرة على الأساسيات

هناك مصطلحان رئيسيان يجب علينا فهمهما:

معرف التتبع

معرف التتبع هو معرف مكون من 16 بايتًا، يُمثل عادة على شكل سلسلة هكساديسمال تتكون من 32 حرفًا. يتم إنشاؤه تلقائيًا في بداية التتبع ويظل ثابتًا عبر جميع الأجزاء التي تم إنشاؤها بواسطة طلب معين. وهذا يجعل من السهل رؤية كيف يسافر الطلب من خلال خدمات أو مكونات مختلفة في نظام ما.

معرف الجزء

تحصل كل عملية فردية ضمن تتبع على معرف جزء خاص بها، والذي عادة ما يكون قيمة عشوائية تتكون من 64 بت. تشترك الأجزاء في نفس معرف التتبع، ولكن لكل جزء معرف جزء فريد، بحيث يمكنك تحديد بالضبط أي جزء من سير العمل يمثله كل جزء (مثل استعلام قاعدة بيانات أو استدعاء لخدمة صغيرة أخرى).

كيف ترتبط؟

معرف التتبع و معرف الجزء يكملان بعضهما البعض. 

عندما يتم بدء طلب، يتم إنشاء معرف تتبع وتمريره إلى جميع الخدمات المعنية. بدوره، ينشئ كل خدمة جزءًا يحمل معرف جزء فريد مرتبط بمعرف التتبع، مما يتيح لك تصور الدورة الكاملة للطلب من البداية إلى النهاية.

حسنًا، لماذا لا نستخدم فقط Jaeger? لماذا نحتاج إلى OpenTelemetry (OTEL) وجميع مواصفاته? هذا سؤال رائع! دعنا نقوم بتفكيكه خطوة بخطوة.

تعرف على المزيد حول Jaeger هنا.

ملخص قصير

  • Jaeger هو نظام لتخزين وتصور الأثر الموزع. يقوم بجمع، وتخزين، والبحث، وعرض البيانات التي تظهر كيفية انتقال الطلبات من خلال خدماتك.
  • OpenTelemetry (OTEL) هو معيار (ومجموعة من المكتبات) لجمع بيانات التلميتري (أثار، مقاييس، سجلات) من تطبيقاتك وبنيتك التحتية. لا يرتبط بأي أداة تصور أو خلفية واحدة.

ببساطة:

  • OTEL هو كالـ “لغة عالمية” ومجموعة من المكتبات لجمع التلميتري.
  • Jaeger هو خلفية وواجهة مستخدم لعرض وتحليل الأثر الموزع.

لماذا نحتاج إلى OTEL إذا كان لدينا بالفعل Jaeger؟

1. معيار واحد للجمع

في الماضي، كانت هناك مشاريع مثل OpenTracing وOpenCensus. توحد OpenTelemetry هذه النهج لجمع المقاييس والأثر في معيار واحد عالمي.

2. تكامل سهل

تكتب كودك بـ Go (أو لغة أخرى)، وتضيف مكتبات OTEL لحقن المعترضات والشرائح تلقائيًا، وهذا كل شيء. بعد ذلك، لا يهم أين تريد إرسال تلك البيانات – Jaeger، Tempo، Zipkin، Datadog، خلفية مخصصة – يعتني OpenTelemetry بالسباكة. كل ما عليك فعله هو استبدال المصدر.

3. ليس فقط أثر

يغطي OpenTelemetry الأثر، ولكنه يتعامل أيضًا مع المقاييس والسجلات. تنتهي بأدوات واحدة لجميع احتياجاتك من التلميتري، ليس فقط التتبع.

4. Jaeger كخلفية

يعتبر Jaeger خيارًا ممتازًا إذا كنت مهتمًا بشكل أساسي في تصور تتبع التوزيع. لكنه لا يوفر الأدوات عبر اللغات بشكل افتراضي. من ناحية أخرى، يوفر OpenTelemetry طريقة موحدة لجمع البيانات، ثم تقرر أين ترسلها (بما في ذلك Jaeger).

في الممارسة العملية، غالبًا ما تعمل معًا:

تستخدم تطبيقك OpenTelemetry → يتواصل عبر بروتوكول OTLP → يذهب إلى جامع OpenTelemetry (HTTP أو grpc) → يصدر إلى Jaeger للتصور.


الجزء التقني

تصميم النظام (قليلًا)

دعنا نرسم بسرعة عددًا من الخدمات التي ستقوم بما يلي:

  1. خدمة الشراء – تعالج الدفع وتقوم بتسجيله في MongoDB
  2. CDC مع Debezium – تستمع للتغييرات في جدول MongoDB وترسلها إلى Kafka
  3. معالج الشراء – يستهلك الرسالة من Kafka ويتصل بخدمة المصادقة للبحث عن user_id من أجل التحقق
  4. خدمة المصادقة – خدمة مستخدم بسيطة

باختصار:

  • 3 خدمات Go
  • Kafka
  • CDC (Debezium)
  • MongoDB

الجزء البرمجي

لنبدأ بالبنية التحتية. لربط كل شيء في نظام واحد، سنقوم بإنشاء ملف Docker Compose كبير. سنبدأ بإعداد القياس.

ملاحظة: جميع الشفرات متاحة عبر رابط في نهاية المقال، بما في ذلك البنية التحتية.

YAML

 

سنقوم أيضًا بتكوين المجمع — العنصر الذي يجمع بين البيانات التشخيصية.

هنا، نختار gRPC لنقل البيانات، مما يعني أن التواصل سيحدث عبر HTTP/2:

YAML

 

تأكد من ضبط أي عناوين حسب الحاجة، وسوف تكون قد انتهيت من التكوين الأساسي.

نعلم بالفعل أن OpenTelemetry (OTEL) يستخدم مفهومين رئيسيين — معرف التتبع و معرف النطاق — التي تساعد في تتبع ومراقبة الطلبات في الأنظمة الموزعة.

تنفيذ الشفرة

الآن، دعونا نلقي نظرة على كيفية جعل هذا يعمل في شفرتك بلغة Go. نحتاج إلى الاستيرادات التالية:

Go

 

ثم، نضيف وظيفة لتهيئة متتبعنا في main() عند بدء تشغيل التطبيق:

Go

 

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

Go

 

لدينا تتبع عند طبقة الخدمة — رائع! ولكن يمكننا الذهاب أعمق، من خلال تجهيز طبقة قاعدة البيانات:

Go

 

الآن، لدينا رؤية كاملة لرحلة الطلب. انتقل إلى واجهة المستخدم الرسومية لـ Jaeger، ابحث عن آخر 20 تتبعًا تحت خدمة المصادقة، وسترى جميع النطاقات وكيف تتصل في مكان واحد.

الآن، كل شيء ظاهر. إذا كنت بحاجة إليه، يمكنك تضمين الاستعلام بأكمله في الوسوم. ومع ذلك، تذكر أنه يجب عليك ألا تحمل نظام التحكم في التلميتري بشكل زائد — أضف البيانات بتروي. أنا أقوم ببساطة بتوضيح ما يمكن عمله، لكن تضمين الاستعلام بالكامل بهذه الطريقة ليس شيئًا أوصي به بشكل عام.

عميل-خادوم gRPC

إذا كنت ترغب في رؤية تتبع يمتد عبر خدمتي gRPC، فهو أمر بسيط تمامًا. كل ما عليك فعله هو إضافة المعاقبين الجاهزين من المكتبة. على سبيل المثال، على الجانب الخادوم:

Go

 

في الجانب العميل، الشيفرة قصيرة بنفس القدر:

Go

 

هذا كل شيء! تأكد من تكوين مصدري التصدير بشكل صحيح، وسترى هوية تتبع واحدة تُسجل عبر هذه الخدمات عندما يُطلب من العميل استدعاء الخادوم.

معالجة أحداث CDC والتتبع

هل ترغب في التعامل مع الأحداث من CDC أيضًا؟ يمكنك القيام بنهج بسيط وهو أن تضمن هوية التتبع في الكائن الذي تخزنه MongoDB. بهذه الطريقة، عندما يلتقط Debezium التغيير ويُرسله إلى Kafka، تكون هوية التتبع جزءًا من السجل بالفعل.

على سبيل المثال، إذا كنت تستخدم MongoDB، يمكنك القيام بشيء كهذا:

Go

 

ثم يلتقط Debezium هذا الكائن (بما في ذلك trace_id) ويُرسله إلى Kafka. على الجانب الاستهلاكي، عليك ببساطة تحليل الرسالة الواردة، استخراج trace_id، ودمجه في سياق التتبع الخاص بك:

Go

 

Go

 

بديل: استخدام رؤوس Kafka

أحيانًا، من الأسهل تخزين معرّف الإتباع (Trace ID) في رؤوس Kafka بدلاً من تضمينه في الحمولة ذاتها. بالنسبة لسير العمل CDC، قد لا يكون هذا متاحًا بشكل افتراضي – يمكن لـ Debezium تقييد ما يتم إضافته إلى الرؤوس. ولكن إذا كنت تتحكم في جانب المنتج (أو إذا كنت تستخدم منتج Kafka قياسي)، يمكنك القيام بشيء من هذا القبيل مع Sarama:

حقن معرّف الإتباع في الرؤوس

Go

 

استخراج معرّف الإتباع على جانب المستهلك

Go

 

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

  1. تضمين معرّف الإتباع في سجل قاعدة البيانات حتى يتدفق بشكل طبيعي عبر CDC.
  2. استخدام رؤوس Kafka إذا كان لديك مزيد من التحكم على جانب المنتج أو إذا كنت ترغب في تجنب تضخيم حمولة الرسالة.

بأي حال، يمكنك الحفاظ على تتبعك متسقًا عبر الخدمات المتعددة – حتى عندما يتم معالجة الأحداث بشكل غير متزامن عبر Kafka و Debezium.

الاستنتاج

استخدام OpenTelemetry و Jaeger يوفر تتبع طلبات مفصل، مما يساعدك على تحديد مكان وسبب حدوث التأخير في الأنظمة الموزعة.

إضافة Prometheus تكمل الصورة بالمقاييس – المؤشرات الرئيسية للأداء والاستقرار. معًا، تشكل هذه الأدوات كومة شاملة للرصد، مما يمكن من اكتشاف وحل المشاكل بشكل أسرع، وتحسين الأداء، وزيادة موثوقية النظام بشكل عام.

أستطيع القول إن هذا النهج يسرّع بشكل كبير عملية حل المشاكل في بيئة الخدمات المصغرة وهو واحد من أول الأمور التي نقوم بتنفيذها في مشاريعنا.

روابط

Source:
https://dzone.com/articles/control-services-otel-jaeger-prometheus