编写Dockerfiles时,两个约束通常很重要:图像大小和图像构建时间。
这是一种常见的观察,即时间和空间的使用通常可以相互交换。但是,通过在开发中使用快速构建时间以及在生产中使用小而慢的构建来避免这种选择会很有用。
例如,如果我在项目中编写类似这样的内容,我可以在frequently_changing_source_code
更改时快速重建开发中的图像,因为安装了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
上述结果比下一版本的版本更大,它实现了相同的功能,但牺牲了构建时间。现在每当frequently_changing_source_code
更改时,重建派生图像都会导致重新安装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
我可以想象解决这个问题的方法:例如,编写一组稍微复杂的Dockerfile,它们在某种开发标志上进行参数化,这对开发构建有第一个行为,第二个用于生成构建。我怀疑这不会导致人们喜欢阅读和使用的Dockerfiles。
那么,我怎样才能最好地实现我的目标而不会让其他开发人员感到惊讶:即尽可能多地使用尊重docker约定的Dockerfiles?
关于我考虑的答案的一些注释:
我知道docker的图层缓存行为(这就是为什么我示例中的两个图像的ADD命令都在最后)。
我知道可以使用-v挂载代码。使用-v是我惯常的做法,但这个问题是关于构建图像,这也是在开发过程中发生的事情(有时会发生很多)。
一个明显的建议是消除基本图像。但是,请注意,对于相关项目,基本图像通常是多个图像的基础,因此将基础与这些图像合并将导致每个Dockerfiles中出现一堆重复的指令。也许这是最差的选择。
另请注意(再次,在我参与的项目中)仅仅frequently_changing_source_code
的存在本身并没有显着地促进构建时间:它是重新安装的包像build-essential那样那样做。 another-pypi-project-requiring-build-essential
通常确实对构建时间有很大贡献,但也许还不足以消除开发构建中的这一步骤。
最后,虽然它是docker常用的一个很好的功能,但它可以在开发中使用与生产中相同的配置,但这种特殊的变化来源对我们来说并不是一个重要的问题。
答案 0 :(得分:1)
过去对此并没有很好的答案。您可以构建两个不同的图像,一个用于快速移动的开发人员,另一个用于紧凑分发,或者您选择一个不太理想的图像。如果开发人员自己编译代码并将编译后的产品直接挂载到容器中作为卷进行测试而无需重建,那么就有一个潜在的解决方法。
但是上周,docker增加了在17.05.0-ce-rc1中进行多阶段构建的能力(参见pr 32063)。它们允许您以单独的部分构建应用程序的各个部分,并在最后将结果复制到另一个图像中,同时缓存所有图层,而最终图像仅包含构建的最后一部分的图层。因此,对于您的场景,您可以使用以下内容:
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个图层,来自build-env的一个复制命令和一对添加,从而产生了一个小图像。如果他们只更改那些ADD
命令中的文件,那么只会运行这些步骤。
这里有更详细的early blog post。现在可以作为RC使用,你可以期待它在docker的17.05边缘发布,希望在接下来的几周内。如果您想查看另一个真正使用的示例,请查看miragesdk Dockerfile。