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

لفترة طويلة كنت أبحث عن أداة من شأنها أن تساعد في التعامل مع مثل هذه المشاكل (كتبت عن ذلك على Habré: 1 ، 2 ) ، ولكن في النهاية قمت بحلتي مفتوحة المصدر. في المقال ، أتحدث عن فوائد نهج شبكة الخدمة وشارك أداة جديدة لتنفيذه.
يعد التتبع الموزع حلاً مشتركًا لمشكلة العثور على الأخطاء في الأنظمة الموزعة. ولكن ماذا لو لم يقم النظام بعد بتطبيق مثل هذا النهج في جمع المعلومات حول تفاعلات الشبكة ، أو ما هو أسوأ ، في جزء من النظام الذي يعمل بالفعل بشكل صحيح ، وفي جزء منه لا يعمل ، لأنه لا يتم إضافته إلى الخدمات القديمة؟ لتحديد السبب الجذري للمشكلة بالضبط ، يجب أن يكون لديك صورة كاملة لما يحدث في النظام. من المهم بشكل خاص فهم الخدمات الميكروية التي تشارك في مسارات الأعمال الهامة.
هنا ، يمكن أن يساعدنا نهج شبكة الخدمة ، والذي سيتعامل مع جميع الآلات لجمع معلومات الشبكة بمستوى أقل مما تفعله الخدمات نفسها. هذا النهج يسمح لنا باعتراض كل حركة المرور وتحليلها على الطاير. علاوة على ذلك ، يجب ألا تعرف التطبيقات المتعلقة بها أي شيء.
نهج شبكة الخدمة
تتمثل الفكرة الرئيسية لنهج شبكة الخدمة في إضافة طبقة بنية أساسية أخرى عبر الشبكة ، مما سيتيح لنا القيام بأي شيء من خلال التفاعل بين الخدمات. تعمل معظم التطبيقات على النحو التالي: تتم إضافة حاوية جانبية إضافية مع وكيل شفاف إلى كل خدمة microservice ، يتم من خلالها تمرير كل حركة مرور الخدمة الواردة والصادرة. وهذا هو المكان الذي يمكننا فيه تحقيق التوازن بين العميل ، وتطبيق سياسات الأمان ، وفرض قيود على عدد الطلبات وجمع معلومات مهمة حول تفاعل الخدمات في الإنتاج.

حلول
يوجد بالفعل العديد من تطبيقات هذا النهج: Istio و linkerd2 . أنها توفر العديد من الميزات من خارج منطقة الجزاء. ولكن في الوقت نفسه تأتي النفقات العامة الكبيرة على الموارد. علاوة على ذلك ، كلما زاد حجم الكتلة التي يعمل فيها هذا النظام ، ستكون هناك حاجة إلى مزيد من الموارد للحفاظ على البنية التحتية الجديدة. في Avito ، نقوم بتشغيل مجموعات kubernetes مع الآلاف من مثيلات الخدمة (ولا يزال عددها يتزايد بسرعة). في التطبيق الحالي ، يستهلك Istio ~ 300 ميغابايت من ذاكرة الوصول العشوائي لكل مثيل خدمة. نظرًا للعدد الكبير من الميزات ، تؤثر الموازنة الشفافة أيضًا على إجمالي وقت استجابة الخدمات (حتى 10 مللي ثانية).
نتيجة لذلك ، نظرنا في الميزات التي نحتاجها في الوقت الحالي تمامًا ، وقررنا أن السبب الرئيسي الذي جعلنا نبدأ في تطبيق هذه الحلول هو القدرة على جمع معلومات التتبع من النظام بأكمله بشفافية. أردنا أيضًا التحكم في تفاعل الخدمات والقيام بمعالجات متنوعة مع الرؤوس التي يتم نقلها بين الخدمات.
في النهاية ، توصلنا إلى قرارنا: Netramesh .
Netramesh
Netramesh هو حل شبكة خدمة خفيفة الوزن مع قابلية لا نهائية بغض النظر عن عدد الخدمات في النظام.
كانت الأهداف الرئيسية للحل الجديد هي زيادة بسيطة في الموارد والأداء العالي. من بين الميزات الرئيسية ، أردنا على الفور أن نكون قادرين على إرسال مسافات التتبع بشفافية لنظام Jaeger الخاص بنا.
اليوم ، يتم تنفيذ معظم الحلول السحابية على Golang. وبالطبع ، هناك أسباب لذلك. تعد كتابة تطبيقات شبكة Golang التي تعمل بشكل غير متزامن مع I / O وتوسيع نطاقها إلى kernels حسب الحاجة مريحة وبسيطة إلى حد ما. و ، وهو أمر مهم للغاية أيضًا ، يكون الأداء كافيًا لحل هذه المشكلة. لذلك ، اخترنا أيضا Golang.
إنتاجية
ركزنا جهودنا على تحقيق أقصى قدر من الأداء. بالنسبة للحل الذي يتم نشره بجوار كل مثيل من الخدمة ، يلزم استهلاك قليل من ذاكرة الوصول العشوائي ووقت المعالج. وبطبيعة الحال ، يجب أن يكون التأخير في الإجابة صغيرًا أيضًا.
دعونا نرى ما هي النتائج.
RAM
يستهلك Netramesh حوالي 10 ميجابايت بدون حركة مرور و 50 ميجابايت بحد أقصى مع حمولة تصل إلى 10000 RPS لكل مثيل.
وكيل Istio المبعوث يستهلك دائمًا ~ 300 ميغابايت في مجموعاتنا مع الآلاف من الحالات. هذا لا يسمح لك بتوسيع نطاقه إلى الكتلة بأكملها.


