Pod blijft in Pending: waarom Kubernetes je workload niet kan schedulen

Een pod in Pending-status is geaccepteerd door de API-server, maar geen enkele node kan hem draaien. De scheduler heeft elke node gecontroleerd, nul geschikte gevonden, en wacht tot de omstandigheden veranderen. De oplossing hangt volledig af van welk filter faalde: te weinig CPU of geheugen, een taint zonder bijpassende toleration, een node-affinity mismatch, een ongebonden PersistentVolumeClaim, of een ResourceQuota die pod-creatie blokkeert nog voordat scheduling begint.

Wat Pending precies betekent

De podlevenscyclus kent vijf fasen: Pending, Running, Succeeded, Failed, Unknown. Pending betekent dat de API-server de pod heeft geaccepteerd maar er nog geen container draait. In vrijwel alle gevallen is de reden dat de kube-scheduler geen node kon vinden die aan alle filters voldoet.

De scheduler doorloopt bij elke scheduling-cyclus twee fasen. Eerst een filteringfase die predicates toepast (PodFitsResources, NodeSelector, TaintToleration, VolumeBinding, en meer) om nodes te vinden die de pod kunnen draaien. Als er nul overblijven, blijft de pod Pending. Daarna een scoringfase die de overgebleven nodes rangschikt. De scheduler kiest de hoogst scorende node en bindt de pod eraan.

Een pod die langer dan een paar seconden Pending blijft, raakt een filter dat geen enkele node op dit moment kan passeren. De Events-sectie van kubectl describe pod vertelt je precies welk filter.

Het FailedScheduling-event lezen

Dit is de belangrijkste diagnostische stap. Doe dit altijd eerst:

kubectl describe pod <pod-name> -n <namespace>

Scroll naar de Events-sectie onderaan. Een pod die vastzit in Pending genereert een Warning FailedScheduling-event van default-scheduler. Het bericht volgt een vast formaat:

Warning  FailedScheduling  2m  default-scheduler  0/5 nodes are available:
  2 Insufficient cpu,
  1 node(s) had taint {node-role.kubernetes.io/control-plane: }, that the pod didn't tolerate,
  2 node(s) didn't match Pod's node affinity/selector.
  preemption: 0/5 nodes are available: 5 Preemption is not helpful for scheduling.

Elke regel noemt een scheduler-predicate dat gefaald heeft en hoeveel nodes het afwees. Een node kan op meerdere predicates tegelijk falen, dus de tellingen tellen niet op tot het totaal. Als preemption: ... Preemption is not helpful for scheduling verschijnt, is de constraint niet resource-gerelateerd (het is een taint, affinity of volume-probleem) en zou het preempten van lagere-prioriteit pods niks helpen.

Alle pending pods in het cluster zoeken:

kubectl get pods -A --field-selector=status.phase=Pending

Events specifiek filteren:

kubectl get events -n <namespace> --field-selector=reason=FailedScheduling --sort-by='.lastTimestamp'

Onvoldoende clusterresources

De meest voorkomende oorzaak. Het bericht zegt Insufficient cpu, Insufficient memory of Insufficient ephemeral-storage.

De scheduler gebruikt resource requests, niet limits, om te bepalen of een pod op een node past. Limits worden pas later afgedwongen door de kubelet (CPU-throttling, OOM kills). Een cluster waar containers hoge requests maar laag daadwerkelijk gebruik hebben, lijkt "vol" voor de scheduler terwijl het echte verbruik laag is. Voor een uitgebreidere uitleg over hoe requests en limits samenwerken, zie Kubernetes resource requests en limits.

De scheduler vergelijkt ook met allocatable capaciteit, niet de ruwe nodecapaciteit:

Allocatable = Capacity - kube-reserved - system-reserved - eviction-threshold

Een 4-CPU / 16 GiB node heeft doorgaans zo'n 3,7 CPU / 13 GiB allocatable na systeemreserveringen.

Resource-uitputting diagnosticeren

Controleer de allocated resources per node:

kubectl describe node <node-name>

Zoek de Allocated resources-sectie:

Allocated resources:
  Resource           Requests      Limits
  --------           --------      ------
  cpu                3800m (95%)   0 (0%)
  memory             12Gi  (78%)   16Gi (100%)

Het Requests-percentage is relatief ten opzichte van allocatable. Als het dicht bij 100% zit, kan geen nieuwe pod die resource aanvragen.

Bekijk wat de pod aanvraagt:

