Kubernetes موازنة التحميل وتوسيع نطاق الاتصالات طويلة الأمد


ستساعدك هذه المقالة على فهم كيفية عمل موازنة الحمل في Kubernetes ، وماذا يحدث عند قياس الاتصالات طويلة العمر ولماذا يجب أن تفكر في الموازنة من جانب العميل إذا كنت تستخدم HTTP / 2 أو gRPC أو RSockets أو AMQP أو البروتوكولات الأخرى طويلة العمر. 

قليلا عن كيفية إعادة توزيع حركة المرور في Kubernetes 


يوفر Kubernetes مجردين ملائمين لطرح التطبيقات: الخدمات والنشر.

تصف عمليات النشر كيفية وكم عدد نسخ تطبيقك التي يجب تشغيلها في أي وقت. يتم نشر كل تطبيق ضمن (Pod) ويتم تعيين عنوان IP.

تشبه خدمات الميزات موازن التحميل. وهي مصممة لتوزيع حركة المرور عبر المواقد المتعددة.

دعونا نرى كيف يبدو .

  1. في الرسم البياني أدناه ، سترى ثلاث حالات من نفس التطبيق وموازناً للتحميل:

  2. يسمى موازن التحميل بالخدمة ، ويتم تعيينه بعنوان IP. يتم إعادة توجيه أي طلب وارد إلى إحدى الحجرات:

  3. يحدد البرنامج النصي للنشر عدد مثيلات التطبيق. لن تضطر أبدًا إلى النشر مباشرةً تحت:

  4. يتم تعيين عنوان IP الخاص بكل جراب:



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

هذا على النحو التالي .

  1. هناك طلب حليقة 10.96.45.152 للخدمة:

  2. تختار الخدمة أحد عناوين البودات الثلاثة كوجهة:

  3. يتم إعادة توجيه حركة المرور إلى جراب معين:



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

عندما تفي الواجهة الأمامية بالطلب إلى الواجهة الخلفية ، لا تحتاج إلى معرفة عدد المداخن التي تخدمها الخلفية بالضبط: يمكن أن يكون هناك واحد أو عشرة أو مائة.

أيضا ، لا تعرف الواجهة الأمامية أي شيء عن عناوين المداخن التي تخدم الواجهة الخلفية.

عندما تقدم الواجهة الأمامية طلبًا للواجهة الخلفية ، فإنها تستخدم عنوان IP لخدمة الواجهة الخلفية ، والذي لا يتغير.

إليك كيف تبدو .

  1. تحت 1 يطلب مكون داخلي الواجهة الخلفية. بدلاً من اختيار واحدة محددة للواجهة الخلفية ، تقوم بإجراء طلب خدمة:

  2. تختار الخدمة أحد القرون الخلفية كعنوان الوجهة:

  3. تنتقل حركة المرور من الموقد 1 إلى الموقد 5 المحدد بواسطة الخدمة:

  4. تحت 1 ، لا يعرف بالضبط عدد المداخن الموجودة تحت سن 5 المخفية خلف الخدمة:



ولكن كيف تقوم الخدمة بالضبط بتوزيع الطلبات؟ هل يبدو أن الموازنة المستديرة روبن تستخدم؟ دعنا نحصل على حق. 

موازنة في خدمات Kubernetes


خدمات Kubernetes غير موجودة. لا توجد عملية للخدمة التي تم تعيينها لعنوان IP ومنفذ.

يمكنك التحقق من ذلك بالانتقال إلى أي عقدة في الكتلة وتشغيل الأمر netstat -ntlp.

لا يمكنك حتى العثور على عنوان IP المخصص للخدمة.

يقع عنوان IP الخاص بالخدمة في طبقة التحكم ، في وحدة التحكم ، ويتم تسجيله في قاعدة البيانات - إلخ. يتم استخدام نفس العنوان من قبل مكون آخر - kube-proxy.
يتلقى Kube-proxy قائمة بعناوين IP لجميع الخدمات ويشكل مجموعة من قواعد iptables على كل عقدة في المجموعة.

تنص هذه القواعد على: "إذا رأينا عنوان IP الخاص بالخدمة ، فنحن بحاجة إلى تعديل عنوان الوجهة للطلب وإرساله إلى إحدى اللوحات."

يتم استخدام عنوان IP الخاص بالخدمة فقط كنقطة دخول ولا يتم تقديمه من خلال أي عملية استماع إلى عنوان IP هذا والمنفذ.

