Dockerfile 参考
原文:https://docs.docker.com/reference/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 的格式:
# 注释
INSTRUCTION arguments
指令不区分大小写。但是,约定是使用大写,以便更容易地将它们与参数区分开来。
Docker 按顺序在 Dockerfile 中运行指令。一个 Dockerfile 必须以 FROM 指令开始。这可能是在解析器指令、注释和全局范围的 ARGs 之后。FROM 指令指定了您正在构建的父镜像。FROM 可能只能被一个或多个 ARG 指令先行,这些指令声明了在 Dockerfile 中的 FROM 行中使用的参数。
BuildKit 将以 # 开头的行视为注释,除非该行是有效的解析器指令。行中其他位置的 # 标记被视为参数。这允许像以下这样的声明:
# 注释
RUN echo '我们正在运行一些 # 酷的东西'
在执行 Dockerfile 指令之前,注释行被移除。在以下示例中,执行 echo 命令之前,注释被移除。
RUN echo hello \
# 注释
world
以下示例是等价的。
RUN echo hello \
world
注释不支持行延续字符。
INFO
关于空白的注意事项
为了向后兼容,忽略注释 (#) 和指令(如 RUN)前的前导空白,但不鼓励使用。这些情况下,前导空白不会保留,因此以下示例是等效的:
# 这是一行注释
RUN echo hello
RUN echo world
# 这是一行注释
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
因为在不是解析器指令的注释后出现,所以被视为注释:
# 关于我的 dockerfile
# directive=value
FROM ImageName
以下 unknowndirective
被视为注释,因为它没有被识别。已知的 syntax
指令被视为注释,因为它在不是解析器指令的注释后出现。
# unknowndirective=value
# syntax=value
在解析器指令中允许使用非换行空白。因此,以下行被视为相同:
#directive=value
# directive =value
# directive= value
# directive = value
# dIrEcTiVe=value
支持以下解析器指令:
syntax
escape
syntax
使用 syntax
解析器指令声明构建中使用的 Dockerfile 语法版本。如果未指定,BuildKit 使用捆绑的 Dockerfile 前端版本。声明语法版本可以让您在不升级 BuildKit 或 Docker Engine 的情况下自动使用最新的 Dockerfile 版本,甚至使用自定义 Dockerfile 实现。
大多数用户将希望将此解析器指令设置为 docker/dockerfile:1
,这会导致 BuildKit 在构建前拉取 Dockerfile 语法的最新稳定版本。
# syntax=docker/dockerfile:1
有关解析器指令如何工作的更多信息,请参见 自定义 Dockerfile 语法。
escape
# escape=\
或
# escape=`
escape
指令设置用于在 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>
环境替换
环境变量(通过 the ENV
statement 声明)也可以在某些指令中用作变量,由 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
的第一个出现为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 形式字符串允许您使用 escape character(默认为反斜杠)来转义换行符,将单个指令延续到下一行。这使得它更容易与更长的命令一起使用,因为它允许您将它们分成多行。例如,考虑这两行:
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
指令初始化新的构建阶段,并设置后续指令的 base image。因此,有效的 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
。默认情况下,使用构建请求的目标平台。全局构建参数可以在此标志的值中使用,例如 automatic platform ARGs 允许您强制一个阶段使用本地构建平台(--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 形式:
RUN [OPTIONS] <command> ...
# Exec 形式:
RUN [OPTIONS] [ "<command>", ... ]
有关这两种形式的区别的更多信息,请参阅 shell 或 exec 形式。
shell 形式最常用,并允许您使用换行符 escapes 或 heredocs 将较长的指令拆分为多行:
RUN <<EOF
apt-get update
apt-get install -y curl
EOF
RUN
指令的可用 [OPTIONS]
包括:
--mount
--network
--security
Cache invalidation for RUN instructions
RUN
指令的缓存在下次构建时不会自动失效。像 RUN apt-get dist-upgrade -y
这样的指令在下次构建期间会被重用。可以通过使用 --no-cache
标志来使 RUN
指令的缓存失效,例如 docker build --no-cache
。
有关更多信息,请参见 Dockerfile 最佳实践指南。
RUN
指令的缓存可以通过 ADD 和 COPY 指令使缓存失效。
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
这种挂载类型允许将文件或目录绑定到构建容器。绑定挂载默认为只读。
选项 | 描述 |
---|---|
target1 | 挂载路径。 |
source | 来源路径在 from 中。默认为 from 的根。 |
from | 构建阶段或镜像名称作为源的根。默认为构建上下文。 |
rw ,readwrite | 允许在挂载上写入。写入的数据将被丢弃。 |
RUN --mount=type=cache
这种挂载类型允许构建容器缓存编译器和包管理器的目录。
选项 | 描述 |
---|---|
id | 可选 ID,用于标识单独/不同的缓存。默认为 target 的值。 |
target1 | 挂载路径。 |
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
。
选项 | 描述 |
---|---|
target1 | 挂载路径。 |
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 git@gitlab.com 2>&1 | tee /hello
# "Welcome to GitLab, @GITLAB_USERNAME_ASSOCIATED_WITH_SSHKEY" 应该在此处打印
# 构建进度的类型定义为 `plain`。
$ eval $(ssh-agent)
$ ssh-add ~/.ssh/id_rsa
(在此处输入您的密码短语)
$ 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,但适用于每条指令)
:::Warning 使用 --network=host
受到 network.host
权限的保护,需要在启动 buildkitd 守护进程时启用 --allow-insecure-entitlement network.host
标志或在 buildkitd config 中,并且构建请求需要 --allow network.host
标志。 :::
RUN --security
INFO
注意
尚未在稳定语法中提供,使用 docker/dockerfile:1-labs
版本。
RUN --security=<sandbox|insecure>
默认安全模式是 sandbox
。使用 --security=insecure
,构建器在不安全模式下运行命令,没有沙箱,这允许运行需要提升权限的流程(例如 containerd)。这相当于运行 docker run --privileged
。
:::Warning
要访问此功能,权限 security.insecure
应在启动 buildkitd 守护进程时启用,使用 --allow-insecure-entitlement security.insecure
标志或在 buildkitd 配置中,并且构建请求需要 --allow security.insecure
标志。 :::
默认 sandbox 模式可以通过 --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
指令提供默认参数,则应该在 exec 形式 中指定 CMD
和 ENTRYPOINT
。
INFO
注意
不要将 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"
INFO
注意
确保使用双引号而不是单引号。特别是当您使用字符串插值时(例如 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> ...
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 ...
INFO
替代语法
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
指令将新文件、目录或远程文件 URL 从 <src>
复制并将它们添加到镜像的文件系统中,路径为 <dest>
。
可以指定多个 <src>
资源,但如果它们是文件或目录,则它们的路径被解释为相对于构建上下文的源。
每个 <src>
可能包含通配符,并且匹配将使用 Go 的 filepath.Match 规则完成。例如:
要添加构建上下文根目录中以 "hom" 开头的所有文件:
ADD hom* /mydir/
在以下示例中,?
是单字符通配符,匹配例如 "home.txt"。
ADD hom?.txt /mydir/
<dest>
是绝对路径,或相对于 WORKDIR
的路径,源将在目标容器内的该路径下被复制。
下面的示例使用相对路径,并将 "test.txt" 添加到 <WORKDIR>/relativeDir/
:
ADD test.txt relativeDir/
而这个示例使用绝对路径,并将 "test.txt" 添加到 /absoluteDir/
ADD test.txt /absoluteDir/
添加包含特殊字符(如 [
和 ]
)的文件或目录时,您需要根据 Golang 规则转义这些路径,以防止它们被视为匹配模式。例如,要添加名为 arr[0].txt
的文件,请使用以下命令;
ADD arr[[]0].txt /mydir/
在 <src>
是远程文件 URL 的情况
下,目标将具有 600 的权限。如果被检索的远程文件具有 HTTP Last-Modified
标头,则该标头的时间戳将用于设置目标文件的 mtime
。然而,像在 ADD
中处理的任何其他文件一样,mtime
不包括在确定文件是否已更改以及是否应更新缓存的决定中。
INFO
注意
如果您通过 STDIN 传递 Dockerfile(docker build - < somefile),则没有构建上下文,因此 Dockerfile 只能包含基于 URL 的 ADD 指令。您还可以通过 STDIN 传递压缩存档:(docker build - < archive.tar.gz),Dockerfile 在存档的根目录中,存档的其余部分将用作构建的上下文。
如果您的 URL 文件受到身份验证保护,您需要使用 RUN wget
、RUN curl
或容器内的其他工具,因为 ADD
指令不支持身份验证。
INFO
注意
第一个遇到的 ADD 指令会使 Dockerfile 中所有后续指令的缓存失效,如果 <src>
的内容已更改。这包括使 RUN 指令的缓存失效。有关更多信息,请参见 Dockerfile 最佳实践指南 - 利用构建缓存。
ADD 遵循以下规则:
<src>
路径必须在构建上下文内;您不能使用ADD ../something /something
,因为构建器只能访问上下文中的文件,而../something
指定了构建上下文根目录的父文件或目录。如果
<src>
是 URL 并且<dest>
以尾部斜杠结束,则文件名从 URL 推断出来,文件被下载到<dest>/<filename>
。例如,ADD http://example.com/foobar / 将创建文件 /foobar。URL 必须具有足够的路径,以便在这种情况下可以发现适当的文件名(http://example.com 不起作用)。如果
<src>
是目录,则复制该目录的所有内容,包括文件系统元数据。
INFO
注意
目录本身不被复制,只复制其内容。
如果
<src>
是本地tar
存档,并且是已识别的压缩格式 (identity
、gzip
、bzip2
或xz
),则它被解压为目录。远程 URL 的资源不会被解压缩。当目录被复制或解压时,其行为与tar -x
相同。结果是:- 存在于目标路径的任何内容和
- 源树的内容,冲突以“2.”的方式逐个文件解决。
INFO
注意
文件是否被识别为已识别的压缩格式完全基于文件的内容,而不是文件的名称。例如,如果一个空文件恰好以 .tar.gz
结束,这不会被识别为压缩文件,也不会生成任何解压缩错误消息,而文件将简单地被复制到目标。
如果
<src>
是任何其他类型的文件,它将连同其元数据一起单独复制。在这种情况下,如果<dest>
以尾部斜杠 / 结束,它将被视为目录,<src>
的内容将写入<dest>/base(<src>)
。如果指定了多个
<src>
资源,无论是直接指定还是由于使用通配符,那么<dest>
必须是目录,并且必须以斜杠 / 结尾。如果
<src>
是文件,并且<dest>
没有以尾部斜杠结束,则
<src>
的内容将被写为文件名 <dest>
。
- 如果
<dest>
不存在,它将被创建,连同其路径中的所有缺失目录。
添加私有 Git 仓库
要通过 SSH 添加私有仓库,请创建以下形式的 Dockerfile:
# syntax=docker/dockerfile:1
FROM alpine
ADD git@git.example.com:foo/bar.git /bar
可以使用 docker build --ssh 或 buildctl build --ssh 构建此 Dockerfile,例如,
docker build --ssh default
buildctl build --frontend=dockerfile.v0 --local context=. --local dockerfile=. --ssh default
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 源。
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>
。
可以指定多个 <src>
资源,但文件和目录的路径将根据构建上下文的源进行解释。
每个 <src>
可能包含通配符,并且匹配将使用 Go 的 filepath.Match 规则完成。例如:
要添加构建上下文根目录中以 "hom" 开头的所有文件:
COPY hom* /mydir/
在以下示例中,?
是单字符通配符,匹配例如 "home.txt"。
COPY hom?.txt /mydir/
<dest>
是绝对路径,或相对于 WORKDIR
的路径,源将在目标容器内的该路径下被复制。
下面的示例使用相对路径,并将 "test.txt" 添加到 <WORKDIR>/relativeDir/
:
COPY test.txt relativeDir/
而这个示例使用绝对路径,并将 "test.txt" 添加到 /absoluteDir/
COPY test.txt /absoluteDir/
复制
包含特殊字符(如 [
和 ]
)的文件或目录时,您需要根据 Golang 规则转义这些路径,以防止它们被视为匹配模式。例如,要复制名为 arr[0].txt
的文件,请使用以下命令:
COPY arr[[]0].txt /mydir/
INFO
注意
如果您使用 STDIN 构建(docker build - < somefile
),则没有构建上下文,因此 COPY
无法使用。
COPY
可选接受一个标志 --from=<name>
,可用于设置源位置为之前的构建阶段(使用 FROM .. AS <name>
创建),将用作用户发送的构建上下文的替代来源。如果找不到指定名称的构建阶段,则尝试使用同名的镜像。
COPY
遵循以下规则:
<src>
路径根据构建上下文解析。如果您指定了构建上下文之外的相对路径,例如 COPY ../something /something,则自动剥离父目录路径。这个示例中的有效源路径变为 COPY something /something如果
<src>
是目录,则复制该目录的所有内容,包括文件系统元数据。
INFO
注意
目录本身不被复制,只复制其内容。
如果 <src>
是任何其他类型的文件,它将连同其元数据一起单独复制。在这种情况下,如果 <dest>
以尾部斜杠 / 结束,它将被视为目录,<src>
的内容将写入 <dest>/base(<src>)
。
如果指定了多个 <src>
资源,无论是直接指定还是由于使用通配符,那么 <dest>
必须是目录,并且必须以斜杠 / 结尾。
如果 <src>
是文件,并且 <dest>
没有以尾部斜杠结束,则 <src>
的内容将被写为文件名 <dest>
。
如果 <dest>
不存在,它将被创建,连同其路径中的所有缺失目录。
INFO
注意
第一个遇到的 COPY
指令会使 Dockerfile 中所有后续指令的缓存失效,如果 <src>
的内容已更改。这包括使 RUN 指令的缓存失效。有关更多信息,请参见 Dockerfile 最佳实践指南 – 利用构建缓存。
COPY --from
默认情况下,COPY
指令从构建上下文复制文件。COPY --from
标志让您可以从镜像、构建阶段或命名上下文中复制文件。
COPY [--from=<image|stage|context>] <src> ... <dest>
要从 多阶段构建 中的构建阶段复制,请指定您要从中复制的阶段名称。您可以使用 AS
关键字与 FROM
指令一起指定阶段名称。
# 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 /
您还可以直接从其他镜像复制文件。以下示例从官方 Nginx 镜像复制一个 nginx.conf
文件。
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf
COPY --from
的源路径始终从您指定的镜像或阶段的文件系统根解析。
COPY --chown --chmod
::: 注意
当前只支持八进制表示法。非八进制支持正在 moby/buildkit#195
1 中跟踪。 :::
COPY [--chown=<user>:<group>] [--chmod=<perms> ...] <src> ... <dest>
--chown
和 --chmod
功能仅支持用于构建 Linux 容器的 Dockerfile,不适用于 Windows 容器。由于 Linux 和 Windows 之间没有用户和组所有权概念的转换,使用 /etc/passwd
和 /etc/group
进行用户名和组名转换为 ID 的使用限制了此功能仅对 Linux OS 容器有效。
所有从构建上下文复制的文件和目录默认以 UID 和 GID 0 创建。除非提供了可选的 --chown
标志,指定给复制内容的所有权的用户名、组名或 UID/GID 组合。--chown
标志的格式允许使用用户名和组名字符串或直接整数 UID 和 GID 的任何组合。提供用户名而没有组名或 UID 而没有 GID 将使用相同的数字 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 不需要查找,也不依赖于容器根文件系统的内容。
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
INFO
注意
尚未在稳定语法中提供,请使用 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/
# 构建上下文:
# ./x/y/a.txt
# ./x/y/b.txt
#
# 输出:
# /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
INFO
注意
尚未在稳定语法中提供,请使用 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
或 docker run
命令行参数。它还以 /bin/sh -c
的子命令启动您的 ENTRYPOINT
,这不会传递信号。这意味着可执行文件将不是容器的 PID 1
,并且不会收到 Unix 信号。在这种情况下,您的可执行文件不会从 docker stop <container>
接收 SIGTERM
。
Dockerfile 中只能有一个 ENTRYPOINT
指令。如果您列出了多个,则只有最后一个 ENTRYPOINT
会生效。
Exec 形式 ENTRYPOINT 示例
您可以使用 exec 形式的 ENTRYPOINT
设置相当稳定的默认命令和参数,然后使用任何形式的 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
并且您可以优雅地请求 top
使用 docker stop test
关闭。
以下 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"]
如果您需要为单个可执行文件编写启动脚本,您可以确保最终可执行文件接收 Unix 信号,使用 exec
和 gosu
命令:
#!/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
# 注意:我已使用 sh 编写此文件,以便它也能在 busybox 容器中工作
# 如果您需要在服务停止后也执行手动清理,
# 或者需要启动容器中的多个服务,
# 请使用 trap
trap "echo TRAPed signal" HUP INT QUIT TERM
# 在此处开始后台服务
/usr/sbin/apachectl start
echo "[按回车键退出] 或运行 'docker stop <container>'"
read
# 在此处停止服务并进行清理
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
INFO
注意
您可以使用 --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 |
INFO
注意
如果 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
命令。
INFO
注意,当为用户指定一个组时,用户将仅具有指定的组成员资格。任何其他配置的组成员资格都将被忽略。
WARNING
当用户没有主组时,镜像(或下一条指令)将以 root 组运行。
在 Windows 上,如果它不是内置帐户,则必须首先创建用户。这可以通过作为 Dockerfile 的一部分调用 net user 命令来完成。
FROM microsoft/windowsservercore
# 在容器中创建 Windows 用户
RUN net user /add patrick
# 为后续命令设置
USER patrick
WORKDIR
WORKDIR /path/to/workdir
WORKDIR
指令设置任何后续 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
最终 pwd
命令的输出在此 Dockerfile 中将是 /path/$DIRNAME
如果未指定,默认工作目录是 /
。实际上,如果您不是从头开始构建 Dockerfile(FROM scratch
),您使用的基础镜像很可能已设置了 WORKDIR
。
因此,为避免在未知目录中执行意外操作,最佳做法是显式设置您的 WORKDIR
。
ARG
ARG <name>[=<default value>]
ARG
指令用于定义一个变量,用户可以在构建时通过 docker build
命令使用 --build-arg <varname>=<value>
标志传递给构建器。
:::警告
不建议使用构建参数传递如用户凭据、API 令牌等秘密信息。构建参数在 docker 历史命令和最大模式来源认证中可见,这些认证默认附加到镜像上,如果你使用 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
指令。考虑以下包含 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 构建:
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: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_CACHE_MOUNT_NS | 字符串 | 设置可选的缓存 ID 命名空间。 |
BUILDKIT_CONTEXT_KEEP_GIT_DIR | 布尔值 | 触发 Git 上下文保留 .git 目录。 |
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
变量除非有相应的 ARG
语句在 Dockerfile 中,否则都不会影响缓存。
例如,考虑这两个 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
(参见 moby/buildkit 问题 #816)。
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
)
健康检查将在容器启动后间隔秒数首次运行,然后每次上次检查完成后间隔秒数再次运行。
如果一次检查运行的时间超过超时秒数,则认为检查失败。
开始期为需要启动时间的容器提供初始化时间。在该期间内的探测失败不会计入最大重试次数。然而,如果健康检查在开始期间成功,则认为容器已启动,并且所有连续的失败都会计入最大重试次数。
开始间隔是开始期间健康检查之间的时间。这个选项需要 Docker 引擎版本 25.0 或更高版本。
Dockerfile 中只能有一个 HEALTHCHECK
指令。如果列出多个,则只有最后一个 HEALTHCHECK
会生效。
CMD
关键字后的命令可以是 shell 命令(例如 HEALTHCHECK CMD /bin/check-running
)或 exec 数组(如其他 Dockerfile 命令中所见;参见例如 ENTRYPOINT
的详细信息)。
命令的退出状态指示容器的健康状态。可能的值包括:
- 0:成功 - 容器健康且可用
- 1:不健康 - 容器运行不正确
- 2:保留 - 不要使用此退出代码
例如,为了每五分钟检查一次网站的主页是否能在三秒内被服务器提供:
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 上是 ["cmd", "/S", "/C"]
。SHELL
指令必须在 Dockerfile 中以 JSON 形式编写。
SHELL
指令在 Windows 上特别有用,那里有两个常用且相当不同的原生 shell:cmd
和 powershell
,以及可用的其他替代 shell,包括 sh
。
SHELL
指令可以出现多次。每个 SHELL
指令都覆盖之前的所有 SHELL
指令,并影响所有后续指令。例如:
FROM microsoft/windowsservercore
# 作为 cmd /S /C echo default 执行
RUN echo default
# 作为 cmd /S /C powershell -command Write-Host default 执行
RUN powershell -command Write-Host default
# 作为 powershell -command Write-Host hello 执行
SHELL ["powershell", "-command"]
RUN Write-Host hello
# 作为 cmd /S /C echo hello 执行
SHELL ["cmd", "/S", "/C"]
RUN echo hello
以下指令可以受到 SHELL
指令的影响,当它们在 Dockerfile 中使用 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 -sample 'hello world'
---> Running in be6d8e63fe75
hello world
---> 8e559e9bf424
Removing intermediate container be6d8e63fe75
Successfully built 8e559e9bf424
PS E:\myproject>
SHELL
指令还可以用于修改 shell 操作的方式。例如,使用 SHELL cmd /S /C /V:ON|OFF
在 Windows 上,可以修改延迟环境变量扩展的语义。