Lesson 2 · Containers & Docker
Images, layers & Dockerfiles
The read-only template a container runs from — and why layer order decides your build speed.
Your win: read a Dockerfile, explain what an image
layer is and how the build cache uses it, and describe the multi-stage build this
repo actually ships.
Image vs container (nail this first)
An image is a read-only template: a stack of filesystem layers plus
metadata (which process to run, env vars, exposed ports). A container is a
running instance of that image, with a thin writable layer on top.1
One image → many containers. You build an image once and run it many times —
exactly why all ~25 services share one new-backend:locally image.
Layers: the key idea
An image is built up in layers, and each layer is an immutable set of filesystem changes.2 Roughly, each instruction in a Dockerfile creates one layer:
Layers are cached and shared. Build the image again and Docker reuses every layer whose inputs haven't changed. But there's a catch that runs the whole show:
COPY
source too early and every code edit re-installs all your dependencies.
The Dockerfile
A Dockerfile is the recipe. The instructions you'll see 90% of the time:
| Instruction | Does |
|---|---|
FROM | start from a base image (the first layer) |
RUN | execute a command at build time → a new layer |
COPY | copy host files into the image |
ENV | set an environment variable |
ENTRYPOINT | the process that runs when the container starts |
CMD | default arguments (easily overridden) |
Build with docker build -t name:tag .; the result lands in a
registry (this repo uses asia.gcr.io/...) tagged
name:tag.
Multi-stage builds — what this repo does
A single Dockerfile can have several FROM stages, where a later stage copies
artifacts out of an earlier one with COPY --from=. You build with a fat
toolchain but ship only a lean runtime.3
curl-ready(line 4) — a throwawaydebian:12.11+ curl.download-grpc-health-probe/download-modd(lines 9, 16) — stages that only exist to fetch tool binaries.developer(line 40) — the real runtime image. ItCOPY --from=s those tools in (lines 45–46), then copies the build outputs:COPY ./build/server /server(line 50), plusmigrations,features, the BDD test binary, etc.ENTRYPOINT ["/server"](line 59).
build/server; which service runs is chosen by
command: at run time, not at build time. (2) The base is
debian:12.11, not distroless — because dev/CI need bash,
curl, netcat, cron (line 44) for debugging and tests.
no-op stage
Line 28 defines a stage with no /server and
ENTRYPOINT ["modd"]. It exists so the stack can deploy an empty placeholder
fast, then hot-swap the freshly built binary in — the local reload trick you'll meet in
Lesson 3 and Part 2.
Docker — “Understanding the image layers” + multi-stage builds
The layer model, the immutability/cache rules, and how multi-stage builds keep runtime images small. Short and authoritative.
→ docs.docker.com — Understanding the image layers
→ docs.docker.com — Multi-stage builds
Check yourself (from memory)
Q1. An image differs from a container in that an image is…
Q2. You edit source copied high up in a Dockerfile. Result?
Q3. A multi-stage build exists mainly to…
COPY --from=
only the artifacts into a small final image.
FROM stages; a final stage COPY --from=s artifacts out of build
stages, so you build with a fat toolchain but ship a lean runtime. Repo:
local/Dockerfile.development — download-* stages feed the developer
stage, which does COPY ./build/server /server (one binary, many services) on
a debian:12.11 base.Dockerfile.development stage graph, or see how
.dockerignore and layer order cut build time? Ask me.