Dockerfile 参考
Docker 可以通过读取 Dockerfile 中的指令来自动构建镜像。Dockerfile 是一个文本文件,其中包含用户在命令行中组装镜像时可能调用的所有命令。本页描述了您可以在 Dockerfile 中使用的命令。
概述
Dockerfile 支持以下指令
| 指令 | 描述 |
|---|---|
ADD | 添加本地或远程文件和目录。 |
ARG | 使用构建时变量。 |
CMD | 指定默认命令。 |
COPY | 复制文件和目录。 |
ENTRYPOINT | 指定默认可执行文件。 |
ENV | 设置环境变量。 |
EXPOSE | 描述您的应用程序正在监听的端口。 |
FROM | 从基础镜像创建一个新的构建阶段。 |
HEALTHCHECK | 在启动时检查容器的健康状况。 |
LABEL | 向镜像添加元数据。 |
MAINTAINER | 指定镜像的作者。 |
ONBUILD | 指定镜像在构建中使用时的指令。 |
RUN | 执行构建命令。 |
SHELL | 设置镜像的默认 shell。 |
STOPSIGNAL | 指定退出容器的系统调用信号。 |
USER | 设置用户和组 ID。 |
VOLUME | 创建卷挂载。 |
WORKDIR | 更改工作目录。 |
格式
以下是 Dockerfile 的格式
# Comment
INSTRUCTION arguments指令不区分大小写。但是,约定是使用大写字母来更容易地与参数区分开来。
Docker 按顺序执行 Dockerfile 中的指令。Dockerfile **必须以 `FROM` 指令开头**。这可以在解析器指令、注释和全局范围的ARG之后。`FROM` 指令指定了您正在构建的基础镜像。`FROM` 只能由一个或多个 `ARG` 指令先行,这些指令声明了用于 Dockerfile 中 `FROM` 行的参数。
BuildKit 将以 `#` 开头的行视为注释,除非该行是有效的解析器指令。行中其他任何地方的 `#` 标记都视为参数。这允许以下语句
# Comment
RUN echo 'we are running some # of cool things'在执行 Dockerfile 指令之前,注释行会被删除。在 shell 执行 `echo` 命令之前,以下示例中的注释会被删除。
RUN echo hello \
# comment
world以下示例是等效的。
RUN echo hello \
world注释不支持行继续符。
注意关于空白字符的注意事项
为了向后兼容,注释(`#`)和指令(例如 `RUN`)之前的行首空白字符会被忽略,但不建议这样做。在这些情况下,行首空白字符不会保留,因此以下示例是等效的
# this is a comment-line RUN echo hello RUN echo world# this is a comment-line RUN echo hello RUN echo world然而,指令参数中的空白字符不会被忽略。以下示例按指定打印带有行首空白字符的 `hello world`
RUN echo "\ hello\ world"
解析器指令
解析器指令是可选的,并影响 Dockerfile 中后续行的处理方式。解析器指令不会为构建添加层,也不会显示为构建步骤。解析器指令以特殊类型的注释形式编写,即 `# directive=value`。单个指令只能使用一次。
支持以下解析器指令
一旦处理了注释、空行或构建器指令,BuildKit 将不再查找解析器指令。相反,它将任何格式化为解析器指令的内容视为注释,并且不会尝试验证它是否可能是解析器指令。因此,所有解析器指令都必须位于 Dockerfile 的顶部。
解析器指令的键,例如 `syntax` 或 `check`,不区分大小写,但按约定使用小写。指令的值区分大小写,并且必须以适合该指令的大小写形式编写。例如,`#check=skip=jsonargsrecommended` 无效,因为检查名称必须使用 Pascal 命名法,而不是小写。通常还会在任何解析器指令后包含一个空行。解析器指令中不支持行继续符。
由于这些规则,以下所有示例均无效
因行继续而无效
# direc \
tive=value因出现两次而无效
# directive=value1
# directive=value2
FROM ImageName由于出现在构建器指令之后,被视为注释
FROM ImageName
# directive=value由于出现在非解析器指令的注释之后,被视为注释
# About my dockerfile
# directive=value
FROM ImageName以下 `unknowndirective` 被视为注释,因为它无法识别。已知的 `syntax` 指令被视为注释,因为它出现在非解析器指令的注释之后。
# unknowndirective=value
# syntax=value解析器指令中允许非换行空白字符。因此,以下行都被视为相同
#directive=value
# directive =value
# directive= value
# directive = value
# dIrEcTiVe=value语法
使用 `syntax` 解析器指令声明用于构建的 Dockerfile 语法版本。如果未指定,BuildKit 将使用 Dockerfile 前端的捆绑版本。声明语法版本允许您自动使用最新的 Dockerfile 版本,而无需升级 BuildKit 或 Docker Engine,甚至无需使用自定义 Dockerfile 实现。
大多数用户会希望将此解析器指令设置为 `docker/dockerfile:1`,这将导致 BuildKit 在构建之前拉取 Dockerfile 语法的最新稳定版本。
# syntax=docker/dockerfile:1有关解析器指令如何工作的更多信息,请参阅自定义 Dockerfile 语法。
转义
# escape=\或者
# escape=``escape` 指令设置用于转义 Dockerfile 中字符的字符。如果未指定,默认转义字符为 `\`。
转义字符用于转义行中的字符和转义换行符。这允许 Dockerfile 指令跨多行。请注意,无论 Dockerfile 中是否包含 `escape` 解析器指令,`RUN` 命令中都不会执行转义,除了在行尾。
将转义字符设置为 `` ` `` 在 `Windows` 上特别有用,因为 `\` 是目录路径分隔符。`` ` `` 与 Windows PowerShell 保持一致。
考虑以下示例,它将在 Windows 上以不明显的方式失败。第二行末尾的第二个 `\` 将被解释为换行符的转义,而不是第一个 `\` 的转义目标。同样,第三行末尾的 `\`,假设它实际上被视为一条指令,将导致它被视为行继续符。此 Dockerfile 的结果是第二行和第三行被视为一条指令
FROM microsoft/nanoserver
COPY testfile.txt c:\\
RUN dir c:\结果为
PS E:\myproject> docker build -t cmd .
Sending build context to Docker daemon 3.072 kB
Step 1/2 : FROM microsoft/nanoserver
---> 22738ff49c6d
Step 2/2 : COPY testfile.txt c:\RUN dir c:
GetFileAttributesEx c:RUN: The system cannot find the file specified.
PS E:\myproject>
解决上述问题的一种方法是使用 `/` 作为 `COPY` 指令和 `dir` 的目标。然而,这种语法充其量令人困惑,因为它不符合 Windows 路径的自然习惯,最坏的情况是容易出错,因为并非所有 Windows 命令都支持 `/` 作为路径分隔符。
通过添加 `escape` 解析器指令,以下 Dockerfile 按预期成功,并使用 Windows 上文件路径的自然平台语义
# escape=`
FROM microsoft/nanoserver
COPY testfile.txt c:\
RUN dir c:\结果为
PS E:\myproject> docker build -t succeeds --no-cache=true .
Sending build context to Docker daemon 3.072 kB
Step 1/3 : FROM microsoft/nanoserver
---> 22738ff49c6d
Step 2/3 : COPY testfile.txt c:\
---> 96655de338de
Removing intermediate container 4db9acbb1682
Step 3/3 : RUN dir c:\
---> Running in a2c157f842f5
Volume in drive C has no label.
Volume Serial Number is 7E6D-E0F7
Directory of c:\
10/05/2016 05:04 PM 1,894 License.txt
10/05/2016 02:22 PM <DIR> Program Files
10/05/2016 02:14 PM <DIR> Program Files (x86)
10/28/2016 11:18 AM 62 testfile.txt
10/28/2016 11:20 AM <DIR> Users
10/28/2016 11:20 AM <DIR> Windows
2 File(s) 1,956 bytes
4 Dir(s) 21,259,096,064 bytes free
---> 01c7f3bef04f
Removing intermediate container a2c157f842f5
Successfully built 01c7f3bef04f
PS E:\myproject>
检查
# check=skip=<checks|all>
# check=error=<boolean>`check` 指令用于配置如何评估构建检查。默认情况下,所有检查都会运行,并且失败被视为警告。
您可以使用 `#check=skip=
# check=skip=JSONArgsRecommended,StageNameCasing要禁用所有检查,请使用 `#check=skip=all`。
默认情况下,即使有警告,构建失败的构建检查也会以零状态码退出。要使构建在警告时失败,请设置 `#check=error=true`。
# check=error=true注意当使用 `check` 指令并带有 `error=true` 选项时,建议将Dockerfile 语法固定到特定版本。否则,将来添加新检查时,您的构建可能会开始失败。
要组合 `skip` 和 `error` 选项,请使用分号分隔它们
# check=skip=JSONArgsRecommended;error=true要查看所有可用的检查,请参阅构建检查参考。请注意,可用的检查取决于 Dockerfile 语法版本。为了确保您获得最新的检查,请使用`syntax`指令将 Dockerfile 语法版本指定为最新稳定版本。
环境变量替换
环境变量(通过`ENV` 语句声明)也可以在某些指令中用作 Dockerfile 要解释的变量。转义也用于将类似变量的语法原样包含到语句中。
Dockerfile 中的环境变量用 `$variable_name` 或 `${variable_name}` 表示。它们被等效对待,大括号语法通常用于解决没有空白字符的变量名称问题,例如 `${foo}_bar`。
`${variable_name}` 语法还支持以下标准 `bash` 修饰符中的一些
${variable:-word}表示如果variable已设置,则结果将是该值。如果variable未设置,则word将是结果。${variable:+word}表示如果variable已设置,则word将是结果,否则结果为空字符串。
在使用 Dockerfile 中的 `# syntax=docker/dockerfile-upstream:master` 语法指令时,Dockerfile 语法的预发布版本中支持以下变量替换
${variable#pattern}从字符串开头查找,移除variable中最短匹配的pattern。str=foobarbaz echo ${str#f*b} # arbaz${variable##pattern}从字符串开头查找,移除variable中最长匹配的pattern。str=foobarbaz echo ${str##f*b} # az${variable%pattern}从字符串末尾反向查找,移除variable中最短匹配的pattern。string=foobarbaz echo ${string%b*} # foobar${variable%%pattern}从字符串末尾反向查找,移除variable中最长匹配的pattern。string=foobarbaz echo ${string%%b*} # foo${variable/pattern/replacement}将variable中第一次出现的pattern替换为replacementstring=foobarbaz echo ${string/ba/fo} # fooforbaz${variable//pattern/replacement}将variable中所有出现的pattern替换为replacementstring=foobarbaz echo ${string//ba/fo} # fooforfoz
在所有情况下,`word` 可以是任何字符串,包括其他环境变量。
`pattern` 是一个 glob 模式,其中 `?` 匹配任何单个字符,`*` 匹配任意数量的字符(包括零个)。要匹配字面值 `?` 和 `*`,请使用反斜杠转义:`\?` 和 `\*`。
您可以通过在变量前添加 `\` 来转义整个变量名称:例如,`\$foo` 或 `\${foo}` 将分别转换为 `\$foo` 和 `${foo}` 字面值。
示例(解析后的表示在 `#` 之后显示)
FROM busybox
ENV FOO=/bar
WORKDIR ${FOO} # WORKDIR /bar
ADD . $FOO # ADD . /bar
COPY \$FOO /quux # COPY $FOO /quuxDockerfile 中的以下指令列表支持环境变量
ADDCOPYENVEXPOSEFROMLABELSTOPSIGNALUSERVOLUMEWORKDIRONBUILD(与上述支持的指令之一结合使用时)
您还可以将环境变量与 `RUN`、`CMD` 和 `ENTRYPOINT` 指令一起使用,但在这些情况下,变量替换由命令 shell 处理,而不是由构建器处理。请注意,使用 exec 形式的指令不会自动调用命令 shell。请参阅变量替换。
环境变量替换在整个指令中对每个变量使用相同的值。更改变量的值仅在后续指令中生效。请考虑以下示例
ENV abc=hello
ENV abc=bye def=$abc
ENV ghi=$abcdef的值变为helloghi的值变为bye
.dockerignore 文件
您可以使用 `.dockerignore` 文件将文件和目录从构建上下文中排除。有关更多信息,请参阅.dockerignore 文件。
Shell 和 exec 形式
RUN、CMD 和 ENTRYPOINT 指令都有两种可能的格式
INSTRUCTION ["executable","param1","param2"](exec 形式)INSTRUCTION command param1 param2(shell 形式)
exec 形式可以避免 shell 字符串混乱,并使用特定的命令 shell 或任何其他可执行文件来调用命令。它使用 JSON 数组语法,其中数组中的每个元素都是命令、标志或参数。
shell 形式更宽松,强调易用性、灵活性和可读性。shell 形式会自动使用命令 shell,而 exec 形式则不会。
Exec 形式
exec 形式被解析为 JSON 数组,这意味着您必须在单词周围使用双引号 ("),而不是单引号 (')。
ENTRYPOINT ["/bin/bash", "-c", "echo hello"]exec 形式最适合用于指定 ENTRYPOINT 指令,并结合 CMD 设置可在运行时覆盖的默认参数。有关更多信息,请参阅ENTRYPOINT。
变量替换
使用 exec 形式不会自动调用命令 shell。这意味着正常的 shell 处理(例如变量替换)不会发生。例如,RUN [ "echo", "$HOME" ] 不会处理 $HOME 的变量替换。
如果您需要 shell 处理,请使用 shell 形式,或使用 exec 形式直接执行 shell,例如:RUN [ "sh", "-c", "echo $HOME" ]。当使用 exec 形式并直接执行 shell 时(与 shell 形式的情况一样),是 shell 进行环境变量替换,而不是构建器。
反斜杠
在 exec 形式中,您必须转义反斜杠。这在 Windows 上尤为重要,因为反斜杠是路径分隔符。否则,以下行将由于不是有效的 JSON 而被视为 shell 形式,并以意想不到的方式失败
RUN ["c:\windows\system32\tasklist.exe"]此示例的正确语法是
RUN ["c:\\windows\\system32\\tasklist.exe"]Shell 形式
与 exec 形式不同,使用 shell 形式的指令始终使用命令 shell。shell 形式不使用 JSON 数组格式,而是一个常规字符串。shell 形式字符串允许您使用转义字符(默认为反斜杠)转义换行符,以便将单个指令延续到下一行。这使得它更容易与较长的命令一起使用,因为它允许您将它们分成多行。例如,考虑以下两行
RUN source $HOME/.bashrc && \
echo $HOME它们等同于以下行
RUN source $HOME/.bashrc && echo $HOME您还可以使用 heredocs 与 shell 形式一起分解支持的命令。
RUN <<EOF
source $HOME/.bashrc && \
echo $HOME
EOF有关 heredocs 的更多信息,请参阅此处文档。
使用不同的 shell
您可以使用 `SHELL` 命令更改默认 shell。例如
SHELL ["/bin/bash", "-c"]
RUN echo hello有关更多信息,请参阅SHELL。
FROM
FROM [--platform=<platform>] <image> [AS <name>]或者
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]或者
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]`FROM` 指令初始化一个新的构建阶段,并为后续指令设置基础镜像。因此,有效的 Dockerfile 必须以 `FROM` 指令开头。该镜像可以是任何有效的镜像。
- `ARG` 是 Dockerfile 中唯一可以出现在 `FROM` 之前的指令。请参阅理解 ARG 和 FROM 如何交互。
- `FROM` 可以在单个 Dockerfile 中多次出现,以创建多个镜像或将一个构建阶段用作另一个的依赖项。只需记下每个新 `FROM` 指令之前提交输出的最后一个镜像 ID。每个 `FROM` 指令都会清除先前指令创建的任何状态。
- 可以选择通过在 `FROM` 指令中添加 `AS name` 来为新的构建阶段命名。该名称可在后续的 `FROM
`、`COPY --from= ` 和 `RUN --mount=type=bind,from=` 指令中引用在此阶段构建的镜像。 - `tag` 或 `digest` 值是可选的。如果省略其中任何一个,构建器默认假定 `latest` 标签。如果构建器找不到 `tag` 值,则会返回错误。
可选的 `--platform` 标志可用于指定镜像的平台,以防 `FROM` 引用多平台镜像。例如,`linux/amd64`、`linux/arm64` 或 `windows/amd64`。默认情况下,使用构建请求的目标平台。全局构建参数可用于此标志的值,例如自动平台 ARG 允许您强制阶段使用原生构建平台(`--platform=$BUILDPLATFORM`),并使用它在阶段内进行交叉编译到目标平台。
理解 ARG 和 FROM 如何交互
FROM 指令支持在第一个 FROM 之前出现的任何 ARG 指令声明的变量。
ARG CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD /code/run-app
FROM extras:${CODE_VERSION}
CMD /code/run-extras在 FROM 之前声明的 ARG 在构建阶段之外,因此不能在 FROM 之后的任何指令中使用。要在构建阶段内使用在第一个 FROM 之前声明的 ARG 的默认值,请使用不带值的 ARG 指令
ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_versionRUN
RUN 指令将执行任何命令以在当前镜像之上创建新层。添加的层用于 Dockerfile 的下一步。RUN 有两种形式
# Shell form:
RUN [OPTIONS] <command> ...
# Exec form:
RUN [OPTIONS] [ "<command>", ... ]有关这两种形式之间差异的更多信息,请参阅shell 或 exec 形式。
shell 形式最常用,它允许您将较长的指令分成多行,可以使用换行转义符,也可以使用heredocs
RUN <<EOF
apt-get update
apt-get install -y curl
EOFRUN 指令可用的 [OPTIONS] 为
| 选项 | 最低 Dockerfile 版本 |
|---|---|
--device | 1.14-labs |
--mount | 1.2 |
--network | 1.3 |
--security | 1.1.2-labs |
RUN 指令的缓存失效
RUN 指令的缓存不会在下次构建期间自动失效。RUN apt-get dist-upgrade -y 等指令的缓存将在下次构建期间重复使用。可以使用 --no-cache 标志使 RUN 指令的缓存失效,例如 docker build --no-cache。
有关更多信息,请参阅Dockerfile 最佳实践指南。
RUN --device
注意尚未在稳定语法中提供,请使用
docker/dockerfile:1-labs版本。它还需要 BuildKit 0.20.0 或更高版本。
RUN --device=name,[required]RUN --device 允许构建请求CDI 设备可用于构建步骤。
警告
--device的使用受device权限保护,需要在启动 buildkitd 守护程序时使用--allow-insecure-entitlement device标志或在buildkitd 配置中启用,并且对于使用--allow device标志的构建请求。
设备 name 由 BuildKit 中注册的 CDI 规范提供。
在以下示例中,多个设备已在 CDI 规范中注册,用于 vendor1.com/device 供应商。
cdiVersion: "0.6.0"
kind: "vendor1.com/device"
devices:
- name: foo
containerEdits:
env:
- FOO=injected
- name: bar
annotations:
org.mobyproject.buildkit.device.class: class1
containerEdits:
env:
- BAR=injected
- name: baz
annotations:
org.mobyproject.buildkit.device.class: class1
containerEdits:
env:
- BAZ=injected
- name: qux
annotations:
org.mobyproject.buildkit.device.class: class2
containerEdits:
env:
- QUX=injected
annotations:
org.mobyproject.buildkit.device.autoallow: true设备名称格式灵活,接受各种模式以支持多种设备配置
vendor1.com/device:请求此供应商找到的第一个设备vendor1.com/device=foo:请求特定设备vendor1.com/device=*:请求此供应商的所有设备class1:通过org.mobyproject.buildkit.device.class注释请求设备
注意CDI 规范自 0.6.0 起支持注释。
注意要自动允许 CDI 规范中注册的所有设备,您可以设置
org.mobyproject.buildkit.device.autoallow注释。您也可以为特定设备设置此注释。
示例:CUDA 加速的 LLaMA 推理
在此示例中,我们使用 --device 标志通过 CDI 使用 NVIDIA GPU 设备运行 llama.cpp 推理
# syntax=docker/dockerfile:1-labs
FROM scratch AS model
ADD https://hugging-face.cn/bartowski/Llama-3.2-1B-Instruct-GGUF/resolve/main/Llama-3.2-1B-Instruct-Q4_K_M.gguf /model.gguf
FROM scratch AS prompt
COPY <<EOF prompt.txt
Q: Generate a list of 10 unique biggest countries by population in JSON with their estimated poulation in 1900 and 2024. Answer only newline formatted JSON with keys "country", "population_1900", "population_2024" with 10 items.
A:
[
{
EOF
FROM ghcr.io/ggml-org/llama.cpp:full-cuda-b5124
RUN --device=nvidia.com/gpu=all \
--mount=from=model,target=/models \
--mount=from=prompt,target=/tmp \
./llama-cli -m /models/model.gguf -no-cnv -ngl 99 -f /tmp/prompt.txtRUN --mount
RUN --mount=[type=<TYPE>][,option=<value>[,option=<value>]...]RUN --mount 允许您创建构建可以访问的文件系统挂载。这可以用于
- 创建到主机文件系统或其他构建阶段的绑定挂载
- 访问构建机密或 ssh-agent 套接字
- 使用持久性包管理缓存来加速构建
支持的挂载类型有
| 类型 | 描述 |
|---|---|
bind(默认) | 绑定挂载上下文目录(只读)。 |
缓存 | 挂载临时目录以缓存编译器和包管理器的目录。 |
tmpfs | 在构建容器中挂载一个 tmpfs。 |
secret | 允许构建容器访问安全文件(如私钥),而无需将其烘焙到镜像或构建缓存中。 |
ssh | 允许构建容器通过 SSH 代理访问 SSH 密钥,并支持密码短语。 |
RUN --mount=type=bind
此挂载类型允许将文件或目录绑定到构建容器。绑定挂载默认是只读的。
| 选项 | 描述 |
|---|---|
target、dst、destination1 | 挂载路径。 |
source | from 中的源路径。默认为 from 的根目录。 |
来自 | 源的根目录的构建阶段、上下文或镜像名称。默认为构建上下文。 |
rw,readwrite | 允许在挂载上写入。写入的数据将被丢弃。 |
RUN --mount=type=cache
此挂载类型允许构建容器缓存编译器和包管理器的目录。
| 选项 | 描述 |
|---|---|
id | 用于识别独立/不同缓存的可选 ID。默认为 target 的值。 |
target、dst、destination1 | 挂载路径。 |
ro,readonly | 如果设置,则为只读。 |
共享 | shared、private 或 locked 之一。默认为 shared。shared 缓存挂载可由多个写入器并发使用。如果存在多个写入器,private 将创建一个新的挂载。locked 将暂停第二个写入器,直到第一个写入器释放挂载。 |
来自 | 用作缓存挂载基础的构建阶段、上下文或镜像名称。默认为空目录。 |
source | from 中要挂载的子路径。默认为 from 的根目录。 |
mode | 新缓存目录的文件模式(八进制)。默认 0755。 |
uid | 新缓存目录的用户 ID。默认 0。 |
gid | 新缓存目录的组 ID。默认 0。 |
缓存目录的内容在构建器调用之间持续存在,而不会使指令缓存失效。缓存挂载仅用于提高性能。您的构建应适用于缓存目录的任何内容,因为另一个构建可能会覆盖文件,或者如果需要更多存储空间,GC 可能会将其清除。
示例:缓存 Go 包
# syntax=docker/dockerfile:1
FROM golang
RUN --mount=type=cache,target=/root/.cache/go-build \
go build ...示例:缓存 apt 包
# syntax=docker/dockerfile:1
FROM ubuntu
RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt update && apt-get --no-install-recommends install -y gccApt 需要独占访问其数据,因此缓存使用选项 sharing=locked,这将确保使用相同缓存挂载的多个并行构建将互相等待,并且不会同时访问相同的缓存文件。如果您更喜欢在这种情况下让每个构建创建另一个缓存目录,也可以使用 sharing=private。
RUN --mount=type=tmpfs
此挂载类型允许在构建容器中挂载 tmpfs。
| 选项 | 描述 |
|---|---|
target、dst、destination1 | 挂载路径。 |
size | 指定文件系统大小的上限。 |
RUN --mount=type=secret
此挂载类型允许构建容器访问机密值,例如令牌或私钥,而无需将其烘焙到镜像中。
默认情况下,机密作为文件挂载。您还可以通过设置 `env` 选项将机密作为环境变量挂载。
| 选项 | 描述 |
|---|---|
id | 机密 ID。默认为目标路径的基本名称。 |
target、dst、destination | 将机密挂载到指定路径。如果未设置且 `env` 也未设置,则默认为 /run/secrets/ + id。 |
env | 将机密挂载为环境变量而不是文件,或两者兼而有之。(自 Dockerfile v1.10.0 起) |
必需 | 如果设置为 true,则当机密不可用时,指令会出错。默认为 false。 |
mode | 机密文件的文件模式(八进制)。默认 0400。 |
uid | 机密文件的用户 ID。默认 0。 |
gid | 机密文件的组 ID。默认 0。 |
示例:访问 S3
# syntax=docker/dockerfile:1
FROM python:3
RUN pip install awscli
RUN --mount=type=secret,id=aws,target=/root/.aws/credentials \
aws s3 cp s3://... ...$ docker buildx build --secret id=aws,src=$HOME/.aws/credentials .
示例:作为环境变量挂载
以下示例获取机密 API_KEY 并将其作为同名环境变量挂载。
# syntax=docker/dockerfile:1
FROM alpine
RUN --mount=type=secret,id=API_KEY,env=API_KEY \
some-command --token-from-env $API_KEY假设构建环境中设置了 API_KEY 环境变量,您可以使用以下命令构建它
$ docker buildx build --secret id=API_KEY .
RUN --mount=type=ssh
此挂载类型允许构建容器通过 SSH 代理访问 SSH 密钥,并支持密码短语。
| 选项 | 描述 |
|---|---|
id | SSH 代理套接字或密钥的 ID。默认为“default”。 |
target、dst、destination | SSH 代理套接字路径。默认为 /run/buildkit/ssh_agent.${N}。 |
必需 | 如果设置为 true,则当密钥不可用时,指令会出错。默认为 false。 |
mode | 套接字的文件模式(八进制)。默认 0600。 |
uid | 套接字的用户 ID。默认 0。 |
gid | 套接字的组 ID。默认 0。 |
示例:访问 GitLab
# syntax=docker/dockerfile:1
FROM alpine
RUN apk add --no-cache openssh-client
RUN mkdir -p -m 0700 ~/.ssh && ssh-keyscan gitlab.com >> ~/.ssh/known_hosts
RUN --mount=type=ssh \
ssh -q -T git@gitlab.com 2>&1 | tee /hello
# "Welcome to GitLab, @GITLAB_USERNAME_ASSOCIATED_WITH_SSHKEY" should be printed here
# with the type of build progress is defined as `plain`.$ eval $(ssh-agent)
$ ssh-add ~/.ssh/id_rsa
(Input your passphrase here)
$ docker buildx build --ssh default=$SSH_AUTH_SOCK .
您也可以直接指定主机上的 *.pem 文件路径,而不是 $SSH_AUTH_SOCK。但是,不支持带密码的 pem 文件。
RUN --network
RUN --network=<TYPE>RUN --network 允许控制命令在哪个网络环境中运行。
支持的网络类型有
RUN --network=default
等效于完全不提供标志,命令在构建的默认网络中运行。
RUN --network=none
该命令在没有网络访问权限的情况下运行(lo 仍然可用,但仅限于此进程)
示例:隔离外部影响
# syntax=docker/dockerfile:1
FROM python:3.6
ADD mypackage.tgz wheels/
RUN --network=none pip install --find-links wheels mypackagepip 将只能安装 tar 文件中提供的包,这可以由较早的构建阶段控制。
RUN --network=host
该命令在主机的网络环境中运行(类似于 docker build --network=host,但基于每个指令)
警告
--network=host的使用受network.host权限保护,需要在启动 buildkitd 守护程序时使用--allow-insecure-entitlement network.host标志或在buildkitd 配置中启用,并且对于使用--allow network.host标志的构建请求。
RUN --security
注意尚未在稳定语法中提供,请使用
docker/dockerfile:1-labs版本。
RUN --security=<sandbox|insecure>默认的安全模式是 `sandbox`。使用 ` --security=insecure `,构建器将在非安全模式下运行命令,而不使用沙盒,这允许运行需要提升权限的流程(例如 containerd)。这等同于运行 `docker run --privileged`。
警告为了访问此功能,在启动 buildkitd 守护程序时应启用 `security.insecure` 权限,使用 ` --allow-insecure-entitlement security.insecure ` 标志或在 buildkitd 配置 中,以及对于使用 ` --allow security.insecure ` 标志 的构建请求。
默认沙盒模式可以通过 --security=sandbox 激活,但这不执行任何操作。
示例:检查权限
# syntax=docker/dockerfile:1-labs
FROM ubuntu
RUN --security=insecure cat /proc/self/status | grep CapEff#84 0.093 CapEff: 0000003fffffffffCMD
CMD 指令设置了从镜像运行容器时要执行的命令。
您可以使用shell 或 exec 形式指定 CMD 指令
CMD ["executable","param1","param2"](exec 形式)CMD ["param1","param2"](exec 形式,作为ENTRYPOINT的默认参数)CMD command param1 param2(shell 形式)
一个 Dockerfile 中只能有一个 CMD 指令。如果您列出多个 CMD,只有最后一个会生效。
CMD 的目的是为正在执行的容器提供默认值。这些默认值可以包括可执行文件,也可以省略可执行文件,在这种情况下,您还必须指定 ENTRYPOINT 指令。
如果您希望容器每次都运行相同的可执行文件,那么您应该考虑将 ENTRYPOINT 与 CMD 结合使用。请参阅ENTRYPOINT。如果用户向 docker run 指定参数,则这些参数将覆盖 CMD 中指定的默认值,但仍使用默认的 ENTRYPOINT。
如果 CMD 用于为 ENTRYPOINT 指令提供默认参数,则 CMD 和 ENTRYPOINT 指令都应以exec 形式指定。
注意不要混淆
RUN和CMD。RUN实际上运行命令并提交结果;CMD在构建时不会执行任何操作,而是指定镜像的预期命令。
LABEL
LABEL <key>=<value> [<key>=<value>...]`LABEL` 指令为镜像添加元数据。`LABEL` 是一个键值对。要在 `LABEL` 值中包含空格,请像在命令行解析中一样使用引号和反斜杠。一些用法示例
LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."一个镜像可以有多个标签。您可以在一行上指定多个标签。在 Docker 1.10 之前,这会减小最终镜像的大小,但现在不再是这种情况。您仍然可以选择以以下两种方式之一在单个指令中指定多个标签
LABEL multi.label1="value1" multi.label2="value2" other="value3"LABEL multi.label1="value1" \
multi.label2="value2" \
other="value3"注意请务必使用双引号而不是单引号。特别是在使用字符串插值(例如
LABEL example="foo-$ENV_VAR")时,单引号将按原样处理字符串,而不会解包变量的值。
基础镜像(`FROM` 行中的镜像)中包含的标签会被您的镜像继承。如果标签已存在但值不同,则最近应用的值将覆盖任何先前设置的值。
要查看镜像的标签,请使用 docker image inspect 命令。您可以使用 --format 选项仅显示标签;
$ docker image inspect --format='{{json .Config.Labels}}' myimage
{
"com.example.vendor": "ACME Incorporated",
"com.example.label-with-value": "foo",
"version": "1.0",
"description": "This text illustrates that label-values can span multiple lines.",
"multi.label1": "value1",
"multi.label2": "value2",
"other": "value3"
}MAINTAINER(已弃用)
MAINTAINER <name>MAINTAINER 指令设置生成镜像的“作者”字段。`LABEL` 指令是其更灵活的版本,您应该改用它,因为它允许设置您需要的任何元数据,并且可以轻松查看,例如使用 `docker inspect`。要设置与 `MAINTAINER` 字段对应的标签,您可以使用
LABEL org.opencontainers.image.authors="SvenDowideit@home.org.au"然后,这将从 docker inspect 中与其他标签一起可见。
EXPOSE
EXPOSE <port> [<port>/<protocol>...]EXPOSE 指令通知 Docker 容器在运行时监听指定的网络端口。您可以指定端口监听 TCP 还是 UDP,如果未指定协议,则默认为 TCP。
EXPOSE 指令实际上并不发布端口。它充当镜像构建者和容器运行者之间的一种文档,说明哪些端口打算发布。要在运行容器时发布端口,请使用 docker run 上的 -p 标志发布并映射一个或多个端口,或使用 -P 标志发布所有暴露的端口并将它们映射到高序端口。
默认情况下,`EXPOSE` 假定为 TCP。您也可以指定 UDP
EXPOSE 80/udp要同时暴露 TCP 和 UDP,请包含两行
EXPOSE 80/tcp
EXPOSE 80/udp在这种情况下,如果您在 `docker run` 中使用 `-P`,端口将暴露一次用于 TCP,一次用于 UDP。请记住,`-P` 在主机上使用临时的、高序主机端口,因此 TCP 和 UDP 不会使用相同的端口。
无论 EXPOSE 设置如何,您都可以在运行时使用 -p 标志覆盖它们。例如
$ docker run -p 80:80/tcp -p 80:80/udp ...
要在主机系统上设置端口重定向,请参阅使用 -P 标志。`docker network` 命令支持创建用于容器间通信的网络,而无需暴露或发布特定端口,因为连接到网络的容器可以通过任何端口相互通信。有关详细信息,请参阅此功能概述。
ENV
ENV <key>=<value> [<key>=<value>...]ENV 指令将环境变量 设置为值 。此值将存在于构建阶段的所有后续指令的环境中,并且也可以在许多指令中内联替换。该值将解释为其他环境变量,因此如果引号字符未转义,它们将被删除。与命令行解析一样,引号和反斜杠可用于在值中包含空格。
示例
ENV MY_NAME="John Doe"
ENV MY_DOG=Rex\ The\ Dog
ENV MY_CAT=fluffyENV 指令允许一次设置多个 变量,下面的示例将在最终镜像中产生相同的净结果
ENV MY_NAME="John Doe" MY_DOG=Rex\ The\ Dog \
MY_CAT=fluffy使用 ENV 设置的环境变量在从生成的镜像运行容器时将持久存在。您可以使用 docker inspect 查看这些值,并使用 docker run --env 更改它们。
一个阶段会继承其父阶段或任何祖先阶段使用 ENV 设置的任何环境变量。有关更多信息,请参阅手册中的多阶段构建部分。
环境变量的持久性可能导致意外的副作用。例如,设置 `ENV DEBIAN_FRONTEND=noninteractive` 会改变 `apt-get` 的行为,并可能使您的镜像用户感到困惑。
如果环境变量仅在构建期间需要,而不在最终镜像中需要,请考虑为单个命令设置一个值
RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y ...或者使用ARG,它不会持久化在最终镜像中
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y ...注意替代语法
ENV指令还允许使用替代语法ENV,省略=。例如ENV MY_VAR my-value此语法不允许在单个
ENV指令中设置多个环境变量,并且可能令人困惑。例如,以下设置了一个环境变量 (ONE),其值为"TWO= THREE=world"ENV ONE TWO= THREE=world为了向后兼容,支持替代语法,但不鼓励使用,原因如上所述,并且可能会在未来版本中删除。
ADD
ADD 有两种形式。后者形式适用于包含空白字符的路径。
ADD [OPTIONS] <src> ... <dest>
ADD [OPTIONS] ["<src>", ... "<dest>"]可用的 [OPTIONS] 为
| 选项 | 最低 Dockerfile 版本 |
|---|---|
--keep-git-dir | 1.1 |
--checksum | 1.6 |
--chown | |
--chmod | 1.2 |
--link | 1.4 |
--exclude | 1.7-labs |
ADD 指令将新文件或目录从 复制到镜像文件系统中的路径 。文件和目录可以从构建上下文、远程 URL 或 Git 仓库复制。
ADD 和 COPY 指令在功能上相似,但目的略有不同。了解更多关于ADD 和 COPY 之间的区别。
源
您可以使用 ADD 指定多个源文件或目录。最后一个参数必须始终是目标。例如,要将构建上下文中的两个文件 file1.txt 和 file2.txt 添加到构建容器中的 /usr/src/things/
ADD file1.txt file2.txt /usr/src/things/如果您指定多个源文件,无论是直接指定还是使用通配符,则目标必须是一个目录(必须以斜杠 / 结尾)。
要从远程位置添加文件,您可以将 URL 或 Git 仓库的地址指定为源。例如
ADD https://example.com/archive.zip /usr/src/things/
ADD git@github.com:user/repo.git /usr/src/things/BuildKit 检测 的类型并相应地处理它。
- 如果
是本地文件或目录,则目录内容将被复制到指定目标。请参阅从构建上下文添加文件。 - 如果
是本地 tar 归档文件,则它将被解压缩并提取到指定目标。请参阅添加本地 tar 归档文件。 - 如果
是 URL,则 URL 的内容将被下载并放置在指定目标。请参阅从 URL 添加文件。 - 如果
是 Git 仓库,则仓库将被克隆到指定目标。请参阅从 Git 仓库添加文件。
从构建上下文添加文件
任何不以 http://、https:// 或 git@ 协议前缀开头的相对或本地路径都被视为本地文件路径。本地文件路径相对于构建上下文。例如,如果构建上下文是当前目录,ADD file.txt / 会将 ./file.txt 处的文件添加到构建容器文件系统的根目录。
指定带有前导斜杠的源路径或导航到构建上下文之外的源路径(例如 ADD ../something /something)会自动删除任何父目录导航(../)。源路径中的尾部斜杠也会被忽略,使得 ADD something/ /something 等效于 ADD something /something。
如果源是一个目录,则目录的内容将被复制,包括文件系统元数据。目录本身不会被复制,只复制其内容。如果它包含子目录,这些子目录也会被复制,并与目标处的任何现有目录合并。任何冲突都将以所添加内容为准,按文件逐个解决,除非您尝试将目录复制到现有文件上,在这种情况下会引发错误。
如果源是文件,则文件及其元数据将复制到目标。文件权限将保留。如果源是文件,并且目标处存在同名目录,则会引发错误。
如果您通过 stdin 将 Dockerfile 传递给构建(docker build - < Dockerfile),则没有构建上下文。在这种情况下,您只能使用 ADD 指令复制远程文件。您也可以通过 stdin 传递 tar 归档文件:(docker build - < archive.tar),归档文件根目录中的 Dockerfile 和归档文件的其余部分将用作构建上下文。
模式匹配
对于本地文件,每个 可能包含通配符,匹配将使用 Go 的 filepath.Match 规则完成。
例如,要添加构建上下文根目录中所有以 .png 结尾的文件和目录
ADD *.png /dest/在以下示例中,`?` 是一个单字符通配符,例如匹配 `index.js` 和 `index.ts`。
ADD index.?s /dest/添加包含特殊字符(如 `[` 和 `]`)的文件或目录时,您需要按照 Golang 规则转义这些路径,以防止它们被视为匹配模式。例如,要添加名为 `arr[0].txt` 的文件,请使用以下命令;
ADD arr[[]0].txt /dest/添加本地 tar 归档文件
当使用本地 tar 归档文件作为 ADD 的源时,并且归档文件采用已识别的压缩格式(gzip、bzip2 或 xz,或未压缩),归档文件将被解压缩并提取到指定目标。只有本地 tar 归档文件会被提取。如果 tar 归档文件是远程 URL,则归档文件不会被提取,而是下载并放置在目标位置。
当目录被提取时,它具有与 `tar -x` 相同的行为。结果是以下内容的并集
- 目标路径上已存在的任何内容,以及
- 源树的内容,冲突将按文件逐个解决,以添加的内容为准。
注意文件是否被识别为已识别的压缩格式仅基于文件内容,而不是文件名。例如,如果一个空文件恰好以 `.tar.gz` 结尾,这不会被识别为压缩文件,也不会生成任何解压缩错误消息,而是该文件将简单地复制到目标。
从 URL 添加文件
在源是远程文件 URL 的情况下,目标的权限将为 600。如果 HTTP 响应包含 Last-Modified 标头,则该标头中的时间戳将用于设置目标文件的 mtime。但是,像在 ADD 期间处理的任何其他文件一样,mtime 不会包含在确定文件是否已更改以及是否应更新缓存的决定中。
如果目标以尾部斜杠结尾,则文件名将从 URL 路径推断。例如,`ADD http://example.com/foobar /` 将创建文件 `/foobar`。URL 必须具有非平凡的路径,以便可以发现适当的文件名(`http://example.com` 不起作用)。
如果目标不以尾部斜杠结尾,则目标路径将成为从 URL 下载的文件的文件名。例如,`ADD http://example.com/foo /bar` 会创建文件 `/bar`。
如果您的 URL 文件受身份验证保护,您需要使用 `RUN wget`、`RUN curl` 或容器内的其他工具,因为 `ADD` 指令不支持身份验证。
从 Git 仓库添加文件
要使用 Git 仓库作为 `ADD` 的源,您可以将仓库的 HTTP 或 SSH 地址作为源引用。仓库将被克隆到镜像中指定的目的地。
ADD https://github.com/user/repo.git /mydir/您可以使用 URL 片段指定特定的分支、标签、提交或子目录。例如,要添加 `buildkit` 仓库 `v0.14.1` 标签的 `docs` 目录
ADD git@github.com:moby/buildkit.git#v0.14.1:docs /buildkit-docs有关 Git URL 片段的更多信息,请参阅URL 片段。
从 Git 仓库添加时,文件的权限位为 644。如果仓库中的文件设置了可执行位,则其权限将设置为 755。目录的权限设置为 755。
当使用 Git 仓库作为源时,仓库必须可从构建上下文访问。要通过 SSH 添加仓库,无论是公共还是私有,您都必须传递 SSH 密钥进行身份验证。例如,给定以下 Dockerfile
# syntax=docker/dockerfile:1
FROM alpine
ADD git@git.example.com:foo/bar.git /bar要构建此 Dockerfile,请将 --ssh 标志传递给 docker build 以将 SSH 代理套接字挂载到构建。例如
$ docker build --ssh default .
有关使用机密进行构建的更多信息,请参阅构建机密。
目标
如果目标路径以正斜杠开头,则将其解释为绝对路径,并且源文件将复制到相对于当前构建阶段根目录的指定目标。
# create /abs/test.txt
ADD test.txt /abs/尾部斜杠很重要。例如,`ADD test.txt /abs` 在 `/abs` 创建一个文件,而 `ADD test.txt /abs/` 创建 `/abs/test.txt`。
如果目标路径不以正斜杠开头,则将其解释为相对于构建容器的工作目录。
WORKDIR /usr/src/app
# create /usr/src/app/rel/test.txt
ADD test.txt rel/如果目标不存在,则会创建它,以及其路径中所有缺失的目录。
如果源是文件,并且目标不以尾部斜杠结尾,则源文件将作为文件写入目标路径。
ADD --keep-git-dir
ADD [--keep-git-dir=<boolean>] <src> ... <dir>当 是远程 Git 仓库的 HTTP 或 SSH 地址时,BuildKit 默认会将 Git 仓库的内容添加到镜像中,但会排除 .git 目录。
--keep-git-dir=true 标志允许您保留 .git 目录。
# syntax=docker/dockerfile:1
FROM alpine
ADD --keep-git-dir=true https://github.com/moby/buildkit.git#v0.10.1 /buildkitADD --checksum
ADD [--checksum=<hash>] <src> ... <dir>--checksum 标志允许您验证远程资源的校验和。校验和格式为 sha256:。SHA-256 是唯一支持的哈希算法。
ADD --checksum=sha256:24454f830cdb571e2c4ad15481119c43b3cafd48dd869a9b2945d1036d1dc68d https://mirrors.edge.kernel.org/pub/linux/kernel/Historic/linux-0.01.tar.gz /--checksum 标志仅支持 HTTP(S) 源。
ADD --chown --chmod
ADD --link
请参阅COPY --link。
ADD --exclude
请参阅COPY --exclude。
COPY
COPY 有两种形式。后者形式适用于包含空白字符的路径。
COPY [OPTIONS] <src> ... <dest>
COPY [OPTIONS] ["<src>", ... "<dest>"]可用的 [OPTIONS] 为
COPY 指令将新文件或目录从 复制到镜像文件系统中的路径 。文件和目录可以从构建上下文、构建阶段、命名上下文或镜像复制。
ADD 和 COPY 指令在功能上相似,但目的略有不同。了解更多关于ADD 和 COPY 之间的区别。
源
您可以使用 COPY 指定多个源文件或目录。最后一个参数必须始终是目标。例如,要将构建上下文中的两个文件 file1.txt 和 file2.txt 复制到构建容器中的 /usr/src/things/
COPY file1.txt file2.txt /usr/src/things/如果您指定多个源文件,无论是直接指定还是使用通配符,则目标必须是一个目录(必须以斜杠 / 结尾)。
COPY 接受一个标志 --from=,它允许您指定源位置是一个构建阶段、上下文或镜像。以下示例从名为 build 的阶段复制文件
FROM golang AS build
WORKDIR /app
RUN --mount=type=bind,target=. go build -o /myapp ./cmd
COPY --from=build /myapp /usr/bin/有关从命名源复制的更多信息,请参阅--from 标志。
从构建上下文复制
从构建上下文复制源文件时,路径将相对于上下文的根目录进行解释。
指定带有前导斜杠的源路径或导航到构建上下文之外的源路径(例如 COPY ../something /something)会自动删除任何父目录导航(../)。源路径中的尾部斜杠也会被忽略,使得 COPY something/ /something 等效于 COPY something /something。
如果源是一个目录,则目录的内容将被复制,包括文件系统元数据。目录本身不会被复制,只复制其内容。如果它包含子目录,这些子目录也会被复制,并与目标处的任何现有目录合并。任何冲突都将以所添加内容为准,按文件逐个解决,除非您尝试将目录复制到现有文件上,在这种情况下会引发错误。
如果源是文件,则文件及其元数据将复制到目标。文件权限将保留。如果源是文件,并且目标处存在同名目录,则会引发错误。
如果您通过 stdin 将 Dockerfile 传递给构建(docker build - < Dockerfile),则没有构建上下文。在这种情况下,您只能使用 COPY 指令,通过--from 标志从其他阶段、命名上下文或镜像复制文件。您也可以通过 stdin 传递 tar 归档文件:(docker build - < archive.tar),归档文件根目录中的 Dockerfile 和归档文件的其余部分将用作构建上下文。
当使用 Git 仓库作为构建上下文时,复制文件的权限位为 644。如果仓库中的文件设置了可执行位,则其权限将设置为 755。目录的权限设置为 755。
模式匹配
对于本地文件,每个 可能包含通配符,匹配将使用 Go 的 filepath.Match 规则完成。
例如,要添加构建上下文根目录中所有以 .png 结尾的文件和目录
COPY *.png /dest/在以下示例中,`?` 是一个单字符通配符,例如匹配 `index.js` 和 `index.ts`。
COPY index.?s /dest/添加包含特殊字符(如 `[` 和 `]`)的文件或目录时,您需要按照 Golang 规则转义这些路径,以防止它们被视为匹配模式。例如,要添加名为 `arr[0].txt` 的文件,请使用以下命令;
COPY arr[[]0].txt /dest/目标
如果目标路径以正斜杠开头,则将其解释为绝对路径,并且源文件将复制到相对于当前构建阶段根目录的指定目标。
# create /abs/test.txt
COPY test.txt /abs/尾部斜杠很重要。例如,`COPY test.txt /abs` 在 `/abs` 创建一个文件,而 `COPY test.txt /abs/` 创建 `/abs/test.txt`。
如果目标路径不以正斜杠开头,则将其解释为相对于构建容器的工作目录。
WORKDIR /usr/src/app
# create /usr/src/app/rel/test.txt
COPY test.txt rel/如果目标不存在,则会创建它,以及其路径中所有缺失的目录。
如果源是文件,并且目标不以尾部斜杠结尾,则源文件将作为文件写入目标路径。
COPY --from
默认情况下,`COPY` 指令从构建上下文复制文件。`COPY --from` 标志允许您改为从镜像、构建阶段或命名上下文复制文件。
COPY [--from=<image|stage|context>] <src> ... <dest>要从多阶段构建中的构建阶段复制,请指定要复制的阶段的名称。您可以使用 FROM 指令的 AS 关键字指定阶段名称。
# syntax=docker/dockerfile:1
FROM alpine AS build
COPY . .
RUN apk add clang
RUN clang -o /hello hello.c
FROM scratch
COPY --from=build /hello /您还可以直接从命名上下文(使用 --build-context 指定)或镜像复制文件。以下示例从官方 Nginx 镜像复制 nginx.conf 文件。
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.confCOPY --from 的源路径总是从您指定的镜像或阶段的文件系统根目录解析。
COPY --chown --chmod
注意目前只支持八进制表示法。非八进制支持在 moby/buildkit#1951 中跟踪。
COPY [--chown=<user>:<group>] [--chmod=<perms> ...] <src> ... <dest>--chown 和 --chmod 功能仅在用于构建 Linux 容器的 Dockerfile 上受支持,不适用于 Windows 容器。由于用户和组所有权概念在 Linux 和 Windows 之间无法转换,因此使用 /etc/passwd 和 /etc/group 将用户和组名转换为 ID 限制了此功能仅适用于基于 Linux 操作系统的容器。
除非可选的 --chown 标志指定给定的用户名、组名或 UID/GID 组合以请求复制内容的特定所有权,否则从构建上下文复制的所有文件和目录都将以 UID 和 GID 为 0 创建。--chown 标志的格式允许使用用户名和组名字符串或直接的整数 UID 和 GID 的任意组合。提供没有组名的用户名或没有 GID 的 UID 将使用相同的数字 UID 作为 GID。如果提供了用户名或组名,则容器的根文件系统 /etc/passwd 和 /etc/group 文件将用于分别执行从名称到整数 UID 或 GID 的转换。以下示例显示了 --chown 标志的有效定义
COPY --chown=55:mygroup files* /somedir/
COPY --chown=bin files* /somedir/
COPY --chown=1 files* /somedir/
COPY --chown=10:11 files* /somedir/
COPY --chown=myuser:mygroup --chmod=644 files* /somedir/如果容器根文件系统不包含 /etc/passwd 或 /etc/group 文件,并且 --chown 标志中使用了用户或组名,则构建将在 COPY 操作失败。使用数字 ID 不需要查找,也不依赖于容器根文件系统内容。
在 Dockerfile 语法版本 1.10.0 及更高版本中,`--chmod` 标志支持变量插值,允许您使用构建参数定义权限位
# syntax=docker/dockerfile:1.10
FROM alpine
WORKDIR /src
ARG MODE=440
COPY --chmod=$MODE . .COPY --link
COPY [--link[=<boolean>]] <src> ... <dest>在 `COPY` 或 `ADD` 命令中启用此标志,可让您以增强的语义复制文件,您的文件在自己的层中保持独立,并且当更改上一层上的命令时不会失效。
当使用 --link 时,您的源文件将复制到一个空的目标目录中。该目录将转换为一个层,该层链接在您之前的状态之上。
# syntax=docker/dockerfile:1
FROM alpine
COPY --link /foo /bar等同于进行两次构建
FROM alpine和
FROM scratch
COPY /foo /bar并将两个镜像的所有层合并在一起。
使用 --link 的好处
即使上一层已更改,也可以使用 --link 在后续构建中通过 --cache-from 重用已构建的层。这对于多阶段构建尤为重要,因为如果同一阶段中的任何先前命令更改,则 COPY --from 语句以前会失效,导致需要再次重建中间阶段。使用 --link,以前构建生成的层将被重用并合并到新层之上。这也意味着当基础镜像收到更新时,您可以轻松地重新定位您的镜像,而无需再次执行整个构建。在支持它的后端中,BuildKit 可以执行此重新定位操作,而无需在客户端和注册表之间推送或拉取任何层。BuildKit 将检测此情况,并仅创建包含新层和旧层按正确顺序排列的新镜像清单。
当使用 --link 且没有其他需要访问基础镜像中文件的命令时,BuildKit 也可以避免拉取基础镜像。在这种情况下,BuildKit 将只构建 COPY 命令的层,并将其直接推送到注册表,在基础镜像的层之上。
与 --link=false 的不兼容性
当使用 --link 时,COPY/ADD 命令不允许从以前的状态读取任何文件。这意味着如果在以前的状态中目标目录是包含符号链接的路径,则 COPY/ADD 无法跟随它。在最终镜像中,使用 --link 创建的目标路径将始终是仅包含目录的路径。
如果您不依赖于在目标路径中跟随符号链接的行为,则始终建议使用 --link。--link 的性能与默认行为相同或更好,并且它为缓存重用创造了更好的条件。
COPY --parents
注意尚未在稳定语法中提供,请使用
docker/dockerfile:1-labs版本。
COPY [--parents[=<boolean>]] <src> ... <dest>--parents 标志会保留 src 条目的父目录。此标志默认为 false。
# syntax=docker/dockerfile:1-labs
FROM scratch
COPY ./x/a.txt ./y/a.txt /no_parents/
COPY --parents ./x/a.txt ./y/a.txt /parents/
# /no_parents/a.txt
# /parents/x/a.txt
# /parents/y/a.txt此行为类似于 Linux cp 工具的 --parents 或 rsync 的 --relative 标志。
与 Rsync 一样,可以通过在源路径中插入点和斜杠(`./`)来限制保留哪些父目录。如果存在这样的点,则只保留其后的父目录。这对于在阶段之间使用 ` --from ` 进行复制特别有用,因为源路径需要是绝对路径。
# syntax=docker/dockerfile:1-labs
FROM scratch
COPY --parents ./x/./y/*.txt /parents/
# Build context:
# ./x/y/a.txt
# ./x/y/b.txt
#
# Output:
# /parents/y/a.txt
# /parents/y/b.txt请注意,在未指定 --parents 标志的情况下,任何文件名冲突都将使 Linux cp 操作失败并显示明确的错误消息(cp: will not overwrite just-created './x/a.txt' with './y/a.txt'),而 Buildkit 将在目标位置静默覆盖目标文件。
虽然可以为仅包含一个 `src` 条目的 `COPY` 指令保留目录结构,但通常将结果镜像中的层数保持尽可能低更有益。因此,通过 ` --parents ` 标志,Buildkit 能够将多个 `COPY` 指令打包在一起,保持目录结构不变。
COPY --exclude
注意尚未在稳定语法中提供,请使用
docker/dockerfile:1-labs版本。
COPY [--exclude=<path> ...] <src> ... <dest>--exclude 标志允许您指定要排除文件的路径表达式。
路径表达式遵循与 相同的格式,支持通配符并使用 Go 的 filepath.Match 规则进行匹配。例如,要添加所有以“hom”开头的文件,但排除 .txt 扩展名的文件
# syntax=docker/dockerfile:1-labs
FROM scratch
COPY --exclude=*.txt hom* /mydir/您可以为 COPY 指令多次指定 --exclude 选项。多个 --exclude 表示与其模式匹配的文件不会被复制,即使文件路径与 中指定的模式匹配。要添加所有以“hom”开头的文件,但排除 .txt 或 .md 扩展名的文件
# syntax=docker/dockerfile:1-labs
FROM scratch
COPY --exclude=*.txt --exclude=*.md hom* /mydir/ENTRYPOINT
ENTRYPOINT 允许您将容器配置为可执行文件运行。
ENTRYPOINT 有两种可能的格式
exec 形式,这是首选形式
ENTRYPOINT ["executable", "param1", "param2"]shell 形式
ENTRYPOINT command param1 param2
有关不同形式的更多信息,请参阅Shell 和 exec 形式。
以下命令从 nginx 启动一个容器,使用其默认内容,监听端口 80
$ docker run -i -t --rm -p 80:80 nginx
传递给 docker run 的命令行参数将附加到 exec 形式 ENTRYPOINT 中的所有元素之后,并将覆盖使用 CMD 指定的所有元素。
这允许将参数传递给入口点,即 docker run 将把 -d 参数传递给入口点。您可以使用 docker run --entrypoint 标志覆盖 ENTRYPOINT 指令。
ENTRYPOINT 的 shell 形式阻止使用任何 CMD 命令行参数。它还会将您的 ENTRYPOINT 作为 /bin/sh -c 的子命令启动,该子命令不传递信号。这意味着可执行文件将不会是容器的 PID 1,并且不会接收 Unix 信号。在这种情况下,您的可执行文件不会从 docker stop 接收到 SIGTERM。
Dockerfile 中只有最后一个 ENTRYPOINT 指令会生效。
Exec 形式 ENTRYPOINT 示例
您可以使用 ENTRYPOINT 的 exec 形式设置相当稳定的默认命令和参数,然后使用任何一种 CMD 形式设置更容易更改的附加默认值。
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]当您运行容器时,您可以看到 `top` 是唯一的进程
$ docker run -it --rm --name test top -H
top - 08:25:00 up 7:27, 0 users, load average: 0.00, 0.01, 0.05
Threads: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.1 us, 0.1 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem: 2056668 total, 1616832 used, 439836 free, 99352 buffers
KiB Swap: 1441840 total, 0 used, 1441840 free. 1324440 cached Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 19744 2336 2080 R 0.0 0.1 0:00.04 top
要进一步检查结果,您可以使用 `docker exec`
$ docker exec -it test ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 2.6 0.1 19752 2352 ? Ss+ 08:24 0:00 top -b -H
root 7 0.0 0.1 15572 2164 ? R+ 08:25 0:00 ps aux
您可以使用 docker stop test 优雅地请求 top 关闭。
以下 Dockerfile 显示了使用 ENTRYPOINT 在前台运行 Apache(即,作为 PID 1)
FROM debian:stable
RUN apt-get update && apt-get install -y --force-yes apache2
EXPOSE 80 443
VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]
ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]如果您需要为单个可执行文件编写启动脚本,可以通过使用 `exec` 和 `gosu` 命令来确保最终可执行文件接收到 Unix 信号
#!/usr/bin/env bash
set -e
if [ "$1" = 'postgres' ]; then
chown -R postgres "$PGDATA"
if [ -z "$(ls -A "$PGDATA")" ]; then
gosu postgres initdb
fi
exec gosu postgres "$@"
fi
exec "$@"最后,如果您需要在关闭时进行一些额外的清理(或与其他容器通信),或者正在协调多个可执行文件,则可能需要确保 ENTRYPOINT 脚本接收 Unix 信号,将其传递,然后执行更多工作
#!/bin/sh
# Note: I've written this using sh so it works in the busybox container too
# USE the trap if you need to also do manual cleanup after the service is stopped,
# or need to start multiple services in the one container
trap "echo TRAPed signal" HUP INT QUIT TERM
# start service in background here
/usr/sbin/apachectl start
echo "[hit enter key to exit] or run 'docker stop <container>'"
read
# stop service and clean up here
echo "stopping apache"
/usr/sbin/apachectl stop
echo "exited $0"如果您使用 `docker run -it --rm -p 80:80 --name test apache` 运行此镜像,那么您可以使用 `docker exec` 或 `docker top` 检查容器的进程,然后要求脚本停止 Apache
$ docker exec -it test ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.1 0.0 4448 692 ? Ss+ 00:42 0:00 /bin/sh /run.sh 123 cmd cmd2
root 19 0.0 0.2 71304 4440 ? Ss 00:42 0:00 /usr/sbin/apache2 -k start
www-data 20 0.2 0.2 360468 6004 ? Sl 00:42 0:00 /usr/sbin/apache2 -k start
www-data 21 0.2 0.2 360468 6000 ? Sl 00:42 0:00 /usr/sbin/apache2 -k start
root 81 0.0 0.1 15572 2140 ? R+ 00:44 0:00 ps aux
$ docker top test
PID USER COMMAND
10035 root {run.sh} /bin/sh /run.sh 123 cmd cmd2
10054 root /usr/sbin/apache2 -k start
10055 33 /usr/sbin/apache2 -k start
10056 33 /usr/sbin/apache2 -k start
$ /usr/bin/time docker stop test
test
real 0m 0.27s
user 0m 0.03s
sys 0m 0.03s
注意您可以使用
--entrypoint覆盖ENTRYPOINT设置,但这只能设置要执行的二进制文件(不会使用sh -c)。
Shell 形式 ENTRYPOINT 示例
您可以为 ENTRYPOINT 指定一个纯字符串,它将在 /bin/sh -c 中执行。此形式将使用 shell 处理来替换 shell 环境变量,并将忽略任何 CMD 或 docker run 命令行参数。为了确保 docker stop 正确地向任何长时间运行的 ENTRYPOINT 可执行文件发送信号,您需要记住使用 exec 启动它。
FROM ubuntu
ENTRYPOINT exec top -b当您运行此镜像时,您将看到单个 PID 1 进程。
$ docker run -it --rm --name test top
Mem: 1704520K used, 352148K free, 0K shrd, 0K buff, 140368121167873K cached
CPU: 5% usr 0% sys 0% nic 94% idle 0% io 0% irq 0% sirq
Load average: 0.08 0.03 0.05 2/98 6
PID PPID USER STAT VSZ %VSZ %CPU COMMAND
1 0 root R 3164 0% 0% top -b
在 docker stop 时 cleanly 退出。
$ /usr/bin/time docker stop test
test
real 0m 0.20s
user 0m 0.02s
sys 0m 0.04s
如果您忘记在 ENTRYPOINT 的开头添加 exec,
FROM ubuntu
ENTRYPOINT top -b
CMD -- --ignored-param1您可以运行它(为下一步命名)。
$ docker run -it --name test top --ignored-param2
top - 13:58:24 up 17 min, 0 users, load average: 0.00, 0.00, 0.00
Tasks: 2 total, 1 running, 1 sleeping, 0 stopped, 0 zombie
%Cpu(s): 16.7 us, 33.3 sy, 0.0 ni, 50.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 1990.8 total, 1354.6 free, 231.4 used, 404.7 buff/cache
MiB Swap: 1024.0 total, 1024.0 free, 0.0 used. 1639.8 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 2612 604 536 S 0.0 0.0 0:00.02 sh
6 root 20 0 5956 3188 2768 R 0.0 0.2 0:00.00 top
从 top 的输出中可以看到,指定的 ENTRYPOINT 不是 PID 1。
如果您然后运行 docker stop test,容器将无法 cleanly 退出 - stop 命令将在超时后被迫发送 SIGKILL。
$ docker exec -it test ps waux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.4 0.0 2612 604 pts/0 Ss+ 13:58 0:00 /bin/sh -c top -b --ignored-param2
root 6 0.0 0.1 5956 3188 pts/0 S+ 13:58 0:00 top -b
root 7 0.0 0.1 5884 2816 pts/1 Rs+ 13:58 0:00 ps waux
$ /usr/bin/time docker stop test
test
real 0m 10.19s
user 0m 0.04s
sys 0m 0.03s
理解 CMD 和 ENTRYPOINT 如何交互
CMD 和 ENTRYPOINT 指令都定义了运行容器时要执行的命令。有一些规则描述了它们的协作。
Dockerfile 应该至少指定一个
CMD或ENTRYPOINT命令。当将容器用作可执行文件时,应该定义
ENTRYPOINT。CMD应该用作定义ENTRYPOINT命令的默认参数或在容器中执行临时命令的方式。当使用替代参数运行容器时,
CMD将被覆盖。
下表显示了不同 ENTRYPOINT / CMD 组合执行的命令。
| 无 ENTRYPOINT | ENTRYPOINT exec_entry p1_entry | ENTRYPOINT ["exec_entry", "p1_entry"] | |
|---|---|---|---|
| 无 CMD | 错误,不允许 | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry |
| CMD ["exec_cmd", "p1_cmd"] | exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry exec_cmd p1_cmd |
| CMD exec_cmd p1_cmd | /bin/sh -c exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd |
注意如果
CMD是从基础镜像定义的,设置ENTRYPOINT会将CMD重置为空值。在这种情况下,CMD必须在当前镜像中定义才能有值。
VOLUME
VOLUME ["/data"]VOLUME 指令创建指定名称的挂载点,并将其标记为包含来自本机主机或其他容器的外部挂载卷。该值可以是 JSON 数组,VOLUME ["/var/log/"],或者带有多个参数的纯字符串,例如 VOLUME /var/log 或 VOLUME /var/log /var/db。有关更多信息/示例以及通过 Docker 客户端挂载的说明,请参阅通过卷共享目录文档。
docker run 命令使用基本镜像中指定位置存在的任何数据初始化新创建的卷。例如,考虑以下 Dockerfile 片段:
FROM ubuntu
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
VOLUME /myvol此 Dockerfile 会生成一个镜像,导致 docker run 在 /myvol 创建一个新的挂载点,并将 greeting 文件复制到新创建的卷中。
关于指定卷的注意事项
请记住 Dockerfile 中关于卷的以下几点:
基于 Windows 容器的卷:当使用基于 Windows 的容器时,容器内卷的目标必须是以下之一:
- 不存在或为空的目录
- 除
C:之外的驱动器
从 Dockerfile 内部更改卷:如果任何构建步骤在声明卷后更改了卷内的数据,则在使用旧版构建器时,这些更改将被丢弃。使用 Buildkit 时,更改将被保留。
JSON 格式:列表被解析为 JSON 数组。您必须用双引号 (
") 而不是单引号 (') 括住单词。主机目录在容器运行时声明:主机目录(挂载点)本质上是与主机相关的。这是为了保持镜像的可移植性,因为不能保证给定的主机目录在所有主机上都可用。因此,您不能从 Dockerfile 内部挂载主机目录。
VOLUME指令不支持指定host-dir参数。您必须在创建或运行容器时指定挂载点。
USER
USER <user>[:<group>]或
USER <UID>[:<GID>]USER 指令设置用户名称(或 UID)和可选的用户组(或 GID),以用作当前阶段其余部分的默认用户和组。指定的用户用于 RUN 指令,并在运行时运行相关的 ENTRYPOINT 和 CMD 命令。
请注意,当为用户指定组时,用户将仅拥有指定的组成员资格。任何其他配置的组成员资格都将被忽略。
警告当用户没有主组时,镜像(或下一个指令)将以
root组运行。在 Windows 上,如果用户不是内置帐户,则必须首先创建该用户。这可以通过作为 Dockerfile 一部分调用的
net user命令完成。
FROM microsoft/windowsservercore
# Create Windows user in the container
RUN net user /add patrick
# Set it for subsequent commands
USER patrickWORKDIR
WORKDIR /path/to/workdirWORKDIR 指令设置 Dockerfile 中其后所有 RUN、CMD、ENTRYPOINT、COPY 和 ADD 指令的工作目录。如果 WORKDIR 不存在,即使它没有在任何后续 Dockerfile 指令中使用,也会创建它。
WORKDIR 指令可以在 Dockerfile 中使用多次。如果提供了相对路径,它将相对于前一个 WORKDIR 指令的路径。例如:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd此 Dockerfile 中最终 pwd 命令的输出将是 /a/b/c。
WORKDIR 指令可以解析之前使用 ENV 设置的环境变量。您只能使用 Dockerfile 中明确设置的环境变量。例如:
ENV DIRPATH=/path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd此 Dockerfile 中最终 pwd 命令的输出将是 /path/$DIRNAME。
如果未指定,默认工作目录是 /。实际上,如果您不是从头开始构建 Dockerfile (FROM scratch),WORKDIR 很可能由您正在使用的基础镜像设置。
因此,为了避免在未知目录中进行意外操作,最好明确设置您的 WORKDIR。
ARG
ARG <name>[=<default value>] [<name>[=<default value>]...]ARG 指令定义了一个变量,用户可以在构建时使用 docker build 命令并通过 --build-arg 标志将其传递给构建器。
警告不建议使用构建参数来传递诸如用户凭据、API 令牌等秘密信息。构建参数在
docker history命令和max模式出处证明中可见,如果您使用 Buildx GitHub Actions 且您的 GitHub 仓库是公开的,则这些证明默认附加到镜像中。请参阅
RUN --mount=type=secret部分,了解在构建镜像时使用秘密的安全方法。
Dockerfile 可能包含一个或多个 ARG 指令。例如,以下是一个有效的 Dockerfile:
FROM busybox
ARG user1
ARG buildno
# ...默认值
ARG 指令可以可选地包含一个默认值。
FROM busybox
ARG user1=someuser
ARG buildno=1
# ...如果 ARG 指令具有默认值,并且在构建时没有传递值,则构建器使用默认值。
作用域
ARG 变量从它在 Dockerfile 中声明的那一行开始生效。例如,考虑这个 Dockerfile:
FROM busybox
USER ${username:-some_user}
ARG username
USER $username
# ...用户通过调用以下命令构建此文件:
$ docker build --build-arg username=what_user .
- 第 2 行的
USER指令评估为some_user回退,因为username变量尚未声明。 username变量在第 3 行声明,从那时起即可在 Dockerfile 指令中引用。- 第 4 行的
USER指令评估为what_user,因为此时username参数的值为what_user,这是通过命令行传递的。在通过ARG指令定义之前,任何变量的使用都会导致空字符串。
在构建阶段内声明的 ARG 变量会自动被基于该阶段的其他阶段继承。不相关的构建阶段无法访问该变量。要在多个不同阶段中使用参数,每个阶段都必须包含 ARG 指令,或者它们都必须基于同一个 Dockerfile 中声明变量的共享基础阶段。
欲了解更多信息,请参阅变量作用域。
使用 ARG 变量
您可以使用 ARG 或 ENV 指令来指定可用于 RUN 指令的变量。使用 ENV 指令定义的环境变量总是会覆盖同名的 ARG 指令。考虑以下包含 ENV 和 ARG 指令的 Dockerfile。
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=v1.0.0
RUN echo $CONT_IMG_VER然后,假设此镜像使用此命令构建:
$ docker build --build-arg CONT_IMG_VER=v2.0.1 .
在这种情况下,RUN 指令使用 v1.0.0 而不是用户传递的 ARG 设置:v2.0.1。这种行为类似于 shell 脚本,其中局部作用域变量会覆盖作为参数传递或从环境中继承的变量,从其定义点开始。
使用上面的示例,但采用不同的 ENV 规范,您可以创建 ARG 和 ENV 指令之间更有用的交互。
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=${CONT_IMG_VER:-v1.0.0}
RUN echo $CONT_IMG_VER与 ARG 指令不同,ENV 值总是持久化在构建的镜像中。考虑不带 --build-arg 标志的 docker build:
$ docker build .
使用此 Dockerfile 示例,CONT_IMG_VER 仍然存在于镜像中,但其值将是 v1.0.0,因为它是在第 3 行由 ENV 指令设置的默认值。
此示例中的变量扩展技术允许您通过利用 ENV 指令从命令行传递参数并将其持久化到最终镜像中。变量扩展仅支持有限的 Dockerfile 指令集。
预定义 ARG
Docker 有一组预定义的 ARG 变量,您可以在 Dockerfile 中不使用相应的 ARG 指令即可使用。
HTTP_PROXYhttp_proxyHTTPS_PROXYhttps_proxyFTP_PROXYftp_proxyNO_PROXYno_proxyALL_PROXYall_proxy
要使用这些变量,请使用 --build-arg 标志在命令行上传递它们,例如:
$ docker build --build-arg HTTPS_PROXY=https://my-proxy.example.com .
默认情况下,这些预定义变量不包含在 docker history 的输出中。排除它们可减少意外泄露 HTTP_PROXY 变量中敏感身份验证信息的风险。
例如,考虑使用 --build-arg HTTP_PROXY=http://user:pass@proxy.lon.example.com 构建以下 Dockerfile:
FROM ubuntu
RUN echo "Hello World"在这种情况下,HTTP_PROXY 变量的值在 docker history 中不可用,并且不被缓存。如果您更改位置,并且您的代理服务器更改为 http://user:pass@proxy.sfo.example.com,后续构建不会导致缓存未命中。
如果您需要覆盖此行为,您可以通过在 Dockerfile 中添加 ARG 语句来实现,如下所示:
FROM ubuntu
ARG HTTP_PROXY
RUN echo "Hello World"构建此 Dockerfile 时,HTTP_PROXY 会保留在 docker history 中,并且更改其值会使构建缓存失效。
全局范围内的自动平台 ARG
此功能仅在使用 BuildKit 后端时可用。
BuildKit 支持一组预定义的 ARG 变量,其中包含执行构建的节点平台(构建平台)和生成镜像的平台(目标平台)的信息。目标平台可以使用 docker build 上的 --platform 标志指定。
以下 ARG 变量会自动设置:
TARGETPLATFORM- 构建结果的平台。例如linux/amd64、linux/arm/v7、windows/amd64。TARGETOS- TARGETPLATFORM 的操作系统组件TARGETARCH- TARGETPLATFORM 的架构组件TARGETVARIANT- TARGETPLATFORM 的变体组件BUILDPLATFORM- 执行构建的节点的平台。BUILDOS- BUILDPLATFORM 的操作系统组件BUILDARCH- BUILDPLATFORM 的架构组件BUILDVARIANT- BUILDPLATFORM 的变体组件
这些参数在全局范围内定义,因此在构建阶段或您的 RUN 命令中不会自动可用。要在构建阶段中公开其中一个参数,请重新定义它而不带值。
例如:
FROM alpine
ARG TARGETPLATFORM
RUN echo "I'm building for $TARGETPLATFORM"BuildKit 内置构建参数
| 参数 | 类型 | 描述 |
|---|---|---|
BUILDKIT_BUILD_NAME | 字符串 | 覆盖 buildx history 命令 和 Docker Desktop Builds 视图中显示的构建名称。 |
BUILDKIT_CACHE_MOUNT_NS | 字符串 | 设置可选的缓存 ID 命名空间。 |
BUILDKIT_CONTEXT_KEEP_GIT_DIR | 布尔值 | 触发 Git 上下文保留 .git 目录。 |
BUILDKIT_HISTORY_PROVENANCE_V1 | 布尔值 | 为构建历史记录启用 SLSA Provenance v1。 |
BUILDKIT_INLINE_CACHE2 | 布尔值 | 是否将缓存元数据内联到镜像配置中。 |
BUILDKIT_MULTI_PLATFORM | 布尔值 | 选择确定性输出,无论是否多平台输出。 |
BUILDKIT_SANDBOX_HOSTNAME | 字符串 | 设置主机名(默认为 buildkitsandbox) |
BUILDKIT_SYNTAX | 字符串 | 设置前端镜像 |
SOURCE_DATE_EPOCH | 整数 | 设置创建镜像和层的 Unix 时间戳。更多信息请参阅可复现构建。自 Dockerfile 1.5、BuildKit 0.11 起支持。 |
示例:保留 .git 目录
使用 Git 上下文时,.git 目录不会在检出时保留。如果您想在构建期间检索 git 信息,保留它可能很有用。
# syntax=docker/dockerfile:1
FROM alpine
WORKDIR /src
RUN --mount=target=. \
make REVISION=$(git rev-parse HEAD) build$ docker build --build-arg BUILDKIT_CONTEXT_KEEP_GIT_DIR=1 https://github.com/user/repo.git#main
对构建缓存的影响
ARG 变量不像 ENV 变量那样持久化到构建的镜像中。然而,ARG 变量以类似的方式影响构建缓存。如果 Dockerfile 定义了一个 ARG 变量,其值与之前的构建不同,那么在其首次使用时(而不是定义时)会发生“缓存未命中”。特别是,ARG 指令之后的所有 RUN 指令都隐式使用 ARG 变量(作为环境变量),因此可能导致缓存未命中。所有预定义的 ARG 变量都免于缓存,除非 Dockerfile 中存在匹配的 ARG 语句。
例如,考虑这两个 Dockerfile:
FROM ubuntu
ARG CONT_IMG_VER
RUN echo $CONT_IMG_VERFROM ubuntu
ARG CONT_IMG_VER
RUN echo hello如果您在命令行上指定 --build-arg CONT_IMG_VER=,在这两种情况下,第 2 行的规范不会导致缓存未命中;第 3 行会导致缓存未命中。ARG CONT_IMG_VER 会导致 RUN 行被识别为与运行 CONT_IMG_VER= 相同,因此如果 发生变化,您会遇到缓存未命中。
在相同的命令行下考虑另一个示例:
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=$CONT_IMG_VER
RUN echo $CONT_IMG_VER在此示例中,缓存未命中发生在第 3 行。发生未命中是因为 ENV 中的变量值引用了 ARG 变量,并且该变量通过命令行更改。在此示例中,ENV 命令导致镜像包含该值。
如果 ENV 指令覆盖了同名的 ARG 指令,就像这个 Dockerfile:
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=hello
RUN echo $CONT_IMG_VER第 3 行不会导致缓存未命中,因为 CONT_IMG_VER 的值是一个常量(hello)。因此,RUN (第 4 行) 上使用的环境变量和值在不同构建之间没有变化。
ONBUILD
ONBUILD <INSTRUCTION>ONBUILD 指令向镜像添加一个触发指令,该指令将在以后使用该镜像作为另一个构建的基础时执行。该触发器将在下游构建的上下文中执行,就像它已在下游 Dockerfile 中紧接在 FROM 指令之后插入一样。
如果您正在构建一个将用作其他镜像的基础的镜像,例如应用程序构建环境或可以用用户特定配置自定义的守护程序,这将非常有用。
例如,如果您的镜像是一个可重用的 Python 应用程序构建器,它将需要将应用程序源代码添加到特定目录中,并且可能需要在此之后调用构建脚本。您现在不能仅仅调用 ADD 和 RUN,因为您还没有访问应用程序源代码的权限,并且每个应用程序构建都会有所不同。您可以简单地向应用程序开发人员提供一个样板 Dockerfile 以复制粘贴到其应用程序中,但这效率低下、容易出错且难以更新,因为它与应用程序特定代码混合在一起。
解决方案是使用 ONBUILD 注册高级指令,以便在后续构建阶段运行。
其工作原理如下:
- 当遇到
ONBUILD指令时,构建器会向正在构建的镜像的元数据添加一个触发器。该指令不会以其他方式影响当前构建。 - 在构建结束时,所有触发器列表存储在镜像清单中,键为
OnBuild。它们可以使用docker inspect命令进行检查。 - 稍后,该镜像可以使用
FROM指令作为新构建的基础。作为处理FROM指令的一部分,下游构建器会查找ONBUILD触发器,并按其注册的相同顺序执行它们。如果任何触发器失败,FROM指令将中止,这反过来会导致构建失败。如果所有触发器都成功,FROM指令将完成,并且构建照常继续。 - 触发器执行后会从最终镜像中清除。换句话说,它们不会被“孙子”构建继承。
例如,您可以添加如下内容:
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src从阶段、镜像或上下文复制或挂载
从 Dockerfile 语法 1.11 开始,您可以将 ONBUILD 与从其他阶段、镜像或构建上下文复制或挂载文件的指令一起使用。例如:
# syntax=docker/dockerfile:1.11
FROM alpine AS baseimage
ONBUILD COPY --from=build /usr/bin/app /app
ONBUILD RUN --mount=from=config,target=/opt/appconfig ...如果 from 的源是一个构建阶段,则该阶段必须在触发 ONBUILD 的 Dockerfile 中定义。如果它是一个命名上下文,则该上下文必须传递给下游构建。
ONBUILD 限制
- 不允许使用
ONBUILD ONBUILD链式ONBUILD指令。 ONBUILD指令不得触发FROM或MAINTAINER指令。
STOPSIGNAL
STOPSIGNAL signalSTOPSIGNAL 指令设置将发送到容器以退出的系统调用信号。此信号可以是 SIG 格式的信号名称,例如 SIGKILL,也可以是与内核系统调用表中的位置匹配的无符号数字,例如 9。如果未定义,默认为 SIGTERM。
可以使用 docker run 和 docker create 上的 --stop-signal 标志,根据每个容器覆盖镜像的默认停止信号。
HEALTHCHECK
HEALTHCHECK 指令有两种形式:
HEALTHCHECK [OPTIONS] CMD command(通过在容器内运行命令来检查容器健康状况)HEALTHCHECK NONE(禁用从基础镜像继承的任何健康检查)
HEALTHCHECK 指令告诉 Docker 如何测试容器以检查其是否仍在工作。这可以检测例如 Web 服务器陷入无限循环且无法处理新连接的情况,即使服务器进程仍在运行。
当容器指定了健康检查时,除了其正常状态外,它还具有健康状态。此状态最初为 starting。每当健康检查通过时,它就会变为 healthy(无论它之前处于什么状态)。在一定数量的连续失败后,它会变为 unhealthy。
CMD 前面可以出现的选项是:
--interval=DURATION(默认:30s)--timeout=DURATION(默认:30s)--start-period=DURATION(默认:0s)--start-interval=DURATION(默认:5s)--retries=N(默认:3)
健康检查将在容器启动后 interval 秒后首次运行,然后在每次前一个检查完成后再过 interval 秒运行。
如果单次检查运行时间超过 timeout 秒,则认为该检查失败。
需要 retries 次连续的健康检查失败才能将容器视为 unhealthy。
启动周期 (start period) 为需要时间进行引导的容器提供初始化时间。在此期间探测失败不会计入最大重试次数。但是,如果在启动期间健康检查成功,则认为容器已启动,所有连续失败都将计入最大重试次数。
启动间隔 (start interval) 是启动期间健康检查之间的时间。此选项需要 Docker Engine 25.0 或更高版本。
Dockerfile 中只能有一个 HEALTHCHECK 指令。如果您列出多个,则只有最后一个 HEALTHCHECK 会生效。
CMD 关键字后面的命令可以是 shell 命令(例如 HEALTHCHECK CMD /bin/check-running)或 exec 数组(与其他 Dockerfile 命令一样;有关详细信息,请参阅 ENTRYPOINT)。
命令的退出状态指示容器的健康状态。可能的值为:
- 0: 成功 - 容器健康且可供使用
- 1: 不健康 - 容器工作不正常
- 2: 保留 - 不要使用此退出代码
例如,大约每五分钟检查一次 Web 服务器是否能够在三秒内提供网站主页:
HEALTHCHECK --interval=5m --timeout=3s \
CMD curl -f https:/// || exit 1为了帮助调试失败的探测,命令在标准输出或标准错误上写入的任何输出文本(UTF-8 编码)都将存储在健康状态中,并可以通过 docker inspect 查询。此类输出应保持简短(目前仅存储前 4096 字节)。
当容器的健康状态发生变化时,将生成一个包含新状态的 health_status 事件。
SHELL
SHELL ["executable", "parameters"]SHELL 指令允许覆盖用于 shell 形式命令的默认 shell。Linux 上的默认 shell 是 ["/bin/sh", "-c"],Windows 上的默认 shell 是 ["cmd", "/S", "/C"]。SHELL 指令必须以 JSON 形式写入 Dockerfile。
SHELL 指令在 Windows 上特别有用,因为 Windows 上有两个常用且截然不同的本机 shell:cmd 和 powershell,以及包括 sh 在内的其他可用 shell。
SHELL 指令可以多次出现。每个 SHELL 指令都会覆盖所有以前的 SHELL 指令,并影响所有后续指令。例如:
FROM microsoft/windowsservercore
# Executed as cmd /S /C echo default
RUN echo default
# Executed as cmd /S /C powershell -command Write-Host default
RUN powershell -command Write-Host default
# Executed as powershell -command Write-Host hello
SHELL ["powershell", "-command"]
RUN Write-Host hello
# Executed as cmd /S /C echo hello
SHELL ["cmd", "/S", "/C"]
RUN echo hello当 Dockerfile 中使用 shell 形式时,以下指令可能会受到 SHELL 指令的影响:RUN、CMD 和 ENTRYPOINT。
以下示例是在 Windows 上常见的模式,可以通过使用 SHELL 指令进行简化:
RUN powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"构建器调用的命令将是:
cmd /S /C powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"这效率低下,原因有二。首先,调用了不必要的 cmd.exe 命令处理器(即 shell)。其次,shell 形式的每个 RUN 指令都需要额外的 powershell -command 前缀命令。
为了提高效率,可以采用以下两种机制之一。一种是使用 RUN 命令的 JSON 形式,例如:
RUN ["powershell", "-command", "Execute-MyCmdlet", "-param1 \"c:\\foo.txt\""]尽管 JSON 形式明确且不使用不必要的 cmd.exe,但它确实需要更多的冗长性,通过双引号和转义。另一种机制是使用 SHELL 指令的 shell 形式,为 Windows 用户提供更自然的语法,尤其是在与 escape 解析器指令结合使用时:
# escape=`
FROM microsoft/nanoserver
SHELL ["powershell","-command"]
RUN New-Item -ItemType Directory C:\Example
ADD Execute-MyCmdlet.ps1 c:\example\
RUN c:\example\Execute-MyCmdlet -sample 'hello world'结果是:
PS E:\myproject> docker build -t shell .
Sending build context to Docker daemon 4.096 kB
Step 1/5 : FROM microsoft/nanoserver
---> 22738ff49c6d
Step 2/5 : SHELL powershell -command
---> Running in 6fcdb6855ae2
---> 6331462d4300
Removing intermediate container 6fcdb6855ae2
Step 3/5 : RUN New-Item -ItemType Directory C:\Example
---> Running in d0eef8386e97
Directory: C:\
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 10/28/2016 11:26 AM Example
---> 3f2fbf1395d9
Removing intermediate container d0eef8386e97
Step 4/5 : ADD Execute-MyCmdlet.ps1 c:\example\
---> a955b2621c31
Removing intermediate container b825593d39fc
Step 5/5 : RUN c:\example\Execute-MyCmdlet 'hello world'
---> Running in be6d8e63fe75
hello world
---> 8e559e9bf424
Removing intermediate container be6d8e63fe75
Successfully built 8e559e9bf424
PS E:\myproject>
SHELL 指令也可以用来修改 shell 的操作方式。例如,在 Windows 上使用 SHELL cmd /S /C /V:ON|OFF 可以修改延迟环境变量扩展语义。
如果需要使用替代 shell,例如 zsh、csh、tcsh 等,SHELL 指令也可以在 Linux 上使用。
Here-Documents(此处文档)
Here-documents 允许将后续 Dockerfile 行重定向到 RUN 或 COPY 命令的输入。如果此类命令包含here-document,Dockerfile 会将下一行(直到仅包含 here-doc 分隔符的行)视为同一命令的一部分。
示例:运行多行脚本
# syntax=docker/dockerfile:1
FROM debian
RUN <<EOT bash
set -ex
apt-get update
apt-get install -y vim
EOT如果命令只包含一个 here-document,其内容将使用默认 shell 进行评估。
# syntax=docker/dockerfile:1
FROM debian
RUN <<EOT
mkdir -p foo/bar
EOT或者,可以使用 shebang 标头来定义解释器。
# syntax=docker/dockerfile:1
FROM python:3.6
RUN <<EOT
#!/usr/bin/env python
print("hello world")
EOT更复杂的示例可能会使用多个 here-documents。
# syntax=docker/dockerfile:1
FROM alpine
RUN <<FILE1 cat > file1 && <<FILE2 cat > file2
I am
first
FILE1
I am
second
FILE2示例:创建内联文件
通过 COPY 指令,您可以将源参数替换为 here-doc 指示符,将 here-document 的内容直接写入文件。以下示例使用 COPY 指令创建了一个包含 hello world 的 greeting.txt 文件。
# syntax=docker/dockerfile:1
FROM alpine
COPY <<EOF greeting.txt
hello world
EOF常规 here-doc 变量扩展和制表符剥离规则适用。以下示例展示了一个小的 Dockerfile,它使用带有 here-document 的 COPY 指令创建了一个 hello.sh 脚本文件。
# syntax=docker/dockerfile:1
FROM alpine
ARG FOO=bar
COPY <<-EOT /script.sh
echo "hello ${FOO}"
EOT
ENTRYPOINT ash /script.sh在这种情况下,文件脚本会打印 "hello bar",因为变量在执行 COPY 指令时被扩展。
$ docker build -t heredoc .
$ docker run heredoc
hello bar
如果您引用 here-document 单词 EOT 的任何部分,变量将不会在构建时扩展。
# syntax=docker/dockerfile:1
FROM alpine
ARG FOO=bar
COPY <<-"EOT" /script.sh
echo "hello ${FOO}"
EOT
ENTRYPOINT ash /script.sh请注意,这里的 ARG FOO=bar 是多余的,可以删除。变量在运行时,即脚本被调用时进行解释。
$ docker build -t heredoc .
$ docker run -e FOO=world heredoc
hello world
Dockerfile 示例
有关 Dockerfile 示例,请参阅: