[Docker] Is it good enough to call it a multi-stage build? → The story that became so good

Conclusion

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

Verification procedure

environment

Create a Nuxt application

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.

Add the following files to the root directory

Dockerfile-Single

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.

Dockerfile-Multi

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 ʻasand 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.

.dockerignore

.dockerignore


node_modules
dist
Dockerfile*

The Dockerfile is like an error, but node_modules and 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.

Build each image and check the operation

For single stage

$ 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!)

For multi-stage

$ 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.

How to use multi-stage build

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.

Digression

You can reduce the network communication caused by npm install by removing node_modules from .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 <none: none>. To avoid this, do one of the following:

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)

reference

Recommended Posts

[Docker] Is it good enough to call it a multi-stage build? → The story that became so good
The story of making it possible to build a project that was built by Maven with Ant
About the matter that tends to be confused with ARG of Dockerfile which is a multi-stage build
The story that docker had a hard time
The story of making a binding for libui, a GUI library for Ruby that is easy to install
How to build an environment with Docker, which is the minimum required to start a Rails application
A good way to make a recursive function that reverses the characters
It's almost the end of the year, so it's a story of cleaning up to use Docker comfortably at the beginning of the year
A program that determines whether the entered integer is close to an integer
I want to summarize Apache Wicket 8 because it is a good idea
A shell script that builds a Docker image and pushes it to ECR
[Docker] How to build when the source code is bind-mounted on the container
[Java small story] Monitor when a value is added to the List
A story that was embarrassing to give anison file to the production environment
If it is Ruby, it is efficient to make it a method and stock the processing.
Find a value that is convenient to have a method and make it a ValueObject
The patchForObject added to RestTemplate cannot be used effectively if it is a default implementation (the one that uses HttpURLConnection).
The story that .java is also built in Unity 2018
[Swift] The story that switch is often used for enum judgment
A story that suffered from a space that does not disappear even if trimmed with Java The cause is BOM
The story that docker had a hard time
A story about making a Builder that inherits the Builder
Java Error Handling Basics-The story that catch is only picked up in the foreground
A small story that is sometimes useful in Maven
PATH does not pass ... The cause is the character code
The story that the forced update could not be implemented
[Docker] Is it good enough to call it a multi-stage build? → The story that became so good
A story that took time to establish a connection
A story about making a Builder that inherits the Builder
[Rails] [Docker] Copy and paste is OK! How to build a Rails development environment with Docker
The end of catastrophic programming # 03 "Comparison of integers, if" a> b ", assume that it is" a --b> 0 ""
A story that I had a hard time trying to build PHP 7.4 on GCE's CentOS 8
Create a package that is a common development component of Automation Anywhere A2019 # 1-First, build and use the SDK sample as it is