Python > Deployment and Distribution > Containerization > Creating Dockerfiles for Python Applications

Dockerfile for a Flask Application with Multi-Stage Build

This snippet demonstrates a Dockerfile using a multi-stage build to create a smaller and more secure image for a Flask application.

Dockerfile Content

FROM python:3.9-slim-buster AS builder: Defines the first stage, named 'builder', using a Python base image. WORKDIR /app: Sets the working directory in the first stage. COPY requirements.txt .: Copies the requirements file to the working directory in the first stage. RUN pip install --no-cache-dir -r requirements.txt: Installs the Python dependencies in the first stage. COPY . .: Copies the application code to the working directory in the first stage. FROM python:3.9-slim-buster: Defines the second stage, using another Python base image. WORKDIR /app: Sets the working directory in the second stage. COPY --from=builder /app/venv /app/venv: Copies only the virtual environment, the app.py, the static folder and templates folder from the 'builder' stage. ENV FLASK_APP=app.py and ENV FLASK_RUN_HOST=0.0.0.0: Sets environment variables for the Flask application. EXPOSE 5000: Exposes port 5000. CMD ["flask", "run"]: Defines the command to run the Flask application.

# Stage 1: Build the application
FROM python:3.9-slim-buster AS builder

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

# Stage 2: Create a minimal image for deployment
FROM python:3.9-slim-buster

WORKDIR /app

COPY --from=builder /app/venv /app/venv
COPY --from=builder /app/app.py /app/app.py
COPY --from=builder /app/static /app/static
COPY --from=builder /app/templates /app/templates


ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0

EXPOSE 5000

CMD ["flask", "run"]

Explanation of Multi-Stage Builds

Multi-stage builds allow you to use multiple FROM statements in your Dockerfile. Each FROM instruction starts a new stage of the build. You can selectively copy artifacts from one stage to another, leaving behind unnecessary dependencies and tools. This results in a smaller, more efficient, and more secure final image.

Real-Life Use Case

Consider a Flask web application. The first stage can install all build dependencies (e.g., compilers, libraries). The second stage then copies only the necessary artifacts (e.g., compiled code, static assets) from the first stage into a clean base image. This way, the final image doesn't contain any build tools, reducing its size and potential attack surface.

Best Practices

  • Name your build stages using AS for clarity.
  • Only copy necessary artifacts from one stage to another.
  • Use minimal base images (e.g., slim-buster) for the final stage.

Interview Tip

Be prepared to explain the benefits of multi-stage builds, such as smaller image sizes, improved security, and faster deployment times. Also, be ready to discuss how to selectively copy artifacts between stages.

When to use them

Use multi-stage builds when you want to create optimized Docker images, especially for applications that require build dependencies. This is particularly useful for compiled languages (e.g., Go, C++) and applications with complex build processes.

Memory footprint

Multi-stage builds significantly reduce the image size, leading to a smaller memory footprint. By only including the necessary artifacts in the final image, you avoid including unnecessary dependencies and build tools.

Alternatives

If multi-stage builds are too complex, consider using techniques like optimizing your base image by removing unnecessary packages or using a smaller base image. However, multi-stage builds are generally the most effective approach for minimizing image size.

Pros

  • Smaller image sizes: Reduces the image size by only including necessary artifacts.
  • Improved security: Minimizes the attack surface by excluding build tools and dependencies.
  • Faster deployment times: Smaller images lead to faster deployment times.

Cons

  • Increased complexity: Requires a deeper understanding of Dockerfile syntax and multi-stage build concepts.
  • Debugging can be harder: Debugging can be more complex as you're working with multiple stages.

FAQ

  • What is the purpose of the AS builder?

    The AS builder assigns a name to the first stage, allowing you to reference it later in the Dockerfile when copying artifacts.
  • Why do I need to copy static and template files?

    These files contains the application logic and user interface that must be provided from the service.
  • How can I optimize the Dockerfile further?

    You can further optimize the Dockerfile by using a .dockerignore file, optimizing the base images, and combining RUN commands.