From 25409d3bd7c37bfcbc3422ecfe259bc6c81c2371 Mon Sep 17 00:00:00 2001 From: ced4568 Date: Wed, 26 Aug 2026 09:05:40 -0500 Subject: [PATCH] feat(root): author Kubernetes foundations curriculum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit root-05-m00-kubernetes-mental-model — a new, full gold-standard-pattern front-door module for ROOT 05: what Kubernetes solves, the desired-state model, the control-plane/worker split, and Pods/Namespaces/labels through kubectl. Lesson, demonstration (watching a Pod get replaced after manual deletion), knowledge check, guided exercise (planning a Namespace layout), a real Lab (namespaces-basics), and a reflection — sequenced ahead of the existing bare kubectl-fluency lab bank. Co-Authored-By: Claude Sonnet 5 --- .../01-lesson-mental-model.md | 108 ++++++++++++++++++ .../02-demonstration-desired-state.md | 76 ++++++++++++ .../m00-mental-model/03-knowledge-check.md | 48 ++++++++ .../04-guided-exercise-namespaces.md | 57 +++++++++ .../m00-mental-model/05-lab-intro.md | 44 +++++++ .../m00-mental-model/06-reflection.md | 44 +++++++ 6 files changed, 377 insertions(+) create mode 100644 content/root/root-05-kubernetes-foundations/m00-mental-model/01-lesson-mental-model.md create mode 100644 content/root/root-05-kubernetes-foundations/m00-mental-model/02-demonstration-desired-state.md create mode 100644 content/root/root-05-kubernetes-foundations/m00-mental-model/03-knowledge-check.md create mode 100644 content/root/root-05-kubernetes-foundations/m00-mental-model/04-guided-exercise-namespaces.md create mode 100644 content/root/root-05-kubernetes-foundations/m00-mental-model/05-lab-intro.md create mode 100644 content/root/root-05-kubernetes-foundations/m00-mental-model/06-reflection.md diff --git a/content/root/root-05-kubernetes-foundations/m00-mental-model/01-lesson-mental-model.md b/content/root/root-05-kubernetes-foundations/m00-mental-model/01-lesson-mental-model.md new file mode 100644 index 0000000..94610cd --- /dev/null +++ b/content/root/root-05-kubernetes-foundations/m00-mental-model/01-lesson-mental-model.md @@ -0,0 +1,108 @@ +# What Kubernetes Solves & the Desired-State Model + +## Before Kubernetes: the problem + +Imagine you're running a web application on three servers by hand. One server crashes at 2am — +someone has to notice, log in, and restart the application. Traffic grows, so you need a fourth +server — someone has to provision it, install dependencies, and configure it to match the other +three. You ship a new version — someone has to update all four servers, one at a time, without +taking the whole application down. + +None of this is hard in isolation. It becomes unmanageable at scale, and it depends entirely on +a human noticing and reacting fast enough. Kubernetes exists to make a computer do that +noticing and reacting instead — continuously, automatically, and the same way every time. + +## The core idea: desired state + +You don't tell Kubernetes *how* to run your application step by step. You tell it *what you +want to be true* — "3 replicas of this container should be running" — and Kubernetes +continuously works to make that true, correcting drift whenever reality doesn't match. + +This is the single most important mental shift for someone new to Kubernetes: you're not +writing a script that runs once. You're declaring a **desired state**, and a background process +never stops comparing the real world against it. + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: web +spec: + replicas: 3 # <- the desired state: "3 of these, always" + ... +``` + +If a Pod crashes, Kubernetes doesn't "restart the crashed one" as a special case — it just +notices reality (2 running) no longer matches desired state (3 wanted) and creates a +replacement. Same mechanism, no special-casing. + +## The cluster mental model + +A Kubernetes cluster has two conceptually different kinds of machines: + +- **The control plane** — the "brain." It stores the desired state (in `etcd`), watches actual + state, and makes decisions (the API server, scheduler, and controllers all live here). You + talk to the control plane through `kubectl`; you never SSH into it to manage workloads. +- **Worker nodes** — where your containers actually run. Each node runs a `kubelet` (an agent + that starts/stops containers as the control plane instructs) and a container runtime. + +You describe desired state to the control plane. The control plane decides which nodes should +run what. The nodes' kubelets make it actually happen. You never manually place a Pod on a +specific node in normal operation — that placement decision belongs to the scheduler. + +## Declarative, not imperative + +"Imperative" means giving step-by-step instructions ("start a container, then another, then +configure networking between them"). "Declarative" means describing an end state and letting +the system figure out the steps. Kubernetes' API objects (Pods, Deployments, Services, and +everything else you'll learn in ROOT_) are declarative — you write YAML describing what should +exist, `kubectl apply` submits it, and the control plane does the rest. + +This is also why Kubernetes YAML looks the way it does: it's not a script, it's a specification. + +## Pods, Namespaces, and labels — the vocabulary you need first + +- **Pod** — the smallest deployable unit. Usually one container, sometimes a small group of + tightly-coupled containers that must run together on the same node and share networking. + You'll rarely create bare Pods directly in real use (a Deployment manages them for you, see + ROOT 06) but understanding the Pod itself is foundational to everything built on top of it. +- **Namespace** — a way to partition one cluster into logically separate areas (e.g. `dev`, + `staging`, teams, or projects), so names don't collide and access/quotas can be scoped. +- **Labels** — arbitrary key/value tags attached to objects (`app: web`, `env: prod`). Labels + are how almost everything in Kubernetes finds related objects — a Deployment finds "its" Pods + by label selector, not by name pattern matching. + +## kubectl: your one tool for all of this + +`kubectl` is the command-line client that talks to the control plane's API server. Nearly +everything you'll do in ROOT_ Labs goes through it: `kubectl get` (list/inspect), +`kubectl describe` (detailed status + recent events — your primary troubleshooting tool), +`kubectl apply -f` (submit a desired-state YAML file), `kubectl logs` (see what a container +printed). You'll build real fluency with these across this module and the ones that follow. + +## Instructor Notes + +**Approximate timing:** 15 minutes read, 10-12 minutes for a live walkthrough with discussion. + +**Demo plan:** Before the demonstration activity, it's worth drawing the control-plane/worker +split on a whiteboard (or sharing a simple diagram) and tracing one request: `kubectl apply` -> +API server -> etcd -> scheduler -> kubelet -> container runtime. Students retain the mental +model much better when they've seen the path, not just read about it. + +**Likely misconceptions to watch for:** +- Students often assume `kubectl` talks directly to nodes/containers. It doesn't — it only ever + talks to the API server. Worth stating explicitly. +- "Declarative" is sometimes read as "automatic" in a vague sense. Be concrete: it means "you + describe the end state; a controller loop makes it true," not "Kubernetes guesses what you + want." + +**Discussion question:** "If desired state says 3 replicas and you manually run `kubectl delete +pod` on one of them, what happens next, and why?" (Answer: a new one is created almost +immediately — deleting a Pod doesn't change the Deployment's desired replica count, so the +control loop restores it.) + +**Optional extension:** Ask students to predict what would happen if they edited a running Pod's +container image directly with `kubectl edit pod` instead of updating the Deployment that manages +it (answer: it works until that specific Pod is replaced for any reason, at which point the new +Pod reverts to whatever the Deployment's template says — a good early illustration of "the +Deployment is the source of truth, not the running Pod"). diff --git a/content/root/root-05-kubernetes-foundations/m00-mental-model/02-demonstration-desired-state.md b/content/root/root-05-kubernetes-foundations/m00-mental-model/02-demonstration-desired-state.md new file mode 100644 index 0000000..badede5 --- /dev/null +++ b/content/root/root-05-kubernetes-foundations/m00-mental-model/02-demonstration-desired-state.md @@ -0,0 +1,76 @@ +# Demonstration: Desired State Reconciliation + +Watch what actually happens when reality drifts from desired state — this is the mechanism the +whole lesson describes, made concrete. + +## Setup + +A Namespace called `demo` exists with a Deployment `web` declaring `replicas: 2`. Right now, +reality matches desired state: + +``` +$ kubectl get pods -n demo +NAME READY STATUS RESTARTS AGE +web-7d8f9c6b5d-abcde 1/1 Running 0 3m +web-7d8f9c6b5d-fghij 1/1 Running 0 3m +``` + +## Someone (or something) deletes one Pod + +``` +$ kubectl delete pod web-7d8f9c6b5d-abcde -n demo +pod "web-7d8f9c6b5d-abcde" deleted +``` + +## What happens in the next second or two, without anyone doing anything else + +``` +$ kubectl get pods -n demo +NAME READY STATUS RESTARTS AGE +web-7d8f9c6b5d-fghij 1/1 Running 0 3m +web-7d8f9c6b5d-klmno 0/1 ContainerCreating 0 1s +``` + +A brand new Pod (`...-klmno`, a new random suffix — this is NOT the deleted Pod coming back) +appeared on its own. Nobody ran `kubectl create` again. The Deployment's controller noticed +actual state (1 running Pod matching its label selector) no longer matched desired state +(`replicas: 2`), and created exactly one replacement to close the gap — not two, not zero. + +A few seconds later: + +``` +$ kubectl get pods -n demo +NAME READY STATUS RESTARTS AGE +web-7d8f9c6b5d-fghij 1/1 Running 0 3m +web-7d8f9c6b5d-klmno 1/1 Running 0 8s +``` + +Back to 2/2 — desired state restored, entirely without human intervention. + +## Why this matters beyond the demo + +This exact mechanism is what makes a node crash, a Pod eviction, or a container being +OOM-killed something Kubernetes recovers from automatically instead of something that pages a +human at 2am. The controller loop doesn't know or care WHY a Pod disappeared — crash, manual +delete, node failure — it only ever asks "does reality match desired state right now?" and acts +if not. Understanding this one loop explains a huge amount of Kubernetes' behavior that would +otherwise look like unrelated special cases. + +## Instructor Notes + +**Demo script:** Run this live rather than only showing the captured output above — have +students watch `kubectl get pods -n demo --watch` in one terminal while you delete a Pod from +another. Watching the new Pod name/age appear in real time lands the "this is a NEW Pod, not a +restart" point far better than static text. + +**Approximate timing:** 8-10 minutes live; 5 minutes as a self-paced read. + +**Common misconception to correct:** students frequently describe this as "Kubernetes restarted +the Pod." Correct this explicitly — a deleted Pod is gone permanently; a *new* Pod object was +created to satisfy the replica count. This distinction matters later when debugging (the new +Pod has zero history from the old one — no old logs, no old restart count). + +**Optional extension:** ask what would happen if you deleted the Deployment itself instead of +one Pod (answer: all of its Pods are deleted and NOT replaced, because the thing declaring +desired state is now gone — a good way to reinforce that the Deployment, not any individual +Pod, is the actual source of truth). diff --git a/content/root/root-05-kubernetes-foundations/m00-mental-model/03-knowledge-check.md b/content/root/root-05-kubernetes-foundations/m00-mental-model/03-knowledge-check.md new file mode 100644 index 0000000..e7f015e --- /dev/null +++ b/content/root/root-05-kubernetes-foundations/m00-mental-model/03-knowledge-check.md @@ -0,0 +1,48 @@ +# Knowledge Check: The Kubernetes Mental Model + +Answer each question yourself before reading the answer that follows it. + +--- + +**1. You run `kubectl delete pod` on one Pod belonging to a Deployment with `replicas: 3`. A +few seconds later, `kubectl get pods` shows 3 Pods again, but one has a different name than any +of the original three. What actually happened?** + +*Answer:* The deleted Pod is gone permanently. The Deployment's controller noticed actual state +(2 Pods) no longer matched desired state (3 Pods) and created ONE new Pod to close the gap. It +is not the old Pod restarting — it's a brand new object. + +--- + +**2. A Deployment has `replicas: 3`, and there are 5 nodes in the cluster. Which of these +decides WHICH node each of the 3 Pods actually lands on?** + +*Answer:* The scheduler (part of the control plane), not the person who wrote the Deployment +YAML and not the nodes themselves. You declare desired state; the scheduler makes the placement +decision based on available resources and any placement rules configured. + +--- + +**3. True or false: `kubectl` communicates directly with the kubelet on whichever node your +Pod happens to be running on.** + +*Answer:* False. `kubectl` only ever talks to the API server (part of the control plane). The +API server is the single entry point for every read and write — it never bypasses the control +plane to reach a node directly for a normal `get`/`apply`/`describe` call. + +--- + +**4. Why is Kubernetes YAML described as "declarative" rather than a script?** + +*Answer:* A script describes a sequence of steps to execute once. Kubernetes YAML describes an +END STATE you want to exist ("3 replicas of this container should be running") — a controller +loop then continuously works to keep reality matching that description, correcting drift +whenever it appears, indefinitely, not just once at apply time. + +## Instructor Notes + +**Facilitation:** Ask these aloud in a live classroom before revealing answers — question 1 in +particular tends to surface the "it restarted" misconception the demonstration activity should +have already addressed; if it hasn't landed, this is the moment to circle back. + +**Approximate timing:** 5 minutes. diff --git a/content/root/root-05-kubernetes-foundations/m00-mental-model/04-guided-exercise-namespaces.md b/content/root/root-05-kubernetes-foundations/m00-mental-model/04-guided-exercise-namespaces.md new file mode 100644 index 0000000..0c55f4e --- /dev/null +++ b/content/root/root-05-kubernetes-foundations/m00-mental-model/04-guided-exercise-namespaces.md @@ -0,0 +1,57 @@ +# Guided Exercise: Planning a Namespace Layout + +This is a paper-and-thinking exercise — work through it before the Lab, not during it. + +## The scenario + +You're setting up a small Kubernetes cluster for a class project with three teams (`frontend`, +`backend`, `data`), each running their own set of Pods, and you also need a shared area for +tools everyone uses (a shared logging dashboard, for example). + +## Your task + +Sketch out, on paper or in a scratch file: + +1. What Namespaces would you create, and why? (Hint: think about who needs to see what, and + whether any team's mistake — e.g. deleting the wrong Pod by accident — should be able to + affect another team.) +2. If `frontend` team members should be able to manage their own Pods but NOT touch `backend`'s + or `data`'s, what does that suggest about how Namespaces relate to access control? (You'll + learn the actual mechanism, RBAC, in ROOT 09 — for now just reason about why Namespace + boundaries make that kind of restriction possible at all.) +3. Would you put the shared logging dashboard in one of the three team Namespaces, or somewhere + else? Justify your answer. +4. If two teams both need to run something they each call `web`, what would happen if they were + in the SAME Namespace? What changes if they're in separate Namespaces? + +## A reasonable answer to check yourself against + +1. Three Namespaces (`frontend`, `backend`, `data`) — one per team, so each team's objects are + isolated from the others and a mistake in one Namespace (deleting the wrong Pod, applying a + bad manifest) can't accidentally affect a different team's workloads. +2. Namespace boundaries are what RBAC rules can be scoped TO — "this person can manage Pods in + the `frontend` Namespace" is a real, common permission model. Without separate Namespaces, + you'd have to distinguish access by resource name pattern instead of by boundary, which is + far more error-prone. +3. A separate Namespace (e.g. `shared-tools` or `platform`) — it isn't owned by any one team, + and mixing it into a team's Namespace would make it unclear who's responsible for it or + accidentally couple its lifecycle to that team's. +4. In the SAME Namespace, object names must be unique — a second `web` Deployment would either + fail to create or overwrite the first, depending on how it's applied. In SEPARATE + Namespaces, both `frontend/web` and `backend/web` can coexist without conflict — the + Namespace is part of what makes a name unique, not just the name itself. + +If your answer differed meaningfully from this, re-read the lesson's Namespace section before +continuing to the Lab. + +## Instructor Notes + +**Facilitation:** Good as a 2-3 person small-group discussion in a live classroom before +revealing the reasonable answer — question 4 in particular often produces a genuine "oh" moment +once students realize name uniqueness is Namespace-scoped, not cluster-wide. + +**Approximate timing:** 10 minutes live with discussion; 10 minutes self-paced. + +**Optional extension:** ask what would need to be true for the `data` team to be ALLOWED to read +(but not modify) something in `shared-tools` — a good bridge to RBAC's read vs. write verbs, +covered properly in ROOT 09. diff --git a/content/root/root-05-kubernetes-foundations/m00-mental-model/05-lab-intro.md b/content/root/root-05-kubernetes-foundations/m00-mental-model/05-lab-intro.md new file mode 100644 index 0000000..413790f --- /dev/null +++ b/content/root/root-05-kubernetes-foundations/m00-mental-model/05-lab-intro.md @@ -0,0 +1,44 @@ +# Launch Lab: Working with Namespaces + +Time to apply the mental model for real — you'll create an actual isolated Namespace and deploy +into it, in your own disposable Lab environment. + +## What you're doing + +You'll create a Namespace (`team-alpha`) and deploy an application into it with a specific +replica count — directly applying the "isolated area within one cluster" idea from the guided +exercise, and the desired-state model from the lesson (once you declare `replicas: 2`, that +becomes the standing desired state for that Deployment, enforced continuously). + +## Before you start + +Think back to the demonstration: once your Deployment exists with its desired replica count, +Kubernetes will keep it there on its own. You don't need to "check back" and manually fix +anything — if you want to prove this to yourself, try deleting one of your Pods after you've +finished the task and watch what happens. + +## Your task in this Lab + +Create the Namespace, then deploy the application inside it with the exact replica count +specified. Use `kubectl get namespace` and `kubectl get deployment -n ` to confirm +your work — the same verification pattern you'll use constantly throughout ROOT_. + +This Lab has its own in-context hints if you get stuck. Try working from the lesson and guided +exercise first — the commands you need are exactly the ones described there. + +When you're done, return to this module to close out with a reflection on why this isolation +matters beyond just "keeping things organized." + +## Instructor Notes + +**Classroom flow:** This is most students' first real `kubectl apply`/`create` in ROOT_ beyond +whatever pilot exposure they've had — a good moment to circulate and check that basic +`kubectl` command syntax isn't the blocker (as opposed to the Namespace concept itself, which +the guided exercise should have already addressed). + +**Approximate timing:** 10-15 minutes. + +**Common mistake to watch for:** creating the Deployment in the DEFAULT namespace instead of +`team-alpha` (forgetting `-n team-alpha` or the namespace field in the manifest) — this is a +very common first mistake and a good real-world illustration of why Namespace isolation only +works if you're actually specifying it every time. diff --git a/content/root/root-05-kubernetes-foundations/m00-mental-model/06-reflection.md b/content/root/root-05-kubernetes-foundations/m00-mental-model/06-reflection.md new file mode 100644 index 0000000..a868868 --- /dev/null +++ b/content/root/root-05-kubernetes-foundations/m00-mental-model/06-reflection.md @@ -0,0 +1,44 @@ +# Reflection: Why Desired State Matters in Production + +You just created an isolated Namespace and a Deployment with a declared replica count. Take a +few minutes to connect this to why it matters beyond passing this Lab's validation. + +## Reflection questions + +1. If your `team-alpha` Namespace were a real team's production environment, what's one mistake + in a DIFFERENT namespace that Namespace isolation would have protected you from? +2. You declared `replicas: 2` rather than manually creating two Pods. What would you have had + to do manually, over and over, if Kubernetes only supported creating individual Pods with no + concept of desired state? +3. Think of one non-Kubernetes system you've used (a thermostat, a spell-checker, an + auto-save feature) that also works by continuously comparing "what is" to "what should be." + What made you think of it? + +## Real Ops takeaway + +**Namespaces are your first real production safety boundary, and they're free.** A +misconfigured `kubectl delete` scoped to the wrong Namespace is a common, entirely preventable +class of incident — teams that consistently namespace their environments (dev/staging/prod, or +per-team) limit the blast radius of almost any operator mistake to just that Namespace. This +costs nothing to set up and prevents a real, common category of outage. + +**Desired state is what makes self-healing possible at all.** Every "the platform recovered on +its own" story you'll hear about Kubernetes in production traces back to exactly the mechanism +you just watched in the demonstration: a controller comparing actual state to desired state and +correcting the gap. Understanding this loop is what lets you predict Kubernetes' behavior +instead of being surprised by it. + +This module is complete. Continue to the next module. + +## Instructor Notes + +**Discussion questions for a live classroom:** use all three reflection questions as discussion +prompts. Question 3 in particular tends to produce good analogies that make the abstract +"controller loop" concept concrete and memorable. + +**Approximate timing:** 8-10 minutes live discussion; 5 minutes self-paced writing. + +**Optional extension:** ask students to name one place in their own life (not computing) where +"declare the goal and let a system enforce it" would work better than "manually do the thing +every time" — a genuinely reflective prompt that reinforces the mental model beyond Kubernetes +specifically.