Wed Nov 1, 2023
Multi-stage builds in Dockerfiles are a feature that allows you to create more efficient and smaller Docker images by using multiple build stages.
This is particularly useful when you're working with complex applications or software stacks where the development and build environment differs from the production environment.
Multi-stage builds help to separate the build tools and dependencies from the final production image, resulting in a smaller and more secure final image.
Before diving into this comprehensive guide on Multi-Stage Builds, it's essential to ensure you have the following prerequisites:
Multi-stage builds are a feature introduced in Docker to address a common problem in containerization: image size. Traditional Dockerfiles can produce large, bloated images because they include all the build tools, libraries, and dependencies needed during the build process. However, these components are unnecessary in the final runtime image and can significantly increase the image's size.
Multi-stage builds offer a solution to this problem by allowing you to use multiple "stages" within a single Dockerfile. Each stage represents a distinct phase of the build process, and you can copy artifacts from one stage to another. This separation of concerns enables you to create a minimal runtime image while keeping all the necessary build tools and dependencies in earlier stages.
1. Defining Stages with FROM: In a Dockerfile, you can create multiple build stages by using the FROM instruction. Each FROM instruction starts a new stage, and you can specify a base image for that stage which can have its own RUN, COPY, and other Dockerfile commands.
FROM base_image_1 as stage_name_1# Define instructions for the first stageFROM base_image_2 as stage_name_2# Define instructions for the second stage# Additional stages if needed
3. Copying Artifacts Between Stages: To share files or artifacts between stages, you use the COPY --from=<stage_name> instruction. Let us see an example:
In this example, the second stage copies the build artifacts created in the first stage by referencing stage_name_1. This way, you can effectively use only the necessary files in the final image without carrying over build tools or intermediate files.FROM base_image_1 as stage_name_1# Build your applicationRUN some_build_command# Generate build artifactsFROM base_image_2 as stage_name_2# Copy artifacts from the first stageCOPY --from=stage_name_1 /path/to/artifacts /destination
4. Final Stage: You typically end your Dockerfile with the final stage where you create the runtime image. This stage usually uses a minimal image designed for production, reducing the image size.
Project repo: https://github.com/sampathshivakumar/my-app.git
It is a Simple Maven project for Java applications, including a source directory, a
pom.xml file, and a sample Java class to get you started with a basic "Hello World" program.
Let us Build Docker images for this Project using both Single-stage and multi-stage Dockerfile so that we can clearly see the advantages of multi-stage Dockerfile.
# Use an official Maven image as a parent imageFROM maven:3.8.3-openjdk-11 AS builder# Set the working directory in the containerWORKDIR /app# Copy the project's pom.xml and source codeCOPY ./ /app# Build the Maven projectRUN mvn clean package# Use the same builder image to run the Java application# Define the CMD to run your Java applicationCMD ["java", "-cp", "target/my-app-1.0-SNAPSHOT.jar", "com.mycompany.app.App"]
we can see the size of the Docker image we obtained is 681 MB using a Single Dockerfile.
Let us run the docker container to see the output
# Use an official Maven image as a parent imageFROM maven:3.8.3-openjdk-11 AS builder# Set the working directory in the containerWORKDIR /app# Copy the project's pom.xml and source codeCOPY ./ /app# Build the Maven projectRUN mvn clean package# Use an official OpenJDK image as a parent imageFROM openjdk:11-jre-slim# Set the working directory in the containerWORKDIR /app# Copy the JAR file from your local machine to the containerCOPY --from=builder /app/target/*.jar /app/# Define the CMD to run your Java applicationCMD ["java", "-cp", "my-app-1.0-SNAPSHOT.jar", "com.mycompany.app.App"]
The Multi-Stage Dockerfile results in a much smaller image size of 223MB, which is significantly more compact compared to the Single-Stage Dockerfile's image size of 681MB.
In this blog, we've explored what is Multi-Stage Dockerfile and the advantages of it over a Single-Stage Dockerfile when building container images for your applications.
I hope you enjoyed reading this blog and found it informative. If you have any questions or topics you'd like us to cover in future blogs, please don't hesitate to connect with me on LinkedIn.
Thank you for joining us on this Docker journey.