pod عالق في Pending أو يظهر أحداث FailedScheduling يعني جدولة Kubernetes لا يمكنها العثور على عقدة مناسبة لتشغيله. يوضح هذا الدليل كيفية التشخيص وإصلاح الأسباب الأكثر شيوعاً.

الأعراض

حالة pod هي Pending ولا تتقدم إلى Running. تشغيل kubectl describe pod يظهر أحداث مثل:

Name:           example-app-5f7c9d4bfb-7x8kq
Namespace:      default
Status:         Pending
Events:
  Type     Reason             Age    Message
  ----     ------             ----   -------
  Warning  FailedScheduling   3m     0/3 nodes are available:
           insufficient cpu on 3 nodes

رسائل أخرى ممكنة:

insufficient memory on 3 nodes
node has a taint...that the pod does not tolerate
pod has unbound immediate PersistentVolumeClaims
no nodes match the pod's node selector
pod affinity/anti-affinity not satisfied
exceeds quota
exceeds LimitRange

لماذا يحدث

مرتبة حسب الاحتمالية:

  1. CPU أو ذاكرة ناقصة على جميع العقد — يطلب pod موارد أكثر من أي عقدة متاحة. إما أن العنقود مفرط الاشتراك أو طلبات pod غير واقعية.
  2. node selector أو affinity constraint غير مرضي — pod مقيد لعقد معينة (حسب التسمية أو قواعد affinity) وتلك العقد غير موجودة أو غير متاحة.
  3. Taints على العقد التي pod لا يتسامح معها — عقد المستوى التحكم والعقد المتخصصة الأخرى لها taints. Pods بدون tolerations مطابقة لا يمكنها الجدولة هناك.
  4. PersistentVolumeClaim (PVC) pending — PVC لا يمكنه الارتباط بـ PersistentVolume، لذا pod ينتظر بلا نهاية.
  5. Pod quota أو LimitRange تجاوزت — Namespace لديه resource quotas أو limit ranges تمنع إنشاء pods جديدة.
  6. Topology spread constraints أو pod anti-affinity — يتطلب pod نشر عبر عقد أو لا يمكنه العمل على نفس العقدة مثل pods أخرى، لكن لا عقد مناسبة موجودة.

الحل

1. CPU أو ذاكرة ناقصة على جميع العقد

تحقق من الموارد المتاحة:

kubectl top nodes

مثال إخراج:

NAME       CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
node-1     1900m        95%    3800Mi          95%
node-2     1800m        90%    3600Mi          90%
node-3     1950m        98%    3900Mi        98%

جميع العقد ممتلئة. اعرض ما يستهلك الموارد:

kubectl top pod -A | sort --reverse --numeric-sort --key 3 | head -20

الخيار أ: قيّم أو احذف pods ذات أولوية منخفضة لتحرير مساحة.

الخيار ب: زد طلبات الموارد للـ pod إذا كانت عالية جداً:

kubectl get pod <pod-name> -o yaml | grep -A 5 "resources:"

إذا كان pod يطلب 1000m CPU لكن العقد لديها فقط 100m حر، قلل الطلبات:

resources:
  requests:
    cpu: "100m"      # قلل من 500m
    memory: "256Mi"
  limits:
    cpu: "500m"
    memory: "512Mi"

طبّق وأعد المحاولة:

kubectl apply -f deployment.yaml

الخيار ج: أضف مزيد من العقد للعنقود.

لعناقيد السحابة، فعّل autoscaling:

# مثال GKE
gcloud container clusters update <cluster-name> \
  --enable-autoscaling \
  --min-nodes 1 \
  --max-nodes 10

للمعادن أو on-prem، أضف العقد يدوياً:

# اتبع تعليمات مزود السحابة أو أداة النشر

2. Node selector أو affinity constraint غير مرضي

تحقق من node selector للـ pod:

kubectl get pod <pod-name> -o yaml | grep -A 5 "nodeSelector:"

مثال:

nodeSelector:
  disktype: ssd
  gpu: "true"

سيعمل pod فقط على عقد بكلا التسميتين. تحقق إذا كانت أي عقد لديها هذه التسميات:

kubectl get nodes --show-labels | grep ssd
kubectl get nodes --show-labels | grep gpu

إذا لم توجد عقد مطابقة، إما:

أ. أضف التسميات لعقد موجودة:

kubectl label node <node-name> disktype=ssd gpu=true

ب. أزل nodeSelector من pod إذا لم يكن مطلوباً بالفعل:

# أزل أو أعلّق nodeSelector
# nodeSelector:
#   disktype: ssd

لـ affinity constraints (أكثر تعقيداً من nodeSelector)، تحقق من مواصفات pod:

kubectl get pod <pod-name> -o yaml | grep -A 20 "affinity:"

مثال affinity يتطلب تسمية معينة:

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: disktype
          operator: In
          values:
          - ssd

إذا لم توجد عقد بالتسمية المطلوبة، أضفها أو أرخِ affinity لتفضيل:

affinity:
  nodeAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:    # تغيير من required
    - preference:
        matchExpressions:
        - key: disktype
          operator: In
          values:
          - ssd
      weight: 100

طبّق pod المحدّث:

kubectl apply -f deployment.yaml
kubectl rollout restart deployment/<deployment-name>

3. Taints على العقد التي pod لا يتسامح معها

عقد المستوى التحكم والعقد المتخصصة الأخرى لها taints. تحقق منها:

kubectl describe node <node-name> | grep Taint

مثال إخراج:

Taints:   node-role.kubernetes.io/control-plane:NoSchedule

هذا taint يعني pods لا يمكنهم العمل على المستوى التحكم إلا إذا تسامحوا معه. إذا كان pod يجب أن يُسمح له هناك، أضف toleration مطابق:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: special-app
spec:
  template:
    spec:
      tolerations:
      - key: "node-role.kubernetes.io/control-plane"
        operator: "Equal"
        value: ""
        effect: "NoSchedule"
      containers:
      - name: app
        image: example:latest

طبّق وأعد المحاولة:

kubectl apply -f deployment.yaml

لـ taints مخصصة، يسري نفس الأسلوب:

# تحقق من taint
kubectl describe node <node-name> | grep Taint
# مثال: my-taint=myvalue:NoSchedule

# أضف toleration مطابق
tolerations:
- key: "my-taint"
  operator: "Equal"
  value: "myvalue"
  effect: "NoSchedule"

4. PersistentVolumeClaim (PVC) pending

تحقق إذا كان PVC موجود وملزوم:

kubectl get pvc

# الإخراج:
# NAME       STATUS    VOLUME   CAPACITY   ACCESS MODES
# my-pvc     Pending   <none>   <none>     RWO

إذا كان PVC pending، لا يمكنه الارتباط بأي PersistentVolume. تحقق السبب:

kubectl describe pvc <pvc-name>

الأسباب الشائعة:
– لا يوجد PV: أنشئ PV أو StorageClass.
– StorageClass لا يوجد: أنشئه أو تأكد من عنقود له واحد افتراضي.

أنشئ PersistentVolume:

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolume
metadata:
  name: my-pv
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: /data
EOF

أو فعّل التوفير الديناميكي بإنشاء StorageClass:

cat <<EOF | kubectl apply -f -
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast
provisioner: kubernetes.io/host-path
EOF

ثم حدّث PVC لاستخدام هذا StorageClass:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  storageClassName: fast
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

طبّق و PVC يجب أن يلتزم:

kubectl apply -f pvc.yaml
kubectl get pvc    # يجب أن تكون الآن Bound

5. Pod quota أو LimitRange تجاوزت

تحقق إذا كان namespace لديه resource quotas:

kubectl get resourcequota -n <namespace>
kubectl describe resourcequota <quota-name> -n <namespace>

مثال:

Name:       cpu-quota
Namespace:  default
Resource    Used    Hard
--------    ----    ----
cpu         1000m   2000m
memory      1Gi     2Gi
pods        5       10

pod الجديد قد يتجاوز quota. تحقق من طلبات pod:

kubectl get pod <pod-name> -o yaml | grep -A 5 "resources:"

الخيار أ: زد quota:

kubectl edit resourcequota <quota-name>

وزد قيم hard.

