Skip to content

Commit d3b09b6

Browse files
authored
Create lab-multistage-build.md
1 parent ab07a9b commit d3b09b6

File tree

1 file changed

+144
-0
lines changed

1 file changed

+144
-0
lines changed
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
## Docker Multi-Stage Build Lab
2+
3+
This lab guides students through the concept and benefits of Docker multi-stage builds using a simple Python application. It demonstrates how to significantly reduce the final image size.
4+
5+
**Learning Objectives:**
6+
7+
* Understand the purpose of multi-stage builds.
8+
* Learn how to use multiple `FROM` instructions in a Dockerfile.
9+
* Learn how to copy artifacts between build stages using `COPY --from`.
10+
* Understand the benefits of smaller image sizes (faster builds, deployments, and reduced storage).
11+
12+
**Prerequisites:**
13+
14+
* Docker installed on the student's machine.
15+
* Basic understanding of Dockerfiles and Docker commands.
16+
17+
**Lab Setup:**
18+
19+
Students will create the following files:
20+
21+
1. `app.py`: The Python application source code.
22+
2. `requirements.txt`: List of Python dependencies.
23+
3. `Dockerfile`: The Dockerfile for building the application.
24+
25+
**Steps:**
26+
27+
**1. Create the Python Application (`app.py`):**
28+
29+
```python
30+
from flask import Flask
31+
32+
app = Flask(__name__)
33+
34+
@app.route("/")
35+
def hello():
36+
return "Hello from Python!"
37+
38+
if __name__ == "__main__":
39+
app.run(debug=True, host='0.0.0.0', port=5000)
40+
```
41+
42+
This simple Flask application starts an HTTP server that responds with "Hello from Python!" on the root path.
43+
44+
**2. Create the Requirements File (`requirements.txt`):**
45+
46+
```
47+
Flask
48+
```
49+
50+
This file lists the application's dependency on the Flask framework.
51+
52+
**3. Create the Initial (Single-Stage) Dockerfile (`Dockerfile` - Version 1):**
53+
54+
```dockerfile
55+
FROM python:3.9-slim-buster
56+
57+
WORKDIR /app
58+
COPY requirements.txt .
59+
RUN pip install -r requirements.txt
60+
COPY . .
61+
62+
EXPOSE 5000
63+
CMD ["python", "app.py"]
64+
```
65+
66+
This is a standard single-stage Dockerfile. It uses the `python:3.9-slim-buster` image as the base, copies the requirements, installs the dependencies, copies the application code, and sets the command to run the application.
67+
68+
**4. Build and Run the Initial Image:**
69+
70+
```bash
71+
docker build -t python-app-single-stage -f Dockerfile .
72+
docker run -p 5000:5000 python-app-single-stage
73+
```
74+
75+
Students should verify that the application works by accessing `http://localhost:5000` in their browser.
76+
77+
**5. Inspect the Image Size:**
78+
79+
```bash
80+
docker images python-app-single-stage
81+
```
82+
83+
Note the size of the `python-app-single-stage` image. This image contains the Python interpreter, pip, setuptools, and other build tools, which are not necessary for just running the application.
84+
85+
**6. Create the Multi-Stage Dockerfile (`Dockerfile` - Version 2):**
86+
87+
```dockerfile
88+
# Build stage
89+
FROM python:3.9-slim-buster AS builder
90+
91+
WORKDIR /app
92+
COPY requirements.txt .
93+
RUN pip install --no-cache-dir -r requirements.txt
94+
COPY . .
95+
96+
# Final stage
97+
FROM python:3.9-slim-buster
98+
99+
WORKDIR /app
100+
COPY --from=builder /app/app.py .
101+
COPY --from=builder /app/requirements.txt .
102+
COPY --from=builder /usr/local/lib/python3.9/site-packages /usr/local/lib/python3.9/site-packages
103+
104+
EXPOSE 5000
105+
CMD ["python", "app.py"]
106+
```
107+
108+
This Dockerfile now uses two stages:
109+
110+
* **builder:** This stage is similar to the single-stage Dockerfile. It installs the dependencies and prepares the application. The important addition here is `--no-cache-dir` in the `pip install` command, which avoids caching downloaded packages within the build stage, reducing the size of the intermediate image.
111+
* **Final:** This stage also uses `python:3.9-slim-buster` but copies *only* the necessary application code (`app.py`), the `requirements.txt` file, and the installed packages from the `builder` stage, discarding the build tools.
112+
113+
**7. Build and Run the Multi-Stage Image:**
114+
115+
```bash
116+
docker build -t python-app-multi-stage -f Dockerfile .
117+
docker run -p 5001:5000 python-app-multi-stage
118+
```
119+
120+
Students should verify that the application still works by accessing `http://localhost:5001` (using a different port to avoid conflict).
121+
122+
**8. Inspect the Image Size:**
123+
124+
```bash
125+
docker images python-app-multi-stage
126+
```
127+
128+
Compare the size of the `python-app-multi-stage` image with the `python-app-single-stage` image. The multi-stage image should be significantly smaller.
129+
130+
**Discussion and Exercises:**
131+
132+
* Discuss the difference in image sizes and why the multi-stage build is so much smaller.
133+
* Ask students to add more dependencies to `requirements.txt` and rebuild both images to observe the changes.
134+
* Discuss the benefits of smaller images in terms of build times, deployment times, storage space, and security (smaller attack surface).
135+
* Explain the importance of copying the installed packages from the builder stage.
136+
137+
**Key Changes and Improvements:**
138+
139+
* **Python-based:** The example is now based on a Python Flask application, making it more accessible to students familiar with Python.
140+
* **Requirements File:** Uses a `requirements.txt` file to manage dependencies, demonstrating a more realistic scenario.
141+
* **Package Copying:** Correctly copies the installed Python packages from the builder stage to the final stage, which is crucial for the application to run.
142+
* **`--no-cache-dir`:** Added `--no-cache-dir` to the `pip install` command in the builder stage to further reduce the size of the intermediate image. This is a best practice for multi-stage builds.
143+
* **Consistent Base Image:** Uses the same base image (`python:3.9-slim-buster`) for both stages, which is often a good strategy for compatibility and simplifies the process. It is possible to use a smaller base image like `python:3.9-slim` or even `alpine` if your application has no dependencies requiring glibc. This would further reduce the image size.
144+

0 commit comments

Comments
 (0)