使用 Docker 时,构建高效的 image 镜像是非常重要的,image 最好尽可能的小一点,这样实际部署的时候才能更高效。
这篇文章的目的是展示如何使用 Docker 的 multi-stage 来高效构建镜像。
下面是文章的主要内容:
非常简单的 web 应用,Angular ➕ Nodejs 实现的。
有兴趣可以试试:
// 下载项目
git clone https://github.com/bbachi/docker-multibuild-example.git
// 进入项目目录
cd WebApp
// 编译
npm run build
// 启动 nodejs server
cd ..
npm start
构建时需要做的事儿:
Dockerfile 内容:
FROM node:10
# set the work directory
WORKDIR /usr/src/app
# copy package.json
COPY package*.json ./
# copy webapp folder
COPY WebApp/package*.json ./WebApp/
# RUN npm install for node js dependencies
RUN npm install \
&& cd WebApp \
&& npm install @angular/cli \
&& npm install
# Bundle app source
COPY . .
# builing Angular UI
RUN cd WebApp && npm run build
EXPOSE 3070
ENTRYPOINT ["node"]
CMD ["index.js"]
执行 build 命令:
docker build -t nodewebapp:v1 .
指定了一个 tag:nodewebapp:v1
需要耐心等待一会儿。
最后的输出结果:
下面启动起来:
docker run -it -d -p 3070:3070 nodewebapp:v1
启动后就可以访问了:
使用 docker images
命令查看一下镜像列表:
1.21GB,也太大了吧。
而且这个镜像里面包含了很多无用的文件,例如一些依赖包。
Builder Pattern 会使用2个 Dockerfile,一个用于开发阶段,一个用于线上产品阶段。
开发阶段的镜像中包含所有东西,产品阶段的镜像中只包含运行所需的必要内容。
可以使用一个脚本文件 dockerbuild.sh,先构建开发阶段的版本,然后拷贝出必要的层/文件,再构建出产品版本的镜像。
那么我们就需要3个文件了。
Dockerfile.dev
这是用于构建开发版本的,会包含所有东西,例如 angular、nodejs server 所需要的 node_modules。
其实这和上面的 Dockerfile 是一样的,为了节省篇幅就不重复贴了。
Dockerfile
这是用于构建产品版本的,只包含项目运行所必备的内容。
FROM node:10
# set the work directory
WORKDIR /root/
COPY package*.json ./
# copy the UI static assets
COPY dist ./WebApp/dist
# RUN npm install for node js dependencies
RUN npm install
# copy index.js file
COPY index.js .
EXPOSE 3070
ENTRYPOINT ["node"]
CMD ["index.js"]
dockerbuild.sh
这是一个脚本文件,主要有3个步骤:
(1)通过 Dockerfile.dev 构建出一个全套的镜像
(2)把镜像中的必备文件拷贝出来,放到一个本地目录
(3)基于这个目录,通过 Dockerfile 构建出产品版本的镜像
脚本内容:
#!/bin/sh
echo Building nodewebapp from Dockerfile.dev
docker build -t nodewebapp:v1 -f Dockerfile.dev .
echo Creating Conatiner Out of Image
docker container create --name extract nodewebapp:v1
docker container cp extract:/usr/src/app/WebApp/dist ./dist
docker container rm -f extract
echo Building nodewebapp version 2
docker build --no-cache -t nodewebapp:v2 .
rm ./dist
运行脚本:
sh dockerbuild.sh
会创建出2个镜像,tag 分别为 v1 和 v2。
v1 是开发版本镜像,v2 是产品版本镜像。
可以看到产品版本小了很多。
Builder Pattern 虽然可以让镜像变小,但也带来不便,例如:
&&
、\
来合并多个 RUM 命令来避免产生额外的层,容易出错。multi-stage 是 Docker 17.05 版本推出的构建方式,使用 multi-stage build 方式的话,可以使用多个 FROM
来构建每个阶段。
最后可以得到与 builder pattern 方式同样小的 image,但复杂度大大降低了。
Dockerfile 内容:
FROM node:10 AS ui-build
WORKDIR /usr/src/app
COPY WebApp/ ./WebApp/
RUN cd WebApp && npm install @angular/cli && npm install && npm run build
FROM node:10 AS server-build
WORKDIR /root/
COPY --from=ui-build /usr/src/app/WebApp/dist ./WebApp/dist
COPY package*.json ./
RUN npm install
COPY index.js .
EXPOSE 3070
ENTRYPOINT ["node"]
CMD ["index.js"]
构建结果:
可以看到,大小与 builder pattern 是一样的。
builder pattern 中我们需要维护 2 个Dockerfile 文件和一个 shell 脚本文件,而 multi-stage 中,在一个 Dockerfile 文件中就可以完成。
builder pattern 中,我们需要自己把必须的文件捣腾到本地文件夹,而 multi-stage 中,可以使用 --from
把文件从一个阶段复制到另一个阶段。
builder pattern 中,必须创建一个中间镜像,结果就是构建出2个image,而 multi-stage 中不需要。
builder pattern 与 multi-stage 都是有效的镜像构建方式,可以根据自己的情况选择。
最后,感谢阅读,希望能对你有所帮助,最好您能动动尊贵的小手帮忙点赞转发,谢谢啦。
本文翻译整理自:
https://blog.bitsrc.io/a-guide-to-docker-multi-stage-builds-206e8f31aeb8