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 的顶部。
解析器指令不区分大小写,但惯例是小写。同样,惯例是在任何解析器指令之后包含一个空行。解析器指令不支持行延续字符。
由于这些规则,以下示例都是无效的
由于行延续而无效
# 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>
环境替换
环境变量(使用 `ENV` 语句 声明)也可以在某些指令中用作变量,由 Dockerfile 解释。转义也用于将类似于变量的语法文字地包含到语句中。
环境变量在 Dockerfile 中用 `$variable_name` 或 `${variable_name}` 表示。它们被视为等效的,大括号语法通常用于解决没有空格的变量名的问题,例如 `${foo}_bar`。
`${variable_name}` 语法还支持下面指定的一些标准 `bash` 修饰符
- `${variable:-word}` 表示如果设置了 `variable`,则结果将是该值。如果未设置 `variable`,则 `word` 将是结果。
- `${variable:+word}` 表示如果设置了 `variable`,则 `word` 将是结果,否则结果为空字符串。
以下变量替换在 Dockerfile 语法的预发布版本中受支持,当在您的 Dockerfile 中使用 `# syntax=docker/dockerfile-upstream:master` 语法指令时
`${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` 的第一个出现替换为 `replacement`
string=foobarbaz echo ${string/ba/fo} # fooforbaz
`${variable//pattern/replacement}` 将 `variable` 中 `pattern` 的所有出现替换为 `replacement`
string=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 /quux
以下 Dockerfile 中的指令列表支持环境变量
ADD
COPY
ENV
EXPOSE
FROM
LABEL
STOPSIGNAL
USER
VOLUME
WORKDIR
- `ONBUILD`(与上述支持的指令之一组合时)
您也可以将环境变量与 `RUN`、`CMD` 和 `ENTRYPOINT` 指令一起使用,但在这些情况下,变量替换由命令 shell 处理,而不是由构建器处理。请注意,使用 exec 形式的指令不会自动调用命令 shell。请参阅 变量替换。
环境变量替换在整个指令中对每个变量使用相同的值。更改变量的值只对后续指令有效。考虑以下示例
ENV abc=hello
ENV abc=bye def=$abc
ENV ghi=$abc
- `def` 的值变为 `hello`
- `ghi` 的值变为 `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 上尤其重要,因为反斜杠是路径分隔符。否则,以下行将被视为 shell 格式,因为不是有效的 JSON,并以意外方式失败。
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
您还可以使用 shell 格式的 heredocs 来拆分命令
RUN <<EOF
source $HOME/.bashrc && \
echo $HOME
EOF
有关 heredocs 的更多信息,请参见 Here-documents.
使用不同的 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 <name>
、COPY --from=<name>
和RUN --mount=type=bind,from=<name>
指令中使用,以引用在此阶段构建的镜像。 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_version
RUN
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
EOF
RUN
指令的可用 [OPTIONS]
为
RUN 指令的缓存失效
RUN
指令的缓存不会在下次构建时自动失效。例如,RUN apt-get dist-upgrade -y
这样的指令的缓存将在下次构建时被重用。可以通过使用 --no-cache
标志来使 RUN
指令的缓存失效,例如 docker build --no-cache
。
有关更多信息,请参见 Dockerfile 最佳实践指南。
RUN --mount
RUN --mount=[type=<TYPE>][,option=<value>[,option=<value>]...]
RUN --mount
允许您创建构建可以访问的文件系统挂载。这可以用来
- 创建到主机文件系统或其他构建阶段的绑定挂载
- 访问构建机密或 ssh-agent 套接字
- 使用持久化包管理缓存来加速您的构建
支持的挂载类型为
类型 | 描述 |
---|---|
bind (默认) | 绑定挂载上下文目录(只读)。 |
cache | 挂载一个临时目录以缓存编译器和包管理器的目录。 |
tmpfs | 在构建容器中挂载一个 tmpfs 。 |
secret | 允许构建容器访问安全文件,例如私钥,而不会将它们烘焙到镜像中。 |
ssh | 允许构建容器通过 SSH 代理访问 SSH 密钥,并支持密码短语。 |
RUN --mount=type=bind
此挂载类型允许将文件或目录绑定到构建容器。默认情况下,绑定挂载是只读的。
选项 | 描述 |
---|---|
target 1 | 挂载路径。 |
source | from 中的源路径。默认为 from 的根目录。 |
from | 用于源根目录的构建阶段或镜像名称。默认为构建上下文。 |
rw ,readwrite | 允许对挂载进行写入。写入的数据将被丢弃。 |
RUN --mount=type=cache
此挂载类型允许构建容器缓存编译器和包管理器的目录。
选项 | 描述 |
---|---|
id | 可选的 ID 用于标识独立/不同的缓存。默认为 target 的值。 |
target 1 | 挂载路径。 |
ro ,readonly | 如果设置,则为只读。 |
sharing | shared 、private 或 locked 之一。默认为 shared 。shared 缓存挂载可以被多个写入者同时使用。private 如果有多个写入者,则会创建一个新的挂载。locked 会暂停第二个写入者,直到第一个写入者释放挂载。 |
from | 用作缓存挂载基础的构建阶段。默认为空目录。 |
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 gcc
Apt 需要对其数据的独占访问,因此缓存使用选项 sharing=locked
,这将确保使用相同缓存挂载的多个并行构建将互相等待,而不是同时访问相同的缓存文件。如果您更喜欢让每个构建在这种情况下来创建另一个缓存目录,您也可以使用 sharing=private
。
RUN --mount=type=tmpfs
此挂载类型允许在构建容器中挂载 tmpfs
。
选项 | 描述 |
---|---|
target 1 | 挂载路径。 |
size | 指定文件系统大小的上限。 |
RUN --mount=type=secret
此挂载类型允许构建容器访问安全文件,例如私钥,而不会将它们烘焙到镜像中。
选项 | 描述 |
---|---|
id | 机密的 ID。默认为目标路径的基名。 |
target | 挂载路径。默认为 /run/secrets/ + id 。 |
required | 如果设置为 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 .
RUN --mount=type=ssh
此挂载类型允许构建容器通过 SSH 代理访问 SSH 密钥,并支持密码短语。
选项 | 描述 |
---|---|
id | SSH 代理套接字或密钥的 ID。默认为 "default"。 |
target | SSH 代理套接字路径。默认为 /run/buildkit/ssh_agent.${N} 。 |
required | 如果设置为 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 [email protected] 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
允许控制在哪个网络环境中运行命令。
支持的网络类型有
类型 | 描述 |
---|---|
default (默认) | 在默认网络中运行。 |
none | 在没有网络访问的情况下运行。 |
host | 在主机的网络环境中运行。 |
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 mypackage
pip
只能安装 tarfile 中提供的软件包,这些软件包可以通过前面的构建阶段控制。
RUN --network=host
命令在主机的网络环境中运行 (类似于 docker build --network=host
,但在每个指令的基础上)
警告
使用
--network=host
受network.host
权限的保护,该权限需要在使用--allow-insecure-entitlement network.host
标志启动 buildkitd 守护程序或在 buildkitd 配置 中启用时,以及使用--allow network.host
标志 的构建请求。
RUN --security
注意
稳定语法中尚未提供,请使用
docker/dockerfile:1-labs
版本。
RUN --security=<sandbox|insecure>
默认安全模式为 sandbox
。使用 --security=insecure
,构建器将在不安全模式下运行命令,而无需沙箱,这允许运行需要提升权限的流程(例如 containerd)。这相当于运行 docker run --privileged
。
警告
为了访问此功能,应在使用
--allow-insecure-entitlement security.insecure
标志启动 buildkitd 守护程序或在 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: 0000003fffffffff
CMD
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> <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="[email protected]"
然后,这将与其他标签一起从 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> ...
ENV
指令将环境变量 <key>
设置为值 <value>
。此值将在构建阶段的所有后续指令的环境中,并且可以在许多指令中 内联替换。该值将被解释为其他环境变量,因此如果引号字符未转义,它们将被删除。与命令行解析类似,可以使用引号和反斜杠在值中包含空格。
示例
ENV MY_NAME="John Doe"
ENV MY_DOG=Rex\ The\ Dog
ENV MY_CAT=fluffy
ENV
指令允许一次设置多个 <key>=<value> ...
变量,下面的示例将在最终镜像中产生相同的净结果
ENV MY_NAME="John Doe" MY_DOG=Rex\ The\ Dog \
MY_CAT=fluffy
使用 ENV
设置的环境变量将在从生成的镜像运行容器时持久化。您可以使用 docker inspect
查看这些值,并使用 docker run --env <key>=<value>
更改它们。
阶段会继承其父阶段或任何祖先使用 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 <key> <value>
,省略=
。例如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]
是
ADD
指令将新文件或目录从 <src>
复制并添加到映像文件系统中的路径 <dest>
。文件和目录可以从构建上下文、远程 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 [email protected]:user/repo.git /usr/src/things/
BuildKit 检测 <src>
的类型并相应地进行处理。
- 如果
<src>
是本地文件或目录,则该目录的内容将被复制到指定的目标。请参阅 从构建上下文添加文件。 - 如果
<src>
是本地 tar 存档,它将被解压缩并提取到指定的目标。请参阅 添加本地 tar 存档。 - 如果
<src>
是 URL,则 URL 的内容将被下载并放置在指定的目标。请参阅 从 URL 添加文件。 - 如果
<src>
是 Git 仓库,则该仓库将被克隆到指定的目标。请参阅 从 Git 仓库添加文件。
从构建上下文添加文件
任何不以 http://
、https://
或 git@
协议前缀开头的相对或本地路径都被视为本地文件路径。本地文件路径相对于构建上下文。例如,如果构建上下文是当前目录,则 ADD file.txt /
将 ./file.txt
中的文件添加到构建容器文件系统的根目录。
从构建上下文添加源文件时,其路径被解释为相对于上下文的根目录。如果指定了超出构建上下文范围的相对路径,例如 ADD ../something /something
,则父目录路径将被自动剥离。此示例中的有效源路径变为 ADD something /something
。
如果源是目录,则将复制该目录的内容,包括文件系统元数据。目录本身不会被复制,只有其内容会被复制。如果它包含子目录,这些子目录也将被复制,并与目标处的任何现有目录合并。任何冲突都将以文件为单位解决,有利于要添加的内容,除非您试图将目录复制到现有文件上,在这种情况下会引发错误。
如果源是文件,则该文件及其元数据将被复制到目标。文件权限将被保留。如果源是文件,并且目标处存在同名的目录,则会引发错误。
如果通过标准输入将 Dockerfile 传递到构建(docker build - < Dockerfile
),则没有构建上下文。在这种情况下,您只能使用 ADD
指令复制远程文件。您也可以通过标准输入传递 tar 存档:(docker build - < archive.tar
),存档根目录下的 Dockerfile 和存档的其余部分将用作构建的上下文。
模式匹配
对于本地文件,每个 <src>
可以包含通配符,匹配将使用 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 [email protected]: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 [email protected]: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>
当 <src>
是远程 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 /buildkit
ADD --checksum
ADD [--checksum=<hash>] <src> ... <dir>
--checksum
标志允许您验证远程资源的校验和。
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
指令将新的文件或目录从 <src>
复制并添加到图像文件系统的路径 <dest>
。 文件和目录可以从构建上下文、构建阶段、命名上下文或图像中复制。
ADD
和 COPY
指令在功能上相似,但服务于略微不同的目的。了解更多关于 ADD
和 COPY
之间的区别.
源
您可以使用 COPY
指定多个源文件或目录。 最后一个参数必须始终是目标。 例如,要将两个文件 file1.txt
和 file2.txt
从构建上下文复制到构建容器中的 /usr/src/things/
COPY file1.txt file2.txt /usr/src/things/
如果直接或使用通配符指定多个源文件,则目标必须是目录(必须以斜杠 /
结尾)。
COPY
接受一个标志 --from=<name>
,它允许您指定源位置为构建阶段、上下文或图像。 以下示例从名为 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
。
如果源是目录,则将复制该目录的内容,包括文件系统元数据。目录本身不会被复制,只有其内容会被复制。如果它包含子目录,这些子目录也将被复制,并与目标处的任何现有目录合并。任何冲突都将以文件为单位解决,有利于要添加的内容,除非您试图将目录复制到现有文件上,在这种情况下会引发错误。
如果源是文件,则该文件及其元数据将被复制到目标。文件权限将被保留。如果源是文件,并且目标处存在同名的目录,则会引发错误。
如果您通过标准输入将 Dockerfile 传递给构建(docker build - < Dockerfile
),则没有构建上下文。 在这种情况下,您只能使用 COPY
指令使用 --from
标志 从其他阶段、命名上下文或图像复制文件。 您还可以通过标准输入传递 tar 存档:(docker build - < archive.tar
),存档根目录中的 Dockerfile 和存档的其余部分将用作构建的上下文。
当使用 Git 存储库作为构建上下文时,复制文件的权限位为 644。 如果存储库中的文件设置了可执行位,它将具有设置为 755 的权限。 目录的权限设置为 755。
模式匹配
对于本地文件,每个 <src>
可以包含通配符,匹配将使用 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
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/
如果目标不存在,它将被创建,以及其路径中所有缺失的目录。
如果源是文件,并且目标没有以尾部斜杠结尾,则源文件将被写入目标路径作为文件。
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 <name>=<source>
指定)或图像复制文件。 以下示例从官方 Nginx 图像复制 nginx.conf
文件。
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf
COPY --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 操作系统的容器可行。
从构建上下文复制的所有文件和目录都使用 0
的 UID 和 GID 创建,除非可选的 --chown
标志指定给定的用户名、组名或 UID/GID 组合来请求复制内容的特定所有权。 --chown
标志的格式允许使用用户名和组名字符串或直接使用整数 UID 和 GID 的任何组合。 提供用户名而不提供组名或提供 UID 而不提供 GID 将使用与 GID 相同的数字 UID。 如果提供用户名或组名,则容器的根文件系统 /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 无需查找,也不依赖于容器根文件系统内容。
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.7-labs
版本。
COPY [--parents[=<boolean>]] <src> ... <dest>
--parents
标志保留 src
条目的父目录。 此标志默认为 false
。
# syntax=docker/dockerfile:1.7-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.7-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.7-labs
版本。
COPY [--exclude=<path> ...] <src> ... <dest>
--exclude
标志允许您为要排除的文件指定路径表达式。
路径表达式遵循与 <src>
相同的格式,支持通配符并使用 Go 的 filepath.Match 规则。 例如,要添加所有以 "hom" 开头的文件,但不包括扩展名为 .txt
的文件
COPY --exclude=*.txt hom* /mydir/
您可以为 COPY
指令多次指定 --exclude
选项。 多个 --excludes
是不复制的与其模式匹配的文件,即使文件路径与 <src>
中指定的模式匹配。 要添加所有以 "hom" 开头的文件,但不包括扩展名为 .txt
或 .md
的文件
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 <image>
的命令行参数将附加到 exec 格式 ENTRYPOINT
中的所有元素之后,并将覆盖使用 CMD
指定的所有元素。
这允许将参数传递给入口点,例如,docker run <image> -d
将把 -d
参数传递给入口点。您可以使用 docker run --entrypoint
标志覆盖 ENTRYPOINT
指令。
ENTRYPOINT
的 shell 形式会阻止使用任何 CMD
命令行参数。它还会将您的 ENTRYPOINT
作为 /bin/sh -c
的子命令启动,该命令不会传递信号。这意味着可执行文件将不会是容器的 PID 1
,并且不会接收 Unix 信号。在这种情况下,您的可执行文件不会从 docker stop <container>
接收 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
上干净地退出。
$ /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
,容器将不会干净地退出——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 内更改卷:如果任何构建步骤在声明卷后更改了卷中的数据,这些更改将被丢弃。
JSON 格式:该列表被解析为 JSON 数组。您必须用双引号 (
"
) 而不是单引号 ('
) 将单词括起来。主机目录是在容器运行时声明的:主机目录(挂载点)本质上是依赖于主机的。这是为了保持镜像的可移植性,因为无法保证给定的主机目录在所有主机上都可用。出于这个原因,您不能从 Dockerfile 内挂载主机目录。
VOLUME
指令不支持指定host-dir
参数。您必须在创建或运行容器时指定挂载点。
USER
USER <user>[:<group>]
或者
USER <UID>[:<GID>]
USER
指令设置用户名(或 UID)以及可选的用户组(或 GID),用作当前阶段剩余部分的默认用户和组。指定的用户用于 RUN
指令,并在运行时运行相关的 ENTRYPOINT
和 CMD
命令。
请注意,在为用户指定组时,用户将只有指定的组成员资格。任何其他配置的组成员资格将被忽略。
警告
当用户没有主组时,将使用
root
组运行镜像(或后续指令)。在 Windows 上,如果用户不是内置帐户,则必须先创建用户。这可以通过将
net user
命令作为 Dockerfile 的一部分来完成。
FROM microsoft/windowsservercore
# Create Windows user in the container
RUN net user /add patrick
# Set it for subsequent commands
USER patrick
WORKDIR
WORKDIR /path/to/workdir
WORKDIR
指令为 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>]
ARG
指令定义一个变量,用户可以在构建时使用 docker build
命令使用 --build-arg <varname>=<value>
标志将其传递给构建器。
警告
不建议使用构建参数来传递机密信息,例如用户凭据、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
变量是在后续的第 3 行定义的。第 4 行的 USER
评估为 what_user
,因为 username
参数已定义,并且 what_user
值已在命令行上传递。在通过 ARG
指令定义之前,任何对变量的使用都将导致空字符串。
ARG
指令在定义它的构建阶段结束时将超出范围。要在多个阶段使用参数,每个阶段都必须包含 ARG
指令。
FROM busybox
ARG SETTINGS
RUN ./run/setup $SETTINGS
FROM busybox
ARG SETTINGS
RUN ./run/other $SETTINGS
使用 ARG 变量
您可以使用 ARG
或 ENV
指令来指定可用于 RUN
指令的变量。使用 ENV
指令定义的环境变量始终会覆盖同名 ARG
指令。请考虑此 Dockerfile,它包含 ENV
和 ARG
指令。
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_PROXY
http_proxy
HTTPS_PROXY
https_proxy
FTP_PROXY
ftp_proxy
NO_PROXY
no_proxy
ALL_PROXY
all_proxy
要使用它们,请使用--build-arg
标志在命令行上传递它们,例如
$ docker build --build-arg HTTPS_PROXY=https://my-proxy.example.com .
默认情况下,这些预定义变量将从docker history
的输出中排除。排除它们可以降低意外地在HTTP_PROXY
变量中泄露敏感身份验证信息的风险。
例如,考虑使用--build-arg HTTP_PROXY=http://user:[email protected]
构建以下 Dockerfile
FROM ubuntu
RUN echo "Hello World"
在这种情况下,HTTP_PROXY
变量的值在docker history
中不可用,并且不会被缓存。如果您要更改位置,并且您的代理服务器更改为http://user:[email protected]
,则后续构建不会导致缓存未命中。
如果您需要覆盖此行为,则可以通过在 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 内置构建参数
Arg | 类型 | 描述 |
---|---|---|
BUILDKIT_CACHE_MOUNT_NS | String | 设置可选的缓存 ID 命名空间。 |
BUILDKIT_CONTEXT_KEEP_GIT_DIR | Bool | 触发 Git 上下文以保留.git 目录。 |
BUILDKIT_INLINE_CACHE 2 | Bool | 将内联缓存元数据内联到镜像配置中还是不内联。 |
BUILDKIT_MULTI_PLATFORM | Bool | 无论是否为多平台输出,都选择确定性输出。 |
BUILDKIT_SANDBOX_HOSTNAME | String | 设置主机名(默认buildkitsandbox ) |
BUILDKIT_SYNTAX | String | 设置前端镜像 |
SOURCE_DATE_EPOCH | Int | 设置创建镜像和层的 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_VER
FROM ubuntu
ARG CONT_IMG_VER
RUN echo hello
如果您在命令行上指定--build-arg CONT_IMG_VER=<value>
,在这两种情况下,第 2 行上的规范都不会导致缓存未命中;第 3 行会导致缓存未命中。ARG CONT_IMG_VER
导致RUN
行被识别为与运行CONT_IMG_VER=<value> echo hello
相同,因此如果<value>
发生更改,您将获得缓存未命中。
考虑在同一命令行下的另一个示例
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
ONBUILD 限制
- 不允许使用
ONBUILD ONBUILD
链接ONBUILD
指令。 ONBUILD
指令可能不会触发FROM
或MAINTAINER
指令。ONBUILD COPY --from
不受支持。
STOPSIGNAL
STOPSIGNAL signal
STOPSIGNAL
指令设置将发送到容器以退出其系统调用信号。此信号可以是格式为SIG<NAME>
的信号名称,例如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
。
启动周期为需要时间引导的容器提供初始化时间。在该期间内的探测失败不会计入最大重试次数。但是,如果在启动周期内健康检查成功,则认为容器已启动,所有后续失败将计入最大重试次数。
启动间隔是在启动周期内健康检查之间的间隔时间。此选项需要 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 http://localhost/ || exit 1
为了帮助调试失败的探测,命令在 stdout 或 stderr 上写入的任何输出文本(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
指令必须在 Dockerfile 中以 JSON 形式编写。
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-document 允许将后续 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-document。
# 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 的示例,请参阅