编写Dockerfile的最佳实践

一般性的指导原则和推荐

容器应该是短暂的

通过Dockerfile定义的镜像产生的容器应当尽可能的短暂(ephemeral)。在这里,ephemeral的意思是它可以被停止、被销毁,也可以是一个通过绝对少的安装(set-up)和配置构建出来的新容器。

使用.dockerignore文件

为了提升构建性能,可在docker项目根目录下添加一个.dockerignore文件,以排除某些文件和目录。

避免安装不必要的软件包

为了降低复杂性,减少依赖、文件大小和构建时间,应该避免安装那些可有可无的软件包。

每个容器只运行一个进程

在几乎所有情形中,你应该在单个容器中只运行一个进程。将应用解耦合至多个容器中可使水平扩展和容器重用更容易。

最小化分层数

你需要在Dockerfile的可读性(进而长期的可维护性)和分层数的最小化之间找到平衡。要有策略地,且谨慎地考虑你所使用的分层数。

对多行参数进行排序

只要可能,就对多行参数按照字母数字编码进行排序,以便以后更容易修改。这有助于避免重复的软件包,且使得参数列表更容易修改。也使得PR更容易阅读和审查。还有,在多行参数的反斜线(\)前添加一个空格很有帮助。

构建缓存

在构建镜像的过程中,Docker会按顺序逐步执行Dockerfile中的每一条指令。在每一条指令被检查的时候,Docker会查看缓存中是否存在可重用的镜像,而不是创建一个新的重复的镜像。如果你完全不想使用缓存,那就给 docker build 命令加上 –no-cache=true 选项吧。
但如果你使用缓存,理解它何时会和不会找到一个匹配的镜像就很重要了。Docker使用的基本规则概括如下:

  • 如果从一个缓存中已有的基础镜像开始构建,那么下一个指令就会被拿来与衍生自该基础镜像的所有子镜像比较,看看是否有镜像是使用完全一样的指令构建出来。如果没有,缓存就会被作废(If not, the cache is invalidated.)。
  • 大部分情况下,比较Dockerfile中的指令和子镜像就够了。但是,某些指令需要多一点的检查和解释。
  • 对于ADD和COPY指令来说,镜像中的文件内容会被检查,并会为每个文件计算校验和。但文件的最后修改和最后访问时间并未被考虑进校验和中。在缓存查找期间,校验和被拿来与已存在的镜像中的校验和做比较。如果任何文件中的任何东西改变了,例如内容和元数据,那么缓存就被作废。
  • 除了ADD和COPY命令,缓存检查(cache checking)不会查看容器中的文件以确定缓存是否匹配。例如,当执行 RUN apt-get -y update 命令时,容器中被更新的文件并不会被检查以确定缓存是否命中。在这种情况下,仅仅是命令字符串本身会被用来查找匹配。

参考资源

Best practices for writing Dockerfiles
Dockerfile reference

0%