تسع نصائح أداء Kubernetes

تسع نصائح أداء Kubernetes

أهلاً بكم! اسمي Oleg Sidorenkov ، أعمل في DomClick كقائد لفريق البنية التحتية. لقد استخدمنا Cube للبيع منذ أكثر من ثلاث سنوات ، وخلال هذا الوقت مررنا بالعديد من اللحظات المثيرة للاهتمام معه. سأخبرك اليوم كيف يمكنك ، من خلال النهج الصحيح ، تحقيق المزيد من الأداء من Vanilla Kubernetes لمجموعتك. جاهز استعد ابدأ!

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

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

1. تتبع موارد الفريق والتطبيق

تسع نصائح أداء Kubernetes

من أكثر الأساليب المبتذلة والفعالة إدخال الطلبات / الحدود. افصل بين التطبيقات حسب مساحات الأسماء ومساحات الأسماء بواسطة فرق التطوير. قم بتعيين التطبيق قبل نشر القيم لاستهلاك وقت المعالج والذاكرة والتخزين المؤقت.

resources:
   requests:
     memory: 2Gi
     cpu: 250m
   limits:
     memory: 4Gi
     cpu: 500m

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

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

➜  ~ kubectl describe limitranges --namespace ops
Name:       limit-range
Namespace:  ops
Type        Resource           Min   Max   Default Request  Default Limit  Max Limit/Request Ratio
----        --------           ---   ---   ---------------  -------------  -----------------------
Container   cpu                50m   10    100m             100m           2
Container   ephemeral-storage  12Mi  8Gi   128Mi            4Gi            -
Container   memory             64Mi  40Gi  128Mi            128Mi          2

تذكر أن تحد من موارد مساحة الاسم بحيث لا يمكن لأمر واحد أن يأخذ جميع موارد الكتلة:

➜  ~ kubectl describe resourcequotas --namespace ops
Name:                   resource-quota
Namespace:              ops
Resource                Used          Hard
--------                ----          ----
limits.cpu              77250m        80
limits.memory           124814367488  150Gi
pods                    31            45
requests.cpu            53850m        80
requests.memory         75613234944   150Gi
services                26            50
services.loadbalancers  0             0
services.nodeports      0             0

كما ترى من الوصف resourcequotas، إذا كان الأمر ops يريد نشر البودات التي ستستهلك 10 وحدة معالجة مركزية أخرى ، فلن يسمح المجدول بتنفيذها وسيصدر خطأ:

Error creating: pods "nginx-proxy-9967d8d78-nh4fs" is forbidden: exceeded quota: resource-quota, requested: limits.cpu=5,requests.cpu=5, used: limits.cpu=77250m,requests.cpu=53850m, limited: limits.cpu=10,requests.cpu=10

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

2. اختر أفضل تخزين للملفات

تسع نصائح أداء Kubernetes

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

  • استخدم محركات أقراص الحالة الصلبة عالية الأداء أو قم بالتبديل إلى NVMe (إذا كنت تدير أجهزتك الخاصة).

  • تقليل مستوى التسجيل.

  • قم بالموازنة "الذكية" للقرون التي تغتصب القرص (podAntiAffinity).

توضح لقطة الشاشة أعلاه ما يحدث تحت وحدة تحكم nginx-ingress مع قرص عند تمكين تسجيل access_logs (حوالي 12 ألف سجل / ثانية). مثل هذه الحالة ، بالطبع ، يمكن أن تؤدي إلى تدهور جميع التطبيقات على هذه العقدة.

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

3. بناء صور محسنة

تسع نصائح أداء Kubernetes

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

التحسين يعني أن الصور:

  • تحتوي على تطبيق واحد فقط أو تؤدي وظيفة واحدة فقط ؛

  • صغر الحجم ، لأن الصور الكبيرة تنتقل بشكل أسوأ عبر الشبكة ؛

  • لديك نقاط نهاية تتعلق بالصحة والجاهزية يمكن لبرنامج Kubernetes استخدامها لاتخاذ إجراء في حالة التعطل ؛

  • استخدام أنظمة تشغيل صديقة للحاويات (مثل Alpine أو CoreOS) أكثر مقاومة لأخطاء التكوين ؛

  • استخدام إصدارات متعددة المراحل بحيث يمكنك فقط نشر التطبيقات المترجمة وليس المصادر المصاحبة.

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

  1. انخفاض حمل الشبكة على الكتلة بأكملها.

  2. تقليل وقت بدء تشغيل الحاوية.

  3. حجم أصغر لسجل Docker بالكامل.

