Day 3/40 - Multi Stage Docker Build

Praful DhawalePraful Dhawale
5 min read

Multi-stage Docker Builds

In Docker, a Multi-stage build is a way to create Docker images that are smaller and more secure. It achieves this by separating the build process into multiple stages, each with a specific purpose.

Imagine building an application that needs to be compiled. A typical single-stage build would involve:

  1. Installing all the build dependencies (compilers, libraries etc.)

  2. Copying your source code

  3. Compiling the code into an executable

  4. (Optional) Running tests

The final image would include all of these elements, even though only the executable is actually needed to run the application.

A Multi-stage build addresses this by creating separate stages:

  1. Build stage: This stage uses a base image with all the build dependencies. You would copy your source code, compile it and (optionally) run tests in this stage.

  2. Runtime stage: This stage uses a minimal base image, often the empty scratch image. You would copy only the final executable (and any other required files) from the build stage to this final image.

By separating the build environment from the final runtime environment, you get a smaller image that's easier to manage and more secure because it doesn't contain unnecessary components.

Here are some benefits of using Multi-stage builds:

  • Smaller image size: Less storage space used and faster transfer times.

  • Improved security: Reduced attack surface by eliminating unnecessary components.

  • Faster builds: Build context is smaller in subsequent builds.

If you're working with Docker and want to create optimized images, Multi-stage builds are a valuable technique to consider.

Get started

  • Clone the below sample repository, or you can use any web application that you have
git clone https://github.com/piyushsachdeva/todoapp-docker.git

  • cd into the directory
cd todoapp-docker/
  • Create an empty file with the name Dockerfile
touch Dockerfile
  • Using the text editor of your choice, paste the below content: Note: Details about the below Dockerfile have already been shared in the video
FROM node:18-alpine AS installer
WORKDIR /app
COPY package*.json ./
RUN npm install 
COPY . .
RUN npm run build
FROM nginx:latest AS deployer
COPY --from=installer /app/build /usr/share/nginx/html

The provided code snippet defines a Dockerfile using a Multi-stage build process. Here's a breakdown of what each line does:

1. FROM node:18-alpine AS installer

  • This line defines the first stage named "installer".

  • It uses the node:18-alpine image as the base. This image includes the Node.js version 18 runtime environment built on top of the Alpine Linux distribution (known for being lightweight).

  • The AS installer part assigns the name "installer" to this stage for future reference.

2. WORKDIR /app

  • This line sets the working directory within the "installer" stage to /app. This is where subsequent commands will be executed unless otherwise specified.

3. COPY package.json ./*

  • This line copies all files with names starting with "package" and ending with ".json" (likely package.json and potentially others) from the build context (your local directory where the Dockerfile resides) to the current working directory (/app) within the "installer" stage.

4. RUN npm install

  • This line executes the command npm install within the "installer" stage. This assumes there's a package.json file present (copied in the previous step) and it will download and install all the dependencies listed in that file using the npm package manager.

5. COPY . .

  • This line copies all files and folders from the build context (.) to the current working directory (.) within the "installer" stage. This likely copies your entire application codebase alongside the installed dependencies.

6. RUN npm run build

  • This line executes the command npm run build within the "installer" stage. This assumes you have a script named "build" defined in your package.json file that performs actions like compiling your application code into a production-ready format.

7. FROM nginx:latest AS deployer

  • This line defines a second stage named "deployer".

  • It uses the nginx:latest image as the base. This image includes the latest version of the Nginx web server.

  • Similar to before, AS deployer assigns the name "deployer" to this stage.

8. COPY --from=installer /app/build /usr/share/nginx/html

  • This line uses the COPY --from instruction. Here,

    • --from=installer specifies that we're copying from the "installer" stage we defined earlier.

    • /app/build is the source path within the "installer" stage. This likely points to the compiled application code generated by the npm run build command.

    • /usr/share/nginx/html is the destination path within the "deployer" stage. This is the default directory where Nginx serves web content.

  • Build the docker image using the application code and Dockerfile

docker build -t todoapp-docker .

  • Verify the image has been created and stored locally using the below command:
docker images
  • To start the docker container, use the below command
docker run -dp 3000:3000 todoapp-docker
  • Verify your app. If you have followed the above steps correctly, your app should be listening on localhost:3000

  • To enter(exec) into the container, use the below command

docker exec -it containername sh
or
docker exec -it containerid sh
  • To view docker logs
docker logs containername
or
docker logs containerid
  • To view the content of Docker container
docker inspect

  • Cleanup the old docker images from local repo using below command:
docker image rm image-id

Key Learnings:

  1. Basic Git Operations: Cloning a repository and navigating directories.

  2. Dockerfile Creation and Multi-stage Builds: Efficiently building Docker images by splitting the build process into multiple stages.

  3. Building Docker Images: Using docker build to create a Docker image from a Dockerfile.

  4. Managing Docker Images and Containers: Viewing, tagging, pushing, pulling, running, accessing, and cleaning up Docker images and containers.

  5. Docker Hub: Creating repositories and pushing/pulling images to/from Docker Hub.

  6. Application Deployment with Docker: Deploying a web application using Docker and Nginx.

    Thank you for reading this article. If you liked it, please follow.

Reference:

https://docs.docker.com/build/building/multi-stage/

https://docs.docker.com/reference/cli/docker/

0
Subscribe to my newsletter

Read articles from Praful Dhawale directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Praful Dhawale
Praful Dhawale

DevOps Engineer has to investigate and resolve technical issues, provide level 2 technical support, perform root cause analysis for production errors, build tools to improve customer experience, and develop software to integrate with internal back-end systems