
本文基于 [Docker 官方 Dockerfile 参考](https://docs.docker.com/reference/dockerfile/) 整理所有关键字，并说明几组容易混淆的指令之间的区别。

## 指令一览

Dockerfile 支持以下指令（按字母序）：

| 指令        | 说明                                               |
| ----------- | -------------------------------------------------- |
| ADD         | 添加本地或远程文件、目录（含 URL、压缩包自动解压） |
| ARG         | 声明构建期变量                                     |
| CMD         | 指定容器默认要运行的命令或参数                     |
| COPY        | 从构建上下文复制文件、目录到镜像                   |
| ENTRYPOINT  | 指定容器默认的可执行程序                           |
| ENV         | 设置环境变量（写入镜像，运行时可用）               |
| EXPOSE      | 声明容器监听的端口                                 |
| FROM        | 从基础镜像开始新的构建阶段                         |
| HEALTHCHECK | 配置容器健康检查                                   |
| LABEL       | 为镜像添加元数据                                   |
| MAINTAINER  | 指定镜像作者（已废弃，建议用 LABEL）               |
| ONBUILD     | 当本镜像被作为基础镜像使用时触发的指令             |
| RUN         | 在构建时执行命令，产生新层                         |
| SHELL       | 设置 RUN、CMD、ENTRYPOINT 使用的默认 shell         |
| STOPSIGNAL  | 设置容器退出时发送的系统信号                       |
| USER        | 设置后续指令及运行时的用户/组                      |
| VOLUME      | 声明挂载点                                         |
| WORKDIR     | 设置工作目录                                       |

约定上指令用大写，便于与参数区分；指令本身不区分大小写。Dockerfile 必须由 `FROM` 开头（前面只能有解析器指令、注释和全局 `ARG`），指令按顺序执行。

## 解析器指令（Parser directives）

解析器指令写在文件最上方，形式为 `# directive=value`，不参与构建层。每条指令只能出现一次，且必须出现在任何普通注释或构建指令之前。

- `syntax`：指定使用的 Dockerfile 语法版本，例如 `# syntax=docker/dockerfile:1` 使用最新稳定语法。
- `escape`：设置转义符，默认 `\`；在 Windows 下可设为 `` ` `` 避免与路径中的 `\` 冲突。
- `check`：控制构建检查行为，如 `# check=skip=JSONArgsRecommended` 跳过指定检查，`# check=error=true` 让检查失败时构建失败。

## Shell 形式与 Exec 形式

`RUN`、`CMD`、`ENTRYPOINT` 都有两种写法：

- Shell 形式：`INSTRUCTION command param1 param2`，通过 shell 执行，支持变量替换、多行续行等。
- Exec 形式：`INSTRUCTION ["executable","param1","param2"]`，按 JSON 数组解析，直接执行可执行文件，不经过 shell，因此不会自动做变量替换（若需要可显式写 `["sh","-c","echo $HOME"]`）。

Exec 形式中需用双引号，且反斜杠要转义（Windows 路径需写 `\\`）。

## 部分关键字的区别

### ADD 和 COPY

- ADD：源可以是「构建上下文中的路径」或「URL」；若源是本地 tar 等归档，会自动解压到目标；行为较多，语义相对复杂。ADD 不支持 `--from`。
- COPY：只从构建上下文复制本地文件/目录，不解压、不拉 URL；支持 `COPY --from=<阶段名|镜像>`，可从命名构建阶段或外部镜像复制，多阶段构建时常用。语义单一，可预期性更好。

需要从构建上下文复制文件时，优先用 COPY；要从上一阶段或其它镜像复制时只能用 COPY --from=...；只有需要 URL 或自动解压归档时再用 ADD。

### CMD 和 ENTRYPOINT

- CMD：定义容器默认的「命令或参数」。若 Dockerfile 中有多条 CMD，仅最后一条生效。`docker run` 末尾若带了参数，会覆盖整条 CMD。
- ENTRYPOINT：定义容器启动时「固定执行的可执行程序」。`docker run` 末尾的参数会作为参数传给 ENTRYPOINT，而不是替换 ENTRYPOINT。

常见用法：用 ENTRYPOINT 指定程序（如 `ENTRYPOINT ["/app/server"]`），用 CMD 提供默认参数（如 `CMD ["--port","8080"]`）；运行时可覆盖 CMD，而 ENTRYPOINT 一般不变（除非用 `--entrypoint`）。

### ARG 和 ENV

- ARG：仅构建期有效，用于 `docker build` 时传入或 Dockerfile 内默认值；不会写入最终镜像，容器运行时无法使用。
- ENV：在构建期和运行期都有效，会写入镜像；容器内可通过环境变量访问。

在 `FROM` 之前的 `ARG` 只能用于 `FROM` 的镜像名/标签等；若要在阶段内再次使用该 ARG 的默认值，需在阶段内再写一行不带值的 `ARG 变量名`。

## 环境变量替换

在 Dockerfile 中可用 `$var` 或 `${var}` 引用已通过 `ENV` 定义的变量；部分指令（如 `WORKDIR`、`COPY`、`ADD`、`ENV`、`EXPOSE` 等）由构建器做替换。`RUN`、`CMD`、`ENTRYPOINT` 若用 shell 形式，变量替换由 shell 完成；若用 exec 形式且未通过 shell，则不会做变量替换。

## .dockerignore

与 `.gitignore` 类似，`.dockerignore` 可排除构建上下文中不需要的文件，减小上下文体积并避免敏感文件被 COPY/ADD 进镜像。

参考链接：

- [Dockerfile reference](https://docs.docker.com/reference/dockerfile/)
- [.dockerignore file](https://docs.docker.com/build/building/context/#dockerignore-files)