4. استخدم ذاكرة التخزين المؤقت DNS

تسع نصائح أداء Kubernetes

إذا تحدثنا عن أحمال عالية ، فبدون ضبط نظام DNS للمجموعة ، تكون الحياة رديئة جدًا. ذات مرة ، دعم مطورو Kubernetes حل kube-dns. تم تنفيذه أيضًا في بلدنا ، لكن هذا البرنامج لم يتم ضبطه بشكل خاص ولم يقدم الأداء المطلوب ، على الرغم من أن المهمة ، على ما يبدو ، بسيطة. ثم ظهرت النوى ، التي تحولنا إليها ولم نكن نعرف الحزن ، وأصبحت فيما بعد خدمة DNS الافتراضية في K8s. في مرحلة ما ، قمنا بتنمية ما يصل إلى 40 ألف دورة في الثانية لنظام DNS ، ولم يكن هذا الحل أيضًا كافيًا. ولكن ، لصدفة الحظ ، خرج Nodelocaldns ، ويعرف أيضًا باسم ذاكرة التخزين المؤقت المحلية للعقدة ، ويعرف أيضًا باسم NodeLocal DNSCache.

لماذا نستخدمها؟ هناك خطأ في Linux kernel يؤدي ، عند الوصول المتعدد من خلال conntrack NAT عبر UDP ، إلى حالة سباق للكتابة إلى جداول conntrack ، ويتم فقدان جزء من حركة المرور عبر NAT (كل رحلة عبر الخدمة هي NAT). يحل Nodelocaldns هذه المشكلة عن طريق التخلص من NAT والترقية إلى اتصال TCP إلى DNS المنبع ، بالإضافة إلى التخزين المؤقت لاستعلامات DNS الأولية محليًا (بما في ذلك ذاكرة تخزين مؤقت سلبية قصيرة مدتها 5 ثوانٍ).

5. حجم القرون أفقيًا وعموديًا تلقائيًا

تسع نصائح أداء Kubernetes

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

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

تسع نصائح أداء Kubernetesالصورة مأخوذة من https://levelup.gitconnected.com/kubernetes-autoscaling-101-cluster-autoscaler-horizontal-pod-autoscaler-and-vertical-pod-2a441d9ad231

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

على سبيل المثال ، فيما يلي إعدادات البود النموذجية:

resources:
   requests:
     memory: 250Mi
     cpu: 200m
   limits:
     memory: 500Mi
     cpu: 350m

يحدد محرك التوصية أن تطبيقك يحتاج إلى 300m CPU و 500Mi ليعمل بشكل صحيح. سوف تحصل على هذه الإعدادات:

resources:
   requests:
     memory: 500Mi
     cpu: 300m
   limits:
     memory: 1000Mi
     cpu: 525m

كما هو مذكور أعلاه ، يعد هذا قياسًا نسبيًا بناءً على نسبة الطلبات / الحدود في البيان:

  • وحدة المعالجة المركزية: 200 م → 300 م: نسبة 1: 1.75 ؛

  • الذاكرة: 250Mi → 500Mi: نسبة 1: 2.

مع الاحترام ل HPA، ثم آلية العمل أكثر شفافية. يتم تعيين العتبات للمقاييس مثل المعالج والذاكرة ، وإذا تجاوز متوسط ​​جميع النسخ المتماثلة الحد ، فسيتم قياس التطبيق بمقدار +1 pod حتى تنخفض القيمة إلى ما دون الحد ، أو حتى يتم الوصول إلى الحد الأقصى لعدد النسخ المتماثلة.

تسع نصائح أداء Kubernetesالصورة مأخوذة من https://levelup.gitconnected.com/kubernetes-autoscaling-101-cluster-autoscaler-horizontal-pod-autoscaler-and-vertical-pod-2a441d9ad231

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

