Dockerfile 语法详细总结
Dockerfile 语法详细总结
Dockerfile 是用于构建 Docker 镜像的文本配置文件,由一系列指令和参数组成,遵循分层构建原则(每条指令生成一个独立镜像层),是 Docker 镜像构建的核心标准。
一、基础规则与前置约定
-
指令大小写:指令本身不区分大小写,行业惯例使用大写,便于和参数区分。
-
执行顺序:指令从上到下依次执行,Dockerfile 必须以
FROM指令开头(解析器指令、前置ARG除外)。 -
注释规则:行首以
#开头为注释,不支持行内注释(#必须在行首,转义参数场景除外)。 -
转义符:默认转义符为
\,可通过解析器指令# escape=修改(常用于 Windows 容器路径适配)。 -
环境变量引用:通过
$VAR或${VAR}引用,支持 Shell 风格的变量替换:-
${VAR:-default}:变量未定义时使用默认值 -
${VAR:+alt}:变量已定义时使用替代值,否则为空
-
-
.dockerignore 文件:与 Dockerfile 同级,用于排除构建上下文中的文件(如源码、缓存、敏感信息),减少镜像体积、避免缓存失效、防止信息泄露,语法与
.gitignore一致。
二、核心指令全详解
(一)解析器指令(Parser Directives)
必须放在 Dockerfile 最顶部,仅能出现一次,用于配置 Dockerfile 解析器的行为,换行符、注释之外不能有其他内容。
-
escape** 指令**-
语法:
# escape=\或# escape=``` -
作用:修改默认转义符,Windows 容器中常用反引号避免与路径反斜杠冲突。
-
示例:
# escape=```
-
-
syntax** 指令**-
语法:
# syntax=docker/dockerfile:1.7-labs -
作用:指定 Dockerfile 语法版本,启用 BuildKit 新特性(如多平台构建、缓存挂载、机密挂载等),推荐始终指定官方稳定版本。
-
(二)基础镜像与元数据指令
1. FROM(必选,核心指令)
指定构建的基础镜像,是 Dockerfile 的第一条有效指令(解析器指令、前置 ARG 除外),支持多阶段构建。
-
语法:
FROM [--platform=<platform>] <image>[:<tag>] [AS <stage_name>] -
参数说明:
-
--platform:指定镜像平台,如linux/amd64、linux/arm64、windows/amd64,用于跨平台构建。 -
image:tag:基础镜像名称与标签,标签默认latest,推荐固定版本号保证构建一致性。 -
AS <stage_name>:为当前构建阶段命名,多阶段构建中用于后续引用。
-
-
示例:
# 单阶段构建 FROM ubuntu:22.04 # 多阶段构建 FROM golang:1.22 AS builder -
注意事项:
-
一个 Dockerfile 可包含多个
FROM,实现多阶段构建,最终镜像仅保留最后一个阶段的内容。 -
前置
ARG仅在FROM行中有效,阶段内使用需重新声明。 -
优先使用官方轻量镜像(alpine、slim 系列),减少镜像体积与攻击面。
-
2. ARG(构建参数)
定义仅在构建过程中生效的变量,容器运行时不会保留,可通过构建命令覆盖默认值。
-
语法:
ARG <name>[=<default_value>] -
作用域:
-
前置 ARG(
FROM之前定义):仅在FROM行中生效,每个FROM阶段需重新声明才能复用。 -
阶段内 ARG(
FROM之后定义):从声明处到当前阶段结束有效,跨阶段需重新声明。
-
-
示例:
# 前置ARG,用于指定基础镜像版本 ARG UBUNTU_VERSION=22.04 FROM ubuntu:${UBUNTU_VERSION} # 阶段内ARG ARG APP_VERSION=1.0.0 RUN echo "App version: ${APP_VERSION}" -
注意事项:
-
与
ENV核心区别:ARG仅构建时有效,ENV构建+运行时均有效。 -
敏感信息(密钥、密码)禁止使用
ARG,docker history可查看所有构建参数。 -
可通过
docker build --build-arg <key>=<value>覆盖默认值。 -
Docker 预定义 ARG(如
HTTP_PROXY、TARGETARCH)无需在 Dockerfile 中声明即可使用。
-
3. LABEL(镜像元数据标签)
为镜像添加键值对形式的元数据,替代已废弃的 MAINTAINER 指令。
-
语法:
LABEL <key1>=<value1> <key2>=<value2> ... -
示例:
LABEL maintainer="dev@example.com" \ version="1.0.0" \ description="Production image for backend service" \ org.opencontainers.image.authors="Dev Team" -
注意事项:
-
多个标签可合并为一个
LABEL指令,减少镜像层数。 -
可通过
docker inspect <image>查看镜像的所有标签。 -
推荐遵循 OCI 镜像规范定义标准化标签。
-
(三)内容操作指令
1. RUN(构建时执行命令)
在当前镜像层之上执行命令并提交结果,是安装软件、编译代码、修改配置的核心指令。
-
两种语法格式:
-
Shell 格式(默认):
RUN <command>- 底层通过
/bin/sh -c执行,支持管道、通配符、环境变量等 Shell 特性。
- 底层通过
-
Exec 格式(推荐):
RUN ["executable", "param1", "param2"]-
直接执行可执行文件,不启动 Shell 进程,避免 PID1 进程异常、信号传递问题。
-
必须使用双引号,符合 JSON 数组格式。
-
-
-
示例:
# Shell格式:合并命令+清理缓存,减少镜像层 RUN apt-get update && \ apt-get install -y nginx curl && \ rm -rf /var/lib/apt/lists/* # Exec格式 RUN ["apt-get", "install", "-y", "nginx"] -
高级特性(BuildKit):
RUN --mount挂载增强# 缓存挂载:复用npm缓存,不写入镜像层,提升构建速度 RUN --mount=type=cache,target=/root/.npm npm install # 机密挂载:读取构建时的密钥,不写入镜像历史 RUN --mount=type=secret,id=npm_token,target=/root/.npmrc npm install # SSH挂载:复用宿主机SSH代理,拉取私有Git仓库,无需复制私钥 RUN --mount=type=ssh git clone git@github.com:private/repo.git -
注意事项:
-
多条关联命令用
&&合并为一个RUN,减少镜像层数,同时避免缓存异常(如apt-get update与install必须同一条指令)。 -
命令执行后必须清理缓存(如 apt、yum、npm 缓存),减小镜像体积。
-
Exec 格式不会进行环境变量替换,如需使用需手动指定 Shell:
RUN ["sh", "-c", "echo $HOME"]。
-
2. COPY(文件复制)
将构建上下文中的文件/目录,复制到镜像内的目标路径,语义清晰、缓存可控,是文件复制的首选指令。
-
语法:
# 常规格式 COPY [--chown=<user>:<group>] [--chmod=<mode>] [--from=<stage|image>] <src>... <dest> # 路径含空格的格式 COPY [--chown=<user>:<group>] [--chmod=<mode>] [--from=<stage|image>] ["<src>",... "<dest>"] -
核心参数:
-
--chown:指定复制后文件的所属用户与组,仅 Linux 容器有效。 -
--chmod:指定文件权限,如0755、0644。 -
--from:从指定构建阶段(AS命名的阶段)或外部镜像中复制文件,多阶段构建核心用法。 -
src:源路径,相对于构建上下文,支持通配符(*、?),禁止使用 **../** 跳出上下文目录。 -
dest:目标路径,镜像内的绝对路径,或相对于WORKDIR的相对路径。
-
-
示例:
# 复制当前目录所有文件到镜像/app目录 COPY . /app # 带权限配置复制 COPY --chown=nginx:nginx --chmod=0644 ./nginx.conf /etc/nginx/nginx.conf # 多阶段构建:从builder阶段复制编译产物 COPY --from=builder /app/build /app -
注意事项:
-
源路径为目录时,仅复制目录内的所有内容,而非目录本身。
-
多个源路径时,目标路径必须以
/结尾。 -
源文件内容发生变化时,会触发该层及后续所有层缓存失效,因此推荐先复制依赖文件(如
package.json),再复制完整源码。
-
3. ADD(高级文件复制)
功能与 COPY 基本一致,额外支持两个特性:本地压缩包自动解压、远程 URL 下载。
-
语法与
COPY完全一致。 -
核心特性:
-
本地 tar/gzip/bzip2/xz 压缩包会自动解压到目标路径(远程 URL 下载的压缩包不会解压)。
-
支持 HTTP/HTTPS 远程 URL 作为源路径,自动下载到目标路径。
-
-
示例:
# 本地压缩包自动解压 ADD ./package.tar.gz /app # 远程文件下载 ADD https://example.com/binary /usr/local/bin/ -
注意事项:
-
**非必要不使用 **
ADD,常规文件复制优先用COPY,语义更清晰、缓存行为更可控。 -
不推荐用
ADD下载远程文件,应使用RUN curl/wget下载+执行+清理,合并为一层减少镜像体积。 -
仅当需要自动解压本地压缩包时,使用
ADD指令。
-
(四)环境与运行配置指令
1. ENV(环境变量)
设置环境变量,构建过程中后续指令与容器运行时均有效,是配置应用运行环境的核心指令。
-
语法:
# 单变量格式 ENV <key> <value> # 多变量格式(推荐) ENV <key1>=<value1> <key2>=<value2> ... -
示例:
ENV JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64 \ PATH=$PATH:$JAVA_HOME/bin \ APP_ENV=production \ LOG_LEVEL=info -
注意事项:
-
环境变量会永久保留在镜像中,容器运行时可通过
docker run -e <key>=<value>覆盖。 -
敏感信息(密钥、密码)禁止使用
ENV,会通过docker history泄露。 -
多变量合并为一个
ENV指令,减少镜像层数。
-
2. WORKDIR(工作目录)
为后续的 RUN/CMD/ENTRYPOINT/COPY/ADD 指令设置工作目录,目录不存在时会自动创建。
-
语法:
WORKDIR /path/to/workdir -
示例:
WORKDIR /app COPY . . RUN npm install # 相对路径,基于上一个WORKDIR,最终路径为/app/logs WORKDIR logs -
注意事项:
-
禁止使用
RUN cd xxx && command切换目录,cd仅在当前RUN指令有效,后续指令会恢复默认目录。 -
推荐使用绝对路径,避免路径混乱。
-
容器启动后的默认工作目录,为最后一个
WORKDIR指定的路径。
-
3. USER(运行用户)
为后续的 RUN/CMD/ENTRYPOINT 指令指定运行用户与用户组,同时设置容器启动时的默认用户。
-
语法:
USER <user>[:<group>] USER <UID>[:<GID>] -
示例:
# 提前创建非root用户 RUN groupadd -r appgroup && useradd -r -g appgroup appuser # 切换用户 USER appuser -
注意事项:
-
用户/用户组必须提前创建,否则会报错。
-
生产环境推荐使用非 root 用户运行容器,提升安全性,减少攻击面。
-
容器运行时可通过
docker run --user覆盖默认用户。 -
切换用户后,需确保工作目录、文件的权限与用户匹配,可通过
COPY --chown提前设置。
-
4. SHELL(默认Shell配置)
修改 Shell 格式指令(RUN/CMD/ENTRYPOINT)的默认 Shell 解释器。
-
语法:
SHELL ["executable", "parameters"] -
示例:
# Linux切换默认Shell为bash SHELL ["/bin/bash", "-c"] RUN echo $BASH_VERSION # Windows切换为PowerShell SHELL ["powershell", "-Command"] -
注意事项:
-
必须使用 Exec 格式(JSON 数组、双引号)。
-
Linux 默认值为
["/bin/sh", "-c"],Windows 默认值为["cmd", "/S", "/C"]。 -
可多次使用,每次修改仅对后续指令生效。
-
(五)容器启动与生命周期指令
1. CMD(容器启动默认命令)
指定容器启动时默认执行的命令与参数,docker run 时若指定了启动命令,会完全覆盖 CMD 内容。
-
三种语法格式:
-
Exec 格式(推荐):
CMD ["executable", "param1", "param2"]- 直接执行可执行文件,不启动 Shell,PID1 为目标进程,可正常接收系统信号,实现优雅退出。
-
默认参数格式:
CMD ["param1", "param2"]- 必须与 Exec 格式的
ENTRYPOINT配合使用,为ENTRYPOINT提供默认参数。
- 必须与 Exec 格式的
-
Shell 格式:
CMD command param1 param2- 底层通过
/bin/sh -c执行,PID1 为 sh 进程,无法正常接收停止信号,不推荐使用。
- 底层通过
-
-
示例:
# Exec格式(推荐) CMD ["nginx", "-g", "daemon off;"] # 为ENTRYPOINT提供默认参数 ENTRYPOINT ["nginx", "-g"] CMD ["daemon off;"] -
注意事项:
-
一个 Dockerfile 中只能有一个
CMD,多个CMD仅最后一个生效。 -
docker run <image> [command]会完全覆盖CMD的内容,不会追加参数。 -
Shell 格式的
CMD会忽略所有ENTRYPOINT指令。
-
2. ENTRYPOINT(容器入口点)
指定容器启动时执行的入口命令,不会被 ** docker run ** 的命令行参数覆盖,而是将参数传递给 ENTRYPOINT,仅可通过 --entrypoint 强制覆盖。
-
两种语法格式:
-
Exec 格式(推荐):
ENTRYPOINT ["executable", "param1", "param2"]- 生产环境首选,PID1 为目标进程,可正常接收系统信号,支持与
CMD配合实现默认参数+自定义参数。
- 生产环境首选,PID1 为目标进程,可正常接收系统信号,支持与
-
Shell 格式:
ENTRYPOINT command param1 param2- 底层通过
/bin/sh -c执行,PID1 为 sh 进程,无法接收停止信号,且会忽略CMD和docker run的参数,强烈不推荐。
- 底层通过
-
-
核心用法(Exec 格式 + CMD 组合):
# 固定入口命令 ENTRYPOINT ["echo", "Hello"] # 提供默认参数,可被docker run的参数覆盖 CMD ["World"]-
执行
docker run <image>:输出Hello World -
执行
docker run <image> Docker:输出Hello Docker
-
-
注意事项:
-
一个 Dockerfile 中只能有一个
ENTRYPOINT,多个仅最后一个生效。 -
常用于制作可执行镜像(如命令行工具),固定入口命令,仅允许传递参数。
-
可通过
docker run --entrypoint <command> <image>强制覆盖入口点。
-
3. HEALTHCHECK(健康检查)
设置容器的健康检查命令,Docker 会定期执行命令判断容器是否健康,用于服务可用性检测。
-
语法:
# 设置健康检查 HEALTHCHECK [OPTIONS] CMD <command> # 禁用基础镜像继承的健康检查 HEALTHCHECK NONE -
可选参数:
| 参数 | 作用 | 默认值 |
|---|---|---|
--interval=<duration> |
两次健康检查的间隔时间 | 30s |
--timeout=<duration> |
单次检查的超时时间,超时视为检查失败 | 30s |
--start-period=<duration> |
容器启动初始化时间,此期间的失败不计入重试次数 | 0s |
--retries=<number> |
连续失败多少次后,标记容器为不健康 | 3次 |
-
示例:
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \ CMD curl -f http://localhost:8080/health || exit 1 -
注意事项:
-
一个 Dockerfile 中只能有一个
HEALTHCHECK,多个仅最后一个生效。 -
命令退出码决定健康状态:
0=健康,1=不健康,2=保留值(禁止使用)。 -
健康状态可通过
docker ps查看,docker inspect可查看详细检查历史。 -
检查命令必须是执行后立即退出的一次性命令,禁止使用持续运行的进程。
-
4. STOPSIGNAL(容器停止信号)
设置容器停止时,Docker 发送给 PID1 进程的系统信号,默认值为 SIGTERM(15)。
-
语法:
STOPSIGNAL <signal> -
示例:
STOPSIGNAL SIGINT STOPSIGNAL 9 -
注意事项:
-
信号可使用名称(如
SIGTERM)或数字(如15)。 -
用于适配需要自定义信号才能优雅退出的应用。
-
docker stop会先发送该信号,等待超时时间(默认10s)后,发送SIGKILL强制终止容器。
-
5. ONBUILD(触发器指令)
设置构建触发器,当当前镜像被用作其他 Dockerfile 的基础镜像时,会在 FROM 执行后、其他指令执行前,触发执行 ONBUILD 绑定的指令。
-
语法:
ONBUILD <INSTRUCTION> -
示例:
# 基础镜像Dockerfile FROM node:18 WORKDIR /app ONBUILD COPY package*.json ./ ONBUILD RUN npm install ONBUILD COPY . . CMD ["npm", "start"] # 子镜像Dockerfile,无需重复编写构建逻辑 FROM 上述基础镜像 # 构建时会自动执行3条ONBUILD指令 -
注意事项:
-
ONBUILD仅在子镜像构建时触发一次,孙子镜像不会继承触发。 -
不支持嵌套
ONBUILD ONBUILD,也不能绑定FROM、MAINTAINER指令。 -
适合制作通用构建基础镜像,简化子镜像的 Dockerfile 编写。
-
(六)网络与存储指令
1. EXPOSE(端口声明)
声明容器运行时监听的端口与协议,仅作为镜像的文档说明,不会自动发布端口。
-
语法:
EXPOSE <port> [<port>/<protocol>...] -
示例:
EXPOSE 80/tcp EXPOSE 443/tcp EXPOSE 53/udp -
注意事项:
-
默认协议为 TCP,可指定 UDP。
-
端口发布需通过
docker run -p(指定端口映射)或-P(自动映射所有声明端口)实现。 -
即使不写
EXPOSE,容器内服务也可监听端口,该指令仅为元数据,不影响功能。
-
2. VOLUME(匿名卷声明)
声明容器内的目录为匿名卷,容器运行时自动挂载,避免数据写入容器可写层,防止容器删除时数据丢失。
-
语法:
VOLUME ["<path1>", "<path2>"] VOLUME <path1> <path2> -
示例:
VOLUME ["/var/lib/mysql", "/var/log/nginx"] VOLUME /data -
注意事项:
-
声明后,该目录的所有写入都会进入卷,而非容器可写层。
-
VOLUME指令之后的RUN指令,若修改该目录的内容,会完全无效(卷在容器运行时才挂载)。 -
容器运行时可通过
docker run -v覆盖挂载的目录与卷类型。 -
禁止对配置文件目录使用
VOLUME,会导致用户无法修改配置。
-
三、多阶段构建核心用法
多阶段构建是 Dockerfile 17.05+ 支持的核心特性,通过多个 FROM 指令划分构建阶段,最终镜像仅保留最后一个阶段的内容,彻底解决「构建依赖冗余、镜像体积过大」的问题。
核心示例
# 阶段1:构建阶段,包含完整编译环境
FROM golang:1.22 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o myapp main.go
# 阶段2:运行阶段,极简基础镜像,仅保留编译产物
FROM alpine:3.19
WORKDIR /app
# 从builder阶段复制编译好的二进制文件
COPY --from=builder /app/myapp .
EXPOSE 8080
CMD ["./myapp"]核心优势
-
极致减小镜像体积,剔除编译工具、源码、中间文件、依赖缓存。
-
提升镜像安全性,减少攻击面,运行镜像仅包含业务运行必需的文件。
-
分离构建与运行环境,构建阶段使用完整工具链,运行阶段使用轻量基础镜像。
-
优化构建缓存,依赖下载、编译步骤分层缓存,大幅提升重复构建速度。
四、镜像缓存机制与优化规则
Docker 构建时会为每条指令生成缓存层,指令与上下文无变化时直接复用缓存,大幅提升构建速度。
核心缓存规则
-
从基础镜像开始,自上而下比对每条指令,指令字符串与对应上下文无变化则复用缓存。
-
COPY/ADD指令会比对文件的校验和(checksum),校验和变化则缓存失效,后续所有层全部重新构建。 -
RUN等其他指令仅比对指令字符串,指令不变则复用缓存,不关心执行结果是否变化。 -
某一层缓存失效后,后续所有层的缓存全部失效。
缓存优化技巧
-
不变指令前置,易变指令后置:先复制依赖文件、安装依赖,最后复制业务源码,避免源码变化导致依赖层缓存失效。
-
合并关联的
RUN指令,减少镜像层数,同时避免缓存异常(如apt-get update与install必须合并)。 -
合理使用
.dockerignore,排除无关文件,避免上下文变化导致缓存意外失效。 -
使用 BuildKit 缓存挂载(
RUN --mount=type=cache),复用依赖缓存,不写入镜像层。
五、生产环境最佳实践
-
优先使用官方轻量基础镜像(alpine、slim 系列),固定镜像版本,避免
latest标签导致构建不一致。 -
一个容器仅运行一个进程,保持容器的单一职责,便于日志收集、故障排查与水平扩展。
-
合并
RUN指令,命令执行后必须清理缓存,最小化镜像层数与体积。 -
常规文件复制优先使用
COPY,仅自动解压场景使用ADD,禁止用ADD下载远程文件。 -
严格区分
ARG与ENV,构建时变量用ARG,运行时环境用ENV,敏感信息禁止写入 Dockerfile。 -
生产环境使用非 root 用户运行容器,提前配置用户权限与文件归属。
-
始终使用 Exec 格式编写
ENTRYPOINT与CMD,保证进程 PID1 正常接收系统信号,实现优雅退出。 -
强制使用多阶段构建,分离构建与运行环境,最小化最终镜像内容。
-
始终编写
.dockerignore文件,排除源码、缓存、敏感文件、本地构建产物。 -
为有状态服务添加
HEALTHCHECK健康检查,标注LABEL元数据,声明EXPOSE端口,提升镜像可维护性。 -
禁止在 Dockerfile 中硬编码密钥、密码、证书等敏感信息,通过构建机密、运行时环境变量或挂载配置注入。
-
避免在
VOLUME之后修改目录内容,禁止对配置目录使用VOLUME。
(注:文档部分内容可能由 AI 生成)