多个 FROM 指令的意义
多个 FROM 指令并不是为了生成多根的层关系,最后生成的镜像,仍以最后一条 FROM 为准,之前的 FROM 会被抛弃,那么之前的FROM 又有什么意义呢?
每一条 FROM 指令都是一个构建阶段,多条 FROM 就是多阶段构建,虽然最后生成的镜像只能是最后一个阶段的结果,但是,能够将前置阶段中的文件拷贝到后边的阶段中
,这就是多阶段构建的最大意义。
FROM ish2btest_app as app
RUN cd app && echo hello h.txt
WORKDIR app
FROM alpinelatest
RUN mkdir app_test
COPY --from=app app app_test
CMD [binsh]
多个From构建,最终生成的镜像会丢弃前面FROM的镜像,但是该镜像确实是生成了
如上图,test_2from镜像为最终生成的镜像,而 中间none就是我们前一个From,不过我们查看test_2from的history,他并没有包含该镜像
删除中间的镜像,可以尝试
docker rmi $(docker images grep ^none awk '{print $3}')
实践总结-2019-8-15
这在一些场景来说是很有用的,就举一个我遇到的问题,实习的地方是在华为云上进行构建编译的,每次上传代码之后进行编译构建然后再根据Dockerfile打包成镜像。打包成镜像本身倒不是多大问题,关键在于编译,虽然我没有细致的去了解过华为云上编译的环境究竟是什么原理,不过单纯从现象上来看,每次编译都相当于换了一个环境,因为我是使用go mod来管理依赖包,所以每次编译都要下载一遍依赖包,而go的一些依赖包又由于一些原因会导致下载速度非常慢(本身并不是很大),这就导致了我每次编译都要20多分钟+,这哪里受的了。解决的方案可以是本地自己先编译好再上传到仓库,毕竟go支持交叉编译。也可以是将依赖包也上传上去。两种方法都不是很优雅,而且也可能导致其他问题。所以这里就使用了多阶段构建,我事先准备了一个编译专用的镜像,里面下载了所有需要的依赖包,然后每次编译就可以利用多阶段构建了,前一个FROM负责编译,后一个FROM将前面的程序复制过来,生成一个干净的镜像。这样即不用每次都本地编译,也不用上传可能很大的依赖包,而且编译的环境是在镜像中,而这个镜像又是我们自己创建的,换句话说编译的环境也是可控的。而且该编译镜像中也按装有git等工具,当我们通过go mod对一些依赖包进行升级时,再镜像中编译时也会自动去获取。不过如果是对一些被墙的依赖包进行升级或者替换,那就可能还是会卡在编译这里,所以这个时候最好更新一下镜像,将需要的依赖包更新到镜像再重新打包发布,编译的速度从20分钟直接缩短为不到2分钟。