3 · Docker: images & containers
'Works on my machine' is the oldest excuse in software. Docker kills it by packaging your code, its Python version, and every dependency into one portable image that runs identically anywhere.
A Docker IMAGE is a frozen, read-only blueprint of your app + its entire environment (OS libs, the exact Python, every pinned dependency); a CONTAINER is a running instance of that image. Build the image once and it runs identically on any machine, ending 'works on my machine' for good.
Without this:
Without containers, your model depends on whatever happens to be installed on the target server — a different Python, a missing system library, a mismatched scikit-learn — and deployments break in ways you can't reproduce locally.
In the last lesson you pinned dependencies so a rebuild uses the same Python libraries. But your app also depends on things outside Python: the operating system, system libraries, the Python interpreter version itself. Pinning requirements.txt doesn't capture those. Docker does — it packages the entire environment.
Image vs container — the key distinction. An image is a frozen, read-only blueprint: a layered snapshot of a filesystem containing your code, the OS userland, the exact Python, and every installed dependency. It's like a class in OOP, or a .joblib artifact — inert, just a saved template. A container is a running instance of an image — like an object created from a class. You can start many containers from one image; each is an isolated, live process. The slogan: build an image once, run many containers from it.
The Dockerfile is the recipe that builds an image. It's a text file of instructions, each describing one step:
FROM python:3.11-slim— start from an official base image (a minimal Debian with Python 3.11 already installed).slimmeans stripped down — smaller and faster to ship.WORKDIR /app— set the working directory inside the image; subsequent commands run here.COPY requirements.txt .— copy your pinned requirements into the image.RUN pip install -r requirements.txt— install the dependencies at build time, baking them into the image.COPY . .— copy the rest of your code in.CMD ["python", "app.py"]— the default command to run when a container starts.
You run docker build once to turn the Dockerfile into an image, then docker run to launch a container from it. Because the image carries its whole environment, that container behaves identically on your laptop, a teammate's machine, a CI runner, or a cloud server — the long-promised end of "works on my machine".
A first Dockerfile for a Python ML app: base image, workdir, copy + install pinned deps, copy code, default command.
`docker build` turns the Dockerfile into an image; `docker run` launches a container from it. `docker images` lists images, `docker ps` lists running containers.
What is the difference between a Docker image and a Docker container?
- A Docker image is a read-only blueprint of your app + its full environment; a container is a running instance of it — one image spawns many containers.
- A Dockerfile is the build recipe: `FROM` a base image, `WORKDIR`, `COPY` + `RUN pip install` the deps, `COPY` the code, then `CMD` the default command.
- `docker build -t name:tag .` produces the image; `docker run` launches a container — identically on any machine, because the whole environment is baked in.
Almost every deployed ML service ships as a Docker image — it's the universal unit cloud platforms (AWS, Azure, GCP) accept, so the rest of this track builds on the container you define here.
If you remove it: You'd hand-configure each target server and pray its Python and system libraries match yours — the brittle, irreproducible deployments containers were invented to eliminate.