قم بعمل الرياضيات: توسيع نطاق تطبيقات الخدمات المصغرة باستخدام المنسقين

نشرت: 2022-03-11

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

لكن القصة بالتأكيد ليست هي نفسها بدون منظمي الحاوية.

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

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

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

يقوم معظمنا أولاً بتعيينها على قيم "عشوائية" أو افتراضية والتي نقوم بتعديلها بعد ذلك وفقًا لما نجده أثناء المراقبة. دفعني ذلك إلى التفكير: ماذا لو تمكنا من إنشاء إجراء "رياضي" أكثر يساعدنا في العثور على المجموعة الفائزة؟

حساب معلمات تنظيم الحاوية

عندما نفكر في توسيع نطاق الخدمات المصغرة تلقائيًا لأحد التطبيقات ، فإننا نتطلع بالفعل إلى تحسين نقطتين رئيسيتين:

  1. التأكد من إمكانية زيادة النشر بسرعة في حالة حدوث زيادة سريعة في التحميل (حتى لا يواجه المستخدمون المهلات أو HTTP 500s)
  2. خفض تكلفة البنية التحتية (على سبيل المثال ، منع تحميل المثيلات بشكل كافٍ)

هذا يعني بشكل أساسي تحسين عتبات برامج الحاوية للتوسيع والتصغير. (تحتوي خوارزمية Kubernetes على معلمة واحدة للاثنين).

سأوضح لاحقًا أن جميع المعلمات المتعلقة بالمثيل مرتبطة بعتبة الترقية. هذا هو أصعب حساب - ومن هنا هذه المقالة.

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

حساب عتبة الزيادة

لكي تعمل هذه الطريقة ، يجب عليك التأكد من أن طلبك يلبي المتطلبات التالية:

  1. يجب توزيع الحمل بالتساوي عبر كل مثيل للتطبيق الخاص بك (بطريقة round robin)
  2. يجب أن تكون أوقات الطلبات أقصر من الفاصل الزمني لفحص الحمولة لمجموعة الحاوية الخاصة بك.
  3. عليك التفكير في تشغيل الإجراء على عدد كبير من المستخدمين (تم تحديده لاحقًا).

يأتي السبب الرئيسي لهذه الشروط من حقيقة أن الخوارزمية لا تحسب الحمل على أنه لكل مستخدم ولكن كتوزيع (موضح لاحقًا).

الحصول على كل Gaussian

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

تقديم بعض المتغيرات:

  • $ N_ {u} $ ، "العدد الكبير من المستخدمين"
  • $ L_ {u} (t) $ ، الحمل الناتج عن مستخدم واحد يؤدي "عملية استهلاك الموارد" ($ t = 0 $ نقطة حتى اللحظة التي يبدأ فيها المستخدم العملية)
  • $ L_ {tot} (t) $ ، إجمالي الحمل (الناتج عن جميع المستخدمين)
  • $ T_ {tot} $ ، "الفترة الزمنية القصيرة"

في عالم الرياضيات ، بالحديث عن عدد كبير من المستخدمين يؤدون نفس الشيء في نفس الوقت ، يتبع توزيع المستخدمين بمرور الوقت توزيعًا غاوسيًا (أو عاديًا) ، صيغته هي:

\ [G (t) = \ frac {1} {\ sigma \ sqrt {2 \ pi}} e ^ {\ frac {- (t- \ mu) ^ 2} {2 \ sigma ^ 2}} \]

هنا:

  • µ هي القيمة المتوقعة
  • σ هو الانحراف المعياري

ويتم رسمها على النحو التالي (مع $ µ = 0 $):

رسم بياني لتوزيع جاوس ، يوضح كيف أن 99.7٪ من المساحة تقع بين موجب ناقص ثلاثة سيغماس

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

لكن بالنظر إلى الرسم البياني ، نلاحظ أن القيم الواقعة خارج النطاق $ [- 3σ، 3σ] $ قريبة جدًا من الصفر ولا تختلف كثيرًا ، مما يعني أن تأثيرها ضئيل حقًا ويمكن تنحيته جانبًا. هذا صحيح أكثر ، لأن هدفنا هو اختبار توسيع نطاق تطبيقنا ، لذلك نحن نبحث عن أشكال مختلفة من أعداد كبيرة من المستخدمين.

بالإضافة إلى ذلك ، نظرًا لأن الفاصل الزمني $ [- 3σ، 3σ] $ يحتوي على 99.7٪ من مستخدمينا ، فهو قريب بدرجة كافية من الإجمالي للعمل عليه ، ونحتاج فقط إلى ضرب $ N_ {u} $ في 1.003 للتعويض عن الاختلاف. اختيار هذا الفاصل يعطينا $ µ = 3σ $ (بما أننا سنعمل من $ t = 0 $).

