In the previous blog posts here and here, we described how to run a simple “hello world” HTTP server using Fastify inside a Docker container in Node.js and then how to update the Dockerfile
to use a distroless Node.js base image. In this third part, we will explain the benefits of using a multi-stage distroless Dockerfile and how to update the basic Dockerfile to a multi-stage distroless Dockerfile.
Docker multi-stage builds allow you to use multiple FROM
statements in a single Dockerfile to create multiple stages for building your container image. Each stage can have its own dependencies, environment variables, and build instructions. Using a multi-stage build can help you reduce the size of your container images by eliminating unused dependencies and files.
Prerequisites
- Docker installed on your system.
- Basic knowledge of Docker and building Docker images.
- Read: Fastify + Docker = ❤️
- Read: Fastify + Distroless Docker = 😍
Create a new Dockerfile
First, create a new file called Dockerfile.multistage
in the root of your project directory. This file will contain the instructions for building your Docker image using a multi-stage distroless base image.
# Stage 1: Build the application
FROM node:16-alpine as builder
WORKDIR /app
COPY package*.json ./
RUN npm install --production
# Stage 2: Create the production image
FROM gcr.io/distroless/nodejs:16
ENV ADDRESS=0.0.0.0 PORT=3000
WORKDIR /app
COPY package*.json ./
COPY --from=builder /app/node_modules .
COPY . .
CMD ["node", "index.js"]
This Dockerfile
contains two stages:
- Stage 1: Build the application
- Stage 2: Create the production image
In stage 1, we use the official Node.js 16-alpine image as our base image, set the working directory to /app
, copy the package*.json
files to the working directory, install dependencies using npm, copy the rest of the files to the working directory, and run the npm run build
command. This stage is responsible for building our application.
In stage 2, we use the distroless Node.js 16 image as our base image, set the working directory to /app
, copy the node_modules
from the previous stage to the working directory and set the default command to run the node index.js
command. This stage is responsible for creating the production image.
Note that we’re using the --from=builder
flag in the COPY
command to copy the dist
directory from the previous stage to the current stage.
Build the Docker image
Now that you have created the Dockerfile.multistage
, you can use it to build a Docker image. Run the following command in your terminal:
docker build -f Dockerfile.multistage -t fastify-multistage .
This command tells Docker to build an image using the instructions in the Dockerfile.multistage
and tag it with the name fastify-multistage
.
Run the Docker container
Finally, you can run the Docker container using the multi-stage distroless-based image you just built. Run the following command in your terminal:
docker run -p 3000:3000 fastify-multistage
This command tells Docker to run a container using the fastify-multistage
image and map port 3000
inside the container to port 3000
on your local machine.
Congratulations! Similarly to our other blog posts, you can now open http://localhost:3000 in your browser to see a response from the server.