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:
+65
@@ -0,0 +1,65 @@
|
||||
# Docker Compose
|
||||
|
||||
## Real applications are rarely just one container
|
||||
|
||||
A web app usually needs a database, maybe a cache, maybe a background worker — several
|
||||
containers that need to be created, networked together, and started in a sensible order. Doing
|
||||
this manually with individual `docker run` commands works, but gets tedious and error-prone fast,
|
||||
especially remembering the exact flags every time.
|
||||
|
||||
## One file, the whole application
|
||||
|
||||
**Docker Compose** describes an entire multi-container application in one YAML file:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
web:
|
||||
image: nginx:alpine
|
||||
ports:
|
||||
- "8080:80"
|
||||
depends_on:
|
||||
- api
|
||||
api:
|
||||
image: my-api:1
|
||||
```
|
||||
|
||||
- **`services`** — each entry is one container, using the exact same concepts (image, ports,
|
||||
etc.) you already know from plain `docker run`.
|
||||
- **`depends_on`** — controls STARTUP order (api starts before web) — note this ensures order,
|
||||
not that the dependency is fully READY yet, a subtlety real applications often need extra
|
||||
handling for.
|
||||
- **Networking is automatic** — Compose creates a shared network for all services in the file by
|
||||
default, so `web` can reach `api` by name immediately, no manual `docker network create`
|
||||
needed.
|
||||
|
||||
## The commands
|
||||
|
||||
```
|
||||
docker compose up -d # start every service defined in the file, in the background
|
||||
docker compose ps # see the status of every service
|
||||
docker compose logs # see logs from every service, or docker compose logs <service>
|
||||
docker compose down # stop and remove everything Compose created
|
||||
```
|
||||
|
||||
## Compose doesn't replace anything you've learned — it automates it
|
||||
|
||||
Every Compose service maps directly onto a `docker run` command you could write by hand; Compose
|
||||
just describes the whole set declaratively in one file instead of remembering and re-typing
|
||||
several long commands correctly, every time, in the right order.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Lesson goal:** Frame Compose as automation of already-understood concepts, not new magic —
|
||||
students should be able to mentally translate a `services:` block back into individual
|
||||
`docker run` commands.
|
||||
|
||||
**Approximate timing:** 15 minutes read; 10 minutes live.
|
||||
|
||||
**Common misconception to watch for:** treating `depends_on` as "waits until fully healthy" —
|
||||
it only controls START order, not readiness; worth flagging as a real, common source of race
|
||||
conditions in actual production Compose files.
|
||||
|
||||
**Discussion question:** "Why might describing your application declaratively in one file be
|
||||
better than a shell script that runs several `docker run` commands in sequence?" (Good answer:
|
||||
reviewable, versionable, and Compose itself understands the relationships between services,
|
||||
rather than just blindly executing a sequence.)
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
# Demonstration: One File, Two Containers, One Network — Automatically
|
||||
|
||||
## OBSERVE
|
||||
|
||||
```yaml
|
||||
services:
|
||||
web:
|
||||
image: nginx:alpine
|
||||
ports:
|
||||
- "8080:80"
|
||||
cache:
|
||||
image: redis:alpine
|
||||
```
|
||||
|
||||
## HYPOTHESIS
|
||||
|
||||
Predict: without running `docker network create` yourself, will `web` be able to reach `cache`
|
||||
by name after `docker compose up`?
|
||||
|
||||
## INSPECT
|
||||
|
||||
```
|
||||
$ docker compose up -d
|
||||
$ docker compose exec web sh -c "wget -qO- http://cache:6379/ 2>&1 | head -1"
|
||||
```
|
||||
|
||||
(Redis doesn't speak HTTP, so this specific request looks odd, but the connection ITSELF
|
||||
succeeding or failing tells you whether name resolution and network reachability work — a
|
||||
`Connection refused` here would mean it REACHED `cache` but Redis rejected the protocol
|
||||
mismatch, which is actually proof the network worked.)
|
||||
|
||||
## CHANGE
|
||||
|
||||
```
|
||||
$ docker compose ps
|
||||
NAME IMAGE STATUS
|
||||
web-1 nginx:alpine Up
|
||||
cache-1 redis:alpine Up
|
||||
$ docker network ls | grep compose
|
||||
xyz123 myproject_default bridge
|
||||
```
|
||||
|
||||
Compose created a network automatically, named after the project directory, with no manual step.
|
||||
|
||||
## VERIFY
|
||||
|
||||
```
|
||||
$ docker compose down
|
||||
$ docker network ls | grep compose
|
||||
(nothing)
|
||||
```
|
||||
|
||||
`docker compose down` cleaned up EVERYTHING Compose created — containers AND the network — in
|
||||
one command.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Demo goal:** Show the automatic networking as a real, observable fact (a real network exists,
|
||||
named and scoped to the project) rather than an assumed convenience.
|
||||
|
||||
**Approximate timing:** 8 minutes live.
|
||||
|
||||
**Common mistake to watch for:** forgetting `docker compose down` and accumulating orphaned
|
||||
networks/containers across multiple exercises — a good habit to reinforce explicitly.
|
||||
@@ -0,0 +1,40 @@
|
||||
# Knowledge Check: Docker Compose
|
||||
|
||||
---
|
||||
|
||||
**1. What does `depends_on` actually guarantee — and NOT guarantee?**
|
||||
|
||||
*Answer:* It guarantees START order (the dependency's container is started before the dependent
|
||||
service's). It does NOT guarantee the dependency is actually READY/healthy yet — a real,
|
||||
common source of race conditions in production Compose files.
|
||||
|
||||
---
|
||||
|
||||
**2. Do you need to manually run `docker network create` for services in the same Compose file
|
||||
to reach each other by name?**
|
||||
|
||||
*Answer:* No — Compose automatically creates a shared network for all services defined in the
|
||||
file, and they can reach each other by service name immediately.
|
||||
|
||||
---
|
||||
|
||||
**3. What does `docker compose down` clean up, that plain `docker compose stop` would not?**
|
||||
|
||||
*Answer:* `down` removes the containers AND the network Compose created; `stop` only stops the
|
||||
containers, leaving them (and the network) in place to be restarted later.
|
||||
|
||||
---
|
||||
|
||||
**4. Does Compose introduce any concept that isn't already just automating `docker run`/
|
||||
`docker network` commands you already know?**
|
||||
|
||||
*Answer:* No — every Compose service maps directly onto concepts already covered
|
||||
(image, ports, networking); Compose automates and declaratively describes them, it doesn't add
|
||||
new underlying mechanisms.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** Question 1 is worth real emphasis — it's a genuine, common production
|
||||
mistake worth preventing early.
|
||||
|
||||
**Approximate timing:** 5 minutes.
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
# Guided Exercise: Translate a Compose File Back Into Plain `docker run`
|
||||
|
||||
This is a paper exercise — commit to your translation before checking it in the Lab.
|
||||
|
||||
## Your task
|
||||
|
||||
Given this Compose service definition, write the equivalent plain `docker run` command by hand:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
web:
|
||||
image: nginx:alpine
|
||||
ports:
|
||||
- "8080:80"
|
||||
```
|
||||
|
||||
Then explain: what would you ALSO need to do manually (that Compose does automatically) if two
|
||||
services needed to reach each other by name?
|
||||
|
||||
## A worked shape to check yourself against
|
||||
|
||||
```
|
||||
docker run -d -p 8080:80 nginx:alpine
|
||||
```
|
||||
|
||||
For two services to reach each other by name manually, you'd need to `docker network create` a
|
||||
shared network yourself, then add `--network <that-network>` to EACH container's `docker run`
|
||||
command — exactly the manual step Compose eliminates.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** This exercise is the clearest possible proof that Compose isn't new magic —
|
||||
being able to translate it back to `docker run` confirms genuine understanding, not just
|
||||
familiarity with YAML syntax.
|
||||
|
||||
**Approximate timing:** 8-10 minutes live discussion; 5 minutes self-paced.
|
||||
@@ -0,0 +1,26 @@
|
||||
# Reflection: A Real Multi-Service Application, Described and Running
|
||||
|
||||
## Reflection questions
|
||||
|
||||
1. Before this module, how would you have run two related containers and connected them
|
||||
manually? How does that compare to this module's Lab?
|
||||
2. What does a Compose file's `services:` block remind you of, from ROOT_'s later Kubernetes
|
||||
content — even without knowing Kubernetes syntax yet?
|
||||
|
||||
## Real Ops takeaway
|
||||
|
||||
**You just described and ran a real multi-container application declaratively — the exact same
|
||||
"describe what you want, let the tool make it real" pattern behind every later Kubernetes manifest
|
||||
in this program.** This isn't a coincidence: Compose and Kubernetes both solve "orchestrate
|
||||
multiple containers as one application," at different scales. The habit of thinking in terms of
|
||||
declared services rather than manual step-by-step commands is what ROOT 05 builds on directly.
|
||||
|
||||
This module is complete. Continue to the final module of this course.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Discussion questions for a live classroom:** question 2 previews Kubernetes manifests
|
||||
directly — a good moment to name "declarative description of desired state" as a term students
|
||||
will hear constantly starting in ROOT 05.
|
||||
|
||||
**Approximate timing:** 5-8 minutes live discussion; 5 minutes self-paced writing.
|
||||
Reference in New Issue
Block a user