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:
+61
@@ -0,0 +1,61 @@
|
||||
# Container Storage
|
||||
|
||||
## The default: a disposable writable layer
|
||||
|
||||
Every running container gets its own writable layer sitting on top of its image's read-only
|
||||
layers. Anything written there — a log file, a temp file, application data — exists ONLY as long
|
||||
as that specific container does. Destroy the container, and that data is gone permanently. This
|
||||
is ROOT 02's "persistent vs. temporary" question, given a hard default answer: by default,
|
||||
everything in a container is temporary.
|
||||
|
||||
## Bind mounts: sharing a specific host path
|
||||
|
||||
A **bind mount** connects a specific path on the HOST filesystem directly into the container:
|
||||
|
||||
```
|
||||
docker run -v /host/path:/container/path myimage
|
||||
```
|
||||
|
||||
Changes are visible on BOTH sides immediately — useful for development (edit code on the host,
|
||||
see changes in the container instantly) but tightly coupled to that exact host's filesystem
|
||||
layout, which makes it a poor choice for portable, production data.
|
||||
|
||||
## Named volumes: Docker-managed persistent storage
|
||||
|
||||
A **named volume** is storage Docker manages for you, independent of any specific host path:
|
||||
|
||||
```
|
||||
docker volume create my-data
|
||||
docker run -v my-data:/container/path myimage
|
||||
```
|
||||
|
||||
A named volume's data survives container destruction AND recreation — attaching the SAME volume
|
||||
to a brand-new container gives that new container access to exactly the same data the old one
|
||||
left behind. This is the deliberate, production-appropriate way to persist container data.
|
||||
|
||||
## The test that actually proves persistence
|
||||
|
||||
Claiming "this data persists" isn't convincing without proof: write data, DESTROY the container
|
||||
completely, create a brand new one attached to the same volume, and confirm the data is still
|
||||
there. This module's Lab requires exactly that proof, not just a claim.
|
||||
|
||||
## Bridge to Kubernetes
|
||||
|
||||
A Kubernetes `PersistentVolume`/`PersistentVolumeClaim` is the same idea generalized across an
|
||||
entire cluster instead of one machine — the exact concept this module teaches, just orchestrated
|
||||
at a larger scale, which Kubernetes courses build on directly.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Lesson goal:** Make "containers are disposable, volumes are not" a hard, memorable
|
||||
distinction — directly building on ROOT 02's persistent/temporary framing.
|
||||
|
||||
**Approximate timing:** 15 minutes read; 10 minutes live.
|
||||
|
||||
**Common misconception to watch for:** assuming ANY container data survives by default — the
|
||||
default is the OPPOSITE; persistence requires deliberate action (a volume or bind mount).
|
||||
|
||||
**Discussion question:** "Why might a named volume be preferred over a bind mount for
|
||||
production data, even though both technically 'persist' data?" (Answer: a named volume isn't
|
||||
tied to one specific host's exact filesystem path, making it more portable across different
|
||||
hosts/environments.)
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
# Demonstration: Proving Data Loss, Then Proving Persistence
|
||||
|
||||
## OBSERVE
|
||||
|
||||
```
|
||||
$ docker run -d --name no-volume alpine:3.19 sleep 300
|
||||
$ docker exec no-volume sh -c "echo important-data > /tmp/data.txt"
|
||||
$ docker exec no-volume cat /tmp/data.txt
|
||||
important-data
|
||||
```
|
||||
|
||||
Data exists — but is it actually safe?
|
||||
|
||||
## HYPOTHESIS
|
||||
|
||||
Predict: will `/tmp/data.txt` still exist if this exact container is destroyed and an identical
|
||||
new one is started?
|
||||
|
||||
## INSPECT
|
||||
|
||||
```
|
||||
$ docker rm -f no-volume
|
||||
$ docker run -d --name no-volume alpine:3.19 sleep 300
|
||||
$ docker exec no-volume cat /tmp/data.txt
|
||||
cat: /tmp/data.txt: No such file or directory
|
||||
```
|
||||
|
||||
Gone — proven, not assumed. The "new" container is a completely fresh writable layer, even
|
||||
though it has the exact same name and image.
|
||||
|
||||
## CHANGE
|
||||
|
||||
Now with a named volume instead:
|
||||
|
||||
```
|
||||
$ docker rm -f no-volume
|
||||
$ docker volume create demo-vol
|
||||
$ docker run -d --name with-volume -v demo-vol:/data alpine:3.19 sleep 300
|
||||
$ docker exec with-volume sh -c "echo important-data > /data/data.txt"
|
||||
$ docker rm -f with-volume
|
||||
$ docker run -d --name with-volume -v demo-vol:/data alpine:3.19 sleep 300
|
||||
```
|
||||
|
||||
## VERIFY
|
||||
|
||||
```
|
||||
$ docker exec with-volume cat /data/data.txt
|
||||
important-data
|
||||
```
|
||||
|
||||
Proven, not assumed — the exact same destroy-and-recreate sequence, but this time the data
|
||||
survived, because it lived in the volume, not the container's disposable writable layer.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Demo goal:** Prove BOTH the loss and the persistence with real destroy/recreate cycles — the
|
||||
contrast is what makes this lesson land, not either half alone.
|
||||
|
||||
**Approximate timing:** 10 minutes live.
|
||||
|
||||
**Common mistake to watch for:** stopping (not removing) a container and concluding "data
|
||||
persists" — `docker stop` doesn't touch the writable layer at all; only `docker rm` (destroying
|
||||
the container entirely) actually tests real persistence.
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
# Knowledge Check: Container Storage
|
||||
|
||||
---
|
||||
|
||||
**1. By default, what happens to data written inside a running container when that container is
|
||||
destroyed?**
|
||||
|
||||
*Answer:* It's lost permanently — a container's writable layer is disposable by default; nothing
|
||||
persists unless a volume or bind mount was explicitly configured.
|
||||
|
||||
---
|
||||
|
||||
**2. What's the actual difference between a bind mount and a named volume?**
|
||||
|
||||
*Answer:* A bind mount connects a specific HOST filesystem path directly into the container
|
||||
(tightly coupled to that host's exact layout). A named volume is storage Docker manages
|
||||
independently of any specific host path, making it more portable and the production-appropriate
|
||||
choice.
|
||||
|
||||
---
|
||||
|
||||
**3. Does `docker stop` (without removing) test whether a container's data actually persists?**
|
||||
|
||||
*Answer:* No — stopping doesn't touch the writable layer at all. Only fully removing
|
||||
(`docker rm`) and recreating the container is a real test of persistence.
|
||||
|
||||
---
|
||||
|
||||
**4. What Kubernetes concept is this module's volume idea the direct foundation for?**
|
||||
|
||||
*Answer:* PersistentVolume/PersistentVolumeClaim — the same persistence concept, generalized
|
||||
across an entire cluster instead of one machine.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** Question 3 is worth emphasizing — it's a genuine, common mistake in verifying
|
||||
persistence claims.
|
||||
|
||||
**Approximate timing:** 5 minutes.
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
# Guided Exercise: Predict What Survives Before You Test It
|
||||
|
||||
This is a paper exercise — commit to predictions before working in the Lab.
|
||||
|
||||
## Your task
|
||||
|
||||
For each scenario, predict whether the data survives a `docker rm -f` followed by recreating a
|
||||
container with the same name and image:
|
||||
|
||||
1. Data written to `/tmp/file.txt` inside a container with no volumes configured.
|
||||
2. Data written to `/data/file.txt`, where `/data` is mounted from a named volume.
|
||||
3. Data written to `/data/file.txt` in scenario 2, but this time you ALSO run
|
||||
`docker volume rm <the-volume>` before recreating the container.
|
||||
|
||||
## A worked shape to check yourself against
|
||||
|
||||
1. Lost — no volume, so it lived only in the disposable writable layer.
|
||||
2. Survives — the volume itself is untouched by the container's destruction, and the new
|
||||
container reattaches to the same volume.
|
||||
3. Lost — the volume itself was explicitly deleted, not just the container. Persistence depends
|
||||
on the VOLUME surviving, not just avoiding container destruction.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** Item 3 is the important trick question — students who've internalized
|
||||
"volumes always persist" without qualification should be corrected here: the volume itself can
|
||||
still be deleted deliberately.
|
||||
|
||||
**Approximate timing:** 8-10 minutes live discussion; 5 minutes self-paced.
|
||||
@@ -0,0 +1,27 @@
|
||||
# Reflection: You Proved Persistence, You Didn't Just Trust It
|
||||
|
||||
## Reflection questions
|
||||
|
||||
1. Before this module, would you have assumed container data persists by default? How
|
||||
expensive would that wrong assumption be in a real production incident?
|
||||
2. Why did this module insist on actually DESTROYING a container to test persistence, rather
|
||||
than just describing the concept?
|
||||
|
||||
## Real Ops takeaway
|
||||
|
||||
**"I assumed it would persist" is one of the most expensive assumptions in real operations
|
||||
work.** You didn't just learn that volumes persist data — you personally destroyed a container
|
||||
and confirmed data loss, then destroyed another and confirmed data survival, with the SAME
|
||||
sequence of commands each time except for one deliberate volume configuration. That's the level
|
||||
of confidence "verify, don't assume" is supposed to give you, and it's exactly the discipline
|
||||
ROOT 02's troubleshooting framework already built.
|
||||
|
||||
This module is complete. Continue to the next module.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Discussion questions for a live classroom:** question 1 works well as a real cautionary
|
||||
example — ask if anyone has heard of (or experienced) a real data-loss incident caused by this
|
||||
exact wrong assumption.
|
||||
|
||||
**Approximate timing:** 5-8 minutes live discussion; 5 minutes self-paced writing.
|
||||
Reference in New Issue
Block a user