kubectl get pod <pod-name> -o jsonpath='{.spec.containers[*].resources.requests}'

Scan alle nodes tegelijk:

kubectl describe nodes | grep -A 5 "Allocated resources"
kubectl top nodes  # vereist metrics-server

Oplossingen

Situatie Oplossing
Pod requests zijn te hoog Verlaag resources.requests naar het werkelijke gebruik
Alle nodes daadwerkelijk vol Voeg nodes toe of upgrade naar een groter instance-type
Ongebruikte deployments Verwijder workloads die niet meer nodig zijn
Cloudomgeving Schakel Cluster Autoscaler in

Als de Cluster Autoscaler al draait maar de pod nog steeds Pending is, check de events:

kubectl get events -n kube-system | grep cluster-autoscaler

Een NotTriggerScaleUp-event van de autoscaler betekent dat de constraint niet puur resource-gerelateerd is. Een taint- of affinity-mismatch verhindert ook dat een nieuw geschaalde node de pod kan helpen.

Verificatie: bevestig na het aanpassen van requests of toevoegen van nodes dat de pod van Pending naar Running gaat:

kubectl get pod <pod-name> -n <namespace> -w

Taint- en toleration-mismatches

Een taint op een node weert pods die geen bijpassende toleration hebben. Er zijn drie effecten:

  • NoSchedule: harde blokkade voor nieuwe pods, bestaande pods blijven.
  • PreferNoSchedule: zachte voorkeur, scheduler probeert de node te vermijden maar kan toch kiezen.
  • NoExecute: harde blokkade voor nieuwe pods, en bestaande pods zonder de toleration worden verwijderd.

Het FailedScheduling-bericht noemt de taintsleutel:

0/3 nodes are available:
  1 node(s) had taint {dedicated: gpu}, that the pod didn't tolerate,
  1 node(s) had taint {node-role.kubernetes.io/control-plane: }, that the pod didn't tolerate,
  1 Insufficient cpu.

Die control-plane taint is de meest voorkomende valkuil. Single-node clusters, labomgevingen en lokale kind/minikube-setups met maar een node laten elke gewone pod Pending, want de enige node draagt node-role.kubernetes.io/control-plane:NoSchedule.

Taint-mismatches diagnosticeren

Bekijk node taints:

kubectl describe nodes | grep -A 2 Taints

Bekijk pod tolerations:

kubectl describe pod <pod-name> | grep -A 10 Tolerations

Vergelijk de twee. Elke NoSchedule- en NoExecute-taint op een node moet een bijpassende toleration in de podspec hebben.

Veelvoorkomende ingebouwde taints

Taint Betekenis
node-role.kubernetes.io/control-plane:NoSchedule Control-plane node
node.kubernetes.io/unschedulable:NoSchedule Node gecordoned via kubectl cordon
node.kubernetes.io/not-ready:NoExecute Node niet ready
node.kubernetes.io/memory-pressure:NoSchedule Geheugenpressie
node.kubernetes.io/disk-pressure:NoSchedule Schijfpressie

Oplossingen

Voeg een bijpassende toleration toe aan de podspec:

tolerations:
- key: "dedicated"
  operator: "Equal"
  value: "gpu"
  effect: "NoSchedule"

Of verwijder een per ongeluk geplaatste taint van de node (de afsluitende - verwijdert):

kubectl taint node <node-name> dedicated=gpu:NoSchedule-

Voor een gecordoned node:

kubectl uncordon <node-name>

Verificatie: kubectl describe pod <pod-name> toont een Scheduled-event met de toegewezen nodenaam.

Node selector en affinity mismatches

Het FailedScheduling-bericht didn't match Pod's node affinity/selector wijst hierheen.

nodeSelector is de simpelste vorm: een map van label key-value paren die een node moet hebben. Alle keys worden ge-AND. Als een pod nodeSelector: {disktype: ssd, region: us-east-1} instelt, moet een node beide labels hebben.

Node affinity breidt dit uit met rijkere expressies:

  • requiredDuringSchedulingIgnoredDuringExecution: harde constraint, net als nodeSelector maar met operators (In, NotIn, Exists, DoesNotExist, Gt, Lt). Als niet voldaan, blijft de pod Pending.
  • preferredDuringSchedulingIgnoredDuringExecution: zachte constraint met een gewicht. De scheduler probeert het maar blokkeert niet.

Als zowel nodeSelector als nodeAffinity zijn ingesteld, moeten beide voldaan zijn.

