常规的方式来解决docker派生的图像生成时间与图像大小折衷

在编写Dockerfiles时,两个约束常常是重要的:图像大小和图像构build时间。

常见的观察是,时间和空间的使用往往可以相互替代。 然而,通过在开发中快速构build时间和在生产中慢慢构build时避免这种select可能是有用的。

例如,如果我在一个项目中写这样的东西,我可以在frequently_changing_source_code更改源码时更快地重build开发中的图像,因为在衍生图像中有一个可以重复使用的具有build-essential的图层:

基本图像:

 RUN apt install build-essential python-dev && \ pip install some-pypi-project ADD frequently_changing_source_code 

派生图片:

 FROM base_image RUN pip install another-pypi-project-requiring-build-essential ADD more_stuff 

上面的结果是比下一个版本更大的版本,它实现了相同的function,但牺牲了构build时间。 现在每当frequently_changing_source_code发生变化时,重build派生的图像将导致重新安装build-essential

基本图像:

 RUN apt install build-essential python-dev && \ pip install some-pypi-project && \ apt remove build-essential python-dev ADD frequently_changing_source_code 

派生图片:

 FROM base_image RUN apt install build-essential python-dev && \ pip install another-pypi-project-requiring-build-essential && \ apt remove build-essential python-dev ADD more_stuff 

我可以想象解决这个问题的方法:例如,编写一些稍微复杂一点的Docker文件,这些Docker文件在某种开发标志上进行了参数化,这种开发标志具有开发构build的第一个行为,而第二个则是生产构build。 不过,我怀疑这不会导致人们喜欢阅读和使用的Dockerfiles。

那么 ,我怎样才能最好地实现我的目标而不惊讶其他开发人员 :即使用Dockerfiles,尽可能尊重docker约定?

关于我已经考虑的答案的一些笔记:

我意识到docker的图层caching行为(这就是为什么我的例子中的两个图像的ADD命令在最后)。

我知道可以使用-v来挂载代码。 使用-v是我惯常的做法,但是这个问题是关于构build图像,这也是发展中发生的事情(时不时会发生很多)。

一个明显的build议是消除基本的形象。 但是,请注意,对于相关项目,基础图像通常是多个图像的基础,因此将基础与这些基础合并会在每个Docker文件中产生一堆重复的指令。 虽然这也许是最不可能的select。

另外需要注意的是(同样,在我参与的项目中), frequently_changing_source_code切换源代码本身并不会显着地影响构build时间:重新安装像build-essential这样的包。 another-pypi-project-requiring-build-essential通常对构build时间有很大贡献,但也许还不足以消除开发中的这一步骤。

最后,虽然docker的一个很好的特性是可以在开发中使用和生产相同的configuration,但是这个特殊的变化来源并不是我们关心的重点。

在过去,这并没有真正的答案。 您可以创build两个不同的图像,一个用于快速移动的开发人员,另一个用于紧凑分布,或者select一个不太理想的图像。 如果开发人员自己编译代码并将其编译产品直接挂载到容器中作为卷进行testing而不进行重build,则可能会有一个解决方法。

但是上个星期,docker增加了在17.05.0-ce-rc1(见参考资料32063 )中进行多阶段构build的能力。 它们允许您在单独的部分中构build应用程序的一部分,并在最后将结果复制到另一个图像中,同时caching所有图层,而最终图像仅包含构build的最后一部分的图层。 所以对于你的情况,你可以有这样的事情:

 FROM debian:latest as build-env # you can split these run lines now since these layers are only used at build RUN apt install build-essential python-dev RUN pip install some-pypi-project RUN pip install another-pypi-project-requiring-build-essential # you only need this next remove if the build tools are in the same folders as the app RUN apt remove build-essential python-dev FROM debian:latest # update this copy command depending on the pip install location COPY --from=build-env /usr/bin /usr/bin ADD frequently_changing_source_code ADD more_stuff 

第一个构build环境中的所有图层都保留在caching中,让开发人员根据需要添加和删除,而无需重新运行构build必要的安装。 但是在最终的图像中,只增加了3层,一个来自build-env的复制命令和一对添加,导致一个小的图像。 如果他们只改变这些ADD命令中的文件,那么只有这些步骤运行。

这是一个早期的博客文章更详细地进入它。 这现在可以作为一个RC使用,你可以期待在docker的17.05版本发布,希望在接下来的几个星期。 如果你想看另一个真正使用的例子,看看miragesdk Dockerfile 。