6. لا تنسى تقارب العقدة وتقارب القرنة

تسع نصائح أداء Kubernetes

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

إذا كانت لديك عُقد مناسبة لعمليات الحوسبة المكثفة ، فعندئذٍ لتحقيق أقصى قدر من الكفاءة ، من الأفضل ربط التطبيقات بالعقد المناسبة. للقيام بذلك ، استخدم nodeSelector مع تسمية العقدة.

لنفترض أن لديك عقدتين: واحدة بها CPUType=HIGHFREQ وعدد كبير من النوى السريعة ، وآخر به MemoryType=HIGHMEMORY ذاكرة أكبر وأداء أسرع. أسهل طريقة هي تعيين نشر pod لعقدة HIGHFREQعن طريق الإضافة إلى القسم spec محدد مثل هذا:

…
nodeSelector:
	CPUType: HIGHFREQ

الطريقة الأكثر تكلفة والمحددة للقيام بذلك هي الاستخدام nodeAffinity في مجال affinity قسم spec. هناك خياران:

  • requiredDuringSchedulingIgnoredDuringExecution: الإعداد الصعب (لن يقوم المجدول إلا بنشر البودات على عقد محددة (وليس في أي مكان آخر)) ؛

  • preferredDuringSchedulingIgnoredDuringExecution: الإعداد الناعم (سيحاول المجدول النشر إلى عقد محددة ، وإذا فشل ، فسيحاول النشر إلى العقدة المتاحة التالية).

يمكنك تحديد صيغة معينة لإدارة تسميات العقد ، على سبيل المثال ، In, NotIn, Exists, DoesNotExist, Gt أو Lt. ومع ذلك ، تذكر أن الأساليب المعقدة في قوائم طويلة من الملصقات سوف تبطئ عملية اتخاذ القرار في المواقف الحرجة. بمعنى آخر ، لا تبالغ في التعقيد.

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

В podAffinity الحقول affinity قسم spec تتوفر نفس الحقول كما في حالة nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution и preferredDuringSchedulingIgnoredDuringExecution. الاختلاف الوحيد هو ذلك matchExpressions سوف يربط القرون بالعقدة التي تقوم بالفعل بتشغيل حجرة بهذا التصنيف.

المزيد يقدم Kubernetes حقلاً podAntiAffinity، والتي ، على النقيض من ذلك ، لا تربط جرابًا إلى عقدة بقرون معينة.

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

7. التلطيخ والتسامح

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

آلية التشويش - قواعد الحظر - تساعد في ذلك. على سبيل المثال ، يمكنك منع عقد معينة من تشغيل الكبسولات في سيناريوهات معينة. لتطبيق التلطيخ على عقدة معينة ، استخدم الخيار taint في kubectl. حدد المفتاح والقيمة ثم تلطيخ مثل NoSchedule أو NoExecute:

$ kubectl taint nodes node10 node-role.kubernetes.io/ingress=true:NoSchedule

وتجدر الإشارة أيضًا إلى أن آلية التلوث تدعم ثلاثة تأثيرات رئيسية: NoSchedule, NoExecute и PreferNoSchedule.

  • NoSchedule يعني ذلك حتى يكون هناك إدخال مقابل في مواصفات البود tolerations، لا يمكن نشره في العقدة (في هذا المثال node10).

  • PreferNoSchedule - نسخة مبسطة NoSchedule. في هذه الحالة ، سيحاول المجدول عدم تخصيص القرون التي لا تحتوي على إدخال مطابق. tolerations لكل عقدة ، لكن هذا ليس حداً صارماً. إذا لم تكن هناك موارد في الكتلة ، فستبدأ البودات في الانتشار على هذه العقدة.

  • NoExecute - يؤدي هذا التأثير إلى إخلاء فوري للقرون التي ليس لها إدخال مطابق tolerations.

من الغريب أن هذا السلوك يمكن التراجع عنه باستخدام آلية التسامح. يكون هذا مناسبًا عندما تكون هناك عقدة "محظورة" وتحتاج فقط إلى وضع خدمات البنية التحتية عليها. كيف افعلها؟ اسمح فقط لتلك القرون التي يوجد بها تفاوت مناسب.