Inter-pod anti-affinity

Pod anti-affinity met requiredDuringScheduling is een veelvoorkomende Pending-valkuil. Vijf Kafka-replica's met anti-affinity op kubernetes.io/hostname vereisen vijf aparte nodes. Als er maar vier bestaan, blijft de vijfde replica permanent Pending.

Het eventbericht:

0/4 nodes are available: 4 node(s) didn't match pod anti-affinity rules.
preemption: 0/4 nodes are available: 4 Preemption is not helpful for scheduling.

Affinity-problemen diagnosticeren

kubectl get pod <pod-name> -o jsonpath='{.spec.affinity}' | jq
kubectl get pod <pod-name> -o jsonpath='{.spec.nodeSelector}' | jq
kubectl get nodes --show-labels

Vergelijk elke required-regel met de beschikbare nodelabels.

Oplossingen

Probleem Oplossing
nodeSelector-label ontbreekt op nodes Voeg het toe: kubectl label node <node> disktype=ssd
Typfout in label-key in podspec Corrigeer de podspec
Required affinity niet te voldoen Schakel over naar preferredDuringScheduling of voeg nodes toe met de juiste labels
Anti-affinity vereist meer nodes dan er zijn Voeg nodes toe, of downgrade naar preferredDuringScheduling

Verificatie: bevestig dat de pod naar Running gaat en op een node met de verwachte labels draait.

Ongebonden PersistentVolumeClaims

Als een pod een PVC referenceert, blokkeert het VolumeBinding-predicate van de scheduler de scheduling tot de PVC gebonden is. Het eventbericht is ondubbelzinnig:

0/3 nodes are available: 3 pod has unbound immediate PersistentVolumeClaims.

Een PVC blijft ongebonden als er geen PV matcht (capaciteit, access mode of StorageClass-mismatch), als de genoemde StorageClass niet bestaat, of als de dynamische provisioner down is. Zie Kubernetes PersistentVolumes en PersistentVolumeClaims voor een volledige uitleg.

Een specifieke valkuil: zone-conflicten. Als een StorageClass volumeBindingMode: Immediate gebruikt (de standaard), bindt de PVC meteen bij aanmaak aan een PV, mogelijk in een andere availability zone dan waar de pod terechtkomt. De scheduler faalt dan met "node(s) had volume node affinity conflict". Overschakelen naar volumeBindingMode: WaitForFirstConsumer lost dit op door binding uit te stellen tot de pod gescheduled wordt.

PVC-problemen diagnosticeren

kubectl get pvc -n <namespace>
kubectl describe pvc <pvc-name> -n <namespace>
kubectl get pv
kubectl get storageclass

Zoek in de PVC-events naar:

  • "storageclass.storage.k8s.io \"standard\" not found" (ontbrekende StorageClass)
  • "no persistent volumes available for this claim" (geen matchende PV)
  • "waiting for first consumer to be created before binding" (normaal bij WaitForFirstConsumer)
  • "ProvisioningFailed" (CSI-driver of provisioner-pod probleem)

Als de provisioner het probleem is:

kubectl get pods -n kube-system | grep -E "provisioner|csi"
kubectl logs -n kube-system <provisioner-pod>

Oplossingen

Oorzaak Oplossing
Geen matchende PV Maak er handmatig een aan of zorg dat dynamic provisioning geconfigureerd is
StorageClass niet gevonden Maak de StorageClass aan of corrigeer storageClassName in de PVC
Provisioner-pod down Herstart de CSI-driver DaemonSet of Deployment
Zone-mismatch Schakel over naar volumeBindingMode: WaitForFirstConsumer
Access mode mismatch Stem de accessModes van de PVC af op wat de StorageClass ondersteunt

Verificatie: kubectl get pvc -n <namespace> toont Bound-status; de pod gaat naar Running.

ResourceQuota-limieten

ResourceQuota gedraagt zich anders dan alle andere oorzaken in dit artikel. Het blokkeert pod-creatie op admissieniveau, nog voordat de scheduler de pod te zien krijgt. Zie multi-tenancy namespace-isolatie voor een complete gids over quota's in gedeelde clusters. De API-server retourneert een 403 Forbidden:

Error from server (Forbidden): pods "my-pod-7d6fb" is forbidden:
  exceeded quota: namespace-quota,
  requested: requests.memory=700Mi,
  used: requests.memory=600Mi,
  limited: requests.memory=1Gi