الخيار ب: قلل طلبات الموارد للـ pod:

resources:
  requests:
    cpu: "50m"       # قلل
    memory: "128Mi"
  limits:
    cpu: "200m"
    memory: "256Mi"

تحقق أيضاً من قيود LimitRange:

kubectl get limitrange -n <namespace>
kubectl describe limitrange <limit-name>

LimitRange قد يفرض حدود أدنى أو قصوى. ضبّط pod ليناسب النطاق.

طبّق وأعد المحاولة:

kubectl apply -f deployment.yaml

6. Topology spread constraints أو pod anti-affinity

تحقق من مواصفات pod للـ topology spread أو anti-affinity:

kubectl get pod <pod-name> -o yaml | grep -A 15 "topologySpreadConstraints:\|affinity:"

مثال topology spread يتطلب 3 zones:

topologySpreadConstraints:
- maxSkew: 1
  topologyKey: topology.kubernetes.io/zone
  whenUnsatisfiable: DoNotSchedule
  labelSelector:
    matchLabels:
      app: example

إذا كان العنقود لديه فقط zone واحد أو لا عقد كافية لكل zone، pod لا يمكنه الجدولة. إما:

أ. أضف عقد عبر zones متعددة:

kubectl get nodes -o wide | grep "REGION\|ZONE"

ب. أرخِ القيد:

topologySpreadConstraints:
- maxSkew: 1
  topologyKey: topology.kubernetes.io/zone
  whenUnsatisfiable: ScheduleAnyway    # تغيير من DoNotSchedule

أو أزل topology spread بالكامل إذا لم يكن مطلوباً.

طبّق وأعد المحاولة:

kubectl apply -f deployment.yaml

كيف تتجنبه

  1. عيّن دائماً طلبات الموارد والحدود:

    resources:
      requests:
        cpu: "100m"
        memory: "256Mi"
      limits:
        cpu: "500m"
        memory: "512Mi"
    

  2. راقب سعة العنقود:

    kubectl top nodes
    kubectl describe node <node-name> | grep -A 10 "Allocated"
    

  3. استخدم node labels و selectors بحكمة، فقط عند الحاجة الحقيقية. تجنب الإفراط في تقييد pods.

  4. افهم node taints قبل الجدولة:

    kubectl describe node | grep Taint
    

  5. خطط لـ PVC provisioning: تأكد من وجود StorageClass أو PersistentVolumes قبل إنشاء pods تحتاجهم.

  6. عيّن resource quotas معقولة للـ namespaces، لا ضيقة جداً:

    kubectl get resourcequota -A
    

  7. استخدم cluster autoscaling لعناقيد السحابة لإضافة عقد عند pods pending:

    # GKE, EKS, AKS, وغيرها توفر تكوين autoscaling
    

الأسئلة الشائعة

كيف أعرف لماذا pod عالق في Pending؟

شغّل kubectl describe pod <pod> وانظر إلى قسم Events. يظهر سبب الجدولة بالضبط، مثل ‘Insufficient cpu’ أو ‘node has a taint’.

ما الفرق بين Pending و FailedScheduling؟

Pending يعني الجدولة لم تعيّن pod لعقدة بعد. FailedScheduling حدث يشير لفشل محاولة جدولة. تحقق من kubectl describe pod لترى الأحداث.

كيف أرى CPU والذاكرة المتاحة على العقد؟

استخدم kubectl top nodes أو kubectl describe node لترى الموارد المخصصة/المتاحة. تحقق أيضاً من kubectl get nodes لترى حالة NotReady.

ما taints و tolerations، ولماذا تحجب pods؟

Taint يعلّم عقدة غير مناسبة لـ pods معينة. Toleration على pod يسمح له بالعمل على عقد مع taints. تحقق من kubectl describe node | grep Taint و tolerations في مواصفات pod.

كيف تم التحقق من هذا الدليل

كُتب يدويًا من الوثائق الرسمية، وأُعيد فحص كل أمر، ثم رُوجع مرة ثانية قبل النشر. آخر مراجعة في .

المصادر الرسمية التي قُرئت لهذا المقال

شاركها.

التعليقات مغلقة.