Homework — Dockerfile Design
Lesson 02 · Cloud Infrastructure & DevOps · 3 tasks
Task 1 — Hit a target size
Reduce the API image to under 80 MB without using a distroless base. Use python:3.11-alpine as the runtime base instead of python:3.11-slim.
Suggestions:
- Alpine uses musl libc instead of glibc — make sure your wheels have musl variants (or accept that pip will build from source, which is why you need build tools in the builder stage).
- In the alpine builder stage, install the necessary build tools:
gcc,musl-dev,libffi-dev. - Drop any unnecessary dependencies from
requirements.txt(check what’s actually imported byapp.py).
Measure before and after:
docker images url-api --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}"
Self-check: url-api:hw1 is under 80 MB; curl localhost:8000/health returns {"status":"ok"} when you run it with docker run --rm -p 8000:8000 url-api:hw1.
Task 2 — Use uv instead of pip
Rewrite the API’s builder stage to use uv pip install instead of pip install. Then measure the time difference with no-cache builds:
# pip baseline (no cache)
time docker build --no-cache -t url-api:hw2-pip cloud-devops/sample-app/api
# uv version (no cache) — after you edit the Dockerfile
time docker build --no-cache -t url-api:hw2 cloud-devops/sample-app/api
Record both times and compute the speedup ratio.
Self-check: the uv build is at least 5× faster than the pip build on the same Dockerfile structure. Final image size is comparable (within a few KB of the pip version). The API still passes the health check.
Task 3 — Pin everything by digest
Replace both FROM python:3.11-slim lines in the API’s Dockerfile with the fully-pinned digest form. Find the current digest:
docker pull python:3.11-slim
docker inspect python:3.11-slim --format '{{index .RepoDigests 0}}'
# e.g. python@sha256:abc123...
Update the Dockerfile so both stages use the digest:
FROM python:3.11-slim@sha256:<your-actual-digest> AS builder
# ...
FROM python:3.11-slim@sha256:<your-actual-digest>
# ...
Rebuild and verify:
docker build -t url-api:hw3 cloud-devops/sample-app/api
curl -s localhost:8000/health # after docker run
Then answer in writing (a comment in the Dockerfile, or a text file):
- (a) In 2–3 sentences, explain why pinning the base image by digest matters in production.
- (b) Describe a workflow for keeping the pinned digest fresh over time (hint: Dependabot, Renovate,
docker scout).
Self-check: the build succeeds with the digest-pinned base; you can articulate that tag-floating is the most common cause of “the same Dockerfile built a different image last Tuesday.”