容器化和自动化运维

写好Dockerfile的12个技巧

2018-11-17  本文已影响0人  jianweixs

Docker镜像由只读层组成,每个层都代表一个Dockerfile指令。这些层是堆叠的,每一层都是前一层变化的增量。示例Dockerfile

FROM ubuntu:15.04
COPY . /app
RUN make /app
CMD python /app/app.py

每条指令创建一个层:

运行图像并生成容器时,可以 在基础图层的顶部添加新的可写层(“容器图层”)。对正在运行的容器所做的所有更改(例如写入新文件,修改现有文件和删除文件)都将写入此可写容器层。

1.使用标签

​ 给镜像打上标签,易读的镜像标签可以帮助了解镜像的功能。

2.使用统一的Base镜像,比如busybox或者alpine,谨慎选择基础镜像,尽量选择当前官方的镜像库中的镜像。

​ 很多教程中建议大家使用alpine镜像,更建议大家使用centos,Ubuntu这样的镜像。同时,在构建自己的Docker镜像时,只安装和更新必须使用的包,FROM指令应该包含的参数tag,比如使用centos:7.5.1504而不是FROM centos

3.充分利用缓存

​ 在镜像的构建过程中,Docker 会遍历 Dockerfile 文件中的指令,然后按顺序执行。在执行每条指令之前,Docker 都会在缓存中查找是否已经存在可重用的镜像,如果有就使用现存的镜像,不再重复创建。如果你不想在构建过程中使用缓存,你可以在 docker build 命令中使用 --no-cache=true 选项。

但是,如果你想在构建的过程中使用缓存,你得明白什么时候会,什么时候不会找到匹配的镜像,遵循的基本规则如下:

一旦缓存失效,所有后续的 Dockerfile 指令都将产生新的镜像,缓存不会被使用。

4.正确使用ADD和COPY指令

这两者很相似,推荐有限选择 COPY,它比 ADD 透明度更高。

如果在 Dockerfile 中使用不用的文件,那么 COPY 它们可以单独使用。这样,特定文件的更改,将确保每一步的构建缓存无效,如:

DOCKERFILECOPY requirements.txt /tmp/
RUN pip install --requirement /tmp/requirements.txt
COPY . /tmp/

COPY . /tmp/ 放在后面,这能够使 RUN 的缓存无效的数量减少。尽量使用docker volume共享文件,而不是用ADD指令添加文件。

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

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

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

6. 版本控制和自动构建

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

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

7.RUN指令

​ 为了使Dockerfile易读、易理解和可维护,在使用比较长的RUN指令是可以使用反斜杠\分隔多行。将多行参数按字母顺序排序(比如要安装多个包时)。这可以帮助你避免重复包含同一个包,更新包列表时也更容易。也便于 PRs 阅读和审查。建议在反斜杠符号 \ 之前添加一个空格,以增加可读性。

示例:

RUN yum update && yum install -y \
  vim \
  ntpdate \
  git \
  nginx

6.CMD和ENTRYPOINT指令

​ CMD和ENTRYPOINT指令指定了容器运行的默认命令,推荐二者结合使用。使用exec格式ENTRYPOINT指令设置固定的默认命令和参数,然后使用CMD指令设置可变的参数。

7.不要在Dockerfile中做端口映射

​ Docker的两个核心概念是可重复性和可移植性,镜像应该可以在任何主机上运行多次。映射端口会破坏镜像的可移植性,且这样的镜像只能在一台主机上启动一个容器。所以端口映射应在docker run命令中用-p参数指定。

# 不要在Dockerfile中做如下映射
EXPOSE 80:8080
# 仅仅暴露80端口,需要另做映射
EXPOSE 80

8.使用多阶段构建

Docker 17.05 以上版本中,你可以使用 多阶段构建 来减少所构建镜像的大小。

9.避免安装不必要的包

​ 为了降低复杂性、减少依赖、减小文件大小、节约构建时间,你应该避免安装任何不必要的包。例如,不要在数据库镜像中包含一个文本编辑器。

10.一个容器只运行一个进程

​ 应该保证在一个容器中只运行一个进程。将多个应用解耦到不同容器中,保证了容器的横向扩展和复用。例如 web 应用应该包含三个容器:web应用、数据库、缓存。

​ 如果容器互相依赖,你可以使用 Docker 自定义网络 来把这些容器连接起来。

12.镜像层数尽可能少

​ 你需要在 Dockerfile 可读性(也包括长期的可维护性)和减少层数之间做一个平衡。

参考资料:

Best practices for writing Dockerfiles

How to write excellent Dockerfiles

上一篇 下一篇

猜你喜欢

热点阅读