Docker 最佳实践
本文衍生自官方的 Best practices for writing Dockerfiles , 在此之上增加了个人见解。
除此之外,官方还提供了几个标准的 Dockerfile 作为示例:
容器应该短命
docker 中的容器(container)应该是用过即弃的,每次使用时都应该重新从镜像(image)中构建。
使用 .dockerignore
docker 在构建过程中会把同目录中的所有 .dockerignore
和 Dockerfile
以外的文件打包发给镜像, 在文件过多时这会很耗时间。
每个容器一个程序
docker 的设计初衷就是应用级别的隔离。需要多个应用交互时请使用 link 命令进行组合。
容器的层数不宜过多
虽然从可读性来说,应该尽可能地拆分命令,但是官方并不推荐过度使用。
多行命令按字母排序
这会增加代码的可读性:
RUN apt-get update && apt-get install -y \
bzr \
cvs \
git \
mercurial \
subversion
cache
docker 在构建时,Dockerfile 的每一行都会新增一个 cache 层,如果不需要可以使用`docker build --no-cache=true` 禁用。
使用 cache 时应该注意 cache 的有效情况。
Dockerfile 命令
FROM
尽可能使用官方镜像,官方镜像多是来自于精简版的 Debian。
RUN
处于易读性的考虑,过长或者复杂的命令应该使用 \
分割成多行。 在使用 apt-get
时还需要注意:
- 不要单独使用
RUN apt-get update
,会引起缓存问题 - 避免使用
RUN apt-get upgrade
或者dist-upgrade
,在 docker 镜像中执行升级命令可能存在权限问题。 - 推荐写法:
RUN apt-get update && apt-get install -y package-bar package-foo package-baz
CMD
CMD 命令只应该运行镜像所对应的命令。虽然允许 CMD executable param1 param2
的写法, 但是 CMD ["executable", "param1", "param2"…]
更不容易出错。
示例:
CMD ["apache2","-DFOREGROUND"]
CMD ["perl", "-de0"]
如果你熟悉 ENTRYPOINT 的话,推荐组合使用。
EXPOSE
端口映射应该尽可能地使用默认端口。
ENV
ENV 可以用于保证程序的正常运行,也可以用于容器的设置,还可以用于版本号的设置,这样维护起来更方便:
ENV PG_MAJOR 9.3
ENV PG_VERSION 9.3.4
RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …
ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH
ADD COPY
ADD 和 COPY 的功能类似,不过 COPY 命令的功能更加直观一些,因此推荐使用。
相比之下,ADD 支持添加远程资源,并且会自动 tar 打包或者解包。不过下载远程文件更推荐使用 RUN wget
或者 curl
。
ENTRYPOINT
ENTRYPOINT 应该用于 镜像的主命令,并使用 CMD 作为默认设置,以 s3cmd 为例:
ENTRYPOINT ["s3cmd"]
CMD ["--help"]
获取帮助:docker run s3cmd
执行命令:docker run s3cmd ls s3://mybucket
这在镜像名与程序重名时非常有用。
ENTRYPOINT 也可以用于启动脚本:
COPY ./docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"]
这段脚本为用户提供了多种和 Postgres 交互的途径:
你可以简单地启动 Postgres: docker run postgres
。
或者运行 postgres
并传入参数:docker run postgres postgres --help
。
你甚至可以从镜像中启动一个完全不同的程序,比如 Bash:docker run --rm -it postgres bash
VOLUME
VOLUME 通常用作数据卷,对于任何可变的文件,包括数据库文件、代码库、配置文件……都应该使用 VOLUME 挂载。
USER
推荐不需要特殊权限的命令在变更用户后执行,当然首先需要创建对应的用户。以 postgres 为例:RUN groupadd -r postgres && useradd -r -g postgres postgres
。
注意:镜像中的用户和用户组的 UID/GID 是不确定的,”下次“镜像重建时并不一定会分配到相同的 UID/GID。 因此,如果有必要,请务必指定 UID/GID。
不过频繁地切换用户身份会增加不必要的 cache 层,不推荐切换用户后再切换回来。
WORKDIR
使用 WORKDIR 而不要使用 RUN cd … && do-something
,这样更便于阅读。
ONBUILD
不太明白,不过官方给了一个例子:Ruby’s ONBUILD variants