إليك ما ستبدو عليه مواصفات الكبسولة:

spec:
   tolerations:
     - key: "node-role.kubernetes.io/ingress"
        operator: "Equal"
        value: "true"
        effect: "NoSchedule"

هذا لا يعني أنه خلال إعادة النشر التالية ، سيضرب الكبسولة هذه العقدة بالضبط ، وهذه ليست آلية Node Affinity و nodeSelector. ولكن من خلال الجمع بين العديد من الميزات ، يمكنك تحقيق إعداد جدولة مرن للغاية.

8. تعيين أولوية نشر جراب

فقط لأنك قمت بتكوين روابط pod-to-node لا يعني أنه يجب معاملة جميع القرون بنفس الأولوية. على سبيل المثال ، قد ترغب في نشر بعض Pods قبل غيرها.

تقدم Kubernetes طرقًا مختلفة لتعيين أولوية Pod و Preemption. يتكون الإعداد من عدة أجزاء: الكائن PriorityClass والأوصاف الميدانية priorityClassName في مواصفات الكبسولة. فكر في مثال:

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 99999
globalDefault: false
description: "This priority class should be used for very important pods only"

نخلق PriorityClass، أعطه اسمًا ووصفًا وقيمة. الأعلى value، كلما زادت الأولوية. يمكن أن تكون القيمة أي عدد صحيح 32 بت أقل من أو يساوي 1،000،000،000. يتم حجز القيم الأعلى لأقراص النظام ذات المهام الحرجة ، والتي لا يمكن استباقها عادةً. سيحدث الإخلاء فقط إذا لم يكن للقرن ذي الأولوية العالية مكان للالتفاف ، فسيتم إخلاء بعض القرون من عقدة معينة. إذا كانت هذه الآلية جامدة للغاية بالنسبة لك ، فيمكنك إضافة الخيار preemptionPolicy: Never، وبعد ذلك لن يكون هناك أي إجراءات وقائية ، سيكون البود هو الأول في قائمة الانتظار وينتظر المجدول للعثور على موارد مجانية له.

بعد ذلك ، نقوم بإنشاء جراب نحدد فيه الاسم priorityClassName:

apiVersion: v1
kind: Pod
metadata:
  name: static-web
  labels:
    role: myrole
 spec:
  containers:
    - name: web
      image: nginx
      ports:
        - name: web
          containerPort: 80
          protocol: TCP
  priorityClassName: high-priority
          

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

وبالتالي ، إذا لزم الأمر ، يمكنك زيادة كفاءة نشر الخدمات الهامة ، مثل nginx-ingress-controller ، و coredns ، وما إلى ذلك.

9. تحسين مجموعة ETCD الخاصة بك

تسع نصائح أداء Kubernetes

يمكن أن يسمى ETCD دماغ الكتلة بأكملها. من المهم جدًا الحفاظ على تشغيل قاعدة البيانات هذه على مستوى عالٍ ، لأن سرعة العمليات في "Cube" تعتمد عليها. معيار إلى حد ما ، وفي نفس الوقت ، سيكون الحل الجيد هو الاحتفاظ بمجموعة ETCD على العقد الرئيسية من أجل الحصول على حد أدنى من التأخير في kube-apiserver. إذا لم يكن ذلك ممكنًا ، فضع ETCD في أقرب مكان ممكن ، مع عرض نطاق ترددي جيد بين المشاركين. انتبه أيضًا إلى عدد العقد من ETCD التي يمكن أن تسقط دون الإضرار بالعنقود.

تسع نصائح أداء Kubernetes

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

إذا تحدثنا عن إعداد الخدمة ، فهناك بعض التوصيات:

  1. احصل على أجهزة جيدة ، بناءً على حجم الكتلة (يمكنك القراءة هنا).

  2. قم بتعديل بعض المعلمات إذا قمت بنشر مجموعة بين زوج من DC أو تترك الشبكة والأقراص الخاصة بك الكثير مما هو مرغوب فيه (يمكنك القراءة هنا).

اختتام

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

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