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]>
87 lines
4.7 KiB
Markdown
87 lines
4.7 KiB
Markdown
# 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.
|
|
|
|
```yaml
|
|
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).
|