تمديد واستكمال Kubernetes (مراجعة وتقرير الفيديو)



في 8 أبريل ، في مؤتمر Saint HighLoad ++ 2019 ، كجزء من قسم DevOps والعمليات ، تم إعداد تقرير بعنوان "تمديد Kubernetes وتكميله" ، والذي تم إنشاؤه بواسطة ثلاثة موظفين من Flant. نتحدث فيه عن العديد من المواقف التي أردنا فيها توسيع قدرات Kubernetes واستكمالها ، ولكن لم نجد لها حلاً بسيطًا جاهزًا. ظهرت الحلول اللازمة في شكل مشاريع مفتوحة المصدر ، وهذا العرض مخصص أيضًا لهم.

وفقًا للتقاليد ، يسعدنا تقديم مقطع فيديو مع تقرير (50 دقيقة ، أكثر إفادة من المقال) والضغط الرئيسي في شكل نص. دعنا نذهب!

K8s Kernel والإضافات


تقوم Kubernetes بتحويل مقاربة طويلة في الصناعة والإدارة:

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

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

Kubernetes ليس مجموعة مختلطة تعمل على حل جميع مشكلات جميع المستخدمين. نواة Kubernetes مسؤولة فقط عن مجموعة الحد الأدنى من الوظائف الضرورية الموجودة في كل مجموعة:



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



من ناحية أخرى ، توفر K8s فرصًا رائعة لتوسيع الوظائف المتاحة ، مما يساعد على سد احتياجات المستخدم الأخرى المحددة . يتحمل مسؤولو الكتلة مسؤولية الإضافات إلى Kubernetes ، الذين يتعين عليهم تثبيت وتكوين كل ما هو ضروري حتى تصبح المجموعة الخاصة بهم "تأخذ الشكل الضروري" [لحل مشاكلهم المحددة]. أي نوع من الإضافات هذه؟ لنلقي نظرة على بعض الأمثلة.

أمثلة من الإضافات


بعد تثبيت Kubernetes ، قد يفاجأنا أن الشبكة ، وهي ضرورية جدًا لتفاعل القرون داخل العقدة وبين العقد ، لا تعمل من تلقاء نفسها. لا يضمن Core Kubernetes الاتصالات الضرورية - بل إنه يحدد واجهة شبكة ( CNI ) للوظائف الإضافية لجهة خارجية. يجب علينا تثبيت أحد هذه الإضافات ، والتي ستكون مسؤولة عن تكوين الشبكة.



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

من بين الأمثلة الأخرى:

  • التحكم في الدخول (للاطلاع على مراجعة ، راجع مقالنا الأخير ) .
  • مدير الشهادات :

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

  • المقاييس هي مثال آخر على كيفية فصل Kubernetes للواجهة (API Metrics) عن التنفيذ (الوظائف الإضافية لجهة خارجية مثل محول Prometheus ، وكيل الكتلة Datadog ...).
  • للمراقبة والإحصاء ، حيث لا يلزم عملياً بروميثيوس وغرافانا فحسب ، بل أيضًا مقاييس حالة الكوب ، ومصدر العقدة ، إلخ.

وهذه ليست قائمة كاملة بالوظائف الإضافية ... على سبيل المثال ، نحن في شركة Flant اليوم نثبت 29 وظيفة إضافية لكل مجموعة من مجموعات Kubernetes (جميعها تنشئ 249 كائن Kubernetes في المجموع). ببساطة ، نحن لا نرى حياة الكتلة دون إضافات.

أتمتة


تم تصميم المشغلين لأتمتة العمليات الروتينية التي نواجهها يوميًا. فيما يلي أمثلة على الحياة ستكون رائعة لكتابة عامل التشغيل:

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

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

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

كيفية كتابة بيان ل Kubernetes؟


بشكل عام ، المخطط بسيط:



