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:
+50
@@ -0,0 +1,50 @@
|
||||
# Dockerfile and Image Build
|
||||
|
||||
## A Dockerfile is a recipe for an image
|
||||
|
||||
Every image you've used so far (`nginx:alpine`, `alpine:3.19`) was itself BUILT from a
|
||||
Dockerfile by someone else. This module teaches you to write your own.
|
||||
|
||||
## The core instructions
|
||||
|
||||
- **`FROM <image>`** — every Dockerfile starts by choosing a base image to build on top of.
|
||||
Almost nothing is built completely from scratch.
|
||||
- **`WORKDIR <path>`** — sets the working directory for every instruction that follows it (and
|
||||
for the container's default working directory when it runs).
|
||||
- **`COPY <host-path> <image-path>`** — copies files from your BUILD CONTEXT (the directory you
|
||||
run `docker build` from) into the image.
|
||||
- **`RUN <command>`** — executes a command DURING the build, creating a new layer with its
|
||||
result (installing a package, for example).
|
||||
- **`CMD <command>`** (conceptually) — sets the DEFAULT command a container runs when started
|
||||
from this image, if nothing else is specified.
|
||||
- **`ENTRYPOINT <command>`** (conceptually) — similar to `CMD`, but harder to override at
|
||||
`docker run` time — the two are often combined (`ENTRYPOINT` as the fixed program,
|
||||
`CMD` as its default arguments).
|
||||
|
||||
## Build context and `.dockerignore`
|
||||
|
||||
`docker build -t mytag .` — that final `.` is the **build context**: the directory Docker can
|
||||
see and copy files FROM during the build. A `.dockerignore` file (same idea as `.gitignore` from
|
||||
ROOT 03) excludes files from the build context — keeping large or irrelevant files (like a local
|
||||
`.git` folder or dependency caches) out of your image and speeding up builds.
|
||||
|
||||
## Layers, again
|
||||
|
||||
Each instruction in a Dockerfile creates one layer, cached individually. Docker reuses cached
|
||||
layers for instructions that haven't changed since the last build — which is why the ORDER of
|
||||
instructions matters for build speed: put things that change rarely (like `FROM` and installing
|
||||
dependencies) before things that change often (like copying your actual application code), so a
|
||||
small code change doesn't invalidate expensive earlier layers.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Lesson goal:** Give students enough Dockerfile literacy to write a real, small, working image
|
||||
— not exhaustive coverage of every possible instruction.
|
||||
|
||||
**Approximate timing:** 18 minutes read; 10 minutes live.
|
||||
|
||||
**Common misconception to watch for:** copying application code before installing dependencies
|
||||
— reinforce the "changes-rarely-first" layer-ordering principle with a concrete example.
|
||||
|
||||
**Demo idea:** build the same Dockerfile twice with no changes, showing the second build using
|
||||
fully cached layers (`CACHED` in the output) and finishing almost instantly.
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
# Demonstration: Watching Layer Caching Actually Work
|
||||
|
||||
## OBSERVE
|
||||
|
||||
```
|
||||
$ docker build -t demo:1 .
|
||||
Step 1/3 : FROM alpine:3.19
|
||||
Step 2/3 : WORKDIR /app
|
||||
Step 3/3 : COPY greeting.txt .
|
||||
Successfully tagged demo:1
|
||||
```
|
||||
|
||||
## HYPOTHESIS
|
||||
|
||||
Predict: if you run the EXACT same build again with no changes, will it take the same amount of
|
||||
time?
|
||||
|
||||
## INSPECT
|
||||
|
||||
```
|
||||
$ docker build -t demo:1 .
|
||||
Step 1/3 : FROM alpine:3.19
|
||||
Step 2/3 : WORKDIR /app
|
||||
---> Using cache
|
||||
Step 3/3 : COPY greeting.txt .
|
||||
---> Using cache
|
||||
Successfully tagged demo:1
|
||||
```
|
||||
|
||||
`Using cache` — nearly instant, because nothing actually changed since the last build.
|
||||
|
||||
## CHANGE
|
||||
|
||||
Now change `greeting.txt`'s content and rebuild:
|
||||
|
||||
```
|
||||
$ echo "updated" > greeting.txt
|
||||
$ docker build -t demo:1 .
|
||||
Step 2/3 : WORKDIR /app
|
||||
---> Using cache
|
||||
Step 3/3 : COPY greeting.txt .
|
||||
---> (this step re-runs — the file changed)
|
||||
```
|
||||
|
||||
Only the step AFTER the change re-runs — `WORKDIR` (unaffected) still used its cache.
|
||||
|
||||
## VERIFY
|
||||
|
||||
This is exactly why instruction ORDER matters: everything before the first changed instruction
|
||||
stays cached; everything from that point onward must rebuild. Put your least-frequently-changing
|
||||
instructions first.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Demo goal:** Make layer caching a directly OBSERVED behavior (via real build output), not an
|
||||
abstract efficiency claim.
|
||||
|
||||
**Approximate timing:** 8 minutes live.
|
||||
|
||||
**Common mistake to watch for:** ordering a Dockerfile with frequently-changing application code
|
||||
copied BEFORE installing rarely-changing dependencies — invalidates the expensive dependency-
|
||||
install cache on every trivial code change.
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
# Knowledge Check: Dockerfile and Image Build
|
||||
|
||||
---
|
||||
|
||||
**1. What does `WORKDIR` actually set?**
|
||||
|
||||
*Answer:* The working directory for every subsequent instruction in the build, and the
|
||||
container's default working directory when it runs.
|
||||
|
||||
---
|
||||
|
||||
**2. What is the "build context"?**
|
||||
|
||||
*Answer:* The directory (usually specified as `.` at the end of `docker build -t tag .`) that
|
||||
Docker can see and copy files FROM during the build — `COPY` instructions read from it.
|
||||
|
||||
---
|
||||
|
||||
**3. Why does instruction order in a Dockerfile affect build speed?**
|
||||
|
||||
*Answer:* Docker caches each instruction's result as a layer and reuses cached layers for
|
||||
unchanged instructions — everything before the first ACTUALLY-changed instruction stays cached,
|
||||
so putting rarely-changing instructions (like dependency installs) before frequently-changing
|
||||
ones (like application code) minimizes unnecessary rebuilding.
|
||||
|
||||
---
|
||||
|
||||
**4. What's `.dockerignore` for, and what other file does it resemble?**
|
||||
|
||||
*Answer:* It excludes files from the build context (keeping large/irrelevant files out of the
|
||||
image and speeding up builds) — the same idea as ROOT 03's `.gitignore`.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** Question 3 directly reinforces the caching demo — a good check that the
|
||||
concept, not just the observation, landed.
|
||||
|
||||
**Approximate timing:** 5 minutes.
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
# Guided Exercise: Order These Instructions for Maximum Cache Efficiency
|
||||
|
||||
This is a paper exercise — commit to an order before checking it in the Lab.
|
||||
|
||||
## Your task
|
||||
|
||||
You need a Dockerfile with these five things, in SOME order. Application code changes very
|
||||
often; dependencies change rarely. Write the most cache-efficient order:
|
||||
|
||||
- `FROM python:3.11-slim`
|
||||
- `COPY app.py .` (your actual application code — changes often)
|
||||
- `COPY requirements.txt .` (dependency list — changes rarely)
|
||||
- `RUN pip install -r requirements.txt` (installs dependencies — slow)
|
||||
- `WORKDIR /app`
|
||||
|
||||
## A worked shape to check yourself against
|
||||
|
||||
```
|
||||
FROM python:3.11-slim
|
||||
WORKDIR /app
|
||||
COPY requirements.txt .
|
||||
RUN pip install -r requirements.txt
|
||||
COPY app.py .
|
||||
```
|
||||
|
||||
Copying `requirements.txt` and installing dependencies BEFORE copying the actual application
|
||||
code means a code-only change never invalidates the (slow) dependency-install layer — only the
|
||||
final `COPY app.py .` step needs to rerun.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Facilitation:** This is one of the most practically useful habits in the entire module — worth
|
||||
connecting explicitly to real build-time savings on larger, slower dependency installs.
|
||||
|
||||
**Approximate timing:** 8-10 minutes live discussion; 5 minutes self-paced.
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
# Reflection: You Built a Real Image, Not Just Ran Someone Else's
|
||||
|
||||
## Reflection questions
|
||||
|
||||
1. Every image you used before this module (nginx, alpine) was built by someone else, from a
|
||||
Dockerfile you never saw. Does writing your own change how you think about those images?
|
||||
2. Which Dockerfile instruction do you expect to use the most in real application work?
|
||||
|
||||
## Real Ops takeaway
|
||||
|
||||
**A Dockerfile is executable documentation of exactly how an application's environment is
|
||||
built — the same reproducibility promise ROOT 03's version control made about code, now applied
|
||||
to environments.** You've closed the loop this course opened with Module 1's "it works on my
|
||||
machine" problem: a Dockerfile means the environment itself is defined, versioned, and
|
||||
rebuildable identically anywhere, by anyone, forever.
|
||||
|
||||
This module is complete. Continue to the next module.
|
||||
|
||||
## Instructor Notes
|
||||
|
||||
**Discussion questions for a live classroom:** question 1 is a good moment to reflect on the
|
||||
"black box → understood mechanism" arc of this entire course.
|
||||
|
||||
**Approximate timing:** 5-8 minutes live discussion; 5 minutes self-paced writing.
|
||||
Reference in New Issue
Block a user