Dockerfile文件
介绍
Docker 可以通过读取 Dockerfile
中的指令来自动构建镜像。Dockerfile
是一个文本文档,其中包含用户可以在命令行上调用来组装镜像的所有命令。
常用指令
ARG
ARG
-定义
定义一个变量,用户可以在构建时docker build
使用--build-arg <varname>=<value>
标志的命令将该变量传递给构建器。可以包含默认值,如:ARG user1=someuser
,在构建时没有传递user1
的值,则构建器将使用默认值someuser
。
ARG
-语法
ARG <name>[=<default value>] [<name>[=<default value>]...]
ARG
-作用域
ARG
指令定义的变量仅在构建阶段可用,不会保留在最终镜像中
ARG
-使用位置
- 可以在
FROM
指令之前使用,但这种情况下只能被FROM
指令使用 FROM
之后的ARG
在整个构建阶段都可用
ARG
-预定义ARG
Docker 预定义了一些 ARG 变量,无需声明即可使用:
- HTTP_PROXY、http_proxy
- HTTPS_PROXY、https_proxy
- FTP_PROXY、ftp_proxy
- NO_PROXY、no_proxy
- ALL_PROXY、all_proxy
ARG
-示例:
1 | # 在 FROM 之前定义 |
ARG
-最佳实践
- 将频繁变化的 ARG 放在 Dockerfile 的后面,以充分利用构建缓存
- 不要在 ARG 中存储敏感信息,因为它们可以通过 docker history 命令查看
- 如果需要在运行时使用变量,应该使用 ENV 指令
FROM
FROM
-定义
指定基础镜像,用于创建一个新的构建阶段。这是构建Docker镜像时的起点,通常是Dockerfile中的第一条指令。
FROM
-语法
FROM [--platform=<platform>] <image> [AS <name>]
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
FROM
-参数说明
--platform
:指定目标平台(例如:linux/amd64, linux/arm64)image
:基础镜像名称tag
:镜像标签,默认为latestdigest
:镜像的摘要值,用于指定确切的镜像版本AS name
:为构建阶段命名,用于多阶段构建
FROM
-特点
- 一个Dockerfile可以包含多个FROM指令,用于创建多阶段构建
- 每个FROM指令都会清除之前阶段创建的任何状态
ARG
、comments、 parser directives是可以在FROM之前使用的指令
FROM
-示例
1 | # 基本用法 |
FROM
-最佳实践
- 使用官方镜像作为基础镜像
- 优先使用具体的标签而不是latest
- 考虑使用轻量级基础镜像(如alpine)
- 在多阶段构建中使用AS关键字来优化最终镜像大小
WORKDIR
WORKDIR
-定义
设置工作目录,用于后续的 Dockerfile 指令(如 RUN
、CMD
、ENTRYPOINT
、COPY
和 ADD
)的执行环境,默认为/
。
WORKDIR
-语法
WORKDIR /path/to/workdir
WORKDIR
-特点
- 如果指定的目录不存在,Docker 会自动创建该目录且创建一个新的层,反之不会
- 可以使用多个 WORKDIR 指令,每个指令都相对于前一个路径
- 支持使用环境变量,如:
WORKDIR $HOME/app
- 可以使用绝对路径或相对路径
WORKDIR
-示例
1 | # 基本用法 |
WORKDIR
-最佳实践
- 使用绝对路径而不是相对路径,以避免混淆
- 避免使用
RUN cd /path
命令,应该使用 WORKDIR 来改变目录 - 为了可维护性,保持工作目录结构清晰
- 在多阶段构建中,每个阶段都应该明确设置 WORKDIR
- 确保目录名称符合项目的命名规范
COPY
COPY
-定义
将文件或目录从构建上下文复制到镜像中。COPY指令会在复制文件时保留文件的所有元数据(如文件权限、创建时间等)。
COPY
-语法
COPY [--chown=<user>:<group>] [--chmod=<perms>] [--link] <src> ... <dest>
COPY [--chown=<user>:<group>] [--chmod=<perms>] [--link] ["<src>",... "<dest>"]
COPY
-参数说明
--chown
:指定复制到容器内的文件的所有者和组--chmod
:指定复制到容器内的文件的权限--link
:创建硬链接而不是复制文件(可以提高构建效率)<src>
:源文件或目录,支持通配符<dest>
:目标路径
COPY
-特点
- 只能复制构建上下文中的文件
- 如果指定多个源文件,目标必须是目录(以/结尾)
- 会保留文件的元数据(权限、时间戳等)
- 支持Go的filepath.Match规则的通配符
COPY
-示例
1 | # 基本用法 |
COPY
-最佳实践
- 优先使用
COPY
而不是ADD
,除非需要ADD
的特殊功能 - 将不常变化的文件先复制,以利用Docker的缓存机制
- 明确指定文件权限和所有者,确保安全性
- 使用
.dockerignore
排除不需要的文件 - 在多阶段构建中使用
COPY --from
来复制文件
COPY
-ADD
主要区别
功能范围
COPY
:仅支持从构建上下文复制文件/目录到镜像中ADD
:支持额外的特性:- 自动解压
tar
文件(gzip
、bzip2
、xz
等格式) - 支持从URL下载文件
- 自动解压
使用推荐
- 如果不需要自动解压或从URL获取文件,优先使用
COPY
COPY
的行为更可预测,更直观- 需要解压文件时,推荐使用
RUN
命令配合tar
,而不是依赖ADD
- 需要下载文件时,推荐使用
RUN
命令配合wget/curl
,而不是ADD
- 如果不需要自动解压或从URL获取文件,优先使用
示例对比
1
2
3
4
5
6
7
8
9
10
11
12
13
14# 使用 COPY 的推荐方式
COPY app.js /app/
COPY package.json /app/
# 不推荐使用 ADD 下载文件
ADD http://example.com/file.txt /app/
# 推荐使用 RUN 配合 curl
RUN curl -O http://example.com/file.txt
# 不推荐使用 ADD 解压文件
ADD app.tar.gz /app/
# 推荐使用 RUN 配合 tar
COPY app.tar.gz /tmp/
RUN tar -xzf /tmp/app.tar.gz -C /app/ && rm /tmp/app.tar.gz
ADD
ADD
-定义
将文件、目录或远程URL从源位置复制到镜像的目标路径中。相比COPY
指令,ADD
具有额外的功能,如自动解压tar
文件和支持远程URL。
ADD
-语法
ADD [--chown=<user>:<group>] [--chmod=<perms>] [--checksum=<checksum>] <src> ... <dest>
ADD [--chown=<user>:<group>] [--chmod=<perms>] [--checksum=<checksum>] ["<src>",... "<dest>"]
ADD
-参数说明
--chown
:指定添加到容器内的文件的所有者和组--chmod
:指定添加到容器内的文件的权限--checksum
:验证远程资源<src>
:源文件、目录或URL<dest>
:目标路径
ADD
-特点
- 可以从远程URL获取资源
- 自动解压本地tar文件(格式为:gzip、bzip2、xz)
- 支持Go的filepath.Match规则的通配符
- 源路径必须在构建上下文内(URL除外)
- 如果源是URL且目标没有以斜杠结尾,则文件将被下载并复制到目标
- 如果源是本地tar归档且目标没有以斜杠结尾,则归档将被解压到目标路径
ADD
-示例
1 | # 基本用法 |
ADD
-最佳实践
- 仅在需要自动解压tar文件或从URL获取资源时使用
ADD
- 对于简单的文件复制操作,优先使用
COPY
指令 - 使用
--checksum
参数验证远程资源的完整性 - 避免使用
ADD
下载并解压远程tar文件,建议分别使用RUN wget/curl
和tar
命令 - 当使用远程URL时,确保使用HTTPS协议以确保安全性
- 注意URL资源会被缓存,如需更新需要使用不同的URL
RUN
RUN
-定义
在构建镜像时执行命令并创建新的镜像层。是最常用的 Dockerfile 指令之一,用于安装包、创建文件和目录、创建链接等。
RUN
-语法
RUN [OPTIONS] <command>
(Shell 格式)RUN [OPTIONS] ["executable", "param1", "param2"]
(Exec 格式)
RUN
-参数说明
--network
:指定运行命令时的网络模式default
:在默认网络中运行none
:在无网络环境中运行host
:使用主机网络运行
--mount
:在执行命令时挂载额外的文件系统--security
:设置安全选项
RUN
-特点
- 每个
RUN
指令都会创建一个新的镜像层 Shell
格式默认在 Linux 上使用/bin/sh -c
,在 Windows 上使用cmd /S /C
Exec
格式直接调用命令,不会启动shell
解释器- 支持使用
\
将长命令分成多行 - 可以使用环境变量
RUN
-示例
1 | # Shell 格式基本用法 |
RUN
-最佳实践
- 将多个相关命令合并到一个 RUN 指令中,使用
&&
连接,减少镜像层数 - 及时清理不需要的文件,减小镜像大小
- 使用
--no-cache
等选项避免缓存文件 - 优先使用
Shell
格式,除非需要特定shell
或无shell
环境 - 在包管理器命令中使用
-y
标志实现自动确认 - 记得在安装包后清理包管理器缓存
- 考虑使用
set -e
确保命令失败时构建失败
RUN
-CMD
主要区别
执行时机
RUN
:在构建镜像时执行命令CMD
:在容器启动时执行命令
使用次数
RUN
:可以有多个,每个都会创建新的镜像层CMD
:只能有一个有效命令,多个时只有最后一个生效
指令目的
RUN
:用于安装软件包、创建文件等构建镜像所需的操作CMD
:为容器提供默认的启动命令或参数
命令覆盖
RUN
:不能被覆盖,是镜像的固定组成部分CMD
:可以被docker run
的命令行参数覆盖
示例对比
1
2
3
4
5# RUN 示例 - 安装软件包
RUN apt-get update && apt-get install -y nginx
# CMD 示例 - 设置容器启动命令
CMD ["nginx", "-g", "daemon off;"]
CMD
CMD
-定义
指定容器启动时要运行的默认命令。每个Dockerfile只能有一个CMD指令,如果指定了多个,只有最后一个生效。
CMD
-语法
CMD ["executable", "param1", "param2"]
(exec 格式,推荐)CMD ["param1", "param2"]
(作为 ENTRYPOINT 的默认参数)CMD command param1 param2
(shell 格式)
CMD
-特点
- 容器启动时的默认命令
- 可以被
docker run
命令行参数覆盖 - 如果与
ENTRYPOINT
一起使用,则CMD
指定的参数会作为ENTRYPOINT
的默认参数 exec
格式使用JSON数组,可以正确处理Unix信号shell
格式会以/bin/sh -c
的方式执行命令
CMD
-示例
1 | # exec 格式(推荐) |
CMD
-最佳实践
- 优先使用
exec
格式而不是shell
格式 - 为长期运行的程序(如服务器)提供合适的默认命令
- 确保前台运行主进程(避免使用
systemctl
、service
等后台启动命令) - 如果需要运行
shell
脚本,使用exec
格式并显式调用shell
- 当与
ENTRYPOINT
配合使用时,两者都应使用exec
格式 - 在开发环境中可以提供调试友好的默认命令
EXPOSE
EXPOSE
-定义
声明容器运行时监听的端口。这个指令只是声明容器打算使用的端口,实际上并不会自动打开这些端口。
EXPOSE
-语法
EXPOSE <port> [<port>/<protocol>...]
EXPOSE
-特点
- 默认协议为
TCP
,可以显式指定为UDP
- 可以同时指定多个端口
- 可以为同一个端口指定多个协议
- 仅作为文档说明使用,不会实际映射端口
- 实际端口映射需要在
docker run
时使用-p
或-P
参数,-p <host_port>:<container_port>
将容器端口映射到指定主机端口,-P
将所有 EXPOSE 的端口自动映射到主机的随机端口
EXPOSE
-示例
1 | # 基本用法 |
EXPOSE
-最佳实践
- 为应用程序使用的所有端口添加
EXPOSE
指令 - 使用标准端口号(如
HTTP
用80
,HTTPS
用443
) - 在文档中清晰说明暴露的端口用途
- 考虑安全性,只暴露必要的端口
- 在使用
-P
时注意端口随机映射可能带来的影响 - 在生产环境中优先使用显式端口映射(
-p
)而不是自动映射(-P
)
ENV
ENV
-定义
设置环境变量,这些变量在容器运行时可用,并且可以被后续的Dockerfile指令使用。与ARG
不同,ENV
设置的变量会持久化到最终的镜像中。
ENV
-语法
ENV <key>=<value> ...
ENV <key> <value>
ENV
-特点
- 在构建过程中和容器运行时都可用
- 可以在一条指令中设置多个环境变量
- 值中包含空格时需要使用引号
- 可以被运行时通过
docker run --env
或-e
标志覆盖 - 环境变量在镜像中持久化
ENV
-示例
1 | # 基本用法 |
ENV
-最佳实践
- 使用有意义的变量名,遵循命名规范
- 对于敏感信息,考虑使用构建时
ARG
或运行时secrets
- 相关的环境变量应该组合在一起设置
- 为环境变量提供合理的默认值
- 在生产环境中谨慎使用环境变量覆盖
- 使用环境变量来配置应用行为,而不是硬编码值
VOLUME
VOLUME
-定义
创建一个挂载点,用于持久化数据或共享数据。这个指令告诉Docker容器运行时应该将指定的路径当作外部挂载的卷。
VOLUME
-语法
VOLUME ["/path/to/dir"]
(JSON数组格式)VOLUME /path/to/dir
(普通格式)
VOLUME
-特点
- 容器启动时会自动创建指定的挂载点
- 支持指定多个挂载点
- 挂载点的数据在容器外部持久化
- 多个容器可以共享挂载点
- 如果挂载点目录已经包含数据,这些数据会被复制到卷中
VOLUME
-示例
1 | # 基本用法 |
VOLUME
-最佳实践
- 用于存储数据库文件、日志文件等需要持久化的数据,不强制要求在
Dockerfile
中指定,主要是给镜像使用者一个提示,表明这些目录可能会保存重要数据 - 避免在挂载点中存储临时数据
- 使用有意义的目录路径名称
- 考虑使用命名卷而不是匿名卷
- 在生产环境中注意数据备份策略
- 注意卷的权限设置,确保容器进程有适当的访问权限
LABEL
LABEL
-定义
为镜像添加元数据,以键值对的形式存储镜像的额外信息,如维护者信息、版本信息、描述等。这些标签可以帮助组织和管理镜像。
LABEL
-语法
LABEL <key>=<value> <key>=<value> <key>=<value> ...
LABEL
-特点
- 一个镜像可以有多个标签
- 可以在一条指令中指定多个标签
- 标签值中如包含空格,需要使用引号
- 可以使用环境变量
- 可以通过
docker inspect
命令查看镜像的标签
LABEL
-示例
1 | # 基本用法 |
LABEL
-最佳实践
- 使用有意义的标签名和值
- 遵循标准命名约定,如使用 org.opencontainers.image.* 前缀
- 在一条LABEL指令中组合相关的标签
- 保持标签信息的更新
- 不要在标签中存储敏感信息
- 使用标签来记录版本、构建日期等重要信息
USER
USER
-定义
指定运行容器时使用的用户名或UID,以及可选的用户组或GID。这个指令会影响后续的RUN
、CMD
和ENTRYPOINT
指令。
USER
-语法
USER <user>[:<group>]
USER <UID>[:<GID>]
USER
-特点
- 影响后续指令的执行环境
- 可以使用用户名或UID
- 可以可选地指定组名或GID
- 如果用户不存在,需要先创建
- 容器运行时的默认用户是root
USER
-示例
1 | # 使用用户名 |
USER
-最佳实践
- 避免使用root用户运行应用
- 为应用创建专门的用户
- 确保用户具有必要的权限
- 在切换用户前创建必要的目录和文件
- 使用数字ID可以避免用户名冲突
- 考虑使用非特权用户来提高安全性
USER
-安全考虑
- 不要使用root用户运行应用程序
- 为用户分配最小必要权限
- 在生产环境中使用非特权用户
- 注意文件权限和所有权
- 考虑使用多阶段构建来管理权限
HEALTHCHECK
HEALTHCHECK
-定义
用于告诉Docker如何测试容器是否仍在工作。可以检测容器内运行的应用程序是否正常工作,而不是仅仅检查容器是否在运行。
HEALTHCHECK
-语法
HEALTHCHECK [OPTIONS] CMD command
(执行命令检查健康状态)HEALTHCHECK NONE
(禁用从基础镜像继承的健康检查)
HEALTHCHECK
-参数说明
--interval=DURATION
:两次健康检查的间隔,默认为30s--timeout=DURATION
:健康检查命令运行超时时间,默认为30s--start-period=DURATION
:容器启动的初始化时间,默认为0s--start-interval=DURATION
:容器启动后,等待健康检查的时间,默认为0s--retries=N
:当检查失败时,重试的次数,默认为3次
HEALTHCHECK
-特点
- 容器可以处于以下健康状态:
starting
:启动状态,正在等待首次健康检查healthy
:健康状态,最近的健康检查通过unhealthy
:不健康状态,最近的健康检查失败
- 健康检查命令的退出码决定容器的健康状态:
- 0:成功,容器健康
- 1:不健康,容器有问题
- 2:保留,不要使用这个退出码
- Dockerfile 中只能有一个
HEALTHCHECK
指令,如果列出多个指令,则只有最后一个指令HEALTHCHECK
会生效
HEALTHCHECK
-示例
1 | # 基本用法 |
HEALTHCHECK
-最佳实践
- 设置合适的检查间隔,避免过于频繁
- 确保检查命令轻量且可靠
- 为检查命令设置合理的超时时间
- 在容器启动时设置适当的初始化期
- 实现有意义的健康检查逻辑
- 考虑在生产环境中使用更复杂的健康检查
- 记录健康检查的日志以便调试
SHELL
SHELL
-定义
指定用于执行命令的默认shell。这会影响到RUN
指令和CMD
指令的shell形式的执行方式。如果不指定,Linux上默认为["/bin/sh", "-c"]
,Windows上默认为["cmd", "/S", "/C"]
。
SHELL
-语法
SHELL ["executable", "parameters"]
SHELL
-特点
- 影响后续
RUN
和CMD
指令的shell形式执行 - 可以在Dockerfile中多次使用,每次使用都会影响其后的指令
- 必须使用JSON数组格式
- 对exec形式的
RUN
、CMD
和ENTRYPOINT
指令没有影响 - 每个SHELL指令会覆盖所有先前的SHELL指令
SHELL
-示例
1 | # 使用bash作为默认shell |
SHELL
-最佳实践
- 在需要特定shell功能时才更改默认shell
- 确保指定的shell在基础镜像中可用
- 考虑shell切换对脚本兼容性的影响
- 在多平台镜像中注意不同操作系统的shell差异
- 记录SHELL更改的原因,以便维护
- 避免不必要的shell切换,保持一致性
SHELL
-使用场景
- 需要使用特定shell特性时(如bash的数组)
- 在Windows容器中使用PowerShell
- 需要特定shell环境变量或配置时
- 执行依赖于特定shell功能的脚本
- 在多平台构建中处理不同的shell要求
ENTRYPOINT
ENTRYPOINT
-定义
配置容器启动时要运行的可执行文件。与CMD
不同,ENTRYPOINT
指定的命令不会被docker run
的命令行参数覆盖,而是会将这些参数作为ENTRYPOINT
命令的参数。
ENTRYPOINT
-语法
ENTRYPOINT ["executable", "param1", "param2"]
(exec 格式,推荐)ENTRYPOINT command param1 param2
(shell 格式)
ENTRYPOINT
-特点
- 容器启动时的主命令
- 不会被
docker run
命令行参数覆盖 docker run
的命令行参数会被当作ENTRYPOINT
的参数- 可以通过
docker run --entrypoint
覆盖 - 如果与
CMD
一起使用,CMD
会作为ENTRYPOINT
的默认参数 - 每个Dockerfile应该只有一个
ENTRYPOINT
,如果列出多个,只有最后一个生效
ENTRYPOINT
-示例
1 | # exec 格式(推荐) |
ENTRYPOINT
-最佳实践
- 使用
exec
格式而不是shell
格式 - 使用
shell
脚本作为入口点时,记得添加exec "$@"
- 为容器应用程序提供默认的启动命令
- 使用
ENTRYPOINT
脚本进行初始化工作 - 合理组合
ENTRYPOINT
和CMD
- 确保入口点脚本具有可执行权限
- 在入口点脚本中处理信号和进程管理
ENTRYPOINT
-与CMD
的区别
命令覆盖:
ENTRYPOINT
:不会被docker run
参数覆盖CMD
:会被docker run
参数覆盖
参数处理:
ENTRYPOINT
:docker run
参数作为其参数CMD
:docker run
参数替换整个命令
组合使用:
ENTRYPOINT
:定义容器主命令CMD
:提供默认参数
使用场景:
ENTRYPOINT
:适用于作为可执行程序使用的容器CMD
:适用于提供默认的容器启动命令
ONBUILD
ONBUILD
-定义
ONBUILD指令用于在当前镜像被作为基础镜像时执行的命令。这些命令会在子镜像的构建过程中被触发执行,而不是在当前镜像构建时执行。
ONBUILD
-语法
ONBUILD <INSTRUCTION>
ONBUILD
-特点
- 只在构建子镜像时触发执行
- 不能嵌套使用
- 不能使用
FROM
和MAINTAINER
指令 - 构建失败时不会继续执行后续的
ONBUILD
指令 - 可以通过
docker inspect
查看镜像的ONBUILD
触发器
ONBUILD
-示例
1 | # 基本用法 |
ONBUILD
-最佳实践
- 用于创建可复用的基础镜像
- 将通用的构建步骤放在
ONBUILD
中 - 清晰记录
ONBUILD
触发器的用途 - 避免在
ONBUILD
中包含敏感信息 - 保持
ONBUILD
指令的简单和可维护性 - 在文档中说明
ONBUILD
的行为
STOPSIGNAL
STOPSIGNAL
-定义
指定停止容器时要发送的系统调用信号。这个信号将被发送到容器的主进程(PID 1)。如果主进程没有正确处理这个信号,则在超时后容器将被强制终止。
STOPSIGNAL
-语法
STOPSIGNAL signal
STOPSIGNAL
-特点
- 可以使用信号名称(如 SIGTERM)或信号编号(如 15)
- 如果不指定,默认为 SIGTERM
- 可以被
docker stop
命令的--signal
选项覆盖 - 影响容器的优雅停止行为
- 在 Dockerfile 中只能出现一次
STOPSIGNAL
-示例
1 | # 使用信号名称 |
STOPSIGNAL
-最佳实践
- 为应用程序选择合适的停止信号
- 确保应用程序正确处理指定的信号
- 考虑使用 SIGTERM 允许优雅关闭
- 在文档中说明选择特定信号的原因
- 测试信号处理以确保正确的关闭行为
- 考虑设置适当的超时时间(通过
docker stop
)
完整示例
1 | # 第一阶段:使用 ARG 定义构建参数 |
注意事项
- 指令不区分大小写,但是惯例是都大写,以便与其他参数区分开来。
- Docker是按顺序执行
Dockerfile
中的指令。 - 一般情况都以
FROM
指令开头(以下 3 中情况都可以在其前面)。- Parser directives(解析器指令)🔗:
syntax
、escape
、check
(自 Dockerfile v1.8.0 起) - Comments(注释)🔗:以
#
为开头的行。 - ARG(自定义指令)🔗:指令定义了一个变量,用户可以在构建时
docker build
使用--build-arg <varname>=<value>
标志的命令将该变量传递给构建器。
- Parser directives(解析器指令)🔗:
- Parser directives(解析器指令)必须位于
Dockerfile
的顶部且只能使用一次。 - 环境变量在
Dockerfile
中用$variable_name
或表示${variable_name}
,它们被视为等效,括号语法通常用于解决变量名中没有空格的问题。更多详情见Environment replacement,支持环境变量的有ADD
、COPY
、ENV
、EXPOSE
、FROM
、LABEL
、STOPSIGNAL
、USER
、VOLUME
、WORKDIR
、ONBUILD
。 - 可以使用
.dockerignore
文件从构建上下文中排除文件和目录。 docker build
时需要指定一个目录作为构建上下文,默认是当前目录。由于 Docker 构建过程是在 Docker 守护进程中进行的,守护进程只能访问构建上下文中的文件。因此,COPY
、ADD
等指令的源路径必须是构建上下文中的相对路径,这样 Docker 才能找到要复制的文件。