Optimizing Containers: A Deep Dive into Multistage Builds

·

3 min read

Optimizing Containers: A Deep Dive into Multistage Builds

Optimizing your Docker images is a game-changer in the fast-paced world of containerization. In this blog, we'll explore what multistage builds are, and why they are beneficial, and we will create a traditional Dockerfile and a multistage Dockerfile to compare the image size to understand better.

Understanding Multistage Builds

Multi-stage builds in Docker offer a powerful way to optimize the containerization process, delivering smaller, faster, and more secure images. Each stage represents a phase in the build process, and you can selectively copy artifacts from one stage to another. The final image includes only the necessary dependencies required to run the application, resulting in a smaller and more efficient container.

Advantages of Multistage Builds

  1. Reduced Image Size: By discarding unnecessary build artifacts and dependencies in the final stage, multistage builds significantly reduce the size of your Docker images. This can lead to faster image uploads, downloads, and deployments.

  2. Improved Security: This minimizes the attack surface by excluding unnecessary tools, libraries, and binaries that might be present during the build phase but are not required for the runtime environment.

  3. Simplified Build Process: Multistage builds provide a clear separation between different stages of the build process, which improves readability and reduces complexity.

Let's compare the image size of traditional and multi-stage Dockerfile

The Traditional Build Approach

In a Dockerfile, you start with a base image, install dependencies, copy your application code, and configure the environment. While this approach is straightforward, it often leads to larger image sizes that contain unnecessary dependencies and longer build times.

Let's create a Dockerfile and build an image with the name myapp:v3.

FROM ubuntu
WORKDIR /app
COPY requirements.txt /app
COPY devops /app
RUN apt-get update && \
    apt-get install -y python3 python3-pip && \
    pip install -r requirements.txt && \
    cd devops
ENTRYPOINT ["python3"]
CMD ["manage.py", "runserver", "0.0.0.0:8000"]

Multistage Build Approach

In this example, the first stage installs dependencies, copies source code, and builds the application. The second stage copies only the necessary artifacts and installs production dependencies, resulting in a smaller and more secure final image.

Let's create a Multi-stage Dockerfile and build an image with multi-stage:v1.

# Stage 1: Build Stage
FROM ubuntu AS build
WORKDIR /app

# Copy only the necessary files for installing dependencies
COPY requirements.txt /app
RUN apt-get update && \
    apt-get install -y python3 python3-pip && \
    pip3 install --upgrade pip && \
    pip3 install -r requirements.txt

# Stage 2: Production Stage
FROM ubuntu AS production
WORKDIR /app

# Copy only the necessary files for the final image
COPY --from=build /usr/bin/python3 /usr/bin/python3
COPY --from=build /usr/lib/python3 /usr/lib/python3
COPY --from=build /app /app

# Set the entrypoint and default command
ENTRYPOINT ["python3"]
CMD ["manage.py", "runserver", "0.0.0.0:8000"]

Now compare the image sizes with the docker command:

docker images

Output:

Compare the image size of the multi-stage(99.1MB) and myapp(520MB) images. Since only the artifacts needed for runtime are included in the final stage, the resulting image is smaller.

Conclusion

Docker multistage builds offer a powerful solution to streamline your containerized workflows. By efficiently organizing your build process, you can create smaller, more secure, and faster-to-deploy Docker images