Docker镜像缓存的优化
我们在用容器化技术来开发项目时,若用到dockerfile来构建容器镜像,那么想必经常需要在开发调试时不断重新构建镜像。然而我用docker这么久才发现缓存优化的重要性,实在惭愧——假如我们正确地编写dockerfile,那么build的时间或许可以缩减很多,从而加速了整个项目的开发过程。下面就聊一下我从官网上看到的一些重要的dockerfile优化技巧。
build cache的原理
我们直接看一下官网的例子
|
|
我们知道,dockerfile内的每一条命令都是镜像的一个layer,docker通过layer来对镜像的构建进行加速与空间节省,每次构建时,docker从上到下将layer进行应用,最后构建出整个镜像。在重复构建时,假如当前layer和上次构建的缓存一致,那么docker将直接使用缓存以节省时间,然而,当某个layer的内容变更,那么它以及它以后的所有layer都需要重新构建:
优化1:对命令重新排序
由上面build原理可知,命令排序是有讲究的:一个命令layer的变更,会导致它以及之后的所有layer全部需要重新构建。所以我们在编写dockerfile命令的时候,应该尽量把不容易改变的命令写在前面,而相对易变的命令则放到后面执行。如此发生改变时, docker只需要重新构建后面几个改动了的layer。
优化2:减少无谓的动作
考虑命令的必要性,让命令只执行需要的操作,比如命令包管理工具只安装必要的工具、比如拷贝文件时忽略无用的文件(可通过.dockerignore文件实现)
优化3:合并命令
多个RUN命令用&&来合并,使它们在一个layer中被执行。
优化4:使用多段式构建
想象下列场景,我们要从源码编译一个go程序并打包进一个容器,最后作为容器的entrypoint来运行这个程序, 在没有多段式构建之前,我们需要:
- 写一个dockerfile来负责编译go程序
- 写一个dockerfile来负责跑go程序
- 写一个脚本来生成docker1,然后从docker1拿出编译好的go程序以运行docker2
不考虑整个容器的体积或者源码安全性时,我们也可以把两个docker结合到一起,把项目的编译、测试和运行通通打包到一个dockerfile中运行,这明显是很笨重的。所以在docker v17.05后便有了多段式构建:
|
|
在多段式构建下,每个阶段的构建都可以基于新的镜像,而下面的构建则可以引用上面镜像的生成的文件,如此,我们最后便能获得一个体积较小的,只用于运行go程序的镜像了。