Dockerfile常用参数
核心概念:
- Dockerfile: 一个文本文件,包含一系列用于构建 Docker 镜像的指令。
- 镜像层: 每条指令(除了极少数如
ARG)在执行时都会在基础镜像之上创建一个新的只读层。 - 构建上下文: 运行
docker build时指定的路径(或 URL)。Docker 守护进程在构建开始时会将整个上下文(递归地)发送给它。指令如COPY、ADD操作的文件必须位于此上下文中。
一、基础与必备指令
FROM
- 用途: 指定基础镜像。这是 Dockerfile 的第一条有效指令(
ARG可以在它之前,用于定义后续FROM使用的变量)。 - 语法:dockerfile
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>] - 说明:
<image>: 基础镜像名称(如ubuntu,python,nginx)。<tag>: 镜像标签(如20.04,3.9-slim,latest)。强烈建议指定明确版本,避免因latest变化导致构建失败或不一致。[AS <name>]: 在多阶段构建中,为当前构建阶段命名,方便后续阶段引用。--platform: 指定目标平台(如linux/amd64,linux/arm64)。
- 示例:dockerfile
FROM ubuntu:22.04 FROM python:3.11-slim-bullseye AS builder FROM --platform=linux/arm64 nginx:1.23-alpine
RUN
用途: 在构建过程中执行命令。这些命令在构建时运行,用于安装软件包、编译代码、配置环境等。每条
RUN都会创建一个新的镜像层。语法 (两种形式):
- Shell 形式:
RUN <command>(默认在/bin/sh -c下执行)
dockerfileRUN apt-get update && apt-get install -y curl RUN echo "Hello from the build process" > /tmp/greeting.txt- Exec 形式:
RUN ["executable", "param1", "param2"](直接执行可执行文件,不通过 shell 解析)dockerfileRUN ["/bin/bash", "-c", "echo 'Exec form' > /tmp/exec.txt"]
- Shell 形式:
区别与最佳实践:
- Shell 形式更常见,更易读。 可以使用环境变量、管道
|、逻辑运算符&&、||、;。 - Exec 形式 避免 shell 字符串解析可能带来的意外行为(特别是当参数包含空格或特殊字符时),并且明确指定了要运行的二进制文件。 对于不依赖 shell 功能的简单命令或需要避免 shell 干扰的场景更合适。
- 关键点: 将多个命令合并到一个
RUN指令中(使用&&和\换行),减少不必要的镜像层数量。安装后记得清理缓存(如rm -rf /var/lib/apt/lists/*)。
- Shell 形式更常见,更易读。 可以使用环境变量、管道
示例:
dockerfileRUN apt-get update \ && apt-get install -y --no-install-recommends \ git \ build-essential \ ca-certificates \ && rm -rf /var/lib/apt/lists/* \ && pip install --no-cache-dir some-package
CMD
- 用途: 为容器提供默认的启动命令和参数。在容器启动时执行。一个 Dockerfile 中只能有一个有效的
CMD(如果写了多个,只有最后一个生效)。 - 语法 (三种形式):
- Exec 形式 (推荐):
CMD ["executable","param1","param2"] - Shell 形式:
CMD command param1 param2(会被包装成/bin/sh -c) - 作为
ENTRYPOINT的参数:CMD ["param1","param2"](必须与ENTRYPOINT的 Exec 形式结合使用)
- Exec 形式 (推荐):
- 说明与行为:
CMD定义的是默认行为。用户在docker run命令行中指定的任何参数都会覆盖整个CMD。- Exec vs Shell 形式: 推荐使用 Exec 形式,原因与
RUN类似:避免不必要的 shell 介入,信号处理更直接(容器中的 PID 1 是应用程序本身,能正确接收 SIGTERM 等信号)。 CMD的主要目的是让容器运行一个默认的进程。如果该进程退出,容器也会停止。
- 示例:dockerfile
CMD ["nginx", "-g", "daemon off;"] # 推荐写法,直接运行 nginx 主进程 CMD python app.py # Shell 形式,PID 1 是 /bin/sh CMD ["/bin/bash"] # 启动 bash shell
ENTRYPOINT
- 用途: 配置容器启动时运行的可执行文件。可以理解为容器的主程序。
- 语法 (两种形式):
- Exec 形式 (推荐):
ENTRYPOINT ["executable", "param1", "param2"] - Shell 形式:
ENTRYPOINT command param1 param2(会被包装成/bin/sh -c)
- Exec 形式 (推荐):
- 说明、行为与联系 (
CMD&ENTRYPOINT):ENTRYPOINT定义的是容器启动时必须运行的命令。CMD定义的参数可以作为ENTRYPOINT的默认参数。- 组合使用 (最常见模式):
ENTRYPOINT定义要执行的程序。CMD定义该程序的默认参数。- 用户在
docker run命令行中指定的参数会覆盖CMD的内容,并作为ENTRYPOINT的参数。- 覆盖规则:
- 用户可以在
docker run中使用--entrypoint标志覆盖ENTRYPOINT。 - 用户在
docker run命令行末尾指定的参数会覆盖CMD。- Exec vs Shell 形式: 强烈推荐使用 Exec 形式。Shell 形式会使你的程序成为 shell 的子进程(PID 不是 1),导致无法正确接收 Unix 信号(如 SIGTERM),并且
docker stop可能无法正常停止容器。
- Exec vs Shell 形式: 强烈推荐使用 Exec 形式。Shell 形式会使你的程序成为 shell 的子进程(PID 不是 1),导致无法正确接收 Unix 信号(如 SIGTERM),并且
- 示例:dockerfile
ENTRYPOINT ["/usr/bin/wc"] CMD ["--help"] # 默认运行 `wc --help`。用户运行 `docker run myimage -l` 实际执行 `/usr/bin/wc -l` ENTRYPOINT ["top", "-b"] # 直接运行 top 命令,忽略 CMD 和用户命令行参数。用户运行 `docker run myimage -H` 会报错,因为 -H 被当作 top 的参数,而 top 可能不支持 -H。
COPY vs ADD
共同用途: 从构建上下文复制文件或目录到镜像中。
语法:
dockerfileCOPY [--chown=<user>:<group>] [--chmod=<perms>] <src>... <dest> ADD [--chown=<user>:<group>] [--chmod=<perms>] <src>... <dest><src>: 构建上下文中的源路径(文件或目录)。支持通配符 (*,?)。必须在构建上下文中!<dest>: 镜像中的目标绝对路径,或相对于WORKDIR的路径。--chown,--chmod: 设置复制后文件的所有权/权限 (仅在 Linux 容器中有效)。
关键区别:
特性 COPYADD核心功能 仅复制本地文件/目录 (来自构建上下文) 在 COPY功能基础上增加了两个功能:额外功能1 无 自动解压:如果 <src>是本地压缩文件 (tar,gzip,bzip2,xz) 且目标路径不以斜杠结尾,则自动解压到<dest>。额外功能2 无 远程 URL 支持: <src>可以是 URL。Docker 会下载该 URL 的内容到<dest>。**注意:下载的文件不会被自动解压!** 最佳实践与选择:
COPY是首选: 对于简单的复制本地文件/目录需求,总是使用COPY。它的行为更清晰、更可预测。- 何时使用
ADD: - 需要自动解压本地压缩文件到镜像中时。
- 需要从 URL 下载文件并直接放入镜像中(且不需要解压时)。注意: 通常更推荐在
RUN指令中使用curl或wget下载文件,因为这样可以更好地控制下载过程(如处理失败、校验和)、清理下载缓存以及利用 Docker 的层缓存机制。- 一般建议: 除非你明确需要
ADD的自动解压或 URL 下载功能,否则坚持使用COPY。
- 一般建议: 除非你明确需要
WORKDIR
- 用途: 为后续的
RUN,CMD,ENTRYPOINT,COPY,ADD指令设置工作目录。如果目录不存在,会被自动创建。 - 语法:
WORKDIR /path/to/workdir - 说明:
- 可以多次使用。后续路径如果是相对路径,将基于前一个
WORKDIR设置。 - 非常重要! 它使得 Dockerfile 中的路径引用更加清晰和安全,避免了在
RUN等指令中写冗长的绝对路径。 - 相当于
cd命令的效果,但效果是持久的(影响后续指令)。
- 可以多次使用。后续路径如果是相对路径,将基于前一个
- 示例:dockerfile
WORKDIR /app COPY . . # 将构建上下文当前目录复制到镜像的 /app 目录下 RUN python setup.py install CMD ["python", "main.py"] # 在 /app 下执行 main.py WORKDIR /logs RUN touch output.log # 在 /logs 下创建 output.log
ENV
- 用途: 设置环境变量。这些变量在构建阶段和容器运行阶段都可用。
- 语法:dockerfile
ENV <key>=<value> ... # 一次设置一个或多个变量 (推荐) ENV <key> <value> # 旧式写法 (一次设置一个变量) - 说明:
- 使用
<key>=<value>形式可以清晰地一次设置多个变量。 - 设置的环境变量可以在后续的 Dockerfile 指令(如
RUN)和运行中的容器内部访问。 - 常用于配置应用程序所需的参数(如数据库连接字符串、日志级别、路径)。
- 使用
- 示例:dockerfile
ENV APP_VERSION=1.0.0 \ NODE_ENV=production \ PATH=/usr/local/myapp/bin:$PATH RUN echo "Building version $APP_VERSION for $NODE_ENV" # 构建阶段使用 CMD ["myapp"] # 运行时 myapp 可以访问 NODE_ENV 等变量
二、其他重要指令
ARG
- 用途: 定义在构建时传递给 Docker 构建器的变量。只在构建阶段 (
docker build) 有效,在容器运行阶段 (docker run) 不可用。有作用域限制(声明后生效,直到构建阶段结束或下一个FROM指令)。 - 语法:dockerfile
ARG <name>[=<default value>] - 说明与联系 (
ENV):- 使用
docker build --build-arg <varname>=<value>在构建时覆盖默认值或提供值。 - 常用于控制构建过程的参数化,如指定要下载的软件版本、选择不同的构建配置。
- 与
ENV的关键区别:ARG是构建时变量,构建完成后消失;ENV是运行时环境变量(也在构建时可用)。 ARG值可以在构建时通过ENV指令转换成运行时的环境变量。
- 使用
- 示例:dockerfile
ARG USERNAME=defaultuser ARG APP_VERSION=latest RUN useradd -ms /bin/bash $USERNAME ENV APP_VERSION=$APP_VERSION # 将 ARG 的值传递给 ENV,使其在运行时可用 COPY --chown=$USERNAME app-$APP_VERSION.tar.gz /app/bashdocker build --build-arg USERNAME=produser --build-arg APP_VERSION=2.1.0 -t myapp:2.1.0 .
USER
- 用途: 设置运行后续指令 (
RUN,CMD,ENTRYPOINT) 以及容器运行时的默认用户名或 UID。 - 语法:
USER <user>[:<group>]或USER <UID>[:<GID>] - 说明:
- 安全最佳实践! 强烈建议不要一直以 root 用户运行容器。使用
USER切换到非 root 用户,遵循最小权限原则。 - 确保在切换用户之前,该用户(和组)已经通过
RUN(如useradd)在镜像中创建好。 - 影响
COPY的--chown默认行为(如果不指定--chown,文件所有权由USER决定)。
- 安全最佳实践! 强烈建议不要一直以 root 用户运行容器。使用
- 示例:dockerfile
RUN groupadd -r appuser && useradd -r -g appuser appuser WORKDIR /app COPY --chown=appuser:appuser . . # 明确设置所有权 USER appuser CMD ["python", "app.py"] # 以 appuser 身份运行
EXPOSE
- 用途: 声明容器在运行时监听的网络端口。这是一个文档性指令和元数据设置,并不实际发布端口或导致网络连接。
- 语法:
EXPOSE <port> [<port>/<protocol>...] - 说明:
- 默认协议是 TCP。如需 UDP,需明确指定 (如
EXPOSE 53/udp)。 - 主要作用:
- 告知镜像使用者/运维人员该容器设计上会监听哪些端口。
- 在使用
docker run -P时,Docker 会自动将容器中所有EXPOSE的端口映射到宿主机的高位随机端口。 - 在
docker network连接容器时,EXPOSE的端口信息有助于服务发现。- 实际发布端口(使外部可访问)必须在
docker run时使用-p或-P标志显式完成。
- 实际发布端口(使外部可访问)必须在
- 默认协议是 TCP。如需 UDP,需明确指定 (如
- 示例:
EXPOSE 80/tcp 443/tcp 8080
VOLUME
- 用途: 创建一个具有指定名称的挂载点,并将其标记为从宿主机或其他容器挂载外部卷的位置。
- 语法:
VOLUME ["/data"]或VOLUME /data /otherdata - 说明:
- 主要目的是数据持久化和共享。存储在
VOLUME指定路径下的数据不会被打包进镜像层,并且在容器停止后仍然存在。 - 在
docker run时,如果没有显式使用-v或--mount绑定宿主机目录或命名卷,Docker 会自动创建一个匿名的数据卷绑定到这个挂载点。 - 在 Dockerfile 中使用
VOLUME可以明确告知用户镜像中哪些路径是设计用于存放持久化数据的。 - 在
VOLUME之后对该路径的修改(RUN、COPY)不会生效(因为挂载发生在运行时,会覆盖镜像层中的内容)。最佳实践: 确保VOLUME指令在你需要将文件复制到该目录的操作(如COPY)之后声明。
- 主要目的是数据持久化和共享。存储在
- 示例:dockerfile
COPY initial-data.json /data/ # 将初始数据复制到 /data VOLUME /data # 声明 /data 为卷。运行时,如果用户不绑定卷,Docker 会创建匿名卷,initial-data.json 会被复制到这个匿名卷中。如果用户绑定了一个宿主机目录(可能为空)到 /data,则 initial-data.json 会被隐藏。
三、总结与对比表
| 指令 | 主要用途 | 作用阶段 | 是否创建层 | 关键点/区别/联系 |
|---|---|---|---|---|
FROM | 指定基础镜像 | 构建 | 是 | 必须是第一条有效指令。多阶段构建核心。 |
RUN | 在构建时执行命令 | 构建 | 是 | 合并命令减少层。Shell vs Exec 形式。 |
CMD | 容器默认启动命令/参数 | 容器运行时 | 否 | 定义默认行为,可被 docker run 覆盖。常与 ENTRYPOINT 配合 (提供默认参数)。 |
ENTRYPOINT | 容器主启动程序 | 容器运行时 | 否 | 定义容器主程序。可被 --entrypoint 覆盖。常与 CMD 配合 (CMD 作默认参数)。 |
COPY | 复制本地文件/目录 (上下文 -> 镜像) | 构建 | 是 | 首选复制方式。行为明确。 |
ADD | 复制 + 自动解压本地压缩包 + 下载 URL 文件 | 构建 | 是 | 功能多于 COPY (解压、下载)。仅在需要这些额外功能时使用。 |
WORKDIR | 设置后续指令的工作目录 | 构建 & 运行 | 否 | 影响后续 RUN/CMD/ENTRYPOINT/COPY/ADD 的路径。相当于持久化的 cd。 |
ENV | 设置环境变量 | 构建 & 运行 | 否 | 构建和运行时均可用。用于配置应用程序。 |
ARG | 定义构建时变量 | 仅构建 | 否 | 只在 docker build 时有效。可通过 --build-arg 传递。常转为 ENV 供运行时用。 |
USER | 设置运行用户 (UID/GID) | 构建 & 运行 | 否 | 安全最佳实践! 切换到非 root 用户。需确保用户已创建。 |
EXPOSE | 声明容器监听端口 | 元数据 | 否 | 文档性/声明性。不实际发布端口。-P 依赖它。-p 才是实际发布。 |
VOLUME | 声明数据卷挂载点 | 元数据 | 否 | 声明性。用于持久化和共享数据。运行时自动创建匿名卷或需用户绑定。 |
核心联系:
ENTRYPOINT&CMD: 共同定义容器启动时的行为。ENTRYPOINT是主程序,CMD是其默认参数。用户docker run参数覆盖CMD。COPY&ADD:ADD是COPY的超集,增加了自动解压和 URL 下载功能。简单复制用COPY。ENV&ARG:ARG用于构建时参数化,构建后消失;ENV设置的环境变量在构建和运行时都可用。可以将ARG的值赋给ENV使其在运行时保留。WORKDIR: 为RUN,CMD,ENTRYPOINT,COPY,ADD提供相对路径的基准。- 层创建 (
RUN,COPY,ADD): 这些指令会创建新的镜像层。优化原则是:合并相关操作(尤其是RUN),清理不必要的临时文件,减少最终镜像层数和大小。
最佳实践总结:
- 使用明确的
FROM标签。 - 合并
RUN指令,清理缓存。 - 优先使用
COPY而非ADD。 - 使用
WORKDIR设置绝对路径。 CMD和ENTRYPOINT使用 Exec 形式。- 利用
ENTRYPOINT和CMD的组合。 - 使用
ENV设置配置。 - 使用
ARG参数化构建。 - 始终使用非 root 用户 (
USER) 运行容器。 - 使用
EXPOSE声明端口。 - 使用
VOLUME声明持久化数据位置。 - 注意指令顺序(如
VOLUME在复制初始数据之后)。
掌握这些指令及其区别联系,你就能编写出高效、安全、可维护的 Dockerfile 了。