Un pod bloqué en Pending ou affichant des événements FailedScheduling signifie que le scheduler Kubernetes ne peut pas trouver un nœud adapté pour l’exécuter. Ce guide vous montre comment diagnostiquer et corriger les causes les plus courantes.

Symptôme

L’état du pod est Pending et n’avance pas vers Running. La commande kubectl describe pod affiche des événements comme :

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

Autres messages possibles :

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

Pourquoi ça arrive

Classées par probabilité :

  1. CPU ou mémoire insuffisant sur tous les nœuds — Le pod demande plus de ressources que ce que n’importe quel nœud a disponible. Soit le cluster est sur-abonné, soit les requests du pod sont irréalistes.
  2. Node selector ou contrainte d’affinity non satisfait — Le pod est contraint à des nœuds spécifiques (par label ou règles d’affinity) et ces nœuds n’existent pas ou ne sont pas disponibles.
  3. Taints sur les nœuds que le pod ne tolère pas — Les nœuds du control plane et les autres nœuds spécialisés ont des taints. Les pods sans tolerations correspondantes ne peuvent pas s’y ordonnancer.
  4. PersistentVolumeClaim (PVC) en attente — Le PVC ne peut pas se lier à un PersistentVolume, donc le pod attend indéfiniment.
  5. Pod quota ou LimitRange dépassé — Le namespace a des quotas de ressources ou des limites qui empêchent la création de nouveaux pods.
  6. Topology spread constraints ou pod anti-affinity — Le pod exige une distribution entre nœuds ou ne peut pas s’exécuter sur le même nœud que d’autres pods, mais aucun nœud approprié n’existe.

Correction

1. CPU ou mémoire insuffisant sur tous les nœuds

Vérifiez les ressources disponibles :

kubectl top nodes

Exemple de sortie :

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%

Tous les nœuds sont pleins. Voyez ce qui consomme les ressources :

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

Option A: Réduisez ou supprimez les pods de faible priorité pour libérer de l’espace.

Option B: Augmentez les resource requests du pod s’ils sont trop élevés :

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

Si le pod demande 1000m CPU mais les nœuds n’ont que 100m libre, baissez les requests :

resources:
  requests:
    cpu: "100m"      # Réduit de 500m
    memory: "256Mi"
  limits:
    cpu: "500m"
    memory: "512Mi"

Appliquez et réessayez :

kubectl apply -f deployment.yaml

Option C: Ajoutez plus de nœuds au cluster.

Pour les clusters cloud, activez l’autoscaling :

# Exemple GKE
gcloud container clusters update <cluster-name> \
  --enable-autoscaling \
  --min-nodes 1 \
  --max-nodes 10

Pour bare metal ou on-prem, ajoutez manuellement les nœuds :

# Suivez les instructions de votre fournisseur cloud ou outil de déploiement

2. Node selector ou contrainte d’affinity non satisfait

Vérifiez le node selector du pod :

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

Exemple :

nodeSelector:
  disktype: ssd
  gpu: "true"

Le pod ne s’exécutera que sur les nœuds ayant les deux labels. Vérifiez si des nœuds ont ces labels :

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

Si aucun nœud ne correspond, soit :

A. Ajoutez les labels aux nœuds existants :

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

B. Supprimez le nodeSelector du pod s’il n’est pas réellement nécessaire :

# Supprimez ou commentez nodeSelector
# nodeSelector:
#   disktype: ssd

Pour les contraintes d’affinity (plus complexe que nodeSelector), vérifiez le spec pod :

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

Exemple d’affinity nécessitant un label spécifique :

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

Si aucun nœud n’a le label requis, ajoutez-le ou assouplissez l’affinity à une préférence :

affinity:
  nodeAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:    # Changé de required
    - preference:
        matchExpressions:
        - key: disktype
          operator: In
          values:
          - ssd
      weight: 100

Appliquez le pod mis à jour :

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

3. Taints sur les nœuds que le pod ne tolère pas

Les nœuds du control plane et les autres nœuds spécialisés ont des taints. Vérifiez-les :

kubectl describe node <node-name> | grep Taint

Exemple de sortie :

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

Ce taint signifie que les pods ne peuvent pas s’exécuter sur le control plane à moins qu’ils ne le tolèrent. Si votre pod devrait y être autorisé, ajoutez une toleration correspondante :

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

Appliquez et réessayez :

kubectl apply -f deployment.yaml

Pour les taints personnalisés, le même pattern s’applique :

# Vérifier le taint
kubectl describe node <node-name> | grep Taint
# Exemple : my-taint=myvalue:NoSchedule

# Ajouter la toleration correspondante
tolerations:
- key: "my-taint"
  operator: "Equal"
  value: "myvalue"
  effect: "NoSchedule"