مع Netramesh ، حصلنا على أقل من 10 مرات من استهلاك الذاكرة.
وحدة المعالجة المركزية
استخدام وحدة المعالجة المركزية متساو نسبيا تحت الحمل. ذلك يعتمد على عدد الطلبات في وحدة الوقت إلى جانب السيارة. القيم عند 3000 طلب في الثانية الواحدة في الذروة:


هناك نقطة مهمة أخرى: Netramesh - الحل بدون طائرة التحكم وبدون تحميل لا يستهلك وقت وحدة المعالجة المركزية. مع Istio ، تقوم خدمة sidecar دائمًا بتحديث نقاط نهاية الخدمة. نتيجة لذلك ، يمكننا أن نرى مثل هذه الصورة دون تحميل:

نحن نستخدم HTTP / 1 للتواصل بين الخدمات. كانت الزيادة في وقت الاستجابة لـ Istio عند التقديم عبر المبعوث تصل إلى 5 إلى 10 دقائق ، وهو عدد كبير للغاية بالنسبة للخدمات التي تكون جاهزة للرد في غضون ميلي ثانية واحدة. مع Netramesh ، انخفض هذا الوقت إلى 0.5-2ms.
التدرجية
تتيح كمية صغيرة من الموارد التي ينفقها كل وكيل وضعه بجوار كل خدمة. تم إنشاء Netramesh عمداً بدون مكون طائرة تحكم للحفاظ ببساطة على خفة كل جانب جانبي. غالبًا في حلول شبكة الخدمة ، تقوم طائرة التحكم بتوزيع معلومات اكتشاف الخدمة على كل جانب جانبي. جنبا إلى جنب مع ذلك يأتي معلومات حول المهلات ، وإعدادات التوازن. كل هذا يسمح لك بالقيام بالعديد من الأشياء المفيدة ، لكن لسوء الحظ ، يضخم حجم السيارة الجانبية.
اكتشاف الخدمة

Netramesh لا تضيف أي آليات إضافية لاكتشاف الخدمة. يتم بروكسي كل حركة المرور بشفافية من خلال نيدرا جانبي.
يدعم Netramesh بروتوكول تطبيق HTTP / 1. يتم استخدام قائمة من المنافذ القابلة للتكوين لتحديدها. عادة ، هناك العديد من المنافذ على نظام يتصل عبر HTTP. على سبيل المثال ، نستخدم 80 و 8890 و 8080 للتفاعل مع الخدمات والطلبات الخارجية ، وفي هذه الحالة ، يمكن ضبطها باستخدام NETRA_HTTP_PORTS
البيئة NETRA_HTTP_PORTS
.
إذا كنت تستخدم Kubernetes كأوركسترا وآلية كيانات الخدمة الخاصة بها للتفاعل بين المجموعات ، فستظل الآلية كما هي تمامًا. أولاً ، تحصل الخدمة الميكروية على عنوان IP للخدمة باستخدام kube-dns وتفتح اتصالًا جديدًا به. تم تأسيس هذا الاتصال أولاً باستخدام برنامج netra-sidecar المحلي ، وتصل كافة حزم TCP في البداية إلى netra. بعد ذلك ، يقوم netra-sidecar بإنشاء اتصال بالوجهة الأصلية. يبقى NAT على pod IP على العقدة كما هو بدون netra تمامًا.
تتبع الموزعة والتمرير السياق
يوفر Netramesh الوظيفة اللازمة لإرسال نطاقات تتبع حول تفاعلات HTTP. يقوم Netra-sidecar بتوزيع بروتوكول HTTP ، ويقيس تأخيرات الطلب ، ويسترجع المعلومات الضرورية من رؤوس HTTP. في النهاية ، نحصل على جميع الآثار في نظام جايجر واحد. للضبط ، يمكنك أيضًا استخدام متغيرات البيئة التي توفرها مكتبة goaeger go الرسمية.