فيما يتعلق بالمراسلات مع $ T_ {tot} $ ، فإن اختياره ليكون مساويًا لـ $ 6σ $ ($ [- 3σ، 3σ] $) لن يكون تقديرًا جيدًا ، نظرًا لأن 95.4 بالمائة من المستخدمين في الفاصل الزمني $ [- 2σ، 2σ] $ ، والتي تدوم 4 دولارات. لذا فإن اختيار $ T_ {tot} $ ليكون مساويًا لـ $ 6σ $ سيضيف نصف الوقت لـ 4.3٪ فقط من المستخدمين ، وهو أمر لا يمثل حقًا. لذلك اخترنا أن نأخذ $ T_ {tot} = 4σ $ ، ويمكننا أن نستنتج:

\ (σ = \ frac {T_ {tot}} {4} \) و \ (µ = \ frac {3} {4} * T_ {tot} \)

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

تم إنشاء الحمل بنسبة 99.7 بالمائة من $ N {u} $ ، مما أدى إلى إجراء عملية مستهلكة $ L {u} (t) $ وحيث يقوم 95.4 بالمائة منهم بتنفيذ ذلك خلال المدة $ T {tot} $.

(هذا شيء يستحق التذكر عند استخدام تطبيق الويب.)

بحقن النتائج السابقة في دالة توزيع المستخدم (Gaussian) ، يمكننا تبسيط المعادلة على النحو التالي:

\ [G (t) = \ frac {4 N_ {u}} {T_ {tot} \ sqrt {2 \ pi}} e ^ \ frac {- (4t-3T_ {tot}) ^ 2} {T_ {tot } ^ 2} \]

من الآن فصاعدًا ، بعد تحديد $ σ $ و $ µ $ ، سنعمل على الفاصل الزمني $ t \ in [0، \ frac {3} {2} T_ {tot}] $ (يستمر $ 6σ $).

ما هو إجمالي تحميل المستخدم؟

الخطوة الثانية في القياس التلقائي للخدمات المصغرة هي حساب $ L_ {tot} (t) $.

نظرًا لأن $ G (t) $ هو توزيع ، لاسترداد عدد المستخدمين في وقت معين ، يتعين علينا حساب تكامله (أو استخدام دالة التوزيع التراكمي). ولكن نظرًا لعدم بدء جميع المستخدمين عملياتهم في نفس الوقت ، فستكون هناك فوضى حقيقية عند محاولة إدخال $ L_ {u} (t) $ وتقليل المعادلة إلى صيغة قابلة للاستخدام.

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

بالعودة إلى مجموع Riemann ، فإنه يحتوي على الخاصية التالية المرتبطة بالتكاملات:

\ [\ int_ {a} ^ {b} f (x) dx = \ lim_ {n \ rightarrow \ infty} \ sum_ {k = 1} ^ {n} (x_ {k} - x_ {k-1}) و (س_ {ك}) \]

مع $ x_k $ معرّف كالتالي:

\ [x_ {k} = a + k \ frac {b - a} {n}، 0 \ leq k \ leq n \]

هذا صحيح حيث:

  • $ n $ هو عدد الأقسام الفرعية.
  • $ a $ هو الحد الأدنى ، هنا 0.
  • $ b $ هو الحد الأعلى ، هنا $ \ frac {3} {2} * T_ {tot} $.
  • $ f $ هي الوظيفة — هنا $ G $ — لتقريب مساحتها.

رسم بياني لمجموع Riemann للدالة G. ينتقل المحور X من صفر إلى ثلاثة أنصاف من T-sub-tot ، ويتم تمييز مستطيل واحد يوضح أنه يقع بين x-sub-k-minus-1 و x- الفرعية ك.

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

لاحظ أيضًا أنه يمكننا رؤية الشكل المستطيل للتقسيم الفرعي على الجانب الأيمن من تعريف مجموع ريمان.

الآن بعد أن أصبح لدينا صيغة مجموع Riemann ، يمكننا القول أن قيمة التحميل في الوقت $ t $ هي مجموع عدد المستخدمين لكل قسم فرعي مضروبًا في دالة تحميل المستخدم في الوقت المقابل . يمكن كتابة هذا على النحو التالي:

\ [L_ {tot} (t) = \ lim_ {n \ rightarrow \ infty} \ sum_ {k = 1} ^ {n} (x_ {k} - x_ {k-1}) G (x_ {k}) L_ {u} (t - x_ {k}) \]

