Files
rootnode-academy/content/root/root-08-kubernetes-storage-scheduling/m02-scheduling-resource-governance/01-lesson-scheduling.md
T
ced4568andClaude Sonnet 5 73cea1ba1d feat(root): add storage and scheduling curriculum
Two new PARTIAL modules, moving ROOT 08 off PLANNED for the first time:

root-08-m01-storage-fundamentals — ephemeral vs. persistent storage,
PV/PVC/StorageClass. Lesson + knowledge check + real Labs (emptydir-pod,
pv-pvc-mount, pvc-dynamic-task, storageclass-mcq).

root-08-m02-scheduling-resource-governance — how the scheduler places
Pods and how quotas/limits/priority/disruption budgets shape that
placement. Lesson + knowledge check + real Labs (node-label-selector,
node-taint-toleration, pod-affinity-mcq, priority-class-task,
pod-disruption-budget-task, resource-quota-ns, limitrange-task).

cluster-upgrade-mcq and etcd-backup-mcq deliberately left unwired —
ROOT 11/DevOps territory, out of this phase's scope, not overlooked.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-26 09:06:43 -05:00

4.7 KiB

How the Scheduler Places Pods, and How Governance Shapes It

The scheduler's job

Every time a new Pod needs to run, the scheduler decides which node it lands on — you never manually assign a Pod to a node in normal operation (ROOT 05's mental model). By default, the scheduler picks based on available resources (does this node have enough free CPU/memory), but several mechanisms let you INFLUENCE or CONSTRAIN that decision when the default isn't good enough.

Steering placement: nodeSelector, taints/tolerations, affinity

  • nodeSelector — the simplest constraint: "only schedule this Pod on nodes with THIS label" (e.g. disktype: ssd). A blunt but clear tool.
  • Taints and tolerations — the inverse relationship. A NODE is tainted (marked "don't schedule here unless you specifically tolerate this taint") and a Pod TOLERATES a specific taint to be allowed to land there anyway. Commonly used to reserve nodes for specific workloads (e.g. GPU nodes tainted so only GPU-requiring Pods land there) or to keep general workloads off nodes reserved for something else.
  • Affinity/anti-affinity — more expressive rules: "prefer/require scheduling near Pods with label X" (affinity) or "prefer/require scheduling AWAY from Pods with label X" (anti-affinity). A common real use: anti-affinity between replicas of the same application, so a single node failure can't take down every replica at once.

Priority: which Pods matter more when resources are tight

A PriorityClass lets you mark some Pods as more important than others. When the cluster is under resource pressure, a higher-priority pending Pod can even cause a lower-priority RUNNING Pod to be evicted (preempted) to make room — this is a real, consequential mechanism, not just a scheduling hint, and misconfiguring it can mean an unimportant batch job unexpectedly evicts something that actually mattered.

PodDisruptionBudget: protecting against VOLUNTARY disruption

A PodDisruptionBudget (PDB) limits how many replicas of an application can be taken down AT ONCE during voluntary disruptions — a node drain for maintenance, a cluster upgrade — by declaring a minimum number (or percentage) that must stay available. This does NOT protect against involuntary disruption (a node crashing unexpectedly) — it specifically constrains operations that Kubernetes itself controls the timing of.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: web-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels: { app: web }

Without a PDB, a node drain during routine maintenance could legally take down every replica of web simultaneously if they all happened to be on nodes being drained together — a PDB is what prevents that from being "technically allowed."

ResourceQuota & LimitRange: governance at the Namespace level

A ResourceQuota caps the TOTAL resources (CPU, memory, object counts) a Namespace can consume — useful for multi-team clusters where one team's runaway workload shouldn't be able to starve everyone else. A LimitRange sets DEFAULT and/or min/max resource requests/limits for individual containers within a Namespace, so a Pod created without explicit resource settings still gets sensible ones rather than none at all.

Instructor Notes

Approximate timing: 20 minutes read; 15 minutes live given the number of concepts covered.

Likely misconceptions to watch for:

  • Taints/tolerations are frequently confused with affinity — clarify the directionality: a taint REPELS by default (node says no); a toleration is what lets a specific Pod through anyway. Affinity is the Pod expressing a preference, not the node restricting access.
  • Students sometimes assume a PDB prevents ALL disruption. Be explicit that it only constrains VOLUNTARY disruption (things Kubernetes/an operator controls the timing of) — a hardware failure ignores PDBs entirely, because nobody is "asking permission" for a crash.

Discussion question: "Why would a real team use anti-affinity between replicas of the same application instead of just trusting the scheduler's default spread?" (Answer: the default scheduler does consider spread as one factor among several, but doesn't GUARANTEE it the way an explicit anti-affinity rule does — for something where "don't lose all replicas to one node failure" is a hard requirement, not just a nice-to-have, an explicit rule is safer than an implicit tendency.)

Optional extension: ask what would happen to a Namespace's Pods if a ResourceQuota were added AFTER Pods without explicit resource requests already existed there (a good bridge to why LimitRange defaults matter — a Quota alone doesn't retroactively fix Pods created without resource requests, it just constrains what NEW Pods can consume going forward).