Taking advantage of multi-stage builds can significantly reduce the size of container images. The following is an example of a Nuxt.js application.
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE nuxt single-stage fba421d5de5b About a minute ago 371MB nuxt multi-stage a40d0000d0a8 10 minutes ago 22MB
Create a Nuxt.js application with
npx create-nuxt-app <app name>. The composition is free. No matter how you set it, you will get the same result with some differences in size.
FROM node:lts-alpine WORKDIR /app COPY . ./ RUN npm install -g http-server && \ npm install && \ npm run build EXPOSE 8080 CMD [ "http-server", "dist" ]
For convenience, non-multistage builds are referred to as single stage builds in this article. I didn't understand the correct term, so please let me know if you are familiar with it!
I brought in
http-server to run the application and put the built files on it.
FROM node:lts-alpine AS build-stage WORKDIR /app COPY . ./ RUN npm install && \ npm run build FROM nginx:stable-alpine AS production-stage COPY --from=build-stage /app/dist /usr/share/nginx/html EXPOSE 80 CMD [ "nginx", "-g", "daemon off;" ]
The build stage uses the node.js image, and the execution stage uses the nginx image. You can name the stage with ʻas
and specify which stage the file is from with--from`.
I put the directory finally generated in the build stage in the document root of nginx.
node_modules dist Dockerfile*
The Dockerfile is like an error, but
dist swell depending on what you use. Exclude it during the image creation stage as it will be run inside the container by
npm install and
npm build respectively.
$ docker build -t nuxt:single-stage --file Dockerfile-Single . $ docker run -dit -p 8080:8080 --name nuxt-single nuxt:single-stage $ docker exec nuxt-single wget -S -O- localhost:8080 Connecting to localhost:8080 (127.0.0.1:8080) HTTP/1.1 200 OK (Omitted below: OK if HTML is returned as response data!)
$ docker build -t nuxt:multi-stage --file Dockerfile-Multi . $ docker run -dit -p 80:80 --name nuxt-multi nuxt:multi-stage $ docker exec nuxt-multi wget -S -O- localhost:80 Connecting to localhost:80 (127.0.0.1:80) HTTP/1.1 200 OK (Omitted below: OK if HTML is returned as response data!)
If you compare the image sizes at this point, you'll get results like the one at the beginning.
In the example given this time, the size of the image is reduced by discarding what is necessary when building the application but not when executing it. In other words, it can be said that multi-stage build is effective when creating an image of a container that requires only an execution environment. For example, a container for CI / CD only needs to be a build product, but rather files required only at build time will cause the container size to increase unnecessarily. It's a good use for multi-stage builds.
You can reduce the network communication caused by
npm install by removing
.dockerignore and omitting
npm install. There is not much difference in time between sending
node_modules to the Docker daemon once and running
npm install, depending on your network environment.
Also, when using a multi-stage build, the intermediate image remains as
To avoid this, do one of the following:
--targetoption (of course the tagged images will remain)
docker image pruneafter successfully building the last image
In Related GitHub Issue, it is truncated by "Specification (free translation)", but it can not be managed as an option, or the image builder is updated. It seems that there was a debate about whether it could be dealt with. Currently, it seems that the main line is to make the new builder (BuildKit) the default. (Personally, even if I use BuildKit, I wonder how much difference there is between docker image prune and docker builder prune ...? .. Only intermediate images and caches corresponding to a specific build I want to delete)