|
| 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