تخطيط السعة والتوسع التلقائي

التوسع التلقائي للعنقود وKarpenter

18 دقيقة الدرس 5 من 27

التوسع التلقائي للعنقود وKarpenter

يعمل كلٌّ من HPA وVPA داخل نطاق مجموعة العُقد الموجودة فحسب — يعيدان توزيع الحاويات ويضبطان طلبات الموارد، لكنهما لا يستطيعان إنشاء طاقة جديدة حين ينفد الـCPU أو الذاكرة المخصَّصة في العنقود. تقع تلك المسؤولية على طبقة التوسع التلقائي للعنقود. تتناول هذه الدرس المقاربتين الرئيسيتين على Kubernetes: Cluster Autoscaler (CA) العريق، وبديله الحديث من AWS المسمى Karpenter. نغطي أيضاً تكامل مثيلات Spot وتوحيد العُقد وأنماط الفشل الإنتاجية التي تفاجئ المهندسين في بيئات الإنتاج الكبيرة.

كيف يعمل Cluster Autoscaler

يراقب CA الحاويات ذات الحالة Unschedulable — أي الحاويات التي وضعها المجدول في حالة انتظار لعدم توفر طاقة كافية على أيٍّ من العُقد. حين يرصد CA مثل هذه الحاوية، يحاكي ما إذا كانت إضافة عقدة من مجموعة ASG المُكوَّنة ستُتيح جدولتها. إذا كانت الإجابة نعم، يطلق CA عملية توسع في ASG. على مسار التقليص، يفحص CA دورياً ما إذا كان يمكن إعادة توزيع حاويات أي عقدة على بقية العنقود؛ إذا أمكن ذلك، يُقيِّد العقدة ويُصرِّف حاوياتها مع احترام PodDisruptionBudgets، ثم يطلق تقليص ASG.

معاملات الضبط المهمة على نطاق الإنتاج:

  • --scale-down-utilization-threshold (الافتراضي 0.5) — تُعدّ العقدة ضعيفة الاستخدام إذا كان طلب الـCPU والذاكرة معاً أقل من هذه النسبة. رفعها إلى 0.7 على العناقيد الحساسة للتكلفة يُسرِّع التوحيد لكنه يُخاطر بالاضطراب عند الأحمال المتذبذبة.
  • --scale-down-delay-after-add (الافتراضي 10m) — المدة بعد حدث التوسع قبل إعادة تقييم التقليص. إن كانت قصيرة جداً، ستحدث تذبذبات متكررة؛ 15–20 دقيقة أكثر أماناً للأحمال ذات الأشكال غير المنتظمة.
  • --max-node-provision-time (الافتراضي 15m) — يتخلى CA عن مجموعة العُقد إذا لم تصبح العقدة جاهزة خلال هذه المهلة. مع مثيلات Spot، اضبطها على 8–10m للإخفاق السريع والتحول لمجموعة أخرى.
  • --balance-similar-node-groups — ضروري لنشر متعدد مناطق التوفر: يجبر CA على التوسع بالتساوي بين المناطق بدلاً من ملء منطقة واحدة أولاً.
نمط مضاد شائع — الموارد المطلوبة بإفراط: يتوسع CA بناءً على الموارد المطلوبة لا المستهلكة فعلياً. الحاويات التي تطلب 2 vCPU لكنها تستخدم 0.3 فقط تملأ العنقود على الورق بينما الاستخدام الحقيقي لا يتجاوز 15%. عالج هذا من المصدر بضبط الحجم عبر VPA قبل أن تثق في قرارات CA.

Karpenter: المقاربة الحديثة

يتبنى Karpenter (مشروع CNCF، أصيل لـAWS) معمارية مختلفة جذرياً. بدلاً من إدارة مجموعات ASG، يستدعي Karpenter مباشرةً EC2 RunInstances API لتوفير مثيلات فردية. بدلاً من مجموعات عُقد بأحجام ثابتة، تُعرِّف NodePools تصف القيود — عائلات المثيلات والمعماريات وأنواع الطاقة — ويختار Karpenter المثيل الأمثل في الوقت الفعلي عبر تحزيم الحاويات المعلَّقة مقابل أسعار EC2 الحية وتوافرها.

