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 bijWaitForFirstConsumer)"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: WaitForFirstConsumerop 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.