谈谈 Docker 镜像多阶段构建
Posted 看,未来
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了谈谈 Docker 镜像多阶段构建相关的知识,希望对你有一定的参考价值。
文章目录
导读
通常情况下,构建镜像通常会采用两种方式:
-
将全部组件及其依赖库的编译、测试、打包等流程封装进一个
Docker
镜像中。采用这种方式往往忽略了以下这些问题:
- Dockefile 特别长,可维护性降低。
- 镜像的层次多,体积大,部署时间长。
- 源代码存在泄漏的风险。
-
分散到多个 Dockerfile。事先在一个 Dockerfile 将项目及其依赖库编译测试打包好后,再将其拷贝到运行环境中,这种方式需要我们编写两个 Dockerfile 和一些编译脚本才能将其两个阶段自动整合起来,这种方式虽然可以很好地规避第一种方式存在的风险,但明显部署过程较复杂。
为了解决以上这些问题,Docker v17.05 开始支持多镜像阶段构建 (multistage builds)。只需要编写一个 Dockerfile 即可。
对于我这种深度“极简主义者”来说,这简直就是福音。但是我一直没有写这个,就代表了我的看法。等我详细介绍完,再列举例子之后,大家应该会有自己的想法。
案例
通过一段简单的 C 语言代码的编译、执行来具体演示。demo.c 的内容如下:
# include<stdio.h>
int main()
printf("%s\\n","This is a demo!");
return 0;
2.1 只通过一个 Dockerfile 来构建【方案一】
查看对应的 Dockerfile:
FROM centos:7.8.2003
ENV VERSION 1.0
WORKDIR /demo
COPY demo.c .
RUN yum install -y gcc && \\
gcc -v
RUN gcc demo.c -o demo && \\
rm -f demo.c && \\
yum erase -y gcc && \\
cp demo /usr/local/bin/
CMD ["demo"]
感兴趣的小伙伴可以直接将上面的 Dockerfile 和 docker-entrypoint.sh 在本地构建目录创建,执行 docker build -t redis:6.0.5-buster
进行尝试。
2.2 多个 Dockerfile 实现多阶段构建【方案二】
多阶段构建一般需要多个 Dockerfile 来完成,由于我们只需要源码编译后的产物。所以我们第一个阶段可以直接使用上文中镜像构建后的产物。第二阶段的 Dockerfile 内容如下:
FROM centos:7.8.2003
ENV VERSION 1.0
WORKDIR /demo
COPY demo /usr/local/bin
CMD ["demo"]
执行构建脚本 bash build.sh
, build.sh
的内容如下:
#!/bin/bash
cd stage-1
docker create --name bin demo:1.0
cd ../stage-2
docker cp bin:/usr/local/bin/demo .
docker rm -f bin
docker build -t demo:2.0 .
构建后得到的 Docker 容器运行结果:
$ docker run --rm -it demo:1.0
This is a demo!
$ docker run --rm -it demo:2.0
This is a demo!
两个容器的环境变量:
$ docker run --rm -it demo:1.0 env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=a52af1bec0af
TERM=xterm
VERSION=1.0
HOME=/root
$ docker run --rm -it demo:2.0 env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=f6618fd1244b
TERM=xterm
VERSION=2.0
HOME=/root
2.3 一个 Dockerfile 实现多阶段构建【方案三】
FROM centos:7.8.2003
ENV VERSION 1.0
WORKDIR /demo
COPY demo.c .
RUN yum install -y gcc && \\
gcc -v
RUN gcc demo.c -o demo && \\
rm -f demo.c && \\
yum erase -y gcc && \\
cp demo /usr/local/bin/
FROM centos:7.8.2003
COPY --from=0 /usr/local/bin/demo /usr/local/bin/demo
CMD ["demo"]
这种方式构建的 Docker 容器运行结果:
$ docker run --rm -it demo:3.0
This is a demo!
$ docker run --rm -it demo:3.0 env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=7839b3d568db
TERM=xterm
HOME=/root
三个镜像大小对比:
$ docker images|grep demo
demo 3.0 8766031d380a 39 seconds ago 203MB
demo 2.0 7d9c479cb421 10 minutes ago 203MB
demo 1.0 af331209572f 38 minutes ago 350MB
总结
- 通过观察,方案一构建得到镜像远比方案二和方案三大得多,方案二和方案三的镜像一样大小。
- 方案三并不会继承第一阶段构建的镜像的环境变量等配置,仅仅是复制了第一阶段的构建成果,需要特别注意。
其实,我的顾虑在这一行:
COPY --from=0 /usr/local/bin/demo /usr/local/bin/demo
一开始我会疑惑,因为运行这个程序需要一些内部库的,直到我研究了 Docker 的分层系统。但是,我只是对这一个小项目不疑惑了,一旦要封装的内容复杂起来,需要对整个镜像的把控到一定程度,才能做这种操作,不然少那么一两个关联文件,嘿嘿。所以,要对整个项目有一定的把控。
其他操作
停在特定的构建阶段
构建镜像时,不一定需要构建整个Dockerfile每个阶段。
您可以指定目标构建阶段。以下命令假定您使用的是以前的Dockerfile,但在名为builder的阶段停止:
$ docker build --target builder -t alexellis2/href-counter:latest .
使用此功能可能的一些非常适合的场景是:
- 调试特定的构建阶段
- 在debug阶段,启用所有调试或工具,而在production阶段尽量精简
- 在testing阶段,您的应用程序将填充测试数据,但在production阶段则使用生产数据
使用外部镜像作为stage
使用多阶段构建时,您不仅可以从Dockerfile中创建的镜像中进行复制。
您还可以使用COPY –from指令从单独的image中复制,使用本地image名称,本地或Docker注册表中可用的标记或标记ID。
如有必要,Docker会提取image并从那里开始复制。
语法是:
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf
以上是关于谈谈 Docker 镜像多阶段构建的主要内容,如果未能解决你的问题,请参考以下文章
Docker决战到底 使用多阶段构建Spring Boot应用镜像
Docker多阶段镜像构建Dockerfile脚本示例:构建nodejs前端项目