دعونا نلقي نظرة على ذلك

  1. خذ بعين الاعتبار مجموعة من ثلاث عقد. هناك قرون على كل عقدة:

  2. المداخن المحبوكة باللون البيج هي جزء من الخدمة. نظرًا لأن الخدمة غير موجودة كعملية ، يتم تعطيلها:

  3. الأول يسأل عن الخدمة ويجب أن يسقط على أحد المداخن ذات الصلة:

  4. لكن الخدمة غير موجودة ، ولا توجد عملية. كيف يعمل؟

  5. قبل أن يغادر الطلب العقدة ، يمر عبر قواعد iptables:

  6. تعرف قواعد iptables أنه لا توجد خدمة ، وتستبدل عنوان IP الخاص بها بأحد عناوين IP الخاصة بالقرود المرتبطة بهذه الخدمة:

  7. يتلقى الطلب عنوان IP صالحًا كعنوان الوجهة ويتم معالجته عادةً:

  8. اعتمادًا على هيكل الشبكة ، يصل الطلب في النهاية إلى الموقد:



هل iptables قادرة على موازنة الحمل؟


لا ، يتم استخدام iptables للتصفية ولم يتم تصميمه لتحقيق التوازن.

ومع ذلك ، من الممكن كتابة مجموعة من القواعد التي تعمل مثل الموازن الزائف .

وهذا بالضبط ما يفعله Kubernetes.

إذا كان لديك ثلاث قرون ، فسيكتب kube-proxy القواعد التالية:

  1. اختر الخيار الأول باحتمال 33٪ ، وإلا انتقل إلى القاعدة التالية.
  2. اختر الثاني مع احتمالية 50٪ ، وإلا انتقل إلى القاعدة التالية.
  3. اختر الثالث تحت.

مثل هذا النظام يؤدي إلى حقيقة أن كل فرع يتم اختياره باحتمالية 33٪.



وليس هناك ما يضمن أنه سيتم اختيار أقل من 2 بعد ذلك بعد الملف 1.

ملاحظة : تستخدم iptables وحدة إحصائية لتوزيع عشوائي. وبالتالي ، تعتمد خوارزمية الموازنة على الاختيار العشوائي.

الآن بعد أن فهمت كيفية عمل الخدمات ، دعنا نلقي نظرة على سيناريوهات العمل الأكثر إثارة للاهتمام.

لا يتم قياس الاتصالات طويلة الأمد في Kubernetes بشكل افتراضي


يتم تقديم كل طلب HTTP من الواجهة الأمامية إلى النهاية الخلفية بواسطة اتصال TCP منفصل ، يتم فتحه وإغلاقه.

إذا أرسلت الواجهة الأمامية 100 طلب في الثانية إلى الواجهة الخلفية ، عندئذٍ يتم فتح وإغلاق 100 اتصال TCP مختلف.

يمكنك تقليل وقت معالجة الطلب وتقليل الحمل إذا فتحت اتصال TCP واحد واستخدمته لجميع طلبات HTTP اللاحقة.

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



لا يتم تمكين هذه الميزة افتراضيًا: يجب تكوين كل من الخادم والعميل وفقًا لذلك.

الإعداد نفسه بسيط ويمكن الوصول إليه لمعظم لغات البرمجة والبيئات.

فيما يلي بعض الروابط إلى أمثلة بلغات مختلفة:


ماذا يحدث إذا استخدمنا البقاء على قيد الحياة في Kubernetes؟
لنفترض أن كلاً من الواجهة الأمامية والخلفية يدعمان البقاء على قيد الحياة.

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

على عكس الحالة المعتادة ، عندما يتم إغلاق اتصال TCP بعد تلقي الاستجابة ، يتم الاحتفاظ به مفتوحًا الآن لطلبات HTTP التالية.

ماذا يحدث إذا أرسلت الواجهة الأمامية المزيد من طلبات الواجهة الخلفية؟

لإعادة توجيه هذه الطلبات ، سيتم استخدام اتصال TCP مفتوح ، سيتم إرسال جميع الطلبات إلى نفس الطلب تحت الخلفية ، حيث تم الحصول على الطلب الأول.

ألا يجب على iptables إعادة توزيع حركة المرور؟

ليس في هذه الحالة.

عند إنشاء اتصال TCP ، يمر عبر قواعد iptables ، التي تحدد واحدة محددة للواجهة الخلفية حيث ستنتقل حركة المرور.

نظرًا لأن جميع الطلبات التالية تمر عبر اتصال TCP مفتوح بالفعل ، لم تعد قواعد iptables تسمى.

