Dockerfile 最佳实践之善用 ADD/COPY 来加快 Build

好久没有练习 Docker 技术了,最近一个项目重新使我用起了这项技术,并且在之前的基础上有了一个小的认知提升,那就是「善用 ADD/COPY 来加快 Build」。

之前对 Docker Build Image 有一个误区,总以为 Dockerfile 写得越简洁越干净越好,哪知这会造成意外的后果。拿我的 Python Web App 来说吧,有一步是从用 pip 从 requirements.txt 安装软件包,相信用 Python 的朋友都有经历。于是我是这样写 Dockerfile 的:

COPY . /src/
RUN pip install --requirement /src/requirements.txt

乍看好像没啥问题,把当前目录的所有文件复制到 /src 目录下,复制完毕后再通过 RUN 来安装对应的软件包。

但是这里有一个严重的问题:没有充分的利用 Docker 的 Cache 机制,导致只要当前目录有任何一个小改动,不管 requirements.txt 有没有变化都使会 Build Image 的过程中都完整地跑一遍 pip install 的过程,非常消耗时间…

那么这个问题可以解决吗?那就是要理解 Docker 的 Build Image 原理了。Docker 在编译生成最终 Image 的过程中,Dockerfile 里的每一行都会对应的一个 Intermediate Image,或者更通俗的说:Cache 机制。这个机制会根据具体的指令来决定是使用 Cache,还是判断 Cache 失效,重新生成新的 Intermediate Image。

对 ADD 和 COPY 指令来说,一切都比较容易理解了,如果文件产生了变化,那么 Cache 就失效了,然后之后的 Intermediate Image 都需要重新 Build 了。

所以上面的例子要怎么改才能充分利用 Docker 的 Cache 机制?只需要新增一行并调整顺序:

COPY requirements.txt /src/
RUN pip install --requirement /src/requirements.txt
COPY . /src/
  1. 先复制 requirements.txt 到 /src/ 目录,产生一个 Intermediate Image;
  2. pip install 相关软件包,产生一个 Intermediate Image;
  3. 复制全部代码至 /src/ 目录,产生一个 Intermediate Image。

看起来比之前多了一步,但是好处是利用了 Cache,往后,只要 requirements.txt 的内容不发生变化,重新 Build Image 的时候,将会直接跳过前两步,直接进行第三步了——因为前两步检查 Cache 的结果是依然有效。

这样,尽管 Dockerfile 多了一行,却省了很多时间。

这个最新实践是写在官方文档 Best practices for Dockfiles 里面的 Build Cache 那一节的。官方文档真是要好好读啊。

2 Comments

  1. 经 @ibigbug 告之,可以用 ONBUILD 指令,我去学习一下!

    https://twitter.com/ibigbug/status/1009427668910014464

  2. RUN pip install --requirement /src/requirements.txt -i https://pypi.douban.com/simple

    可以用国内镜像,速度杠杠滴

Leave a Comment