Environment consistency
- Lesson 1
- 35 min
- Module 1: Understand containers
Lesson 1 of 15 · Docker and Cloud Essentials for AI DevelopersEnvironment consistency
1. Understand containers
2. Build the application image
3. Networking, configuration and persistence
4. Cloud building blocks
5. Deploy and clean up
What you will be able to do
Understand why the same project can work on one computer and fail on another, even with a lock file. Then learn the one idea that solves it: put the whole environment in a package, an image, and run that package everywhere.
Before you startWhat to know and to have ready
What you need to know
- Virtual environments and a list of exact versions, from Isolate dependencies: Record the versions in Python for Practical AI Engineering.
requirements-lock.txtin the ticket API, from Environment and first endpoint: Step 1: make the project in Build and Deploy Your First API.- The five things that a hosting platform does, from Hosting bridge: What a hosting platform does in the API course.
- Settings in environment variables, from Configuration and logging: One place for every setting in the API course.
What you need on your computer
- The ticket API at the end of the API course, in
~/projects/ticket-api(Windows:$HOME\projects\ticket-api). It hasrequirements-lock.txtandticket_api/config.py. If the project is missing or broken, copy the API course’s snapshotend-of-m07, as in Use a snapshot. - You do not need Docker for this lesson. You install it in Install and verify: Install Docker. Here, you read Amira’s commands and their real outputs, and the guided practice uses only your terminal.
Tested with Docker Desktop 4.81.0 (Docker Engine 29.6.1) on macOS, Apple silicon. The Windows and Linux steps are not tested.
It works on my computer
Section titled “It works on my computer”Amira owns ticket-api, the support-ticket API from the API course. Her colleague Sam wants to run it too. Sam has Python 3.12 on a Windows laptop. Amira has Python 3.14.6 on a Mac with Apple silicon. Sam clones the project and makes a virtual environment. Then Sam installs the exact versions from the lock file, as the README says:
python -m pip install -r requirements-lock.txtThe install stops at one package, uvloop. On Windows, pip finds no ready-made package for it, so it tries to build it from its source code. The build stops with uvloop’s own message:
RuntimeError: uvloop does not support Windows at the momentThis Windows step is not tested here: the line above is the check in uvloop’s source code that stops the build. Amira can show the cause on her own computer. She asks pip which files exist for Windows and Python 3.12. The --platform and --python-version options make pip look for another computer’s files, without installing anything. She runs pip in a container, which you meet later in this lesson:
docker run --rm python:3.14.8-slim-trixie pip download --disable-pip-version-check --no-deps --only-binary=:all: --platform win_amd64 --python-version 3.12 --dest /tmp/w uvloop==0.23.0The output:
ERROR: Could not find a version that satisfies the requirement uvloop==0.23.0 (from versions: none)ERROR: No matching distribution found for uvloop==0.23.0The same question for two other packages in the lock file, httptools==0.8.0 and watchfiles==1.3.0, gives a Windows file for each (httptools-0.8.0-cp312-cp312-win_amd64.whl, watchfiles-1.3.0-cp310-abi3-win_amd64.whl). Only uvloop has none.
Why is uvloop in the lock file at all? fastapi[standard] installs Uvicorn, the server. Uvicorn asks for uvloop only on systems that are not Windows: uvloop makes the server faster on Linux and macOS. Amira made the lock file with pip freeze on her Mac. pip freeze writes what is installed on this computer, so the Mac’s list has uvloop:
grep uvloop requirements-lock.txtuvloop==0.23.0The lock file is correct for Amira’s Mac. It is not correct for Sam’s laptop. Nobody made a mistake: the two computers are different environments.
Text description of the diagram
The left part shows two computers without a container. Amira’s Mac has four parts: the ticket_api code, the packages from the lock file, Python 3.14.6, and macOS on Apple silicon. The API starts. Sam’s laptop has the same code, but three parts are different: uvloop cannot install, Python is 3.12, and the system is Windows 11. The install stops. The right part shows one image with the code, exact package versions, Python 3.14.8 and the files of Debian 13, a Linux system. docker run starts the same image on both computers, and it runs on both.
The parts of an environment
Section titled “The parts of an environment”The environment of a program is everything around its code that changes how it runs. For the ticket API, it has five parts:
| Part | Example for the ticket API | What the Python and API courses did about it |
|---|---|---|
| The operating system and its libraries | macOS, Windows, or a Linux system such as Debian or Ubuntu | Nothing |
| The interpreter | Python 3.14.6 or Python 3.12 | requires-python = ">=3.12" in pyproject.toml: a range, not one version |
| The packages | FastAPI 0.142.2, Uvicorn, uvloop | The virtual environment isolates them. The lock file fixes their versions. |
| The settings | API_KEY, SHOW_DOCS, CLASSIFIER_MODE |
Environment variables, read in config.py |
| The files | The ticket_api code. Later, a model file. |
Git |
The lock file fixed only one part, the packages, and only for one kind of computer. The operating system and the Python version were still different on every computer. A server adds a third environment, usually Linux, which neither Amira nor Sam has.
The problem gets bigger with AI services. Packages for machine learning often have a different build for each operating system and each kind of processor. A later course, Deploying AI Models for Real Users, puts a trained model in the same kind of package. A model file is one more thing that every copy of the environment must have, in exactly the right version.
Images, containers and Docker
Section titled “Images, containers and Docker”The solution is to package the environment, not only describe it.
- An image is a package that holds a program and everything that it needs to run. That is the files of a small Linux system, the interpreter, the packages and the code. Nobody can change an image after Docker makes it. It also holds a little information about how to start the program, such as its start command.
- A container is a running copy of an image. Docker starts the image’s program as a process, and gives that process its own view of the files, the network and the other processes. You can start many containers from one image.
- Docker is the tool that builds images and runs containers. You type
dockercommands in the terminal. They go to the Docker Engine, a program that does the work. - A registry is a server that stores images, so that other people and servers can download them. Docker Hub is the default public registry. You use images from Docker Hub in this module, and push your own image to a registry in Module 5.
The settings are the one part that stays out of the image. The same image runs on Amira’s Mac with a test key, and on a server with the real key. Module 3 gives a container its settings when it starts.
Text description of the diagram
An image can come from two places. You build it from source files with docker build: a Dockerfile, the ticket_api code and the list of packages. You do that in Module 2. Or you download it from a registry such as Docker Hub with docker pull: an image that someone else built. The image holds read-only layers and a start command. Each docker run starts a new container from the image. A container is a running process with its own writable layer, so container 1 and container 2 do not share their changes.
Worked example: the same image on every computer
Section titled “Worked example: the same image on every computer”Amira compares her Mac with a container. First, her Mac:
python3 --versionuname -smuname -sm prints the name of the operating system and the kind of processor. The output:
Python 3.14.6Darwin arm64Darwin is the core of macOS. arm64 is the processor of Apple silicon. Then she runs the same questions in a container from the official Python image:
docker run --rm python:3.14.8-slim-trixie sh -c 'python --version; uname -sm; grep PRETTY_NAME /etc/os-release'The output:
Python 3.14.8Linux aarch64PRETTY_NAME="Debian GNU/Linux 13 (trixie)"Inside the container, the system is Linux (Debian 13, called “trixie”), with Python 3.14.8, on the same Mac. aarch64 is Linux’s name for arm64. On Sam’s laptop, the same command prints the same Python version and the same Debian version. Only the processor name can be different: an Intel or AMD laptop prints x86_64. Install and verify explains this difference.
The name python:3.14.8-slim-trixie is an image name and a tag. python is the image. The tag after the colon chooses one version of it: Python 3.14.8, the small (“slim”) variant, on Debian trixie.
Docker and the five platform steps
Section titled “Docker and the five platform steps”The API course listed the five things that every hosting platform does, and said that Docker is a different way to describe them. Here is the map. You learn each part in this course:
| Platform step | With Docker | Where in this course |
|---|---|---|
| Build: install Python and the dependencies | A Dockerfile, and docker build, make an image |
Module 2 |
| Start: run the start command | The image’s CMD |
Module 2 |
| Listen: send requests to the app on a port | EXPOSE in the image, and -p when the container starts |
Module 3 |
| Configure: give the app its settings | -e, an environment file or a secret file, when the container starts |
Module 3 |
| Keep it running: HTTPS, restarts, logs | docker logs on your computer; a container platform in the cloud |
Modules 3 and 5 |
With a container, you do the build step yourself, once, on your computer. The platform then only runs the image. The result is the same on your computer, on Sam’s computer and on the server.
Guided practice: list your own environment
Section titled “Guided practice: list your own environment”This practice needs no Docker. You write down the five parts of the environment on your computer, as Amira did.
-
Open a terminal in the project folder
~/projects/ticket-api. -
Show the Python version.
Terminal window python3 --versionTerminal window python --versionTerminal window python3 --versionExpected: one line, such as
Python 3.14.6. -
Show the operating system and the processor.
Terminal window uname -smTerminal window (Get-CimInstance Win32_OperatingSystem).Caption$env:PROCESSOR_ARCHITECTURETerminal window uname -smExpected: for example
Darwin arm64on a Mac with Apple silicon,Linux x86_64on most Linux computers, or a Windows name andAMD64on Windows. -
Look for uvloop in the lock file.
Terminal window grep uvloop requirements-lock.txtTerminal window Select-String uvloop requirements-lock.txtTerminal window grep uvloop requirements-lock.txtExpected:
uvloop==0.23.0if the file was made on macOS or Linux. No line if it was made on Windows. -
Open
ticket_api/config.py, and write down every setting that it reads withos.environ.get. -
Write the five parts in a short table: operating system, interpreter, packages, settings, files.
Show an example answer (Amira’s Mac)
| Part | Amira’s Mac |
|---|---|
| Operating system | macOS (Darwin), Apple silicon (arm64) |
| Interpreter | Python 3.14.6 |
| Packages | The 63 lines of requirements-lock.txt, with uvloop==0.23.0 |
| Settings | ALLOWED_ORIGINS, API_KEY, CLASSIFIER_MODE, CLASSIFIER_TIMEOUT, LOG_LEVEL, SHOW_DOCS |
| Files | The ticket_api folder, pyproject.toml |
Only the packages are fixed by a file in the project. The operating system and the Python version are whatever this computer has.
Your turn: a third computer
Section titled “Your turn: a third computer”A new colleague has a Linux laptop with an Intel processor and Python 3.13. Use your table from the guided practice. Which parts of the environment are different from Amira’s Mac? Can the lock file fix any of them? Does uvloop install? (uvloop 0.23.0 has ready-made Linux packages for Python 3.13 on Intel and AMD processors.)
Show the answer
Three parts are different: the operating system (Linux, not macOS), the processor (x86_64, not arm64), and the interpreter (3.13, not 3.14.6). The lock file fixes none of them: it only fixes the versions of the packages. uvloop installs, because there is a Linux package for Python 3.13. So the install probably works, but it is still a third environment. A bug that depends on the Python version or the system can still appear only on this laptop. An image removes the question: all three people run Python 3.14.8 on Debian 13.
Common mistake
Edit the lock file until it installs
Symptom: Sam deletes the line uvloop==0.23.0 from requirements-lock.txt. The install works, the tests pass, and Sam commits the changed file. Weeks later, the server runs without uvloop, and nobody decided that.
Cause: the lock file now describes Sam’s laptop, not the server. Uvicorn uses uvloop when it is installed, and the slower standard event loop when it is not. So the server’s environment changed because of one person’s laptop. A lock file made on one operating system is a list for that operating system.
Fix:
- Do not edit a lock file by hand to make it install. Put the line back with
git restore requirements-lock.txt. - Make the list of packages on the same kind of system that runs the service. In Module 2, you make
requirements-run.txtinside a Linux container, so it is the list for the Linux image. - On your own computer, use the image to run the service. Use your virtual environment only to write code and run the tests.
For a real AI service: packages such as PyTorch have a different build for each operating system, processor and graphics card. A list that one person makes on a laptop is often wrong for the server. The image is the place where the server’s list is true.
Check your understanding
Amira's lock file pins every package version. Which parts of the environment does it not fix?
Show the answer
It does not fix the operating system and its libraries, or the Python version: those are whatever the computer has. It does not fix the settings either, and it should not. The settings change between computers on purpose: a test key on a laptop, the real key on the server. It also fixes the packages only for the kind of computer that made it: the Mac’s list has uvloop, which Windows cannot install.
Does your answer have the main points of ours?
Sign in and enroll in the course to save your answers. Every check counts toward the certificate.
Check your understanding
Predict: Sam installs Docker on the Windows laptop and runs docker run --rm python:3.14.8-slim-trixie python --version. What does it print, and why is it not Python 3.12?
Show the answer
It prints Python 3.14.8. The command runs Python from the image, not the Python that is installed on Sam’s laptop. The image holds its own Linux system files and its own Python 3.14.8. So every computer that runs this image gets the same version.
Does your answer have the main points of ours?
Sign in and enroll in the course to save your answers. Every check counts toward the certificate.
Check your understanding
Which part of the environment stays out of the image, and why?
Show the answer
The settings, such as API_KEY and SHOW_DOCS. The same image must run on a laptop with a test key, and on a server with the real key and no documentation. If a setting is in the image, you need a different image for each place. And everyone who has the image can read a secret in it. Module 3 gives the settings to the container when it starts.
Does your answer have the main points of ours?
Sign in and enroll in the course to save your answers. Every check counts toward the certificate.
Recap
- An environment has five parts: the operating system and its libraries, the interpreter, the packages, the settings and the files.
- A lock file fixes only the package versions, and only for the kind of computer where it was made. Amira’s Mac list has uvloop, which Windows cannot install.
- An image holds every part except the settings. A container is a running copy of an image. Docker builds images and runs containers. A registry stores images.
- Docker describes the five platform steps from the API course: build, start, listen, configure, keep running.
Next, you look inside an image and run your first containers, and you see which changes a container keeps and which it loses.