
Lab - Writing Docker multi-stage build¶
- In this lab we will learn how to write a multi-stage Docker file
- A multistage build allows you to use multiple images to build a final product.
- In a multistage build, you have a single Dockerfile which build up multiple images inside it to help build the final image.
Why Use Multistage Builds?¶
-
Reduce Image Size
- Use a minimal base image for the final stage
- Only copy necessary artifacts to the final image
- Exclude build tools and intermediate files from production image
-
Improve Security
- Exclude build tools and secrets from the runtime image
- Limit the attack surface by using a smaller final image
- Use a non-root user in the final stage
- Reduce vulnerabilities by minimizing installed packages
-
Better Build Performance
- Leverage Docker’s layer caching mechanism to speed up builds
- Only rebuild stages that have changed
- Each stage can be built and tested independently
- Parallel stage execution when possible
-
Cleaner and More Maintainable Dockerfiles
- Separate concerns by using multiple named stages
- No need for manual cleanup of build dependencies
- Easier to read and maintain with clear stage purposes
- Use meaningful stage names for better clarity
- Add comments to explain each stage’s role
-
Flexible Dependency Management
- Install build dependencies in one stage and runtime dependencies in another
- Use different base images optimized for each stage (e.g.,
golang:alpinefor build,alpinefor runtime) - Use the best-suited image for each stage without bloating the final image
-
Simplified CI/CD Pipelines
- Combine build, test, and deploy stages in a single Dockerfile
- Use
--targetflag to build specific stages for different environments - Consistent build process across development and production
01. Create multi-stage docker file¶
- The first step is to create a Dockerfile.
- Later on we will pass build time arguments to this file to build the desired image
Dockerfile# Get the value of the desired image to build ARG BASE_IMG=curl # Build the base image FROM alpine AS base_image # Add some content to the 2nd image FROM base_image AS build-curl RUN echo -e "This file is from curl image" > image.txt # Add some content to the 3rd image FROM base_image AS build-bash RUN echo -e "This file is from bash image" > image.txt # Build the desired image FROM build-${BASE_IMG} # We can use the FROM command as we see in the previous line or use the # We can also use image index instead # COPY --from=build-${BASE_IMG} image.txt . to copy a specific content RUN cat image.txt CMD ["cat", "image.txt"]
02. Build the desired images¶
- We will use the following script to build multiple images and to test the results
#!/bin/bash -x # Build The curl based image (no-cache) docker build --build-arg BASE_IMG=curl --no-cache -t curl1 . # Build The bash based image (no-cache) docker build --build-arg BASE_IMG=bash --no-cache -t bash1 . ### Build with cache echo -e "" echo -e "---------------------------------" echo -e "" # Build The curl based image (with cache) docker build --build-arg BASE_IMG=curl -t curl2 . # Build The bash based image (with cache) docker build --build-arg BASE_IMG=bash -t bash2 .
03. Test the images¶
-
We will now test the 4 images we build perviously
-
You should see output similar to this one:
04. Quiz¶
- What will be the results of this docker file?
- Try to answer and then build the following
Dockerfileto see the results# Build the base image FROM alpine AS base_image # Add some packages to the base image FROM base_image AS build-curl RUN echo -e "\033[1;33mThis file is from curl image\033[0m" > image.txt # Add some packages to the base image FROM base_image AS build-bash RUN echo -e "\033[1;32mThis file is from bash image\033[0m" > image.txt # Build the desired image FROM build-curl COPY --from=2 image.txt . RUN cat image.txt CMD ["cat", "image.txt"] -
Test your answer with the following command
05. Build a specific target¶
- We can build our specific image and stop at the desired stage
-
In other words we don’t need to build all the images within the docker file
06. In-Class Exercise¶
- Create a
multi-stagedocker file that will build 2 images - The first image will be based on
alpineand will create a file namedalpine.txtwith the content:This is alpine image - The second image will be based on
nodeand will create a file namednode.txtwith the content:This is node image - The final image should be based on
alpineand should copy the files which you created from the previous stages and display their content when the container will run. - Hint: Use the
COPY --from=command to copy files from previous stages
Solution
### Dockerfile Solution Create a file named `Dockerfile-exercise`:# First stage: Alpine image
FROM alpine AS alpine-stage
RUN echo "This is alpine image" > alpine.txt
# Second stage: Node image
FROM node AS node-stage
RUN echo "This is node image" > node.txt
# Final stage: Alpine with files from previous stages
FROM alpine
COPY --from=alpine-stage alpine.txt .
COPY --from=node-stage node.txt .
# Run the command to display contents
CMD cat alpine.txt && cat node.txt