编写Dockerfile的最佳实践

虽然 Dockerfile 简化了镜像构建的过程,而且把这个过程能够进行版本控制,可是不少人构建镜像的时候,都有一种冲动——把可能用到的东西都打包到镜像中。这种不正当的 Dockerfile 使用也会致使不少问题:java

  • docker 镜像太大。若是你常用镜像或者构建镜像,必定会遇到那种很大的镜像,甚至有些能达到 2G 以上
  • docker 镜像的构建时间过长。每一个 build 都会耗费很长时间,对于须要常常构建镜像(好比单元测试)的地方这多是个大问题
  • 重复劳动。屡次镜像构建之间大部份内容都是彻底同样并且重复的,可是每次都要作一遍,浪费时间和资源

这篇文章会讲述一些作法,但愿能解决这些问题。python

做者:cizixsgit

日期:2017-03-28docker

原文连接:cizixs.com/2017/03/28/…shell

但愿读者可以对 docker 镜像有必定的了解,阅读这篇文章至少须要一下前提知识:ubuntu

  • 了解 docker 的基础概念,运行过容器
  • 熟悉 docker 镜像的基础知识,知道镜像的分层结构
  • 最好是负责过某个 docker 镜像的构建(使用 docker build 命令建立过本身的镜像)

Dockerfile 和镜像构建

Dockerfile 是由一个个指令组成的,每一个指令都对应着最终镜像的一层。每行的第一个单词就是命令,后面全部的字符串是这个命令的参数,关于 Dockerfile 支持的命令以及它们的用法,能够参考官方文档,这里再也不赘述。centos

当运行 docker build 命令的时候,整个的构建过程是这样的:缓存

  1. 读取 Dockerfile 文件发送到 docker daemon
  2. 读取当前目录的全部文件(context),发送到 docker daemon
  3. 对 Dockerfile 进行解析,处理成命令加上对应参数的结构
  4. 按照顺序循环遍历全部的命令,对每一个命令调用对应的处理函数进行处理
  5. 每一个命令(除了 FROM)都会在一个容器执行,执行的结果会生成一个新的镜像
  6. 为最后生成的镜像打上标签

编写 Dockerfile 的一些最佳实践

1. 使用统一的 base 镜像

有些文章讲优化镜像会提倡使用尽可能小的基础镜像,好比 busybox 或者 alpine 等。我更推荐使用统一的你们比较熟悉的基础镜像,好比 ubuntu,centos 等,由于基础镜像只须要下载一次能够共享,并不会形成太多的存储空间浪费。它的好处是这些镜像的生态比较完整,方便咱们安装软件,除了问题进行调试。网络

2. 动静分离

常常变化的内容和基本不会变化的内容要分开,把不怎么变化的内容放在下层,建立出来不一样基础镜像供上层使用。好比能够建立各类语言的基础镜像,python2.七、python3.四、go1.七、java7等等,这些镜像包含了最基本的语言库,每一个组能够在上面继续构建应用级别的镜像。python2.7

3. 最小原则:只安装必需的东西

不少人构建镜像的时候,都有一种冲动——把可能用到的东西都打包到镜像中。要遏制这种想法,镜像中应该只包含必需的东西,任何能够有也能够没有的东西都不要放到里面。由于镜像的扩展很容易,并且运行容器的时候也很方便地对其进行修改。这样能够保证镜像尽量小,构建的时候尽量快,也保证将来的更快传输、更省网络资源。

4. 一个原则:每一个镜像只有一个功能

不要在容器里运行多个不一样功能的进程,每一个镜像中只安装一个应用的软件包和文件,须要交互的程序经过 pod(kubernetes 提供的特性) 或者容器之间的网络进行交流。这样能够保证模块化,不一样的应用能够分开维护和升级,也能减少单个镜像的大小。

5. 使用更少的层

虽然看起来把不一样的命令尽可能分开来,写在多个命令中容易阅读和理解。可是这样会致使出现太多的镜像层,而很差管理和分析镜像,并且镜像的层是有限的。尽可能把相关的内容放到同一个层,使用换行符进行分割,这样能够进一步减少镜像大小,而且方便查看镜像历史。

RUN apt-get update\
  && apt-get install -y --no-install-recommends \
  bzr \
  cvs \
  git \
  mercurial \
  subversion \
  && apt get clean
复制代码

6. 减小每层的内容

尽管只安装必须的内容,在这个过程当中也可能会产生额外的内容或者临时文件,咱们要尽可能让每层安装的东西保持最小。

  • 好比使用 --no-install-recommends 参数告诉 apt-get 不要安装推荐的软件包
  • 安装完软件包,清楚 /var/lib/apt/list/ 缓存
  • 删除中间文件:好比下载的压缩包
  • 删除临时文件:若是命令产生了临时文件,也要及时删除

7. 不要在 Dockerfile 中单独修改文件的权限

由于 docker 镜像是分层的,任何修改都会新增一个层,修改文件或者目录权限也是如此。若是有一个命令单独修改大文件或者目录的权限,会把这些文件复制一份,这样很容易致使镜像很大。

解决方案也很简单,要么在添加到 Dockerfile 以前就把文件的权限和用户设置好,要么在容器启动脚本(entrypoint)作这些修改,或者拷贝文件和修改权限放在一块儿作(这样最终也只是增长一层)。

8. 利用 cache 来加快构建速度

若是 Docker 发现某个层已经存在了,它会直接使用已经存在的层,而不会从新运行一次。若是你连续运行 docker build 屡次,会发现第二次运行很快就结束了。

不过从 1.10 版本开始,Content Addressable Storage 的引入致使缓存功能的实效,目前引入了 --cache-from 参数能够手动指定一个镜像来使用它的缓存。

9. 版本控制和自动构建

最好把 Dockerfile 和对应的应用代码一块儿放到版本控制中,而后可以自动构建镜像。这样的好处是能够追踪各个版本镜像的内容,方便了解不一样镜像有什么区别,对于调试和回滚都有好处。

另外,若是运行镜像的参数或者环境变量不少,也要有对应的文档给予说明,而且文档要随着 Dockerfile 变化而更新,这样任何人都能参考着文档很容易地使用镜像,而不是下载了镜像不知道怎么用。

参考资料

相关文章
相关标签/搜索