تنخفض زمن الإمداد من 3–5 دقائق المعتادة لـCA (إطلاق ASG + تمهيد AMI + تسجيل kubelet) إلى أقل من 60 ثانية لمعظم أنواع المثيلات. والأهم أن Karpenter يختار الحجم المناسب تماماً لدفعة الحاويات المعلَّقة دون الاضطرار للتقريب صعوداً إلى الحجم التالي في ASG.

# تثبيت Karpenter عبر Helm (EKS v1.x) -- كوِّن IRSA للتحكم أولاً helm upgrade --install karpenter oci://public.ecr.aws/karpenter/karpenter \ --version "1.0.6" \ --namespace "kube-system" \ --set "settings.clusterName=${CLUSTER_NAME}" \ --set "settings.interruptionQueue=${KARPENTER_INTERRUPT_QUEUE}" \ --set controller.resources.requests.cpu=1 \ --set controller.resources.requests.memory=1Gi \ --wait --- # NodePool: يحدد ما يجوز لـKarpenter توفيره apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: general spec: template: spec: nodeClassRef: apiVersion: karpenter.k8s.aws/v1 kind: EC2NodeClass name: default requirements: - key: karpenter.sh/capacity-type operator: In values: ["spot", "on-demand"] - key: node.kubernetes.io/instance-type operator: In values: ["m5.xlarge","m5.2xlarge","m6i.xlarge","m6i.2xlarge","c5.2xlarge"] - key: topology.kubernetes.io/zone operator: In values: ["us-east-1a","us-east-1b","us-east-1c"] disruption: consolidationPolicy: WhenEmptyOrUnderutilized consolidateAfter: 30s limits: cpu: "1000" memory: 4000Gi --- # EC2NodeClass: التفاصيل الخاصة بـAWS apiVersion: karpenter.k8s.aws/v1 kind: EC2NodeClass metadata: name: default spec: amiSelectorTerms: - alias: al2023@latest subnetSelectorTerms: - tags: karpenter.sh/discovery: "${CLUSTER_NAME}" securityGroupSelectorTerms: - tags: karpenter.sh/discovery: "${CLUSTER_NAME}" role: "KarpenterNodeRole-${CLUSTER_NAME}" instanceStorePolicy: RAID0

تدفق توفير العُقد

يوضح المخطط أدناه مسار التوفير عبر CA (من خلال ASG) مقارنةً بمسار Karpenter المباشر إلى EC2. فهم هذا يُساعدك على الاستدلال حول ميزانيات زمن الاستجابة وأنماط الفشل عند ارتفاعات الحركة.

Cluster Autoscaler vs Karpenter provisioning flow Cluster Autoscaler Path Pending Pod CA Controller (simulation loop) ASG Scale-Out EC2 Launch Template (fixed sizes) Node Ready (~3-5 min) Pod Scheduled (latency ~4 min) Karpenter Path Pending Pod Karpenter Controller (binpack + price) EC2 RunInstances (optimal size) Node Ready (~45-60 s) Pod Scheduled (latency ~1 min)
تدفق توفير العُقد: Cluster Autoscaler (عبر ASG) مقابل Karpenter (مباشرةً لـEC2 API). يحذف Karpenter طبقة ASG ويختار حجم المثيل الأمثل لكل حمل عمل.

استراتيجية مثيلات Spot

تشغيل غالبية الأحمال عديمة الحالة والمتسامحة مع الانقطاع على مثيلات Spot يُخفِّض تكاليف EC2 بنسبة 60–80%. المقاربة الصحيحة هي التنويع: توزيع الطلبات على عائلات وأحجام متعددة حتى لا يُفرِغ انقطاع مجموعة Spot واحدة طاقتك. يتعامل Karpenter مع هذا أصلياً عبر تقييم أنواع مثيلات متعددة في كل NodePool. مع CA تحقق التنويع بتعريف ASGs متعددة وضبط --expander=least-waste.

التعامل الرشيق مع انقطاعات Spot يتطلب أمرين:

  1. معالجة إشعار الانقطاع — يُرسل EC2 تحذيراً قبل دقيقتين من استرداد المثيل. يتكامل Karpenter مع طابور SQS لاستقبال هذه الأحداث وتقييد العقدة وتصريفها قبل انتهاء النافذة. بدون هذا ستتلقى حاوياتك SIGKILL دون تصريف.
  2. حدود انقطاع الحاوية (PDB) — كل نشر إنتاجي يحتاج PDB حتى لا تُلغي عملية التصريف أكثر من نسبة آمنة من النسخ في وقت واحد. التكوين الشائع هو minAvailable: 50% للخدمات عديمة الحالة.
