Kubernetes Scheduling
itCloud native tools and technologies
You should already be familiar with: kubernetes-fundamentals
Kubernetes Scheduling
Scheduling is the matchmaking at the heart of Kubernetes: deciding which node each Pod runs on. Most of the time it's invisible — Pods land somewhere sensible and nobody thinks about it. It becomes visible exactly when it matters: Pods stuck Pending, workloads packed onto the wrong hardware, one noisy neighbor starving a node, or an eviction wave at the worst possible moment. Understanding scheduling turns those from mysteries into design decisions.
The mental model: the kube-scheduler runs a continuous auction. Every Pod without a node comes up for placement; every eligible node is scored; the best-scoring node wins the Pod. Your job is not to place Pods — it's to shape the auction with constraints: what a Pod requires, what it prefers, what nodes repel, and what happens when resources run out.
How the scheduler decides
For each unscheduled Pod, the scheduler works in two phases. Filtering eliminates nodes that can't host the Pod at all — insufficient free resources (measured against requests, not usage), missing labels the Pod requires, untolerated taints, volume topology conflicts. The survivors are feasible nodes. Scoring then ranks them by a weighted set of rules, and the Pod is bound to the winner. If no node passes filtering, the Pod stays Pending — the single most common scheduling symptom, and the events on the Pod (kubectl describe pod) almost always name the reason.
Resource requests: the currency of scheduling
Every container can declare requests (what it's guaranteed, and what the scheduler budgets) and limits (what it may never exceed). The scheduler packs nodes by requests alone: a node is "full" when the sum of requested resources reaches its allocatable capacity, regardless of actual utilization. This makes requests the single most important tuning knob in the whole system — unset requests mean the scheduler is packing blind, and oversized requests waste capacity invisibly. Requests and limits also define a Pod's Quality of Service class (Guaranteed, Burstable, BestEffort), which determines who gets evicted first under node pressure.
Steering placement
A small vocabulary of mechanisms covers nearly every placement need. nodeSelector is the blunt instrument: run only on nodes with these labels. Node affinity is its expressive successor — required rules (hard) and preferred rules (soft, weighted). Pod affinity and anti-affinity place Pods relative to other Pods — co-locate a cache with its consumer, or spread replicas apart; anti-affinity across nodes is the classic availability pattern. Taints and tolerations invert the logic: a taint on a node repels all Pods that don't explicitly tolerate it — the tool for dedicated nodes (GPUs, licensed software) and for marking nodes as troubled (the node controller adds taints like not-ready automatically, with NoExecute taints evicting running Pods). Finally, topology spread constraints distribute Pods evenly across zones, nodes, or any topology domain — the systematic way to survive zone failures.
When resources run out
Scheduling has a flip side: unscheduling. Pod priority and preemption let important Pods evict less important ones when the cluster is full — a PriorityClass assigns the number, and the scheduler preempts lower-priority Pods to make room. Node-pressure eviction is the kubelet defending its node: when memory, disk, or PIDs run short, it evicts Pods (BestEffort and over-request Burstable first) to keep the node alive. API-initiated eviction (what kubectl drain uses) respects PodDisruptionBudgets — your declaration of how many replicas must survive voluntary disruptions like maintenance. Priority, QoS, and PDBs together decide who lives through contention; leaving them all default is a decision too, just an unexamined one.
Who needs this, and when
Application teams need requests/limits and spread constraints from their first production deployment. Platform teams own the deeper machinery: dedicated node pools with taints, PriorityClasses as an organizational policy, autoscaler-friendly affinity design, and scheduler configuration itself (profiles, plugin tuning) where genuinely needed. For specialized hardware — GPUs and accelerators — Dynamic Resource Allocation (DRA) is the newer API for claiming devices, a sign of how far "which node?" has grown beyond CPU and memory.
Limits and honest caveats
The scheduler places Pods at admission time; it does not continuously rebalance a running cluster (that's the descheduler add-on's territory). Affinity rules, especially pod anti-affinity, add real scheduling cost at scale. Preemption ignores PodDisruptionBudgets in the limit — PDBs protect against voluntary disruption, not priority or node pressure. And every constraint you add narrows the feasible-node set: over-constrained Pods are Pending Pods. Constrain deliberately, prefer soft rules where hard ones aren't truly required, and read the Pod's events before theorizing.
