解决docker衍生图像构建时间与图像大小权衡的传统方法

解决docker衍生图像构建时间与图像大小权衡的传统方法,docker,dockerfile,Docker,Dockerfile,在编写DockerFile时,两个约束通常很重要:图像大小和图像构建时间 时间和空间的使用经常可以相互交换,这是一个常见的观察结果。然而,通过在开发中使用快速构建时间和在生产中使用小而慢的构建来避免这种选择是有用的 例如,如果我在项目中编写类似的内容,我可以在频繁更改源代码时快速重建开发中的映像,因为有一个安装了build essential的层可以在派生映像中重用: FROM base_image RUN pip install another-pypi-project-requiring-b

在编写DockerFile时,两个约束通常很重要:图像大小和图像构建时间

时间和空间的使用经常可以相互交换,这是一个常见的观察结果。然而,通过在开发中使用快速构建时间和在生产中使用小而慢的构建来避免这种选择是有用的

例如,如果我在项目中编写类似的内容,我可以在频繁更改源代码时快速重建开发中的映像,因为有一个安装了build essential的层可以在派生映像中重用:

FROM base_image
RUN pip install another-pypi-project-requiring-build-essential
ADD more_stuff
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
基本图像:

RUN apt install build-essential python-dev && \
 pip install some-pypi-project
ADD frequently_changing_source_code
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 pip install another-pypi-project-requiring-build-essential
ADD more_stuff
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
上面的结果是生成的版本比下一个版本更大,它实现了相同的功能,但牺牲了生成时间。现在,每当频繁更改源代码时,重建派生映像将导致重新安装build essential:

基本图像:

RUN apt install build-essential python-dev && \
 pip install some-pypi-project
ADD frequently_changing_source_code
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 pip install another-pypi-project-requiring-build-essential
ADD more_stuff
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
我可以想象解决这个问题的方法:例如,编写一组稍微复杂一些的DockerFile,这些DockerFile在某种开发标志上参数化,第一种行为用于开发构建,第二种行为用于生产构建。不过,我怀疑这不会导致人们喜欢阅读和使用DockerFile

那么,我如何才能在不让其他开发人员感到惊讶的情况下最好地实现我的目标:即使用尽可能尊重docker约定的DockerFile

关于我考虑过的答案的一些注释:

我知道docker的层缓存行为,这就是为什么我的示例中两个图像的ADD命令都在最后

我知道可以使用-v装载代码。使用-v是我的惯常做法,但这个问题是关于构建图像的,这也是开发过程中经常发生的事情,它发生了很多

一个明显的建议是消除基础图像。但是,请注意,对于相关的项目,基本映像通常是多个映像的基础,因此将基本映像与这些映像合并将在每个DockerFile中产生一系列重复的指令。不过,这也许是最不糟糕的选择

还要注意的是,在我参与的项目中,仅仅存在频繁变化的源代码本身并不会显著增加构建时间:这是重新安装像build essential这样的包的结果。另一个需要build essential的pypi项目通常会显著增加构建时间,但可能不足以消除开发构建中的这一步骤


最后,虽然docker的一个经常被引用的优秀特性是,在开发中可以使用与在生产中相同的配置,但这种特殊的变化来源对我们来说并不重要。

在过去,没有一个很好的答案。您要么构建两个不同的映像,一个用于快速移动的开发人员,另一个用于紧凑分发,要么选择一个不太适合其他人的映像。如果开发人员自己编译代码,并简单地将编译后的产品作为卷直接装载到容器中进行测试,而无需重新生成,则有一个潜在的解决方法

但上周docker在17.05.0-ce-rc1中增加了多阶段构建的功能,请参见。它们允许您将应用程序的各个部分构建为单独的部分,并在最后将结果复制到另一个图像中,缓存所有层,而最终图像仅包含构建的最后一部分的层。因此,对于您的场景,您可以有如下内容:

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
第一个构建环境中的所有层都保留在缓存中,允许开发人员根据需要添加和删除,而无需重新运行构建安装。但在最终的图像中,只添加了3个层,一个来自buildenv的copy命令和一对添加的层,从而生成了一个小图像。如果他们只更改那些ADD命令中的文件,那么只有这些步骤才会运行


这里有一个更详细的例子。这是现在可作为一个RC,你可以期待它在17.05边缘版本从docker,希望在未来几周。如果您想看到这方面的另一个示例真正投入使用,请查看。

似乎相关:我将尝试在问题中澄清,但是:选项1:我们已经这样做了,如问题中所示?在单个图像中,问题是关于多个图像。最后指出使用派生特征的动机。选择2:当然,这是我通常的工作方式。但是,在开发过程中(例如调试CI失败时,或更改很少修改的文件时,或以其他方式避免在手动将源树路径映射到映像路径时引入错误),需要大量构建映像的情况并不少见。构建图像是我在这里要问的。选项3:有趣!我得研究一下这个问题
o了解如何/是否与我的意思相关。也许是分开回答或删除选项1/2?虽然选项3可能适合您,但其他选项可能会帮助其他人稍后遇到此问题,因此我犹豫是否删除它们。拆分感觉有点像代表向我乞求,所以不让我说出我真正想说的话,但如果其他人觉得这样更好,我可以这样做。其他人可能会觉得它们有用,但它们是我不想问的问题的答案。我确实认为保持问题的焦点是非常重要的。我对问题进行了编辑,试图让它更清楚,但如果我失败了,请建议或进行编辑。你是对的,我看到了让我困惑的措辞,这完全是我的错,因为我浏览得太快了。让我想一想如何改写它,使它成为一个更好的答案,直接关注你的问题。