دعونا نرى كيف يبدو .

  1. يرسل الفرع الأول طلبًا إلى الخدمة:

  2. أنت تعرف بالفعل ما سيحدث بعد ذلك. الخدمة غير موجودة ، ولكن هناك قواعد iptables ستتعامل مع الطلب:

  3. سيتم اختيار أحد القرون الخلفية كعنوان الوجهة:

  4. يصل الطلب إلى الموقد. عند هذه النقطة ، سيتم إنشاء اتصال TCP دائم بين جرابين:

  5. أي طلب قادم من الجراب الأول يمر عبر اتصال قائم بالفعل:



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

حتى إذا كان لديك جرابان في الواجهة الخلفية ، مع اتصال مستمر ، فستنتقل حركة المرور دائمًا إلى أحدهما.

هل يمكن إصلاح ذلك؟

نظرًا لأن Kubernetes لا تعرف كيفية موازنة الاتصالات المستمرة ، فإن هذه المهمة هي مسؤوليتك.

الخدمات عبارة عن مجموعة من عناوين IP والمنافذ تسمى نقاط النهاية.

يمكن لتطبيقك الحصول على قائمة بنقاط النهاية من الخدمة وتحديد كيفية توزيع الطلبات بينها. يمكنك فتح اتصال دائم بكل طلبات الموقد والتوازن بين هذه الاتصالات باستخدام Round-robin.

أو تطبيق خوارزميات موازنة أكثر تعقيدًا .

يجب أن يتبع رمز العميل المسؤول عن التوازن هذا المنطق:

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

هنا كيف ستبدو .

  1. بدلاً من إرسال الطلب الأول إلى الخدمة ، يمكنك موازنة الطلبات من جانب العميل:

  2. تحتاج إلى كتابة كود يسأل أي القرون هي جزء من الخدمة:

  3. بمجرد استلام القائمة ، احفظها على جانب العميل واستخدمها للاتصال بالقرص:

  4. أنت نفسك مسؤول عن خوارزمية موازنة التحميل:



السؤال الآن: هل تنطبق هذه المشكلة فقط على استمرار HTTP؟

موازنة التحميل من جانب العميل


HTTP ليس البروتوكول الوحيد الذي يمكنه استخدام اتصالات TCP المستمرة.

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

بدلاً من ذلك ، يتم فتح اتصال TCP دائم بقاعدة البيانات واستخدامها.

إذا تم نشر قاعدة بياناتك في Kubernetes وتم توفير الوصول كخدمة ، فستواجه نفس المشاكل كما هو موضح في القسم السابق.

سيتم تحميل نسخة متماثلة لقاعدة بيانات واحدة أكثر من الباقي. لن يساعد Kube-proxy و Kubernetes في تحقيق التوازن بين الاتصالات. يجب الاهتمام بموازنة الاستعلامات لقاعدة البيانات الخاصة بك.

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

فيما يلي مثال للوصول إلى كتلة قاعدة بيانات MySQL من Node.js:

var mysql = require('mysql');
var poolCluster = mysql.createPoolCluster();

var endpoints = /* retrieve endpoints from the Service */

for (var [index, endpoint] of endpoints) {
  poolCluster.add(`mysql-replica-${index}`, endpoint);
}

// Make queries to the clustered MySQL database

هناك أطنان من البروتوكولات الأخرى التي تستخدم اتصالات TCP المستمرة:

  • WebSockets و WebSockets المؤمنة
  • HTTP / 2
  • gRPC
  • RSockets
  • AMQP

يجب أن تكون على دراية بمعظم هذه البروتوكولات.

ولكن إذا كانت هذه البروتوكولات شائعة للغاية ، فلماذا لا يوجد حل موازنة موحد؟ لماذا يلزم تغيير منطق العميل؟ هل يوجد حل Kubernetes أصلي؟

تم تصميم Kube-proxy و iptables لإغلاق معظم سيناريوهات النشر القياسية لـ Kubernetes. هذا من أجل الراحة.

إذا كنت تستخدم خدمة ويب توفر واجهة برمجة تطبيقات REST ، فأنت محظوظ - في هذه الحالة ، لا يتم استخدام اتصالات TCP الدائمة ، يمكنك استخدام أي خدمة Kubernetes.

ولكن بمجرد أن تبدأ في استخدام اتصالات TCP المستمرة ، سيكون عليك معرفة كيفية توزيع الحمل بالتساوي على الخلفية. لا تحتوي Kubernetes على حلول جاهزة لهذه الحالة.

ومع ذلك ، بالطبع ، هناك خيارات قد تساعد.

موازنة الاتصالات طويلة الأمد في Kubernetes


لدى Kubernetes أربعة أنواع من الخدمات:

  1. العنقودية
  2. NodePort
  3. Loadbalancer
  4. بلا رأس

تعتمد الخدمات الثلاثة الأولى على عنوان IP الظاهري ، الذي يستخدمه kube-proxy لبناء قواعد iptables. لكن الأساس الأساسي لجميع الخدمات هو خدمة من النوع بلا رأس.

