Kubernetes · إدارة الموارد

طلبات وحدود Kubernetes: سعة بلا اختناق أو OOM

منهج إنتاجي لتحديد طلبات CPU والذاكرة في Kubernetes واختيار الحدود وحماية العقد والحفاظ على إشارات HPA صحيحة.

المسار المرتبطالسحابة وDevOps وKubernetes
رسم تصوري لبنية Kubernetes يوضح حاويات تحصل على سعة CPU وذاكرة محسوبة مع إبقاء هامش العقدة وخطر الحمل الزائد ظاهرين.
تصوّر بصري للفكرة — يتبعه شرح ومخطط تنفيذي داخل المقال.

طلبات وحدود موارد Kubernetes هي عقد سعة وليست أسطر YAML للزينة. تخبر Requests المجدول بكمية CPU والذاكرة التي يجب أن يعتمد عليها الـPod، بينما تحدد Limits أين يقيّد Runtime الاستهلاك. الطلبات السيئة تنتج توزيعًا ضعيفًا وإشارات Autoscaling مضللة، والحدود السيئة تحوّل الضغط إلى CPU throttling أو OOM kills. المنهج الإنتاجي الآمن هو قياس حمل ممثل، واستخراج Requests من توزيع الطلب وأهداف الخدمة، والتعامل مع حدود CPU والذاكرة بصورة مختلفة، ثم إغلاق الدورة عبر HPA وVPA وسياسات Namespace وتنبيهات التشبع.

هذا الدليل مبني على وثائق Kubernetes الرسمية التي راجعتها في ٥ أكتوبر ٢٠٢٦. المثال الرقمي توضيحي وليس معادلة عامة. شكل الحمل والـRuntime وهدف الاستجابة ونوع العقدة وسياسة الفشل هي التي تحدد القيم الفعلية.

Requests وLimits يجيبان عن سؤالين مختلفين

يجيب CPU أو Memory Request عن سؤال: ما السعة التي يجب أن تكون متاحة قبل وضع هذا الـPod؟ يقارن kube-scheduler مجموع الطلبات بالسعة القابلة للتخصيص في العقد المرشحة. لا يقرر اعتمادًا على انخفاض الاستخدام اللحظي. قد تبدو العقدة فارغة وترفض Pod لأن سعتها المطلوبة ملتزمة أصلًا. يحمي هذا السلوك العنقود عندما تصل عدة أحمال إلى الذروة معًا.

أما Limit فيجيب عن سؤال أثناء التشغيل. في Linux يمرر kubelet القيم عبر Container Runtime إلى cgroups. تُفرض حدود CPU بالـthrottling، فتنتظر العملية بعد استهلاك زمن CPU المسموح. أما حدود الذاكرة فتعمل بصورة تفاعلية: عند تجاوز نطاق cgroup وتحت الضغط قد ينهي Kernel العملية، وقد تظهر الحالة `OOMKilled`. الآليتان ليستا نوعًا واحدًا من الحماية.

يمكن للحاوية تجاوز Request عندما توجد سعة فارغة. لذلك فالطلب ليس سقفًا؛ بل وعد توزيع ووزن وقت التنافس. وإذا وضعت Limit بلا Request ولم تضف سياسة Admission قيمة افتراضية، ينسخ Kubernetes الحد ليصبح طلبًا. قد تحجز هذه الراحة سعة أكبر كثيرًا مما قصدت.

ابدأ من توزيع الحمل لا من المتوسط

يخفي المتوسط اليومي القفزات وCold Starts وGarbage Collection والـEndpoints المختلفة في تكلفتها. اجمع لكل حاوية استخدام CPU وزمن throttling وMemory working set وسبب إعادة التشغيل والLatency والThroughput وQueue depth خلال الحمل العادي والذروة والنشر والوظائف الخلفية. افصل App Container عن Sidecars لأن الطلب وإشارات التوسع مختلفة.

استخدم نافذة ممثلة وPercentiles، لكن لا تحوّل Percentile واحدة آليًا إلى Request. تحتاج API حساسة للزمن CPU Request يمنحها وزن تنافس يكفي لتحقيق هدف الاستجابة عند ضغط العقد المشتركة. قد يقبل Batch Worker ضمان CPU أقل إذا بقي عمر الطابور ضمن هدفه. تحتاج الذاكرة هامشًا للقمم المعروفة لأن خطأها ليس Request أبطأ؛ بل قد يكون Restart للعملية.

صنّف الحمل قبل التحجيم:

- تستفيد الخدمات الثابتة من Requests قريبة من الطلب المستمر المرصود مع هامش مختبر. - قد تستخدم APIs المتذبذبة خط أساس أقل مع HPA يقوده Signal ذو معنى، مع Min Replicas وCPU كافيين لتغطية تأخر التوسع. - تحتاج Runtimes كثيفة الذاكرة إلى حساب Heap وNative memory وCaches والVolumes الموجودة في الذاكرة. - يجب أن تطلب Jobs ما تحتاجه مهمة متزامنة واحدة، وأن تحدّ Concurrency عند Queue بدل إطلاق Pods كثيرة تفترض السعة الفارغة نفسها.

لا تعتمد على تشغيل Staging هادئ واحد. أعد Payloads وConcurrency وDependencies شبيهة بالإنتاج، وكرر القياس بعد تحديث Runtime أو المكتبات.

عامل حدود CPU والذاكرة بصورة مختلفة

CPU مورد قابل للضغط: يبطئ التنافس العمل، ويفرض CPU Limit سقف زمن صلبًا بالـthrottling. قد يسبب حد ضيق قفزات Latency حتى لو كانت في العقدة Cores غير مستخدمة، لأن الحاوية لا تستطيع استعارتها فوق حصتها. لذلك حد CPU قرار منتج لا مجرد ضبط تكلفة.

اختبر للخدمة الحساسة للزمن هل تفرض المنصة CPU Limit أصلًا. قد تضع السياسة المعقولة CPU Request للتوزيع ووزن التنافس، وتحذف الحد عندما تسمح الحوكمة، وتضبط استهلاك Namespace الكامل بالحصص. وإذا كان الحد إلزاميًا فاترك Burst headroom مقاسًا ونبّه على throttled seconds بالنسبة للاستخدام. لا تستنتج من انخفاض المتوسط أن throttling غير مؤذٍ.

الذاكرة غير قابلة للضغط بالطريقة نفسها. ضع Memory Request يمثل الطلب الطبيعي الملتزم، وحدًا فوق القمم المرصودة مع هامش يبرره الـRuntime وقابلية إعادة التشغيل. اضبط Heap أو Cache داخل التطبيق تحت Container Limit حتى تترك مكانًا لـNative allocations والذاكرة التي يحسبها Kernel.

تذكّر أن `emptyDir` الموجود في الذاكرة يدخل في Memory usage. تحذر وثائق Kubernetes من أن Volume غير محدود قد يستهلك حد ذاكرة الـPod، أو ذاكرة العقدة عند غياب الحد. ضع `sizeLimit`، وأدخله في الميزانية، ولا تستخدم tmpfs كـHeap ثانية مخفية.

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

استخدم QoS كنتيجة Eviction لا كشعار

يصنف Kubernetes الـPods إلى `Guaranteed` و`Burstable` و`BestEffort` من Requests وLimits. عند ضغط العقدة يؤثر التصنيف في ترتيب الإخلاء: يُنظر إلى BestEffort قبل Burstable، وGuaranteed أخيرًا. لكنه لا يعني أن Guaranteed لا يفشل، ولا يستبدل Priority أو Disruption Budgets أو مرونة التطبيق.

يحصل Pod على Guaranteed عندما تملك كل حاوية CPU وMemory Requests موجبة وحدودًا مساوية لها. يناسب ذلك أحمالًا حرجة مضبوطة، لكن المساواة تمنع أيضًا تجاوز القيم المحجوزة. تستطيع خدمة Burstable ضمان خط أساس واستعمال سعة العقدة الفارغة. أما BestEffort فلا يملك طلبًا أو حدًا ويكون أول مرشح للإخلاء؛ استخدمه للعمل القابل للرمي فعلًا.

لا تجعل Request مساويًا لحد مبالغ فيه فقط للحصول على Guaranteed. سيحجز المجدول سعة غير مستخدمة، ويزيد عدد العقد، وقد يترك عملًا حقيقيًا Pending. اختر Service Objective أولًا ثم اقبل QoS الناتج بوعي.

حافظ على صدق حسابات HPA

يحسب HPA هدف CPU Utilization نسبةً إلى CPU Request. حاوية تستخدم `300m` مع Request بقيمة `300m` تعرض 100%، والاستخدام نفسه مع Request بقيمة `600m` يعرض 50%. تغيير Request قد يغير قرار Replicas دون أن يتغير Traffic أو الاستهلاك الفعلي.

توضح وثائق HPA الرسمية أيضًا أن غياب Resource Request عن الحاويات المعنية يجعل Utilization غير معرف، فلا يتحرك Autoscaler على ذلك المقياس. يمكن للـSidecars تشويه متوسط الـPod؛ استخدم `ContainerResource` المستقر عندما يجب أن يتبع التوسع App Container لا Sidecar التسجيل أو البروكسي.

يمكن أن يبدأ Deployment وHPA توضيحيان هكذا:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: catalog-api
spec:
  replicas: 3
  template:
    spec:
      containers:
        - name: api
          image: registry.example/catalog-api:2026-10-05
          resources:
            requests:
              cpu: 300m
              memory: 384Mi
            limits:
              memory: 640Mi
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: catalog-api
spec:
  minReplicas: 3
  maxReplicas: 20
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: catalog-api
  metrics:
    - type: ContainerResource
      containerResource:
        name: cpu
        container: api
        target:
          type: Utilization
          averageUtilization: 65

القيم Placeholder. تحقق منها باختبارات ضغط وراقب Control Loop كاملًا: يرتفع Traffic، تُجمع Metrics، يغير HPA Desired Replicas، تُجدول Pods الجديدة وتصبح Ready، ثم يعاد توزيع الحمل. يجب أن تغطي Min Replicas والـHeadroom هذا التأخير. وفي الطوابير قد تكون Requests per second أو Queue age أفضل من CPU.

استخدم VPA أولًا كدليل

يحلل Vertical Pod Autoscaler استخدام CPU والذاكرة الحالي والتاريخي والقمم وأحداث OOM، ثم يعرض Target وLower bound وUpper bound. وهو تثبيت منفصل وليس Core Controller يعمل تلقائيًا في كل Cluster. ابدأ بوضع توصيات مثل `updateMode: Off` حتى يقارن الفريق المقترحات بأهداف الخدمة واختبارات الحمل.

قد تتعارض تغييرات VPA التلقائية مع HPA عندما يتفاعل الاثنان مع CPU أو الذاكرة. تغيير Request يبدل حساب Utilization في HPA وقد يغير عدد Replicas. تقسيم عملي هو توصيات VPA للطلبات وHPA على مقياس خارجي أو خاص بالحمل، أو VPA محدود بعناية مع اختبار سلوك HPA كنظام واحد.

تستطيع إصدارات Kubernetes الحديثة تغيير موارد الحاوية في مكانها على Clusters المدعومة، لكن توفر الميزة لا يلغي السياسة والRollback. تحقق من نسخة العنقود ودعم Runtime وResize policy وحالات Pending وسلوك التطبيق قبل الاعتماد عليها. يبقى الاستبدال التدريجي خط الأساس القابل للنقل.

احمِ Namespaces بالافتراضات والميزانيات

يمكن لـ`LimitRange` حقن Requests وLimits افتراضية، وفرض الحدود الدنيا والعليا، وتقييد النسبة بين الطلب والحد داخل Namespace. يحمي ذلك من الحقول الناقصة، لكن الافتراضات العامة ليست Rightsizing دقيقًا. قد يخفي Default صغير حملًا غير محسوب حتى الإنتاج، ويهدر Default كبير السعة. استخدمها كحماية Admission واطلب من المالكين استبدالها بقيم مقاسة.

يحد `ResourceQuota` مجموع Requests وLimits التي تستهلكها Namespace. يؤسس ميزانية لفريق أو بيئة ويمكنه إجبار Pods الجديدة على تحديد الموارد. لكنه لا يضمن أن تصل كل Namespaces إلى أقصى حصة معًا، ولا ينشئ Nodes. اربطه بتخطيط السعة وNode Autoscaling ولوحة ميزانية واضحة.

حافظ على LimitRange واحدة حتمية لكل Namespace. تنبه وثائق Kubernetes إلى أن وجود عدة LimitRanges يجعل اختيار Default غير حتمي. تحقق من Manifests في CI وAdmission policy، واختبر الـPod النهائي المقبول بدل افتراض أن YAML المرسل بقي كما هو.

راقب العقد من أربع زوايا

