Dockerfile最佳实践
Dockerfile最佳实践
Docker Version: 19.03.5
😄 Written by Zak Zhu
参考
- Breeze老师的docker培训
- Docker Docs | Best practices for writing Dockerfiles(https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#understand-build-context)
- Docker Docs | Dockerfile reference(https://docs.docker.com/engine/reference/builder/)
- Docker Docs | Use multi-stage builds(https://docs.docker.com/develop/develop-images/multistage-build/)
- Best practice on cloud | Dockerfile 最佳实践(http://blog.best-practice.cloud/2019/06/26/Dockerfile最佳实践.html)
- birdben | Docker实战(三十)Dockerfile最佳实践总结(https://birdben.github.io/2017/05/07/Docker/Docker实战(三十)Dockerfile最佳实践总结/)
- Vikings | Dockerfile 最佳实践(https://www.cnblogs.com/vikings-blog/p/4337152.html)
- 柳泉波-翻译 | 10个Docker镜像安全最佳实践(http://dockone.io/article/8828)
一般准则
-
Create ephemeral containers
Dockerfile构建的镜像应该生成尽可能短暂的容器。 “短暂”是指可以停止和销毁容器,然后对其进行重建和替换,并使用绝对的最小的设置和配置.
-
Exclude with .dockerignore
使用.dockerignore文件排除不需要加载到build context内的文件或目录, 以提高构建效率.
如何写.dockerignore文件, 详见官网文档: .dockerignore file
EXAMPLE:
下面.dockerignore示例来自于wushuiyong的walle-web项目
-
Use multi-stage builds
多阶段构建(即multi-stage builds)可以大幅度减小最终图像的大小, 而不必努力减少中间层和文件的数量.
如何使用, 详见官网文档: Use multi-stage builds
-
Don't install unnecessary packages
为了减少容器复杂度, 软件依赖度, 镜像大小和构建时间, 应该尽量避免安装不必要的软件包.
-
Decouple applications
一个容器应该只运行一个进程.
将应用解耦到多个容器中可以更轻松地水平扩展和重新使用容器.
例如, Web应用堆栈可能由三个独立的容器组成, 每个容器具有自己独特的镜像, 以解耦的方式管理web application, database, in-memory cache.
-
Minimize the number of layers
减少镜像层数量:
- 尽可能少使用
RUN
,COPY
,ADD
指令. - 使用多阶段构建(即multi-stage builds).
- 尽可能少使用
-
Sort multi-line arguments
尽可能通过字母数字排序和多行参数来简化以后的更改. 这有助于避免软件包重复, 并使列表更易于更新. 这也使PR易于阅读和查看. 在反斜杠
\
之前添加空格也有帮助.EXAMPLE:
RUN apt-get update && apt-get install -y \ bzr \ cvs \ git \ mercurial \ subversion
-
Leverage build cache
When building an image, Docker steps through the instructions in your Dockerfile, executing each in the order specified. As each instruction is examined, Docker looks for an existing image in its cache that it can reuse.
If you do not want to use the cache at all, you can use the
--no-cache=true
option on thedocker build
command.The basic rules that Docker follows are outlined below:
- Starting with a parent image that is already in the cache, the next instruction is compared against all child images derived from that base image to see if one of them was build using the exact same instruction. If not, the cache is invalidated.
- In most cases, simply comparing the instruction in the Dockerfile with one of the child images is sufficient.
- For the
ADD
andCOPY
instructions, the contents of the files are examined and a checksum is calculated for each file. The last-modified and last-accessed times of the files are not considered in these checksums. During the cache lookup, the checksum is compared against the checksum in the existing images. If anything has changed in the files, then the cache is invalidated. - Aside from the
ADD
andCOPY
commands, cache checking does not look at the files in the container to determine a cache match. For example, when processing aRUN apt-get -y update
command the files updated in the container are not examined to determine if a cache hit exists. In that case just the command string itself is used to find a match.
Once the cache is invalidated, all subsequent Dockerfile commands generate new images and the cache is not used.
细节建议
-
Dockerfile书写顺序按照更新频度升序排列
镜像分层是子层依赖父层, 对应到镜像构建缓存机制中, 如果某一层未命中缓存, 那么其剩下的层都不会使用缓存. 因此如果需要最大利用缓存机制, 推荐将变化频度低的指令尽量放上面, 变化频度高的指令放下面.
-
FROM
指令建议- 使用官方基础镜像
- 指定具体镜像标签
-
LABEL
指令建议- 添加作者, 版本, 时间等信息
-
RUN
指令建议- 尽量使用一个
RUN
指令完成 - 使用
set -o pipefail
避免管道错误被忽略 - 指定需安装的软件包具体版本
- 清空
yum
或apt
的缓存 - 禁止使用
yum upgrade
或apt upgrade
- 尽量使用一个
-
ADD
和COPY
指令建议- File复制使用
COPY
, Tarball复制使用ADD
- 不建议
ADD
从远程URL下载包, 推荐使用curl
或wget
方式 - 提前在build context目录下定义好文件权限
- 使用指令的
--chown
选项指定文件属主和属组
- File复制使用
-
USER
指令建议- If a service can run without privileges, use
USER
to change to a non-root user. Start by creating the user and group in the Dockerfile with something likeRUN groupadd -r postgres && useradd --no-log-init -r -g postgres postgres
. - Consider an explicit UID/GID.
- Consider setting /sbin/nologin to user's shell.
- Avoid installing or using
sudo
as it has unpredictable TTY and signal-forwarding behavior that can cause problems. Consider using "gosu" instead. - Lastly, to reduce layers and complexity, avoid switching
USER
back and forth frequently.
- If a service can run without privileges, use
工具分析
使用静态分析工具, 能够避免常见的错误, 建立工程师自动遵循的最佳实践指南.
-
Haskell Dockerfile Linter
A smarter Dockerfile linter that helps you build best practice Docker images. The linter is parsing the Dockerfile into an AST and performs rules on top of the AST. It is standing on the shoulders of ShellCheck to lint the Bash code inside
RUN
instructions.