لا يوجد عنوان IP مرتبط بالخدمة بدون رأس ، وهو يوفر فقط آلية للحصول على قائمة بعناوين IP ومنافذ المداخن المرتبطة (نقاط النهاية).

تعتمد جميع الخدمات على خدمة بلا رأس.

خدمة ClusterIP هي خدمة بلا رأس مع بعض الإضافات: 

  1. تقوم طبقة الإدارة بتعيين عنوان IP لها.
  2. Kube-proxy يشكل قواعد iptables الضرورية.

وبالتالي ، يمكنك تجاهل وكيل kube واستخدام قائمة نقاط النهاية المتلقاة من الخدمة بدون رأس مباشرةً لموازنة الحمل في التطبيق الخاص بك.

ولكن كيف تضيف منطقًا مشابهًا لجميع التطبيقات المنتشرة في مجموعة؟

إذا تم نشر تطبيقك بالفعل ، فقد تبدو هذه المهمة مستحيلة. ومع ذلك، هناك بديل.

سوف تساعدك شبكة الخدمة


ربما لاحظت بالفعل أن استراتيجية موازنة التحميل من جانب العميل قياسية تمامًا.

عندما يبدأ التطبيق ، فإنه:

  1. الحصول على قائمة بعناوين IP من الخدمة.
  2. يفتح ويحافظ على تجمع اتصال.
  3. تحديث التجمع بشكل دوري ، إضافة أو إزالة نقاط النهاية.

بمجرد أن يرغب التطبيق في تقديم طلب ، فإنه:

  1. تحديد اتصال متاح باستخدام نوع من المنطق (مثل Round-robin).
  2. يفي بالطلب.

تعمل هذه الخطوات مع WebSockets و gRPC و AMQP.

يمكنك فصل هذا المنطق في مكتبة منفصلة واستخدامها في تطبيقاتك.

ومع ذلك ، يمكن استخدام شبكات الخدمة مثل Istio أو Linkerd بدلاً من ذلك.

تكمل Service Mesh تطبيقك بعملية:

  1. يبحث تلقائيًا عن عناوين IP للخدمات.
  2. للتحقق من الاتصالات مثل WebSockets و gRPC.
  3. موازنة الطلبات باستخدام البروتوكول الصحيح.

تساعد شبكة الخدمة على إدارة حركة المرور داخل المجموعة ، ولكنها تستهلك الكثير من الموارد. هناك خيارات أخرى تستخدم مكتبات الطرف الثالث ، مثل Netflix Ribbon ، أو البروكسيات القابلة للبرمجة ، مثل Envoy.

ماذا يحدث إذا تجاهلت مشاكل التوازن؟


لا يمكنك استخدام موازنة التحميل ولا تلاحظ أي تغييرات. دعونا نلقي نظرة على بعض سيناريوهات العمل.

إذا كان لديك عملاء أكثر من الخوادم ، فهذه ليست مشكلة كبيرة.

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



يمكن توزيع الاتصالات بشكل غير متساوٍ: ربما أربعة عملاء متصلين بالخادم نفسه ، ولكن هناك فرصة جيدة لاستخدام كلا الخادمين.

ما هو أكثر إشكالية هو السيناريو المعاكس.

إذا كان لديك عدد أقل من العملاء والمزيد من الخوادم ، فقد لا يتم استخدام مواردك بشكل كاف وسيظهر اختناق محتمل.

افترض أن هناك عميلان وخمسة خوادم. في أحسن الأحوال ، سيكون هناك اتصالان دائمان بخادمين من أصل خمسة خوادم.

ستكون الخوادم الأخرى خاملة:



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

استنتاج


تم تصميم خدمات Kubernetes للعمل في معظم سيناريوهات تطبيق الويب القياسية.

ومع ذلك ، بمجرد أن تبدأ العمل مع بروتوكولات التطبيق التي تستخدم اتصالات TCP المستمرة ، مثل قواعد البيانات أو gRPC أو WebSockets ، لم تعد الخدمات مناسبة. لا توفر Kubernetes آليات داخلية لموازنة اتصالات TCP المستمرة.

هذا يعني أنه يجب عليك كتابة التطبيقات مع إمكانية الموازنة من جانب العميل.

الترجمة أعدها فريق Kubernetes العاص من من Mail.ru .

ماذا تقرأ عن هذا الموضوع :

  1. ثلاثة مستويات من autoscaling في Kubernetes وكيفية استخدامها بفعالية
  2. Kubernetes في روح القرصنة مع قالب التنفيذ .
  3. Kubernetes .

All Articles