Dockerfile 指令详解
什么是 Dockerfile
Dockerfile 是一个文本文件,其内包含了一条条的 指令 (Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
使用 Dockerfile 构建镜像有以下优势:
- 自动化:可以通过
docker build命令自动构建镜像。 - 可重复性:由于 Dockerfile 是文本文件,可以确保每次构建的结果一致。
- 版本控制:Dockerfile 可以纳入版本控制系统(如 Git),便于追踪变更。
- 透明性:任何人都可以通过阅读 Dockerfile 了解镜像的构建过程。
官方文档
Dockerfile官方参考手册:https://docs.docker.com/reference/dockerfile/Dockerfile最佳实践指南:https://docs.docker.com/build/building/best-practices/Docker官方镜像Dockerfile库:https://github.com/docker-library/docs
Dockerfile 指令速查表
| 指令 | 作用 | 要点 | 备注 |
|---|---|---|---|
| FROM | 指定基础镜像 | 必须是第一条构建指令;只有全局 ARG 能写在它之前 | scratch 可作为空基础镜像 |
| RUN | 在新层执行命令 | 合并命令、清理缓存以减小体积 | 每条 RUN 产生一层,多命令用 && 合并 |
| COPY | 复制文件 | 优先使用,支持 --from | --from 可跨构建阶段/镜像复制 |
| ADD | 更高级的复制 | 自动解压 tar;公开远程 artifact 应配合 --checksum | 远程 URL 复制不推荐,优先 curl + RUN |
| CMD | 容器启动默认命令 | 可被 docker run 参数覆盖 | 一个 Dockerfile 只应有一个 CMD |
| ENTRYPOINT | 容器入口点 | 固定启动命令,CMD 作为默认参数 | 推荐 exec form,能正确接收信号 |
| ENV | 设置环境变量 | 构建时 + 运行时均生效 | 会固化进镜像 |
| ARG | 构建参数 | 仅构建时生效,FROM 后需重新声明 | 作用域在声明它的构建阶段内 |
| VOLUME | 定义匿名卷 | 运行时挂载会遮蔽镜像内目录 | 建议放在末尾,避免后续 RUN 写入被丢弃(旧版 builder) |
| EXPOSE | 声明端口 | 仅文档作用,不自动映射 | 仍需 -p 或 compose ports 映射 |
| WORKDIR | 指定工作目录 | 替代 RUN cd,目录不存在会自动创建 | 影响后续 RUN / CMD / COPY 等 |
| USER | 指定运行用户 | 用户必须已存在 | gosu 是运行时降权工具,不是指令,常写在 ENTRYPOINT 脚本里 |
| HEALTHCHECK | 健康检查 | 支持 starting / healthy / unhealthy 状态 | 需配合 --interval、--retries 等参数 |
| ONBUILD | 延迟执行指令 | 只继承一次,不可级联 | 子镜像构建时才触发,不会传递给孙镜像 |
| LABEL | 添加元数据 | 推荐 OCI 标准标签 | 替代已弃用的 MAINTAINER |
| SHELL | 更改默认 shell | 推荐 ["/bin/bash", "-o", "pipefail", "-c"] | 只影响 shell form 的 RUN / CMD / ENTRYPOINT |
| STOPSIGNAL | 设置容器停止信号 | 默认 SIGTERM,可改成其他信号 | 影响 docker stop 时发送的信号 |
| MAINTAINER | 维护者信息 | 已弃用 | 用 LABEL 替代,现代 Dockerfile 不应再使用 |
最佳实践清单
🧱 基础与安全
1. 固定基础镜像版本
不要只用 latest 或宽泛的标签(如 node:21)。为了构建的可复现性和安全性,应固定到具体版本,甚至摘要(digest)。例如 FROM node:21-alpine@sha256:...。
2. 优先使用多阶段构建 将构建环境(编译工具、依赖)与运行环境分离。这能显著减小最终镜像体积,并减少攻击面。
3. 使用非 root 用户运行
在 Dockerfile 中创建用户并切换(USER)。这能限制容器逃逸后的权限。注意,如果基础镜像默认是 root,你需要显式创建用户并授权(例如对工作目录)。
4. 安全地使用构建密钥(Secrets)
不要使用 ARG 或 ENV 传递密码、API Key。使用 RUN --mount=type=secret 临时挂载密钥,这样它们不会留在镜像历史层中。
🚀 构建效率与缓存
5. 优化指令顺序以利用缓存
将不常变化的指令放在前面(如安装依赖),常变化的放在后面(如拷贝源码)。对于 COPY,先拷贝 package.json / requirements.txt,安装依赖后,再拷贝其余代码。
6. 利用 BuildKit 的缓存挂载(Cache Mounts)
对于包管理器(npm, pip, apt, go mod),使用 RUN --mount=type=cache 来缓存下载的包。这能极大加速重复构建。例如:
RUN --mount=type=cache,target=/root/.cache/pip pip install -r requirements.txt7. 始终使用 .dockerignore
排除 .git、node_modules、构建产物、临时文件等,能减小构建上下文体积,加快传输速度,并避免意外将敏感文件打包进镜像。
8. 合并 RUN 指令并清理
虽然层数不是唯一指标,但合并相关的 RUN(特别是 apt-get update 和 install)可以确保清理操作(如 rm -rf /var/lib/apt/lists/*)在同一层完成,真正减小体积。
📝 指令与可读性
9. COPY 优先于 ADD
COPY 语义更清晰。ADD 仅用于需要自动解压本地 tar 包的场景。从远程 URL 下载应使用 RUN curl/wget,以便在同一层删除文件。
10. 使用 exec 形式(JSON 数组)
CMD 和 ENTRYPOINT 应使用 exec 形式(如 CMD ["python", "app.py"]),而不是 shell 形式。这样能正确接收信号,并避免不必要的 shell 解析。
11. 使用 LABEL 替代 MAINTAINER
MAINTAINER 已弃用。使用 LABEL 添加元数据,推荐遵循 OCI 标准(如 org.opencontainers.image.authors)。
12. 使用 WORKDIR 而非 RUN cd
WORKDIR 更清晰,且能确保后续指令在正确的目录下执行。使用绝对路径。
✨ 现代特性(BuildKit)
13. 添加语法指令
在 Dockerfile 首行添加 # syntax=docker/dockerfile:1。这能让你使用最新的稳定语法和构建特性(如 cache mount, secret mount)。
14. 为多平台构建做准备
如果你的项目需要跨架构(如交叉编译场景),应使用 docker buildx build --platform 并利用 BUILDPLATFORM / TARGETPLATFORM 参数进行交叉编译,避免 QEMU 模拟带来的性能损耗。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!












