Kubernetes Scheduling and Placement
Kubernetes Scheduling and Placement ensures efficient resource allocation by automatically placing workloads across nodes based on defined policies and constraints.
Kubernetes Scheduling and Placement is the process by which the platform decides which node in a cluster should run each newly created Pod, and the set of mechanisms operators use to influence that decision. Scheduling operates as a distinct, decoupled stage in a Pod's lifecycle: a Pod exists in the API as soon as it is created, but remains unscheduled until the scheduler explicitly binds it to a specific node, after which the kubelet on that node takes over responsibility for actually running it.
The Scheduling Algorithm
Filtering
The scheduler begins by evaluating every node in the cluster against a set of filtering predicates, eliminating any node that cannot satisfy the Pod's hard requirements: insufficient CPU or memory capacity, an unmet node selector or required affinity rule, a port conflict, or an untolerated taint. Only nodes that pass every filter remain as feasible candidates.
Scoring
Each feasible node is then scored by a set of priority functions, evaluating factors such as how evenly resource usage would be spread across the cluster, how tightly Pods can be packed to minimize fragmentation, and how well a node satisfies preferred (as opposed to required) affinity rules. The node with the highest aggregate score is selected, with ties broken according to a defined policy.
Binding
Once a node is selected, the scheduler writes a binding, updating the Pod's spec.nodeName, back to the API server. This is the definitive action that assigns the Pod to a node; the kubelet on that node observes the newly bound Pod and begins creating its containers.
Resource-Based Scheduling
Requests and Limits
A Pod's containers may declare resource requests, the amount of CPU and memory the scheduler guarantees is available on the chosen node, and limits, the maximum the container is permitted to consume at runtime. Scheduling decisions are based entirely on requests; limits are enforced later by the kubelet and container runtime.
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
Quality of Service Classes
Kubernetes derives a Quality of Service (QoS) class for each Pod from its resource configuration: Guaranteed, when requests equal limits for every container; Burstable, when at least one container specifies requests below its limits; and BestEffort, when no requests or limits are specified at all. This classification governs eviction priority under node resource pressure, with BestEffort Pods evicted first and Guaranteed Pods evicted last.
Node Selection Mechanisms
nodeSelector
The simplest placement constraint, nodeSelector, restricts a Pod to nodes carrying a specific label, expressed as an exact-match key-value requirement.
spec:
nodeSelector:
disktype: ssd
Node Affinity and Anti-Affinity
Node affinity generalizes nodeSelector with richer expressions and a distinction between hard and soft requirements: requiredDuringSchedulingIgnoredDuringExecution must be satisfied for the Pod to be scheduled, while preferredDuringSchedulingIgnoredDuringExecution expresses a weighted preference the scheduler attempts to satisfy but is not required to.
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values: ["us-east-1a", "us-east-1b"]
Pod Affinity and Anti-Affinity
Pod affinity and anti-affinity express placement constraints relative to other Pods rather than node labels, allowing Pods to be co-located, for example to reduce network latency between cooperating services, or spread apart, for example to avoid placing every replica of a service on the same node.
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: codartium-api
topologyKey: kubernetes.io/hostname
Taints and Tolerations
Repelling Pods by Default
A taint applied to a node repels all Pods from being scheduled onto it unless those Pods carry a matching toleration. This inverts the direction of the constraint compared to affinity: rather than a Pod stating where it wants to run, a node states which Pods it is willing to accept.
kubectl taint nodes node-1 dedicated=gpu:NoSchedule
spec:
tolerations:
- key: "dedicated"
operator: "Equal"
value: "gpu"
effect: "NoSchedule"
Taint Effects
A taint's effect determines its strength: NoSchedule prevents new Pods without a matching toleration from being placed, PreferNoSchedule is a soft version the scheduler tries to honor, and NoExecute additionally evicts already-running Pods that lack a matching toleration.
Topology-Aware Scheduling
Topology Spread Constraints
Topology spread constraints allow a Pod template to specify how evenly its replicas should be distributed across a given topology domain, such as availability zones or hostnames, balancing availability against the more limited expressiveness of anti-affinity rules alone.
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: codartium-api
Manual and Custom Scheduling
Static Pod Assignment
A Pod can bypass the scheduler entirely by specifying spec.nodeName directly, which is useful in narrow cases but forgoes all scheduler-driven optimization and validation.
Custom Schedulers
Because the scheduler communicates with the rest of the cluster solely through the API server, an entirely separate scheduler binary can run alongside the default one, handling only Pods that declare its name in spec.schedulerName, enabling specialized scheduling logic for particular workload classes without modifying the default scheduler.