feat(root04): author Containers & Docker

Adds all 8 ROOT 04 modules (why containers exist, images & containers, Docker CLI, container
networking, container storage, Dockerfile & image build, Docker Compose, containers →
Kubernetes) — 43 activities, using the nested Docker Engine Phase 6D built and tested. Modules
1, 2, and 8 are conceptual (no lab by design) — the honest bridge module (M8) explicitly names
what Kubernetes adds beyond what this course already taught, without re-teaching Kubernetes
itself.

Adds 5 new hands-on Docker scenarios plus 2 MCQs, all against the real nested Docker Engine:
docker-cli-basics (run/exec/inspect a real container), docker-container-networking (a genuine
"works internally, unreachable from host" port-publishing failure, diagnosed and fixed by
recreating the container with the correct `-p` flag), docker-container-storage (a real
destroy/recreate cycle proving named-volume persistence, not just described), docker-build-image
(a real Dockerfile, a real build, a real custom image run), and docker-compose-multiservice (a
real two-service Compose application with a real published port). All tested end-to-end via the
real scenario-validation API, including negative tests.

Wires rootnode-containers-assessment (2 required + 4 random-draw scenarios) using the existing
Phase 3B assessment engine.

This completes the entire ROOT_ Foundations sequence (ROOT 01-04) — see
docs/PHASE6E_ROOT_FOUNDATIONS_COMPLETION.md.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
2026-08-26 17:08:14 -05:00
co-authored by Claude Sonnet 5
parent e68b6f445a
commit 682ef2de47
45 changed files with 1970 additions and 0 deletions
@@ -0,0 +1,51 @@
# Images and Containers
## The single most important distinction in this course
**An image is a packaged, read-only ARTIFACT. A container is a RUNNING INSTANCE of an image.**
This is exactly analogous to a program's executable file (the image) versus a running process
of that program (the container) — a distinction ROOT 01's process module already gave you
vocabulary for. You can run the SAME image many times, producing many independent containers,
the same way you can run the same program multiple times as separate processes.
## Layers: how an image is actually built
An image is built from a stack of **layers**, each representing one change (install a package,
copy in application code, set a configuration). Layers are cached and shared — if two images
both start `FROM alpine:3.19`, they share that base layer on disk rather than duplicating it,
which is why pulling a new image that shares layers with one you already have is often much
faster than the total image size suggests.
## Registries, repositories, and tags
- **Registry** — a server that stores and distributes images (Docker Hub is the default public
one; a company might run a private registry).
- **Repository** — a named collection of related images (e.g. `nginx`).
- **Tag** — a specific version within a repository (`nginx:alpine`, `nginx:1.25`, `nginx:latest`).
`latest` is just a conventional tag name, not automatically "the newest" in any enforced sense
— relying on it in production is a common, avoidable source of surprise.
## The core commands, mapped to the model
- **`docker pull <image>`** — download an image from a registry, without running it.
- **`docker run <image>`** — create and start a NEW container from an image (pulls automatically
if not already present locally).
- **`docker stop <container>`** — gracefully stop a running container (its process, not the
image).
- **`docker rm <container>`** — delete a stopped container. This does NOT delete the image it
was created from.
## Instructor Notes
**Lesson goal:** Land the image/container distinction as concretely as possible — nearly every
later confusion in this course traces back to conflating the two.
**Approximate timing:** 15 minutes read; 10 minutes live.
**Common misconception to watch for:** using "image" and "container" interchangeably in casual
speech — gently correct this early and often, since precise vocabulary here prevents real
confusion later (e.g., "did you delete the container or the image?" matters).
**Discussion question:** "If you run `docker run nginx:alpine` three times, how many images
exist afterward? How many containers?" (Answer: 1 image, 3 containers — a good check before
moving to the hands-on Lab.)
@@ -0,0 +1,59 @@
# Demonstration: One Image, Many Independent Containers
## OBSERVE
```
$ docker images | grep alpine
alpine 3.19 a1b2c3d4e5f6 2 weeks ago 7.4MB
```
Exactly one `alpine:3.19` image exists locally right now.
## HYPOTHESIS
Predict: after running three containers from it, how many images will `docker images` show?
## INSPECT
```
$ docker run -d --name c1 alpine:3.19 sleep 300
$ docker run -d --name c2 alpine:3.19 sleep 300
$ docker run -d --name c3 alpine:3.19 sleep 300
$ docker images | grep alpine
alpine 3.19 a1b2c3d4e5f6 2 weeks ago 7.4MB
```
Still exactly one image — confirmed.
## CHANGE
```
$ docker ps --format '{{.Names}}: {{.ID}}'
c3: 9f8e7d6c5b4a
c2: 3d2c1b0a9f8e
c1: 7c6b5a4d3e2f
```
Three DIFFERENT containers, each with its own ID, all created from that same one image.
## VERIFY
```
$ docker rm -f c1 c2 c3
$ docker images | grep alpine
alpine 3.19 a1b2c3d4e5f6 2 weeks ago 7.4MB
```
Removing all three containers left the image completely untouched — proving containers and the
image they came from have independent lifecycles.
## Instructor Notes
**Demo goal:** Make the 1-image-many-containers relationship undeniable by actually counting
both before and after, rather than just stating it.
**Approximate timing:** 8 minutes live.
**Common mistake to watch for:** assuming `docker rm` on a container also removes its image —
a good moment to reinforce that images and containers have separate removal commands
(`docker rm` for containers, `docker rmi` for images) for exactly this reason.
@@ -0,0 +1,38 @@
# Knowledge Check: Images and Containers
---
**1. State the image/container distinction in one sentence.**
*Answer:* An image is a packaged, read-only artifact; a container is a running instance created
from an image — the same relationship as a program file and a running process of it.
---
**2. You run `docker rm -f my-container`. Does this delete the image it was created from?**
*Answer:* No — removing a container never removes the image; they have independent lifecycles
and separate removal commands (`docker rm` vs. `docker rmi`).
---
**3. What does the `latest` tag actually guarantee?**
*Answer:* Nothing enforced — it's just a conventional tag name, not automatically "the newest
version" in any technical sense. Relying on it in production is a common source of surprise.
---
**4. What's a layer, and why does layer sharing matter practically?**
*Answer:* A layer is one recorded change in an image's build (installing something, copying
files, etc.). Layers are cached and shared between images that share a base — meaning pulling a
new image that shares layers with one you already have can be much faster than its total
reported size suggests.
## Instructor Notes
**Facilitation:** Question 2 directly reinforces the previous demo — worth confirming everyone
got it right before moving on.
**Approximate timing:** 5 minutes.
@@ -0,0 +1,31 @@
# Guided Exercise: Trace the Counts Through a Sequence of Commands
This is a paper exercise — commit to your answer before checking it in the Lab.
## Your task
Starting from a completely clean system (no images, no containers), predict the number of
IMAGES and the number of CONTAINERS after each step:
1. `docker pull redis:alpine`
2. `docker run -d --name cache1 redis:alpine`
3. `docker run -d --name cache2 redis:alpine`
4. `docker rm -f cache1`
5. `docker rmi redis:alpine` (attempted while `cache2` is still running)
## A worked shape to check yourself against
1. 1 image, 0 containers.
2. 1 image, 1 container.
3. 1 image, 2 containers.
4. 1 image, 1 container (only `cache1` removed).
5. This actually FAILS — Docker refuses to remove an image that a running container (`cache2`)
still depends on, specifically to prevent exactly this kind of accident. Still 1 image, 1
container, plus an error message.
## Instructor Notes
**Facilitation:** Item 5 is the most valuable one — many students haven't seen Docker's
dependency-protection behavior yet, and it's a good real safety feature to highlight.
**Approximate timing:** 8-10 minutes live discussion; 5 minutes self-paced.
@@ -0,0 +1,24 @@
# Reflection: A Small Distinction With Large Consequences
## Reflection questions
1. Before this module, would you have known why `docker rm` and `docker rmi` are two separate
commands? Does it make sense now?
2. Where else in ROOT_ have you seen this exact "artifact vs. running instance" pattern before?
(Hint: think back to ROOT 01.)
## Real Ops takeaway
**Precise vocabulary prevents real mistakes.** Confusing "stop the container" with "delete the
image," or assuming `latest` means "the current version," are genuine, common sources of
production incidents — not just semantic nitpicking. The image/container distinction you just
solidified is the foundation every command in the rest of this course builds on.
This module is complete. Continue to the next module.
## Instructor Notes
**Discussion questions for a live classroom:** question 2's intended answer is "program vs.
process," from ROOT 01 — a good moment to explicitly connect the two courses.
**Approximate timing:** 5-8 minutes live discussion; 5 minutes self-paced writing.