... ولكن اتضح أن:

  • Kubernetes API هو شيء غير تافه يتطلب الكثير من الوقت لإتقانه ؛
  • البرمجة ليست أيضًا للجميع (يتم اختيار Go باعتبارها اللغة المفضلة لأن هناك إطارًا خاصًا لها - Operator SDK ) ؛
  • مع الإطار على هذا النحو ، وضع مماثل.

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



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

مشغل شل


كيف يعمل؟ في المجموعة يوجد جراب يوجد فيه Go-binary مع مشغل shell. يتم تخزين مجموعة من السنانير بجانبها (لمزيد من التفاصيل عنها ، انظر أدناه) . يشترك مشغل shell نفسه في أحداث معينة في واجهة برمجة تطبيقات Kubernetes ، والتي يقوم عليها بتشغيل الخطافات المقابلة.

كيف يفهم مشغل الصدفة أي السنانير التي يجب تشغيلها في ظل أي أحداث؟ يتم تمرير هذه المعلومات إلى مشغل shell بواسطة السنانير نفسها ويجعلها بسيطة للغاية.

الخطاف عبارة عن برنامج نصي Bash أو أي ملف قابل للتنفيذ آخر يدعم وسيطة واحدة - تكوين وإرجاع JSON ردًا عليه. يحدد الأخير الأشياء التي تهمه وأي الأحداث (لهذه الأشياء) يجب أن تتفاعل:



سأوضح تنفيذ مشغل shell لأحد الأمثلة لدينا - الكشف عن الأسرار للوصول إلى سجل خاص مع صور التطبيق. يتكون من مرحلتين.

الممارسة: 1. كتابة هوك


تتمثل الخطوة الأولى في المعالجة - --config ، مما يشير إلى أننا مهتمون بمساحات الأسماء ، وعلى وجه التحديد ، اللحظة التي تم إنشاؤها فيها:

 [[ $1 == "--config" ]] ; then cat << EOF { "onKubernetesEvent": [ { "kind": "namespace", "event": ["add"] } ] } EOF … 

كيف سيبدو المنطق؟ بسيط جدا جدا:

 else createdNamespace=$(jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH) kubectl create -n ${createdNamespace} -f - << EOF Kind: Secret ... EOF fi 

الخطوة الأولى هي معرفة مساحة الاسم التي تم إنشاؤها ، والخطوة الثانية هي إنشاء سر لمساحة الاسم هذه من خلال kubectl .

الممارسة: 2. وضع صورة


يبقى لنقل الخطاف الذي تم إنشاؤه إلى مشغل shell - كيفية القيام بذلك؟ يأتي المشغل shell نفسه في صورة صورة Docker ، لذلك تتمثل مهمتنا في إضافة ربط إلى دليل خاص في هذه الصورة:

 FROM flant/shell-operator:v1.0.0-beta.1 ADD my-handler.sh /hooks 

يبقى لجمعها ودفع:

 $ docker build -t registry.example.com/my-operator:v1 . $ docker push registry.example.com/my-operator:v1 

اللمسة الأخيرة هي تضمين الصورة في كتلة. للقيام بذلك ، اكتب النشر :

 apiVersion: extensions/v1beta1 kind: Deployment metadata: name: my-operator spec: template: spec: containers: - name: my-operator image: registry.example.com/my-operator:v1 # 1 serviceAccountName: my-operator # 2 

في ذلك ، تحتاج إلى الانتباه إلى نقطتين:

  1. إشارة إلى صورة تم إنشاؤها للتو ؛
  2. هذا هو أحد مكونات النظام التي (كحد أدنى) تحتاج إلى حقوق للاشتراك في الأحداث في Kubernetes وللكشف عن الأسرار بواسطة مساحة الاسم ، لذلك نقوم بإنشاء ServiceAccount (ومجموعة من القواعد) للربط.

النتيجة - لقد حللنا مشكلتنا بطريقة أصلية لـ Kubernetes ، وخلقنا مشغلًا لكشف الأسرار.

ميزات شل المشغل الأخرى


