✦ For everyone, free.

Practical knowledge for real and everyday life

Home

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.

selected node = argmax n score ( n ) , over feasible nodes n

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.