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

أداة HPA في Kubernetes بعمق

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

أداة HPA في Kubernetes بعمق

يُعدّ Horizontal Pod Autoscaler (HPA) المحرّك الأساسي للتوسّع التفاعلي في Kubernetes. يعرف معظم المهندسين الأمر الاختصاري الذي يربطه باستخدام CPU، غير أن القليلين يفهمون كيف تعمل حلقة التحكم فعلياً، أو كيف يضبطون سلوك الاستقرار لتجنّب الاهتزاز في بيئات الإنتاج، أو كيف يقودونه بمقاييس على مستوى التطبيق لا يستطيع CPU التعبير عنها. يُغلق هذا الدرس تلك الثغرة.

آلية عمل حلقة تحكم HPA

يعمل متحكم HPA داخل kube-controller-manager ويستطلع واجهة برمجة المقاييس بفترة استطلاع قابلة للضبط (افتراضياً 15 ثانية، عبر --horizontal-pod-autoscaler-sync-period). في كل دورة يحسب عدد النسخ المطلوبة باستخدام معادلة النسبة:

desiredReplicas = ceil( currentReplicas * (currentMetricValue / desiredMetricValue) ) # مثال: 4 pods، استخدام CPU 80%، الهدف 50% desiredReplicas = ceil( 4 * (80 / 50) ) = ceil(6.4) = 7

تُقيَّد النتيجة الخام ضمن النطاق [minReplicas, maxReplicas]، وتمنع نافذتا استقرار حدوث الاهتزاز. يُبوَّب التوسّع للأعلى بنافذة استقرار توسّع (افتراضياً 0 ثانية — توسّع فوري)، بينما يستخدم التقليص نافذة أطول (افتراضياً 300 ثانية) تُجبر المتحكم على تتبّع القيمة القصوى من توصيات النسخ عبر تلك النافذة قبل حذف أي pod. هذا التباين مقصود: الاستجابة الفورية لارتفاع الحمل، والتحفّظ عند إزالة الطاقة.

مفهوم أساسي — دلالة نافذة الاستقرار: نافذة الاستقرار لا "تنتظر". يسجّل HPA كل توصية تصدر خلال النافذة ويأخذ أكثر القيم تحفظاً (الأقصى للتقليص، الأدنى للتوسّع). نافذة تقليص لمدة خمس دقائق تعني أن الحمل يجب أن ينخفض باتساق قبل حذف أي pods.

بيان HPA جاهز للإنتاج

توفّر واجهة autoscaling/v2 (مستقرة منذ Kubernetes 1.23) عدة مقاييس، وضبط السلوك، وإعداد التسامح. فيما يلي بيان واقعي لخدمة API عالية الإنتاجية:

apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: api-gateway namespace: production spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: api-gateway minReplicas: 6 maxReplicas: 120 metrics: # الإشارة الأساسية: CPU — حماية من حالات الاختناق - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 60 # الإشارة الثانوية: مقياس مخصص من Prometheus عبر المحوّل - type: Pods pods: metric: name: http_requests_in_flight target: type: AverageValue averageValue: "500" behavior: scaleUp: stabilizationWindowSeconds: 0 # استجابة فورية للارتفاعات المفاجئة policies: - type: Percent value: 100 # السماح بمضاعفة عدد النسخ… periodSeconds: 15 - type: Pods value: 10 # …لكن لا تضف أكثر من 10 pods كل 15 ثانية periodSeconds: 15 selectPolicy: Min # اختر السياسة الأكثر تحفظاً scaleDown: stabilizationWindowSeconds: 300 # 5 دقائق من الحمل المنخفض المتسق policies: - type: Percent value: 20 # أزل 20% كحد أقصى في الدقيقة periodSeconds: 60 selectPolicy: Max # الأقصى = الأكثر تحفظاً للتقليص
اضبط minReplicas فوق الصفر دائماً. توجيه Google SRE: احتفظ بعدد نسخ يغطي أسوأ حالة لوقت البدء الفارغ وتأخير مسبار الإقلاع. بالنسبة لخدمة تستغرق 30 ثانية للبدء، فإن pod واحد يتلقى الطلبات في ذروة حركة المرور سيتسبب في ارتفاع حاد في زمن الاستجابة قبل أن يصبح الpod الجديد جاهزاً.

