Four minutes for one changed line
A Dockerfile is a text file of steps that builds a container image, and Docker saves each step as a layer it can reuse. The order of the steps decides how fast a rebuild is.
A team changes one line of a shop's code. The build takes four minutes, because it downloads every library again.
Another team changes the same line and waits one second. Both wrote a build file. They wrote the steps in a different order.
By the end of this lesson
Note
I can write a Dockerfile and say which of its steps run again after a change.
Key words
| Word | What it means |
|---|---|
| Dockerfile | a text file of steps that builds an image |
| Base image | the image a build starts from |
| Layer | the saved result of one build step |
| Cache | saved layers that a build reuses |
| Build | to make an image by running the steps |
The cells students fill in are left blank in this preview.
What the lab cannot do
Note
The lab's docker refuses to build or pull an image, because a build runs steps with rights on the machine. We built the Dockerfiles in this lesson on our own machine. You read them, look at what an image leaves in a container, and check your own file in the assignment. To build for real, use your own machine.
A Dockerfile is a list of steps
Each line starts with an instruction word. FROM names the base image. COPY brings files in. RUN runs a command while the image is built. CMD says what to run when a container starts.
Docker reads the lines from the top. Here is a small shop written that way.
A Dockerfile for a small Python shop
FROM python:3.12-alpine
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
CMD ["python", "app.py"]
The start folder, set by WORKDIR
docker inspect --format '{{.Config.WorkingDir}}' cacheShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
The signal that stops web (set by STOPSIGNAL)
docker inspect --format '{{.Config.StopSignal}}' webShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Worked example: what each instruction leaves behind
| Instruction | What it does in a build | Where you see it later |
|---|---|---|
| FROM | starts from a base image | Config.Image names the image |
| WORKDIR | sets the folder the next steps use | Config.WorkingDir, /data for cache |
| EXPOSE | says which door the program uses | Config.ExposedPorts, 80/tcp for web |
| STOPSIGNAL | says how to ask the program to stop | Config.StopSignal, SIGQUIT for web |
| COPY and RUN | add files and run commands | a new layer each |
The cells students fill in are left blank in this preview.
Each step makes a layer
A build saves the result of every step as a layer. The layers are stacked, and an image is the whole stack. A layer never changes once it is made.
A container adds one thin layer on top, where it writes. docker diff lists what is in that top layer.
Worked example: the layers of the shop image
| Step | Size on our test machine |
|---|---|
| The base image, python:3.12-alpine | about 48 MB for its biggest layer |
| WORKDIR /app | 8 kB |
| COPY requirements.txt . | 12 kB |
| RUN pip install ... | 10.6 MB |
| COPY app.py . | 12 kB |
| CMD ["python", "app.py"] | 0 B |
The cells students fill in are left blank in this preview.
Write a file in web's own folders
docker exec web touch /tmp/layer-demo.txtShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Find it in the container's top layer
docker diff web | grep layer-demoShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
A layer is reused if nothing above changed
Before it runs a step, Docker asks one question. Is this the same step with the same files as last time, and was everything above it the same too? If yes, it reuses the saved layer. That is the cache.
The first step that differs runs again. So does every step after it. Put what changes least at the top.
Order 1: copy everything first (our example)
FROM python:3.12-alpine
WORKDIR /app
COPY . .
RUN pip install --no-cache-dir -r requirements.txt
CMD ["python", "app.py"]
Worked example: edit app.py, then build again
| Step | Order 1 | Order 2 (the first Dockerfile above) |
|---|---|---|
| WORKDIR /app | cached | cached |
| COPY requirements.txt . | not in this order | cached |
| COPY . . | runs again | not in this order |
| RUN pip install ... | runs again, 11 seconds | cached |
| COPY app.py . | not in this order | runs again, under a second |
The cells students fill in are left blank in this preview.
A common mistake
Warning
Many people think Docker reruns only the line they edited. It reruns that step and every step after it. Copy the file that lists your libraries first and install them, then copy your own code. The slow step then stays cached.
Going further
Write the Dockerfile for a tiny site of your own. Start from nginx:1.27-alpine, copy one index.html into /usr/share/nginx/html and add nothing else.
On a machine with Docker, build it twice. The second build should say CACHED on every step.