لا يشخّص Graph واحد تحجيم الموارد. راقب أربع طبقات على الأقل:

1. Scheduling: Pods المعلقة و`FailedScheduling` والطلبات مقابل السعة القابلة للتخصيص وتأخر Node Autoscaler. 2. Runtime: CPU وThrottled time وMemory working set وOOM kills وRestarts وأحداث Eviction. 3. Service: Latency وError rate وThroughput وQueue age والمهل الفائتة. 4. Control loops: Desired/current replicas وحالات HPA وUnavailable replicas وتوصيات VPA.

اربطها حسب Workload revision وContainer. ارتفاع Latency مع CPU throttling يوحي بحد ضيق؛ وارتفاعها بلا تشبع قد يكون في Dependency أو Lock. OOM بعد Deploy قد يدل على Memory regression أو Cache warm-up أو Concurrency متغير. وبقاء Replicas الجديدة Pending أثناء قفزة يعني أن HPA طلب سعة لم يستطع Scheduler أو Node scaler توفيرها.

نبّه على النتائج لا النسب فقط: throttling مستمر يضر Latency، وOOMKilled متكرر، وHPA عالق عند الحد الأقصى، وPods معلقة فوق Rollout budget، ونفاد Namespace quota. احتفظ بتاريخ يكفي للمقارنة قبل تعديل المورد وبعده.

انشر تغييرات الموارد كتغييرات إنتاج

قد ينقل Request جديد Pods إلى Nodes مختلفة، ويغير طلب Cluster Autoscaler، ويبدل HPA Utilization. وقد يغير Limit الاستجابة أو سلوك إعادة التشغيل. عامل الاثنين كتغييرات قابلة للنشر لها Review وCanary وRollback.

ابدأ بنسخة Workload واحدة أو نسبة Canary صغيرة. نفذ اختبارات steady وburst وبطء Dependency. قارن أهداف الخدمة والـthrottling وهامش الذاكرة وReplicas وتكلفة العقد. غيّر بعدًا واحدًا كل مرة؛ تعديل Requests وLimits وهدف HPA وحدود Replicas معًا يمنع تفسير النتيجة.

سجّل سبب كل قيمة: نافذة القياس وPercentile وقاعدة الهامش وقيد Runtime والمالك وتاريخ المراجعة. أعد الفحص بعد تغير Traffic أو الكود أو Runtime أو نوع العقدة أو سياسة التوسع. لا تجعل YAML الثابتة تتحول إلى حكاية قديمة دائمة.

متى لا تعتمد على Requests وLimits وحدها؟

لا تصلح إعدادات الموارد Memory leak أو Queue غير محدودة أو Query مكلفة أو Lock contention أو مكالمات Downstream متزامنة. قد تحتوي بعض النتائج، لكن الحمل ما زال يحتاج Backpressure وحدود تشغيل. كما أنها لا تضمن عدالة العملاء داخل Process واحدة؛ طبّق ميزانيات العملاء في التطبيق والطابور.

قد يكون شكل Pod واحد تجريدًا خاطئًا للأحمال شديدة التغير. افصل العمل الحساس للزمن عن Batch، أو افصل Endpoints الثقيلة، أو استخدم Node pools متخصصة. ولخدمة صغيرة منخفضة المخاطر في Cluster واسع، قد يضيف Autoscaling معقد نقاط فشل أكثر مما يزيل؛ قد تكون Replicas ثابتة مقاسة أبسط.

الثابت الإنتاجي واضح: يعلن كل Pod وعد توزيع مبنيًا على دليل، وتطابق حدود التشغيل سياسة الفشل، ويقرأ Autoscaling Signal ذا معنى، ويرى المشغلون متى يتجاوز الطلب أي طبقة. هكذا تتحول إعدادات Kubernetes من YAML للزينة إلى نظام سعة قابل للمراقبة. للممارسات المرتبطة، راجع النشر الآمن في Kubernetes وقياس الاعتمادية عند رحلة المستخدم وTail Sampling في OpenTelemetry.

المراجع الرسمية

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

إعداد: Noor Yasser

من القرار إلى التنفيذ

تعمل على تحدٍ تقني مشابه؟

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

احجز لقاءً لمدة ٣٠ دقيقةالخدمة المرتبطةتحسين الأداء والبنية السحابية والنشرمشروع من الأعمالمنصة اللوجستيات