سياسات السلوك بالتفصيل

يمنحك حقل behavior تحكماً دقيقاً في معدّل أحداث التوسيع. كل اتجاه يقبل قائمة من السياسات، وselectPolicy تحسم التعارض:

  • Min — اختر السياسة التي تنتج أصغر تغيير. استخدمها للتوسّع للأعلى حين تريد نمواً تحفّظياً.
  • Max — اختر السياسة التي تنتج أكبر تغيير. استخدمها للتقليص لتكون أكثر تحفظاً (احذف أقل pods).
  • Disabled — أوقف التوسيع في ذلك الاتجاه كلياً. مفيد أثناء تجميد النشر.

يتكامل نوعا السياسة Percent وPods جيداً. النمط الشائع: اسمح بالمضاعفة الفورية (Percent: 100) لكن ضع حداً أقصى 10 pods لكل نافذة حتى لا تطلب 50 عقدة جديدة في 15 ثانية وتتخطى حصتك السحابية.

المقاييس المخصصة عبر Prometheus Adapter

لا يخبرك CPU الخام بوقت تشبّع pods؛ بل يخبرك بوقت تشبّع خدمتك الفعلية. عمق طابور الانتظار، وعدد اتصالات WebSocket النشطة، والطلبات قيد التنفيذ، وضغط ذاكرة GPU — كلها إشارات أفضل لكثير من الأحمال. تُعرّض Kubernetes هذه الإشارات عبر واجهة custom.metrics.k8s.io التي يُنفّذها Prometheus Adapter.

قاعدة مجردة لمحوّل Prometheus تُظهر http_requests_in_flight من مقياس Prometheus:

# prometheus-adapter ConfigMap — قسم rules rules: - seriesQuery: 'http_requests_in_flight{namespace!="",pod!=""}' resources: overrides: namespace: {resource: "namespace"} pod: {resource: "pod"} name: matches: "^(.*)$" as: "${1}" metricsQuery: 'avg_over_time(http_requests_in_flight{<<.LabelMatchers>>}[2m])'

تحقّق من ظهور المقياس لمتحكم HPA:

kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/production/pods/*/http_requests_in_flight" \ | jq '.' # مخرجات متوقعة (مختصرة): { "kind": "MetricValueList", "items": [ { "describedObject": { "kind": "Pod", "name": "api-gateway-7d9f5-xkz4n" }, "metricName": "http_requests_in_flight", "value": "423" } ] }

المقاييس الخارجية للإشارات السحابية

تتعامل واجهة external.metrics.k8s.io مع المقاييس غير المرتبطة بأي كائن Kubernetes — عمق طابور SQS، و backlog اشتراك Pub/Sub، وتأخر مجموعة المستهلكين في Kafka. يُعرّضها المحوّل تحت type: External في بيان HPA:

metrics: - type: External external: metric: name: sqs_approx_number_of_messages_visible selector: matchLabels: queue: "order-processing" target: type: AverageValue averageValue: "50" # هدف 50 رسالة لكل pod
المقاييس الخارجية وعقد "لكل pod": يستخدم HPA قيمة AverageValue للمقاييس الخارجية، أي أنه يقسم المقياس الخام على عدد النسخ الحالية لحساب النسخ المطلوبة. عمق طابور 1000 مع هدف 50 يدفع HPA إلى 20 نسخة — لكن هذه الحسابات تصحّ فقط إذا كان كل pod قادراً على معالجة 50 رسالة بشكل متزامن. قم بمعايرة الأهداف مقابل إنتاجية المستهلك الفعلية في اختبارات الحمل قبل التفعيل في الإنتاج.

معامل التسامح وتذبذب المقاييس

يتجاهل HPA الانحرافات الصغيرة عن الهدف لتجنّب التوسيع الدقيق المستمر. التسامح الافتراضي 10% (قابل للضبط عالمياً عبر --horizontal-pod-autoscaler-tolerance). يعني ذلك أن التوسيع يُكبَت حين تكون النسبة currentMetric / desiredMetric ضمن النطاق [0.9, 1.1]. إذا تذبذب مقياسك طبيعياً بمقدار ±15% (مثل تشتت استطلاع Prometheus على نافذة قصيرة)، ستلاحظ ضجيجاً مستمراً في التوسيع. الحل: استخدام نافذة متوسط أطول في قاعدة المحوّل (مثلاً avg_over_time(...[5m]) بدلاً من القيمة اللحظية الخام) أو توسيع نافذة الاستقرار.

HPA مع KEDA للأنماط المتقدمة

يوسّع Kubernetes Event-Driven Autoscaling (KEDA) إمكانيات HPA ولا يحلّ محلّه. يُثبّت KEDA CRD باسم ScaledObject ويُسجّل نفسه كمزوّد مقاييس مخصصة وخارجية. الميزة العملية على HPA الخام هي دعم التوسيع إلى الصفر وأكثر من 50 محوّلاً مدمجاً (تأخر Kafka، وطول قائمة Redis، ومقياس Datadog، وجدول Cron). للخدمات الصغيرة المدفوعة بـ Kafka على نطاق واسع، أصبح KEDA هو المعيار الآن.

لا تشغّل HPA و kubectl scale يدوياً على نفس النشر في آنٍ واحد. سيتجاوز HPA تغييرك اليدوي فوراً في دورة المزامنة التالية. إذا احتجت إلى تثبيت عدد النسخ مؤقتاً (تجميد نشر، حادثة)، إما احذف HPA أو اضبط minReplicas == maxReplicas على العدد المطلوب.
Kubernetes HPA control loop — metrics sources and scaling flow Metrics Sources Resource Metrics CPU / Memory Custom Metrics Prometheus Adapter External Metrics SQS / Kafka / Redis Object Metrics Ingress RPS HPA Controller Ratio Calculation Stabilization Window Behavior Policies Deployment ReplicaSet scale min ≤ replicas ≤ max metrics update after scale event sync every 15 s (default)
حلقة تحكم HPA: تُغذّي المقاييس من أربعة مصادر المتحكم الذي يطبّق حسابات النسبة ونوافذ الاستقرار وسياسات السلوك قبل تعديل عدد نسخ النشر.

تشخيص HPA في الإنتاج

حين لا يحدث التوسيع كما هو متوقع، توفّر هذه الأوامر الصورة الكاملة:

# فحص حالة HPA الحالية والشروط ووقت آخر توسيع kubectl describe hpa api-gateway -n production # مراقبة كائن HPA مباشرة (يُظهر قيم المقاييس في الوقت الفعلي) kubectl get hpa api-gateway -n production -w # التحقق من إمكانية الوصول إلى واجهة المقاييس kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1 | jq '.resources[].name' # أحداث HPA (قرارات التوسيع والأخطاء مسجّلة هنا) kubectl get events -n production --field-selector reason=SuccessfulRescale --sort-by='.lastTimestamp' # أعلام kube-controller-manager ذات الصلة # --horizontal-pod-autoscaler-sync-period=15s # --horizontal-pod-autoscaler-downscale-stabilization=5m0s # --horizontal-pod-autoscaler-tolerance=0.1 # --horizontal-pod-autoscaler-cpu-initialization-period=5m0s # --horizontal-pod-autoscaler-initial-readiness-delay=30s

العلم --horizontal-pod-autoscaler-initial-readiness-delay مهم بشكل خاص: تُستبعد pods التي كانت جاهزة لأقل من هذه المدة من حساب متوسط CPU. يمنع هذا دفعة جديدة من الـ pods الباردة من تخفيض متوسط CPU المرصود بشكل مصطنع وتشغيل تقليص فوري قبل أن تدفأ الـ pods.

سلوك HPA مع مقاييس متعددة: عند تحديد أكثر من مقياس واحد، يحسب HPA عدد النسخ المطلوبة لكل مقياس بشكل مستقل ثم يأخذ الأقصى. يعني هذا أن أي مقياس واحد يطلب مزيداً من الطاقة سيقود التوسيع — وهو افتراضي تحفظي منطقي. لا يمكنك تغيير استراتيجية التجميع هذه في HPA القياسي؛ تمنحك ScaledObjects في KEDA تحكماً أكبر لتحقيق دلالات AND (وسّع فقط حين ترتفع كل المقاييس).