diff --git a/content/root/root-08-kubernetes-storage-scheduling/m01-storage-fundamentals/01-lesson-storage-fundamentals.md b/content/root/root-08-kubernetes-storage-scheduling/m01-storage-fundamentals/01-lesson-storage-fundamentals.md new file mode 100644 index 0000000..206f8eb --- /dev/null +++ b/content/root/root-08-kubernetes-storage-scheduling/m01-storage-fundamentals/01-lesson-storage-fundamentals.md @@ -0,0 +1,74 @@ +# Ephemeral vs. Persistent Storage + +## The default: a container's filesystem is thrown away + +By default, anything a container writes to its own filesystem disappears the moment that +container is replaced — a crash, a rollout, a reschedule, anything. This is a deliberate design +choice (it's part of what makes a Pod safely disposable/replaceable), but it means "just write +files to disk" silently loses data unless you use one of Kubernetes' actual storage mechanisms. + +## emptyDir: shared, but still temporary + +An `emptyDir` volume gives a Pod's containers a shared directory that survives a container +restart WITHIN the same Pod, but is deleted the moment the Pod itself is removed. It's the right +tool for two containers in one Pod that need to share files with each other (a sidecar writing +logs another container reads, for example) — not for anything that needs to outlive the Pod. + +```yaml +volumes: +- name: shared-data + emptyDir: {} +``` + +## PersistentVolume & PersistentVolumeClaim: storage that outlives the Pod + +A **PersistentVolume (PV)** represents an actual piece of storage (a cloud disk, an NFS share, +whatever the cluster's infrastructure provides) as a Kubernetes object. A +**PersistentVolumeClaim (PVC)** is a Pod's REQUEST for storage matching some criteria (size, +access mode) — the Pod references the PVC, not a PV directly, and Kubernetes binds the PVC to a +matching PV. + +``` +Pod ──references──> PVC ("I need 5Gi, ReadWriteOnce") ──binds to──> PV (actual storage) +``` + +This indirection matters: a Pod's manifest doesn't need to know or care exactly which physical +storage backs its PVC — that's resolved separately, and the same PVC keeps working even if the +Pod behind it is replaced. + +## StorageClass & dynamic provisioning + +Manually creating a PV for every PVC doesn't scale. A **StorageClass** describes a category of +storage (e.g. "fast SSD" vs. "cheap archival") and, when a PVC requests that class, the cluster +can automatically provision a matching PV on demand — this is "dynamic provisioning," and it's +the normal way storage works on any real cloud-hosted cluster. A StorageClass also controls the +**reclaim policy** — what happens to the underlying storage after its PVC is deleted (`Delete` +the data, or `Retain` it for manual recovery). + +## The decision in one pass + +| You need... | Use | +|---|---| +| Temporary scratch space shared between containers in ONE Pod | `emptyDir` | +| Storage that survives Pod replacement, manually provisioned | PV + PVC | +| The above, provisioned automatically on demand | PVC + StorageClass (dynamic provisioning) | + +## Instructor Notes + +**Approximate timing:** 15 minutes read; 10 minutes live with the PV/PVC/StorageClass diagram. + +**Likely misconceptions to watch for:** +- Students often assume `emptyDir` is "persistent" because the name doesn't obviously say + otherwise. Be explicit: it survives a CONTAINER restart, not a POD deletion. +- The PV/PVC indirection can seem like unnecessary ceremony at first — frame it as the same + "declare what you want, let the system resolve it" pattern from ROOT 05's desired-state + lesson, applied to storage instead of Pod count. + +**Discussion question:** "If a Pod using a PVC is deleted and replaced, what happens to its +data?" (Answer: it's preserved — the PVC and its bound PV are separate objects from the Pod, +and a new Pod referencing the same PVC picks up right where the data was left, which is the +entire point of using persistent storage over `emptyDir`.) + +**Optional extension:** ask what a `Retain` reclaim policy is protecting against, compared to +`Delete` (answer: accidental data loss from deleting a PVC by mistake — `Retain` keeps the +underlying storage around for manual recovery instead of immediately destroying it). diff --git a/content/root/root-08-kubernetes-storage-scheduling/m01-storage-fundamentals/02-knowledge-check.md b/content/root/root-08-kubernetes-storage-scheduling/m01-storage-fundamentals/02-knowledge-check.md new file mode 100644 index 0000000..0428e88 --- /dev/null +++ b/content/root/root-08-kubernetes-storage-scheduling/m01-storage-fundamentals/02-knowledge-check.md @@ -0,0 +1,44 @@ +# Knowledge Check: Storage Fundamentals + +--- + +**1. Two containers in the same Pod need to share files with each other, but nothing needs to +survive the Pod being replaced. What's the simplest correct choice?** + +*Answer:* `emptyDir` — shared within the Pod, deliberately temporary, exactly matching the +requirement. Reaching for a PV/PVC here would be more machinery than the problem needs. + +--- + +**2. A Pod using a PVC is deleted and a new Pod is created referencing the SAME PVC. What +happens to the data?** + +*Answer:* It's preserved. The PVC and its bound PV exist independently of any specific Pod — the +new Pod picks up exactly where the data was left, which is the entire reason to use persistent +storage instead of `emptyDir` for anything that needs to outlive a single Pod's lifetime. + +--- + +**3. True or false: a Pod's manifest must specify exactly which PersistentVolume backs its +storage request.** + +*Answer:* False. A Pod references a PVC (a request), not a PV directly — the binding between +PVC and an actual PV is resolved separately, which is exactly the indirection that lets the same +Pod manifest work regardless of which physical storage ends up behind it. + +--- + +**4. What does a StorageClass's reclaim policy actually control, and why would a team choose +`Retain` over `Delete` for a production database's storage?** + +*Answer:* It controls what happens to the underlying storage after its PVC is deleted. `Retain` +keeps the data around for manual recovery instead of immediately destroying it — a safety +margin against an accidental `kubectl delete pvc` on something genuinely important, at the cost +of needing manual cleanup later. + +## Instructor Notes + +**Facilitation:** Question 2 is worth confirming lands correctly — it's the single fact that +justifies the entire PV/PVC indirection existing at all. + +**Approximate timing:** 5 minutes. diff --git a/content/root/root-08-kubernetes-storage-scheduling/m02-scheduling-resource-governance/01-lesson-scheduling.md b/content/root/root-08-kubernetes-storage-scheduling/m02-scheduling-resource-governance/01-lesson-scheduling.md new file mode 100644 index 0000000..a4bbd95 --- /dev/null +++ b/content/root/root-08-kubernetes-storage-scheduling/m02-scheduling-resource-governance/01-lesson-scheduling.md @@ -0,0 +1,86 @@ +# 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). diff --git a/content/root/root-08-kubernetes-storage-scheduling/m02-scheduling-resource-governance/02-knowledge-check.md b/content/root/root-08-kubernetes-storage-scheduling/m02-scheduling-resource-governance/02-knowledge-check.md new file mode 100644 index 0000000..18f4fe0 --- /dev/null +++ b/content/root/root-08-kubernetes-storage-scheduling/m02-scheduling-resource-governance/02-knowledge-check.md @@ -0,0 +1,48 @@ +# Knowledge Check: Scheduling & Resource Governance + +--- + +**1. A node is tainted `dedicated=gpu:NoSchedule`. A Pod with no matching toleration is +pending and never gets scheduled anywhere else, even though other untainted nodes have free +capacity. What's actually going on?** + +*Answer:* Nothing is wrong — the taint only affects the tainted node specifically; it doesn't +explain why the Pod isn't landing elsewhere. If it's stuck pending entirely, the cause is +something else (insufficient resources cluster-wide, a nodeSelector/affinity rule that can't be +satisfied anywhere) — the taint itself only ever repels from the ONE node it's applied to. + +--- + +**2. During a planned node drain for maintenance, would you expect a PodDisruptionBudget to +prevent an UNRELATED node from crashing unexpectedly at the same time?** + +*Answer:* No. A PDB only constrains VOLUNTARY disruptions — operations Kubernetes/an operator +controls the timing of, like a drain. An unrelated hardware failure is involuntary and ignores +PDBs entirely; nothing is "asking permission" for a crash. + +--- + +**3. True or false: a higher-priority pending Pod can cause a lower-priority, already-RUNNING +Pod to be evicted to make room.** + +*Answer:* True — this is what Pod priority/preemption actually does under resource pressure, +not just a scheduling hint. Misconfigured priorities can have real, disruptive consequences for +this reason. + +--- + +**4. A team notices new Pods in their Namespace keep getting created with no CPU/memory +requests at all, even though nobody set that intentionally. What governance mechanism would fix +this going forward, and what would it NOT fix?** + +*Answer:* A LimitRange with default requests/limits would apply sensible defaults to future +Pods created without explicit values. It would NOT retroactively change Pods that already exist +— it only affects new Pod creation from the point it's added. + +## Instructor Notes + +**Facilitation:** Question 2 reinforces a genuinely important, often-missed distinction +(voluntary vs. involuntary disruption) — worth confirming it lands, not just that the answer was +guessed correctly. + +**Approximate timing:** 5-7 minutes given the density of this module's lesson.