لتقييد الكائنات من نوع اختيارك الذي ستعمل به الخطاف ، يمكنك تصفيته عن طريق التصفية حسب تصنيفات معينة (أو باستخدام matchExpressions ):

 "onKubernetesEvent": [ { "selector": { "matchLabels": { "foo": "bar", }, "matchExpressions": [ { "key": "allow", "operation": "In", "values": ["wan", "warehouse"], }, ], } … } ] 

يتم توفير آلية إلغاء البيانات المكررة ، والتي - باستخدام مرشح jq - تسمح لك بتحويل JSONs كبيرة من الكائنات إلى كائنات صغيرة ، حيث تبقى تلك المعلمات فقط التي نريد مراقبة التغيير.

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

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

واثنين من الميزات الأخرى للقذيفة المشغل:

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

لتلخيص هذا الجزء من التقرير:



إضافات التثبيت


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

بدأنا العمل مع Kubernetes مع العديد من المجموعات ، الإضافة الوحيدة التي كانت Ingress. كان من الضروري وضعه في كل مجموعة بشكل مختلف ، وقمنا بعمل عدة تكوينات YAML لبيئات مختلفة: المعادن العارية ، AWS ...

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



لترتيب كل شيء ، بدأنا ببرنامج نصي ( install-ingress.sh ) ، والذي أخذ نوع الكتلة المراد نشرها كوسيطة ، وقمنا بإنشاء تكوين YAML المطلوب ووزعه على Kubernetes.

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

  • للعمل مع تكوينات YAML ، مطلوب محرك القالب (في المراحل الأولى من هذا البرنامج هو بسيط) ؛
  • مع زيادة عدد المجموعات ، جاءت الحاجة إلى التحديثات التلقائية (الحل الأول هو وضع برنامج نصي في Git وتحديثه باستخدام cron وتشغيله) ؛
  • مطلوب برنامج نصي مماثل لـ Prometheus ( install-prometheus.sh ) ، إلا أنه من الجدير بالملاحظة أنه يحتاج إلى مزيد من بيانات الإدخال ، وكذلك تخزينه (بطريقة جيدة ، مركزي وفي المجموعة) ، ويمكن إنشاء بعض البيانات (كلمات المرور) تلقائيًا :

  • كان خطر تكرار حدوث خطأ ما في عدد متزايد من الكتل ينمو باستمرار ، لذلك أدركنا أن المثبتين (أي سكربتين: Ingress و Prometheus) يحتاجون إلى إعداد المرحلة (عدة فروع في Git ، عدة cron لتحديثها في الفروع المقابلة: مجموعات مستقرة أو اختبار) ؛
  • أصبح من الصعب العمل مع kubectl apply ، لأنه ليس إعلانيا ويمكنه فقط إنشاء كائنات ، ولكن لا يتخذ قرارات بشأن حالته / kubectl apply ؛
  • افتقرنا إلى بعض الوظائف التي لم ندركها في ذلك الوقت:
    • السيطرة الكاملة على نتيجة تحديثات الكتلة ،
    • التحديد التلقائي لبعض المعلمات (إدخال نصوص التثبيت) استنادًا إلى البيانات التي يمكن الحصول عليها من الكتلة (الاكتشاف) ،
    • تطورها المنطقي في شكل اكتشاف مستمر.

لقد أدركنا كل هذه الخبرة المتراكمة في إطار مشروعنا الآخر - المشغل الإضافي .

الملحق مشغل


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

تضاف إلى السنانير قذيفة المشغل:

  • تخزين القيم
  • مخطط هيلم
  • المكون الذي يراقب مستودع القيم - وفي حالة حدوث أي تغييرات - يطلب من هيلم إعادة رسم المخطط.



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



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

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



يلبي هذا المخطط جميع متطلبات تثبيت الوظائف الإضافية التي تم الإعلان عنها أعلاه:

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

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



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

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

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

أشرطة الفيديو والشرائح


فيديو من الأداء (حوالي 50 دقيقة):



عرض التقرير:



PS


تقارير أخرى على مدونتنا:


قد تكون مهتمًا أيضًا بالمنشورات التالية:

Source: https://habr.com/ru/post/ar449096/


All Articles