ولكن هناك مشكلة. إلى أن تقوم الخدمات بإنشاء رأس uber خاص وإعادة توجيهه ، فلن نرى فترات تتبع متصلة في النظام. وهذا هو ما نحتاج إليه لإيجاد سبب المشكلات بسرعة. هنا Netramesh لديه حل مرة أخرى. يقرأ الوكلاء رؤوس HTTP ويقومون بإنشائها إذا لم يكن لديهم معرف تتبع uber. تقوم Netramesh أيضًا بتخزين معلومات حول الطلبات الواردة والصادرة في ملف تعريف السيارة ومقارنتها عن طريق إثراء الرؤوس اللازمة للطلبات الصادرة. كل ما يجب القيام به في الخدمات هو رمي عنوان X-Request-Id
واحد فقط ، والذي يمكن تهيئته باستخدام NETRA_HTTP_REQUEST_ID_HEADER_NAME
البيئة NETRA_HTTP_REQUEST_ID_HEADER_NAME
. للتحكم في حجم السياق في Netramesh ، يمكنك تعيين متغيرات البيئة التالية: NETRA_TRACING_CONTEXT_EXPIRATION_MILLISECONDS
(الوقت الذي سيتم فيه تخزين السياق) و NETRA_TRACING_CONTEXT_CLEANUP_INTERVAL
(دورية تنظيف السياق).
من الممكن أيضًا الجمع بين عدة مسارات في نظامك عن طريق تعليمها بعلامة جلسة خاصة. يتيح لك Netra تعيين HTTP_HEADER_TAG_MAP
لتحويل رؤوس HTTP إلى علامات نطاق التتبع المناسبة. هذا يمكن أن يكون مفيدا بشكل خاص للاختبار. بعد اجتياز الاختبار الوظيفي ، يمكنك معرفة أي جزء من النظام تأثر بالتصفية بواسطة مفتاح الجلسة المقابل.
تحديد مصدر الطلب
لتحديد مصدر الطلب ، يمكنك استخدام الوظيفة لإضافة رأس تلقائيًا بمصدر. باستخدام NETRA_HTTP_X_SOURCE_HEADER_NAME
البيئة NETRA_HTTP_X_SOURCE_HEADER_NAME
يمكنك تحديد اسم الرأس الذي سيتم تعيينه تلقائيًا. باستخدام NETRA_HTTP_X_SOURCE_VALUE
يمكنك تعيين القيمة التي سيتم بها تعيين رأس مصدر X لجميع الطلبات الصادرة.
يسمح لك هذا بشكل منتظم عبر الشبكة بإجراء توزيع هذا الرأس المفيد. ثم يمكنك استخدامه بالفعل في الخدمات وإضافته إلى السجلات والمقاييس.
حركة المرور Netramesh والتوجيه الداخلية
يتكون Netramesh من عنصرين رئيسيين. الأول ، netra-init ، يضع قواعد الشبكة لاعتراض حركة المرور. يستخدم قواعد إعادة توجيه iptables لاعتراض كل أو جزء من حركة المرور على الجانب الجانبي ، وهو المكون الرئيسي الثاني من Netramesh. يمكنك تكوين المنافذ التي تريد اعتراضها لجلسات TCP الواردة والصادرة: INBOUND_INTERCEPT_PORTS, OUTBOUND_INTERCEPT_PORTS
.
الأداة لديها أيضا ميزة مثيرة للاهتمام - التوجيه الاحتمالي. إذا كنت تستخدم Netramesh حصريًا لجمع مسافات التتبع ، فيمكنك في بيئة الإنتاج توفير الموارد وتمكين التوجيه الاحتمالي باستخدام المتغيرات NETRA_INBOUND_PROBABILITY
و NETRA_OUTBOUND_PROBABILITY
(من 0 إلى 1). القيمة الافتراضية هي 1 (كل حركة المرور يتم اعتراضها).
بعد اعتراض ناجح ، يقبل netra sidecar اتصالًا جديدًا ويستخدم خيار مأخذ التوصيل SO_ORIGINAL_DST
للحصول على الوجهة الأصلية. يقوم Netra بعد ذلك بفتح اتصال جديد بعنوان IP الأصلي وإنشاء اتصال TCP ثنائي الاتجاه بين الأطراف ، والاستماع إلى جميع حركة المرور التي تمر. إذا تم تعريف المنفذ على أنه HTTP ، فسيحاول Netra تحليله وتوجيهه. إذا كان تحليل HTTP غير ناجح ، فسيقوم Netra بالرجوع إلى TCP والبايت بشفافية.
بناء الرسم البياني التبعية
بعد تلقي الكثير من معلومات التتبع في Jaeger ، أريد الحصول على رسم بياني كامل للتفاعلات في النظام. ولكن إذا كان نظامك محملاً بما فيه الكفاية وتراكمت مليارات المتبعات في التتبع يوميًا ، يصبح تجميعها ليس بهذه البساطة. هناك طريقة رسمية للقيام بذلك: تبعيات الشرارة . ومع ذلك ، سوف يستغرق الأمر ساعات لبناء الرسم البياني الكامل وإجبار مجموعة البيانات بأكملها على التنزيل من Jaeger على مدار الـ 24 ساعة الماضية.
إذا كنت تستخدم Elasticsearch لتخزين فترات التتبع ، فيمكنك استخدام أداة مساعدة بسيطة على Golang ستقوم بإنشاء الرسم البياني نفسه في دقائق باستخدام ميزات وقدرات Elasticsearch.

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