4. PersistentVolumeClaim (PVC) en attente

Vérifiez si le PVC existe et est lié :

kubectl get pvc

# Sortie :
# NAME       STATUS    VOLUME   CAPACITY   ACCESS MODES
# my-pvc     Pending   <none>   <none>     RWO

Si le PVC est pending, il ne peut se lier à aucun PersistentVolume. Vérifiez pourquoi :

kubectl describe pvc <pvc-name>

Raisons courantes :
– Aucun PV n’existe : Créez un PV ou une StorageClass.
– StorageClass n’existe pas : Créez-en une ou assurez-vous que le cluster en a une par défaut.

Créez un PersistentVolume :

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

Ou activez le provisioning dynamique en créant une StorageClass :

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

Puis mettez à jour votre PVC pour utiliser cette StorageClass :

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

Appliquez et le PVC devrait se lier :

kubectl apply -f pvc.yaml
kubectl get pvc    # Devrait maintenant être Bound

5. Pod quota ou LimitRange dépassé

Vérifiez si le namespace a des quotas de ressources :

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

Exemple :

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

Votre nouveau pod pourrait dépasser le quota. Vérifiez les requests du pod :

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

Option A: Augmentez le quota :

kubectl edit resourcequota <quota-name>

Et augmentez les valeurs hard.

Option B: Réduisez les resource requests du pod :

resources:
  requests:
    cpu: "50m"       # Réduit
    memory: "128Mi"
  limits:
    cpu: "200m"
    memory: "256Mi"

Aussi vérifiez les contraintes LimitRange :

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

LimitRange pourrait appliquer des ressources minimales ou maximales. Ajustez votre pod pour tenir dans la plage.

Appliquez et réessayez :

kubectl apply -f deployment.yaml

6. Topology spread constraints ou pod anti-affinity

Vérifiez le spec pod pour topology spread ou anti-affinity :

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

Exemple de topology spread exigeant 3 zones :

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

Si le cluster n’a qu’1 zone ou pas assez de nœuds par zone, le pod ne peut pas s’ordonnancer. Soit :

A. Ajoutez des nœuds sur plusieurs zones :

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

B. Assouplissez la contrainte :

topologySpreadConstraints:
- maxSkew: 1
  topologyKey: topology.kubernetes.io/zone
  whenUnsatisfiable: ScheduleAnyway    # Changé de DoNotSchedule

Ou supprimez complètement la topology spread si elle n’est pas nécessaire.

Appliquez et réessayez :

kubectl apply -f deployment.yaml

Éviter la récidive

  1. Définissez toujours les resource requests et limits :

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

  2. Surveillez la capacité du cluster :

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

  3. Utilisez les node labels et selectors stratégiquement, uniquement quand réellement nécessaire. Évitez de sur-contraindre les pods.

  4. Comprenez les taints des nœuds avant d’ordonnancer :

    kubectl describe node | grep Taint
    

  5. Planifiez le provisioning des PVC : Assurez-vous que StorageClass ou PersistentVolumes existent avant de créer des pods qui en ont besoin.

  6. Définissez des quotas de ressources raisonnables pour les namespaces, pas trop serrés :

    kubectl get resourcequota -A
    

  7. Utilisez l’autoscaling des clusters pour les clusters cloud pour ajouter des nœuds quand des pods sont pending :

    # GKE, EKS, AKS, etc. fournissent une configuration d'autoscaling
    

FAQ

Comment savoir pourquoi un pod est bloqué en Pending ?

Exécutez kubectl describe pod <pod> et regardez la section Events. Elle affiche la raison exacte du scheduler, par ex. ‘Insufficient cpu’ ou ‘node has a taint’.

Quelle est la différence entre Pending et FailedScheduling ?

Pending signifie que le scheduler n’a pas encore assigné le pod à un nœud. FailedScheduling est un événement indiquant qu’une tentative d’ordonnancement a échoué. Vérifiez kubectl describe pod pour voir les événements.

Comment voir combien de CPU et mémoire sont disponibles sur les nœuds ?

Utilisez kubectl top nodes ou kubectl describe node pour voir les ressources allouées/disponibles. Aussi vérifiez kubectl get nodes pour voir le statut NotReady.

Qu’est-ce que les taints et tolerations, et pourquoi bloquent-elles les pods ?

Un taint marque un nœud comme inadapté pour certains pods. Une toleration sur un pod lui permet de s’exécuter sur les nœuds taintés. Vérifiez kubectl describe node | grep Taint et les tolerations de votre spec pod.

Comment ce guide a été vérifié

Rédigé à la main à partir de la documentation officielle, chaque commande revérifiée, puis relu une seconde fois avant publication. Dernière relecture le .

Sources officielles consultées pour cet article

Share.

Les commentaires sont fermés.