De pod komt nooit in Pending omdat hij nooit aangemaakt wordt. In plaats daarvan logt de Deployment- of ReplicaSet-controller een FailedCreate-conditie. Je vindt geen FailedScheduling-event op een pod; je moet de controller checken:

kubectl describe deployment <deployment-name> -n <namespace>

Zoek naar ReplicaFailure: True / FailedCreate in de Conditions-sectie, of:

kubectl get events -n <namespace> | grep -i forbidden

Quota-problemen diagnosticeren

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

De output toont gebruikt vs. harde limieten:

Resource         Used    Hard
--------         ----    ----
requests.cpu     1900m   2000m
requests.memory  1.9Gi   2Gi
pods             18      20

Als een quota requests bijhoudt, moet elke pod in die namespace resources.requests specificeren. Een pod zonder requests faalt op admissie. Een LimitRange kan standaard requests automatisch injecteren.

Oplossingen

Situatie Oplossing
Quota daadwerkelijk uitgeput Verwijder ongebruikte workloads of verhoog de quotalimiet
Pods missen resource specs Voeg resources.requests toe aan containers, of stel een LimitRange in met defaults
Quota te conservatief Bekijk het werkelijke namespacegebruik en pas aan

Verificatie: kubectl get events -n <namespace> stopt met het tonen van Forbidden-fouten; replica's bereiken het gewenste aantal.

Minder voorkomende oorzaken

Een paar situaties die minder vaak Pending veroorzaken, maar het uitsluiten waard zijn.

Gecordonde nodes. kubectl get nodes toont SchedulingDisabled in de STATUS-kolom. Oplossing: kubectl uncordon <node-name>.

hostPort binding. Het gebruik van hostPort in een podspec beperkt scheduling tot een pod per node per poort. Als elke node al een pod op die poort draait, blijven nieuwe pods Pending. Oplossing: gebruik een Service in plaats van hostPort.

Pod priority en preemption. Een lagere-prioriteit pod kan worden verwijderd om ruimte te maken voor een hogere-prioriteit pod. Na verwijdering keert de lagere-prioriteit pod terug naar Pending. De events vermelden preemption.

CNI-plugin niet ready. Als de container network interface niet geinitialiseerd is op een node, meldt die NetworkReady=false. Pods slaan die node over. Oplossing: controleer de CNI DaemonSet (kubectl get pods -n kube-system | grep -E "flannel|calico|weave|cilium").

Wanneer escaleren

Als je alle oorzaken hierboven hebt doorlopen en de pod nog steeds vastzit, verzamel dan deze informatie voordat je hulp inschakelt:

  • Volledige output van kubectl describe pod <pod-name> -n <namespace>
  • Output van kubectl describe nodes (alle nodes)
  • Output van kubectl get events -n <namespace> --sort-by='.lastTimestamp'
  • De podspec als YAML: kubectl get pod <pod-name> -o yaml
  • PVC-status als de pod volumes gebruikt: kubectl get pvc -n <namespace>
  • ResourceQuota-status: kubectl describe resourcequota -n <namespace>
  • Kubernetes-versie: kubectl version
  • Cluster Autoscaler-logs als die draait: kubectl logs -n kube-system -l app=cluster-autoscaler --tail=100

Hiermee heeft een platformteam of support-engineer alles in handen om de scheduling-failure te diagnosticeren zonder vervolgvragen.

Herhaling voorkomen

  • Stel resource requests in op basis van geobserveerd gebruik, niet op giswerk. Te hoge requests zijn de belangrijkste oorzaak van onnodige Pending-pods.
  • Gebruik volumeBindingMode: WaitForFirstConsumer op StorageClasses in multi-zone clusters.
  • Draai kubectl describe nodes | grep -A 5 "Allocated resources" regelmatig, of voed Prometheus node-exporter metrics naar dashboards om request-headroom te monitoren voordat pods in de wacht komen.
  • Bekijk ResourceQuotas per kwartaal ten opzichte van het werkelijke namespacegebruik.
  • Bij pod anti-affinity met required, controleer dat het aantal nodes het replicaaantal ondersteunt voordat je opschaalt.

Terugkerende server- of deploymentproblemen?

Ik help teams productie betrouwbaar maken met CI/CD, Kubernetes en cloud—zodat fixes blijven en deploys geen stress meer zijn.

Bekijk DevOps consultancy

Doorzoek deze site

Begin met typen om te zoeken, of blader door de kennisbank en blog.