# NodePool منفصل لأحمال Spot للمهام الدفعية apiVersion: karpenter.sh/v1 kind: NodePool metadata: name: spot-batch spec: template: spec: nodeClassRef: apiVersion: karpenter.k8s.aws/v1 kind: EC2NodeClass name: default requirements: - key: karpenter.sh/capacity-type operator: In values: ["spot"] - key: node.kubernetes.io/instance-type operator: In values: ["m5.2xlarge","m5.4xlarge","m6i.2xlarge","m6i.4xlarge", "c5.4xlarge","c5a.4xlarge","r5.2xlarge","r6i.2xlarge"] taints: - key: workload-type value: batch effect: NoSchedule disruption: consolidationPolicy: WhenEmpty consolidateAfter: 60s limits: cpu: "500" --- # PodDisruptionBudget -- حماية الخدمة أثناء تصريف العقدة apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: api-service-pdb namespace: production spec: selector: matchLabels: app: api-service minAvailable: "50%"

توحيد العُقد (Consolidation)

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

يعمل محرك توحيد Karpenter (consolidationPolicy: WhenEmptyOrUnderutilized) بشكل مستمر. يقيِّم كل عقدة لمعرفة أين يمكن إعادة جدولة حاوياتها. حين يجد حركة توحيد صالحة — إفراغ عقدة كلياً أو استبدالها بمثيل أصغر/أرخص — ينفذ Karpenter دورة التصريف ثم التوفير. هذا أكثر عدوانية بكثير من تقليص CA الذي لا يُخلي إلا العُقد التي أصبحت فارغة تلقائياً.

إعدادات التوحيد الإنتاجية: استخدم WhenEmptyOrUnderutilized مع consolidateAfter: 30s للعناقيد ذات الخدمات الصغيرة عديمة الحالة — يحافظ على التكاليف ضيقة. للأحمال ذات الحالة (قواعد البيانات، وسطاء Kafka) استخدم WhenEmpty فقط، أو أضف تعليق karpenter.sh/do-not-disrupt: "true" لاستثناء تلك الحاويات من التوحيد.

أنماط الفشل الإنتاجية

عدة أنماط فشل تظهر بانتظام في نشر Karpenter على نطاق واسع:

  • بلوغ حدود NodePool: حين يُستنفد limits.cpu أو limits.memory، يتوقف Karpenter عن التوفير وتبقى الحاويات معلَّقة إلى أجل غير مسمى. راقب karpenter_nodepools_limit_usage_percentage وأطلق تنبيهاً قبل 90%.
  • فشل التمهيد بسبب انجراف AMI: إذا أصدر al2023@latest إصداراً معيباً، فكل عقدة جديدة تفشل في الانضمام للعنقود. ثبِّت نسخة محددة أثناء الأعطال: al2023@v20250501. تابع karpenter_nodes_total{phase="NotReady"} في منظومة التنبيه.
  • تذبذب التوحيد: إذا كان HPA متسرعاً في التقليص والتوحيد عدوانياً، قد تدخل في حلقة: HPA يُقلِّص -> Karpenter يوحِّد -> ارتفاع مفاجئ -> HPA يتوسع -> Karpenter يوفِّر. خفِّف هذا بضبط نوافذ تثبيت التقليص في KEDA أو HPA أطول من نافذة التوحيد.
  • PDB يعيق التصريف: إذا كان minAvailable في PDB مساوياً للعدد الكلي من النسخ (خطأ شائع)، يُحجب الإخلاء بشكل دائم ويتوقف التوحيد. افحص PDBs بانتظام بـkubectl get pdb -A.
الاختيار بين CA وKarpenter: إذا كنت على EKS وتشغِّل أحمالاً عديمة الحالة في الغالب، فـKarpenter هو الخيار الواضح للعناقيد الجديدة — زمن استجابة أقل، تحسين أفضل للتكلفة، وعمليات أبسط. للعناقيد على GKE أو AKS أو التي لها متطلبات امتثال صارمة، تظل أدوات التوسع الأصيلة للسحابة مناسبة. لا تخلط بين CA وKarpenter يديران مجموعات عُقد متداخلة — اختر واحداً لكل مجموعة.