بعد استبدال المتغيرات وتبسيط الصيغة ، يصبح هذا:

\ [L_ {tot} (t) = \ frac {6 N_ {u}} {\ sqrt {2 \ pi}} \ lim_ {n \ rightarrow \ infty} \ sum_ {k = 1} ^ {n} (\ frac {1} {n}) e ^ {- {(\ frac {6k} {n} - 3) ^ {2}}} L_ {u} (t - k \ frac {3 T_ {tot}} {2n }) \]

وفويلا ! أنشأنا وظيفة التحميل!

إيجاد عتبة التوسع

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

استنتاج معلمات التنظيم الأخرى

بمجرد أن تجد حد التوسيع ($ S_ {up} $) ، من السهل جدًا حساب المعلمات الأخرى.

من $ S_ {up} $ ستعرف الحد الأقصى لعدد المثيلات. (يمكنك أيضًا البحث عن الحد الأقصى للحمل على وظيفة التحميل الخاصة بك والقسمة على أقصى حمل لكل حالة ، مقربًا لأعلى.)

يجب تحديد الحد الأدنى لعدد المثيلات ($ N_ {min} $) وفقًا لبنيتك الأساسية. (أوصي بالحصول على نسخة متماثلة واحدة على الأقل لكل AZ.) ولكنها تحتاج أيضًا إلى مراعاة وظيفة التحميل: نظرًا لأن وظيفة Gaussian تزداد بسرعة كبيرة ، يكون توزيع الحمل أكثر كثافة (لكل نسخة متماثلة) في البداية ، لذلك أنت قد ترغب في زيادة الحد الأدنى لعدد النسخ المتماثلة لتخفيف هذا التأثير. (سيؤدي هذا على الأرجح إلى زيادة $ S_ {up} $.)

أخيرًا ، بمجرد تحديد الحد الأدنى لعدد النسخ المتماثلة ، يمكنك حساب عتبة خفض الحجم ($ S_ {down} $) مع مراعاة ما يلي: نظرًا لأن تصغير نسخة متماثلة واحدة ليس له تأثير على مثيلات أخرى أكثر من التحجيم من $ N_ {min} + 1 $ to $ N_ {min} $ ، علينا أن نتأكد من أن عتبة الزيادة لن يتم تفعيلها مباشرة بعد التصغير. إذا كان مسموحًا بذلك ، فسيكون لهذا تأثير اليويو. بعبارات أخرى:

\ [(N_ {min} + 1) S_ {down} <N_ {min} S_ {up} \]

أو:

\ [S_ {down} <\ frac {N_ {min}} {N_ {min} +1} S_ {up} \]

أيضًا ، يمكننا أن نعترف بأنه كلما تم تكوين مجموعتك للانتظار لفترة أطول قبل تقليص حجمها ، كان من الأكثر أمانًا تعيين $ S_ {down} $ بالقرب من الحد الأعلى. مرة أخرى ، سيكون عليك إيجاد التوازن الذي يناسبك.

لاحظ أنه عند استخدام نظام تزامن Mesosphere Marathon مع مقياسه التلقائي ، فإن الحد الأقصى لعدد الحالات التي يمكن إزالتها مرة واحدة من التصغير يكون مرتبطًا بـ AS_AUTOSCALE_MULTIPLIER ($ A_ {mult} $) ، مما يعني:

\ [S_ {down} <\ frac {S_ {up}} {A_ {mult}} \]

ماذا عن وظيفة تحميل المستخدم؟

نعم ، هذه مشكلة صغيرة وليست أسهل مشكلة يتم حلها رياضيًا - إن كان ذلك ممكنًا على الإطلاق.

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

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

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

إدخال app-autoscaling-calculator

والآن ، بالنسبة إلى تطبيق الويب الصغير المذكور طوال الوقت: يأخذ كإدخال وظيفة التحميل وتكوين منظم الحاوية وبعض المعلمات العامة الأخرى ويعيد حد الزيادة والأرقام الأخرى ذات الصلة بالمثيل.

المشروع مستضاف على GitHub ، ولكن يتوفر أيضًا إصدار مباشر.

فيما يلي النتيجة التي قدمها تطبيق الويب ، قم بتشغيلها مقابل بيانات الاختبار (على Kubernetes):

رسم بياني يوضح عدد الحالات والتحميل لكل مثيل بمرور الوقت

توسيع نطاق الخدمات المصغرة: لا مزيد من التحسس في الظلام

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

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