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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

تبدو هكذا.

  1. يتم تلقي طلب حليقة 10.96.45.152 إلى الخدمة:

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

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

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

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

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

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

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

كما أن الواجهة الأمامية لا تعرف شيئًا عن عناوين البودات التي تخدم الواجهة الخلفية.

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

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

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

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

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

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

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

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

  4. لا يعرف أقل من 1 بالضبط عدد البودات مثل أقل من 5 المخفية خلف الخدمة:

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

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

تحقيق التوازن في خدمات Kubernetes

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

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

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

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

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

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

دعونا ننظر إلى هذا

  1. النظر في مجموعة من ثلاث العقد. تحتوي كل عقدة على كبسولات:

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

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

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

  3. تطلب الكبسولة الأولى خدمة ويجب أن تذهب إلى إحدى البودات المرتبطة بها:

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

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

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

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

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

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

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

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

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

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

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

هل يمكن لـ iptables تحميل الرصيد؟

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

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

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

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

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

يؤدي هذا النظام إلى اختيار كل جراب باحتمال 33%.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  1. تقوم الكبسولة الأولى بإرسال طلب إلى الخدمة:

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

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

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

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

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

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

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

  5. أي طلب لاحق من الكبسولة الأولى سوف يمر عبر الاتصال الذي تم إنشاؤه بالفعل:

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

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

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

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

وبما أن Kubernetes لا يعرف كيفية موازنة الاتصالات المستمرة، فإن هذه المهمة تقع على عاتقك.

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

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

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

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

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

وهذا هو ما سوف تبدو وكأنها.

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

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

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

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

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

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

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

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

والسؤال الذي يطرح نفسه الآن: هل تنطبق هذه المشكلة فقط على استمرارية 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
  • جي آر بي سي
  • RSockets
  • AMQP

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

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

تم تصميم Kube-proxy وiptables لتغطية حالات الاستخدام الأكثر شيوعًا عند النشر إلى Kubernetes. هذا من أجل الراحة.

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

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

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

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

هناك أربعة أنواع من الخدمات في Kubernetes:

  1. ClusterIP
  2. منفذ العقدة
  3. LoadBalancer
  4. بلا رأس

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

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

تعتمد جميع الخدمات على الخدمة مقطوعة الرأس.

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

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

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

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

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

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

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

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

  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

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

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

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

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

ستكون الخوادم المتبقية خاملة:

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

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

اختتام

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

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

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

الترجمة من إعداد الفريق Kubernetes aaS من Mail.ru.

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

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

المصدر: www.habr.com

إضافة تعليق