Dockerfile 参考
Docker
可以通过读取 Dockerfile
中的指令自动构建镜像。
Dockerfile
是一个文本文档,其中包含用户可以在命令行上调用来组装镜像的所有命令。
本页介绍了可以在 Dockerfile
中使用的命令。
格式
以下是 Dockerfile
的格式:
# Comment
INSTRUCTION arguments
该指令不区分大小写。然而,惯例是它们是大写(UPPERCASE)的,以便更容易地将它们与参数区分开来。
Docker
按顺序运行 Dockerfile
中的指令。Dockerfile
必须以FROM
指令开头。这可能位于 解析器指令 、 注释 和全局范围的 ARGs 之后。FROM
指令指定您正在构建的 父镜像 。FROM
前面只能有一个或多个ARG
指令,这些指令声明 Dockerfile
中FROM
行中使用的参数。
Docker
将以#
开头的行视为注释,除非该行是有效的 解析器指令 。行中任何其他地方的#
标记都被视为参数。允许这样的语句:
# Comment RUN echo 'we are running some # of cool things'
注释行在执行 Dockerfile
指令之前被删除,这意味着执行 echo
命令的 shell
不会处理以下示例中的注释,并且下面的两个示例是等效的:
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
但请注意,指令参数中的空格(例如RUN
之后的命令)将被保留,因此以下示例将按照指定的方式打印带有前导空格的 hello world
:
RUN echo "\ hello\ world"
解析器指令
解析器指令是可选的,它会影响 Dockerfile
中后续行的处理方式。
解析器指令不会向构建添加层,也不会显示为构建步骤。解析器指令以# directive=value
的形式编写为特殊类型的注释。单个指令只能使用一次。
一旦处理完注释、空行或构建器指令,Docker
就不再查找解析器指令。相反,它将任何格式化为解析器指令的内容视为注释,并且不会尝试验证它是否可能是解析器指令。
因此,所有解析器指令必须位于 Dockerfile
的最顶部。
解析器指令不区分大小写。但是,惯例是使用小写字母。约定还要在任何解析器指令后面包括一个空行。解析器指令不支持续行符。
由于这些规则,下面的例子都是无效的:
续行符无效
# direc \
tive=value
因出现两次而无效:
# directive=value1
# directive=value2
FROM ImageName
由于出现在构建器指令之后而被视为注释:
FROM ImageName
# directive=value
由于出现在非解析器指令的注释之后而被视为注释
# About my dockerfile
# directive=value
FROM ImageName
未知指令由于未被识别而被视为注释。此外,由于已知指令出现在不是解析器指令的注释之后,因此被视为注释。
# unknowndirective=value
# knowndirective=value
解析器指令中允许使用非换行空格。因此,以下各行是等效的:
#directive=value
# directive =value
# directive= value
# directive = value
# dIrEcTiVe=value
支持以下解析器指令:
syntax
escape
syntax
此特性仅在使用 BuildKit 作为后端时可用, 在使用经典构建器后端时会被忽略。
有关更多信息,请参阅自定义 Dockerfile 语法 页面。
escape
# 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
上的所有命令都支持 /
作为路径分隔符。
添加转义解析器指令后,以下 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>
Environment
环境变量(使用ENV
语句声明)也可以在某些指令中用作由 Dockerfile
解释的变量。
还可以处理转义以将类似变量的语法按字面意思包含到语句中。
环境变量在 Dockerfile
中用 $variable_name
或 ${variable_name}
表示。
它们被同等对待,大括号语法通常用于解决变量名不带空格的问题,例如 ${foo}_bar
。
${variable_name}
语法还支持一些标准 bash
修饰符,如下所示:
${variable:-word}
表示如果设置了变量,那么结果将是该值。如果没有设置变量,那么结果将是word
。${variable:+word}
表示如果设置了variable
,则结果为word
,否则结果为空字符串
。
在所有情况下,word
可以是任何字符串,包括其他环境变量。
过在变量前添加 \
可以进行转义:例如,\$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
(当与上面支持的指令之一结合使用时)
环境变量替换将为整个指令中的每个变量使用相同的值。换句话说,在这个例子中:
ENV abc=hello ENV abc=bye def=$abc ENV ghi=$abc
将导致 def
的值为 hello
,而不是 bye
。但是,ghi
的值为 bye
,因为它不是将 abc
设置为 bye
那一行指令的一部分。
.dockerignore
文件
在 docker CLI
将上下文发送到 docker
守护进程之前,它会在上下文的根目录中查找名为.dockerignore
的文件。
如果文件存在, CLI
会修改上下文以排除与其中的模式相匹配的文件和目录。这有助于避免不必要地将大型或敏感文件和目录发送到守护程序,
并可能使用ADD
或COPY
指令将它们添加到镜像中。
CLI
将.dockerignore
文件解释为以换行符分隔的模式列表,类似于 Unix shell
的文件 glob
。
为了匹配的目的,上下文的根目录被同时视为工作目录和根目录。
例如,模式 /foo/bar
和 foo/bar
都排除 PATH
的 foo
子目录或位于 URL
的 git
存储库根目录中名为 bar
的文件或目录。
此外两者都不排除其他任何东西。
如果.dockerignore
文件中的一行以 第 1 列
中的#
开头,则该行将被视为注释,并在由 CLI
解释之前被忽略。
# comment */temp* */*/temp* temp?
该文件会导致以下构建行为:
规则 | 行为 |
---|---|
# comment |
忽略 |
*/temp* |
排除根目录的任何直接子目录中名称以 temp 开头的文件和目录。例如,纯文件 /somedir/temporary.txt 被排除,目录 /somedir/temp 也被排除。 |
*/*/temp* |
从根以下两级的任何子目录中排除以 temp 开头的文件和目录。例如,/somedir/subdir/temporary.txt 被排除。 |
temp? |
排除根目录中名称为 temp 的单字符扩展名的文件和目录。例如,/tempa 和 /tempb 被排除。 |
匹配是使用 Go
的 filepath.Match 规则完成的。预处理步骤会删除前导和尾随空格并使用 Go
的 filepath.Clean 消除 .
和 ..
元素。
预处理后的空白行将被忽略。
除了 Go
的 filepath.Match 规则之外,Docker
还支持特殊的通配符字符串 **
,它可以匹配任意数量的目录(包括零)。例如, **/*.go
将排除在所有目录(包括构建上下文的根目录)中找到的所有以 .go
结尾的文件。
以!
开头的行(感叹号)可用于对排除进行例外处理。以下是使用此机制的.dockerignore
文件示例:
*.md !README.md
除 README.md
之外,上下文目录下的所有 Markdown
文件均从上下文中排除。请注意,子目录下的 markdown
文件仍然包含在内。
例外规则!
位置的影响行为:.dockerignore
中与特定文件匹配的最后一行是确定要包含还是排除该文件。考虑以下示例:
*.md !README*.md README-secret.md
所有以 .md
结尾的文件排除,以 README
开头 .md
结尾的文件保留,但是 README-secret.md
文件排除。
考虑以下的例子:
*.md README-secret.md !README*.md
所有以 README
开头 .md
结尾的文件都被保留,中间那行没有任何效果,因为 !README*.md
与 README-secret.md
匹配并且位于最后。所以README-secret.md
依旧会被保留。
您甚至可以使用.dockerignore
文件来排除 Dockerfile
和 .dockerignore
文件。这些文件仍然会发送到守护进程,
因为它需要它们来完成其工作。但 ADD
和 COPY
指令不会将它们复制到镜像中。
最后,您可能想要指定要在上下文中包含哪些文件,而不是要排除哪些文件。要实现此目的,请将*
指定为第一个模式,后跟一个或多个!
的例外模式。
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
指令都会清除先前指令创建的任何状态。- (可选)可以通过将
AS
添加到FROM
指令来为新的构建阶段自定义名称。该名称可以在后续的FROM
和COPY --from=<name>
指令中使用,以引用此阶段构建的镜像。 tag
(标签)或digest
(摘要)值是可选的。如果省略其中任何一个,构建器默认采用最新tag
。如果构建器找不到tag
,则会返回错误。
如果FROM
引用多平台镜像,可选的--platform
标志可用于指定镜像的平台。例如, linux/amd64
、 linux/arm64
或 windows/amd64
。默认情况下,使用构建请求所在主机的目标平台。全局构建参数可以在此标志的值中使用,例如自动平台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
有 2 种形式:
RUN <command>
(shellform,命令在shell中运行,默认情况下在Linux
上为/bin/sh -c
或在Windows
上为cmd /S /C
)RUN ["executable", "param1", "param2"]
(execform)
RUN
指令将在当前镜像之上的新层中执行任何命令并提交结果。生成的提交镜像将用于 Dockerfile
中的下一步。
分层RUN
指令和生成提交符合 Docker
的核心概念,其中提交成本低廉,并且可以从镜像历史记录中的任何点创建容器,就像源代码控制一样。
execform 可以避免shell字符串修改,并使用不包含指定shell可执行文件的基本镜像来运行命令。
可以使用SHELL
命令更改 shellform 的默认shell。
在 shellform 中,您可以使用\
(反斜杠)将单个RUN
指令继续到下一行。例如,考虑以下两行:
RUN /bin/bash -c 'source $HOME/.bashrc && \ echo $HOME'
它们加在一起相当于这一行:
RUN /bin/bash -c 'source $HOME/.bashrc && echo $HOME'
要使用除 /bin/sh
之外的其他shell,请使用 execform 传入所需的shell。例如:
RUN ["/bin/bash", "-c", "echo hello"]
与 shellform 不同,execform 不调用命令shell。这意味着不会发生正常的shell处理。例如,RUN [ "echo", "$HOME" ]
不会对$HOME
进行变量替换。
如果您想要shell处理,则可以使用 shellform 或直接执行shell ,例如:RUN [ "sh", "-c", "echo $HOME" ]
。
当使用 execform 并直接执行shell时,就像 shellform 形式一样,是shell进行环境变量扩展,而不是docker。
RUN
指令的缓存在下一次构建期间不会自动失效。RUN apt-get dist-upgrade -y
等指令的缓存将在下一次构建期间重用。
可以使用 --no-cache
标志使RUN
指令的缓存失效,例如docker build --no-cache
。
有关更多信息,请参阅 Dockerfile 最佳实践指南。
ADD
和COPY
指令可以使RUN
指令的缓存失效。
RUN --mount
RUN --mount
允许您创建构建可以访问的文件系统挂载。这可以用于:
- 与主机文件系统或其他构建阶段之间创建绑定挂载
- 访问构建密钥或ssh-agent套接字
- 使用持久的包管理缓存来加快构建速度
语法:--mount=[type=<TYPE>][,option=<value>[,option=<value>]...]
Mount types
类型 | 描述 |
---|---|
bind (默认) |
绑定挂载上下文目录(只读)。 |
cache |
挂载临时目录以缓存编译器和包管理器的目录。 |
secret |
允许构建容器访问安全文件(例如私钥),而无需将其拷贝到镜像中。 |
ssh |
允许构建容器通过 SSH 代理访问 SSH 密钥,并支持密钥密码。 |
RUN --mount=type=bind
此挂载类型允许将文件或目录绑定到构建容器。默认情况下,bind
挂载是只读的。
选项 | 描述 |
---|---|
target
[1]
|
挂载路径 |
source |
from 中的源路径,默认为 from 的根 |
from |
构建阶段或者源根的镜像名称,默认为构建上下文。 |
rw ,readwrite |
允许在挂载上写入。写入的数据将被丢弃。 |
RUN --mount=type=cache
此挂载类型允许构建容器缓存编译器和包管理器的目录。
选项 | 描述 |
---|---|
id |
用于识别单独/不同缓存的可选 ID。默认为目标值。 |
target
[1]
|
挂载路径。 |
ro ,readonly |
如果设置则为只读。 |
sharing |
shared 、private 或locked 之一。默认为shared。shared 缓存挂载可由多个写入器同时使用。private 会创建一个新的挂载。locked 会暂停第二个写入器,直到第一个写入器释放挂载。 |
from |
构建阶段用作缓存挂载的基础。默认为空目录。 |
source |
from 到 mount 中的子路径。默认为 from 的根。 |
mode |
新缓存目录的文件权限(八进制)。默认 0755 。 |
uid |
新缓存目录的用户 ID。默认0 。 |
gid |
新缓存目录的组 ID。默认0 。 |
缓存目录的内容在构建请求期间保留,不会使指令缓存失效。缓存挂载只能用于获得更好的性能。您的构建应该处理缓存目录中的任何内容,因为如果需要更多的存储空间, 另一个构建可能会覆盖文件,或者 GC 可能会清理文件。
Example: cache Go packages
# syntax=docker/dockerfile:1 FROM golang RUN --mount=type=cache,target=/root/.cache/go-build \ go build ...
Example: cache apt packages
# 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。默认为目标路径的 basename 值。 |
target
[1]
|
挂载路径。默认为 /run/secrets/ +id |
required |
如果设置true,当密钥不可用时,指令会出错。默认为false |
mode |
密钥的文件权限(八进制)。默认 0400 。 |
uid |
密钥文件的用户 ID。默认 0 。 |
gid |
密钥文件的组 ID。默认 0 。 |
Example: access to 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
[1]
|
SSH 代理套接字的路径。默认为 /run/buildkit/ssh_agent.${N} |
required |
如果设置true,当密钥不可用时,指令会出错。默认为false |
mode |
套接字的文件权限(八进制)。默认 0600 。 |
uid |
套接字的用户 ID。默认 0 。 |
gid |
套接字的组 ID。默认 0 。 |
Example: access to 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
允许控制命令在哪个网络环境中运行
语法:--network=<TYPE>
Network types
选项 | 描述 |
---|---|
default (default) |
默认网络。 |
none |
在没有网络的情况下运行 |
host |
在主机的网络环境中运行。 |
RUN --network=default
默认选项,该命令在构建的默认网络中运行。
RUN --network=none
该命令在没有网络的情况下运行(lo
仍然可用,但与此进程隔离)
Example: isolating external effects
# 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 config 启动 buildkitd
守护进程时需要启用该权限,以及一个具有–allow network.host
参数 的构建请求。
RUN --security
docker/dockerfile:1-labs
版本。
RUN --security=insecure
使用--security=insecure
,构建器在没有沙箱的非安全模式下运行命令,这允许运行需要提升权限的构建流程(例如 containerd)。
相当于运行docker run --privileged
。
–allow-insecure-entitlement security.insecure
标志启动 buildkitd
守护程序或在 buildkitd config 中启用权限 security.insecure
,以及使用–allow security.insecure
flag 构建请求。
Example: check entitlements
# syntax=docker/dockerfile:1-labs FROM ubuntu RUN --security=insecure cat /proc/self/status | grep CapEff
#84 0.093 CapEff: 0000003fffffffff
RUN --security=sandbox
默认沙箱模式可以通过--security=sandbox
激活,但这是无操作的。
CMD
CMD
指令有三种形式:
CMD ["executable","param1","param2"]
(exec 形式,这是首选形式)CMD ["param1","param2"]
(作为ENTRYPOINT
的默认参数)CMD command param1 param2
(shell 形式)
一个 Dockerfile
中只能有一条CMD
指令。如果您列出多个CMD
,则只有最后一个CMD
才会生效。
CMD
的主要目的是为执行容器提供默认值。这些默认值可以包含可执行文件,也可以省略可执行文件,在这种情况下,您还必须指定ENTRYPOINT
指令。
果使用CMD
为ENTRYPOINT
指令提供默认参数,则CMD
和ENTRYPOINT
指令都应使用 JSON 数组格式指定。
"
),而不是单引号('
)。
与 shellform 不同,execform 不调用命令 shell。这意味着不会发生正常的 shell 处理。例如,CMD [ "echo", "$HOME" ]
不会对
$HOME
进行变量替换。如果您想要 shell 处理,则可以使用 shellform 或直接执行 shell,例如:CMD [ "sh", "-c", "echo $HOME" ]
。当使用 execfrom 并直接执行 shell 时,就像 shell 形式一样,是 shell 进行环境变量扩展,而不是 docker。
当以 shell
或 exec
格式使用时,CMD
指令设置运行镜像时要执行的命令。
如果您使用CMD
的 shell
形式,则<command>
将在 /bin/sh -c
中执行:
FROM ubuntu CMD echo "This is a test." | wc -
如果您想在没有 shell
的情况下运行<command>
,则必须将命令表示为 JSON 数组,并提供可执行文件的完整路径。
这种数组形式是CMD
的首选格式。任何附加参数必须在数组中单独表示为字符串:
FROM ubuntu CMD ["/usr/bin/wc","--help"]
如果您希望容器每次都运行相同的可执行文件,那么您应该考虑将ENTRYPOINT
与CMD
结合使用。 参阅 ENTRYPOINT
如果用户为docker run
指定参数,那么它们将覆盖CMD
中指定的默认值。
RUN
与CMD
混淆。RUN
实际上运行一个命令并提交结果;CMD
在构建时不执行任何操作,但指定镜像的预期命令。
LABEL
LABEL <key>=<value> <key>=<value> <key>=<value> ...
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 (deprecated)
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 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>
。该值将存在于构建阶段中所有后续指令的环境中,并且也可以在许多指令中 行内替换 。该值将被解释为其他环境变量,因此如果不转义引号字符将被删除。与命令行解析一样,引号和反斜杠可用于在值中包含空格。
Example:
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
指令中设置多个环境变量,并且可能会造成混乱。
例如,以下设置值为"TWO= THREE=world"
的单个环境变量 (ONE
):
ENV ONE TWO= THREE=world
支持替代语法是为了向后兼容,但由于上述原因不鼓励使用,并且可能会在将来的版本中删除。
ADD
ADD
指令有2种形式
ADD [--chown=<user>:<group>] [--chmod=<perms>] [--checksum=<checksum>] <src>... <dest>
ADD [--chown=<user>:<group>] [--chmod=<perms>] ["<src>",... "<dest>"]
包含空格的路径需要用后一种形式。
–chown
和 –chmod
功能仅在用于构建 Linux
容器的 Dockerfile
上受支持,不适用于 Windows
容器。
由于用户和组所有权概念在 Linux 和 Windows 之间无法转换,因此使用 /etc/passwd
和 /etc/group
将用户和组名称转换为 ID
限制了此功能仅适用于基于 Linux
操作系统的容器。
–chmod
自 Dockerfile 1.3 起受支持。目前仅支持八进制表示法。非八进制支持在 moby/buildkit#1951 中进行跟踪。
ADD
指令从<src>
复制新文件、目录或远程文件 URLs
,并将它们添加到路径<dst>
的镜像文件系统中。
可以指定多个<src>
资源,但如果它们是文件或目录,则它们的路径将被解释为相对于构建上下文的源地址。
每个<src>
可能包含通配符,并且将使用 Go
的 filepath.Match 规则进行匹配。例如:
添加以 home
开头的所有文件:
ADD hom* /mydir/
在下面的示例中,?
被替换为任何单个字符,例如 home.txt
。
ADD hom?.txt /mydir/
<dst>
是绝对路径或相对于 的路径,源将被复制到目标容器内。
下面的示例使用相对路径,并将 test.txt
添加到 <WORKDIR>/relativeDir/
:
ADD test.txt relativeDir/
然而本示例使用绝对路径,并将 test.txt
添加到 /absoluteDir/
:
ADD test.txt /absoluteDir/
添加包含特殊字符(例如[
和]
)的文件或目录时,需要遵循 Golang
规则对这些路径进行转义,以防止它们被视为匹配模式。
例如,要添加名为 arr[0].txt
的文件,请使用以下命令:
ADD arr[[]0].txt /mydir/
所有新文件和目录都是使用 UID
和 GID
为 0 创建的,除非可选的--chown
标志指定给定的用户名、组名或 UID/GID
组合来请求所添加内容的特定所有权。
--chown
标志的格式允许用户名和组名是字符串形式或直接整数 UID
和 GID
的任意组合。提供不带组名的用户名或不带 GID
的 UID
将使用与 UID
相同数字的 GID
。如果提供了用户名或组名,则容器的根文件系统/etc/passwd
和/etc/group
文件将分别用于执行从名称到整数 UID
或 GID
的转换。
以下示例显示了--chown
标志的有效定义:
ADD --chown=55:mygroup files* /somedir/ ADD --chown=bin files* /somedir/ ADD --chown=1 files* /somedir/ ADD --chown=10:11 files* /somedir/ ADD --chown=myuser:mygroup --chmod=655 files* /somedir/
如果容器根文件系统不包含/etc/passwd
或/etc/group
文件,并且在--chown
标志中使用用户名或组名,则ADD
操作构建将失败。使用数字 ID
不需要查找,并且不依赖于容器根文件系统内容。
<src>
是远程文件 URL
的情况下,目标将具有 600 的权限。如果正在检索的远程文件具有 HTTP
Last-Modified
头,则该头中的时间戳将用于设置目标文件的mtime
。但是与ADD
期间处理的任何其他文件一样,mtime
不会被包含在文件是否改变以及缓存是否应该更新的判断中。
STDIN
传递 Dockerfile
来构建(docker build - < somefile
),则没有构建上下文,因此 Dockerfile
只能包含基于 URL
的ADD
指令。您还可以通过 STDIN
传递压缩存档:(docker build - < archive.tar.gz
),存档根目录下的 Dockerfile
和存档的其余部分将用作构建的上下文。
如果您的 URL
文件使用身份验证进行保护,则需要使用RUN wget
、RUN curl
或使用容器内的其他工具,因为ADD
指令不支持身份验证。
<src>
的内容已更改,则第一个遇到的ADD
指令将使 Dockerfile
中所有后续指令的缓存无效。
这包括使RUN
指令的缓存无效。有关更多信息,请参阅 Dockerfile
最佳实践指南 – 利用构建缓存
ADD
遵守以下规则:
<src>
路径必须位于构建的上下文中;你不能ADD ../something /something
,因为 docker 构建的第一步是将上下文目录(和子目录)发送到 docker 守护进程。- 如果
<src>
是URL
并且<dest>
不以尾部斜杠/
结尾,则将从该URL
下载文件并将其复制到<dest>
。 - 如果
<src>
是URL
并且<dest>
以尾部斜杠/
结尾,则从URL
推断文件名并将文件下载到<dest>/<filename>
。 比如说,ADD http://example.com/foobar /
将创建文件/foobar
。URL
必须具有资源路径,以便在这种情况下可以发现适当的文件名(http://example.com
将不起作用)。 - 如果
<src>
是目录,则复制该目录的全部内容,包括文件系统元数据。
-
如果
<src>
是可识别压缩格式(identity
、gzip
、bzip2
或xz
)的本地tar
归档,则它将解压为目录。 来自远程URL
的资源不会被解压缩。当复制或解压目录时,它的行为与tar -x
相同,结果是以下各项的并集:- 不管目的地路径上有什么
- 源树的内容,在逐个文件的基础上,冲突以“2”的方式得到解决。
注意:文件是否被标识为可识别的压缩格式完全基于文件的内容,而不是文件的名称。例如,如果一个空文件恰好以 .tar.gz
结尾,则该文件不会被识别为压缩文件,也不会生成任何类型的解压缩错误消息,而该文件将简单地复制到目标。 -
如果
<src>
是任何其他类型的文件,它将与其元数据一起单独复制。在这种情况下,如果<dest>
以尾部斜杠/
结尾,则它将被视为目录,并且<src>
的内容将写入<dest>/base(<src>)
。 -
如果直接或通过使用通配符指定了多个
<src>
资源,则<dest>
必须是目录,并且必须以斜杠/
结尾。 -
如果
<dest>
不以斜杠/
结尾,则它将被视为常规文件,并且<src>
的内容将写入<dest>
。 -
如果
<dest>
不存在,则会与其路径中所有缺失的目录一起创建。
验证远程文件的 checksum
可以使用--checksum
标志验证远程文件的 checksum
ADD --checksum=<checksum> <http src> <dest>
ADD --checksum=sha256:24454f830cdb571e2c4ad15481119c43b3cafd48dd869a9b2945d1036d1dc68d https://mirrors.edge.kernel.org/pub/linux/kernel/Historic/linux-0.01.tar.gz /
–checksum
标志当前仅支持 HTTP
源。
添加 git
存储库
这种形式允许直接将 git
存储库添加到镜像中,而无需在镜像中使用 git
命令:
ADD [--keep-git-dir=<boolean>] <git ref> <dir>
# syntax=docker/dockerfile:1 FROM alpine ADD --keep-git-dir=true https://github.com/moby/buildkit.git#v0.10.1 /buildkit
--keep-git-dir=true
标志添加.git
目录。该标志默认为
false
。
添加 git 私有存储库
要通过 SSH
添加私有存储库,请使用以下形式创建 Dockerfile
:
# syntax=docker/dockerfile:1 FROM alpine ADD [email protected]:foo/bar.git /bar
该 Dockerfile
可以使用 docker build --ssh
或 buildctl build --ssh
构建,例如:
docker build --ssh default
buildctl build --frontend=dockerfile.v0 --local context=. --local dockerfile=. --ssh default
ADD --link
COPY
COPY
有两种形式:
COPY [--chown=<user>:<group>] [--chmod=<perms>] <src>... <dest>
COPY [--chown=<user>:<group>] [--chmod=<perms>] ["<src>",... "<dest>"]
包含空格的路径需要后一种形式
–chown
和–chmod
功能仅在用于构建 Linux
容器的 Dockerfile
上受支持,不适用于 Windows
容器。
由于用户和组所有权概念在 Linux
和 Windows
之间无法转换,因此使用/etc/passwd
和/etc/group
将用户和组名称转换为 ID
限制了此功能仅适用于基于 Linux
操作系统的容器。
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/
所有新文件和目录都是使用 UID
和 GID
为 0 创建的,除非可选的--chown
标志指定给定的用户名、组名或 UID/GID
组合来请求复制内容的特定所有权。
--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
不需要查找,也不依赖于容器根文件系统内容。
STDIN
(docker build - < somefile)
构建,则没有构建上下文,因此无法使用COPY
。
COPY
可以选择接受一个标志--from=<name>
,该标志可用于将源位置设置为先前的构建阶段(使用FROM .. AS <name>
创建),该阶段将用来代替用户发送的构建上下文。如果无法找到具有指定名称的构建阶段,则会尝试使用具有相同名称的镜像。
COPY
遵循以下规则:
<src>
路径必须位于构建的上下文中;你不能COPY ../something /something
,因为 docker 构建的第一步是将上下文目录(和子目录)发送到 docker 守护进程。- 如果
<src>
是目录,则复制该目录的全部内容,包括文件系统元数据。
- 如果
<src>
是任何其他类型的文件,它将与其元数据一起单独复制。在这种情况下,如果<dest>
以尾部斜杠/
结尾,则它将被视为目录,并且<src>
的内容将写入<dest>/base(<src>)
。 - 如果直接或通过使用通配符指定了多个
<src>
资源,则<dest>
必须是目录,并且必须以斜杠/
结尾。 - 如果
<dest>
不以斜杠/
结尾,则它将被视为常规文件,并且<src>
的内容将写入<dest>
。 - 如果
<dest>
不存在,则会与其路径中所有缺失的目录一起创建。
<src>
的内容已更改,则第一个遇到的COPY
指令将使 Dockerfile 中所有后续指令的缓存无效。
这包括使RUN
指令的缓存无效。有关更多信息,请参阅 Dockerfile 最佳实践指南 – 利用构建缓存 。
COPY --link
在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
的性能相当于或优于默认行为,并且它为缓存重用创造了更好的条件。
ENTRYPOINT
ENTRYPOINT
有两种形式
exec 形式,这是首选形式:
ENTRYPOINT ["executable", "param1", "param2"]
shell 形式:
ENTRYPOINT command param1 param2
ENTRYPOINT
允许您配置将来作为可执行文件运行的容器。
例如,以下命令使用默认内容启动 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
指令。
shell 形式防止任何CMD
或run
命令行参数被使用,但缺点是您的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 stop
检查容器的进程,然后要求脚本停止 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
设置,但这只能将二进制文件设置为 exec(不会使用sh -c
)。
"
),而不是单引号 ('
)。
与 shell 形式不同,exec 形式不调用命令 shell。这意味着不会发生正常的 shell 处理。
例如,ENTRYPOINT [ "echo", "$HOME" ]
不会对
$HOME
进行变量替换。
如果您想要 shell 处理,则可以使用 shell 形式或直接执行 shell,例如:ENTRYPOINT [ "sh", "-c", "echo $HOME" ]
。
当使用 exec 形式并直接执行 shell 时,就像 shell 形式一样,是 shell 进行环境变量扩展,而不是 docker。
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
如果您忘记将 exec 添加到ENTRYPOINT
的开头:
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
组合执行的命令:
no ENTRYPOINT | ENTRYPOINT exec_entry p1_entry | ENTRYPOINT [“exec_entry”, “p1_entry”] | |
---|---|---|---|
no CMD | error, not allowed | /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 客户端的安装说明,请参阅 通过Volumes共享目录 文档。
docker run
命令使用基础镜像中指定位置处存在的任何数据来初始化新创建的卷。例如,考虑以下 Dockerfile 片段:
FROM ubuntu RUN mkdir /myvol RUN echo "hello world" > /myvol/greeting VOLUME /myvol
此 Dockerfile 会生成一个镜像,该镜像会导致docker run
在/myvol
处创建一个新的挂载点,并将greeting
文件复制到新创建的卷中。
有关指定 volumes
的注意事项
关于 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
上,如果用户不是内置帐户,则必须首先创建用户。这可以通过作为 Dockerfile
一部分调用的net user
命令来完成。
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
如果不指定,默认工作目录为/
。实际上,如果您不是从 scratch (FROM scratch
)开始构建 Dockerfile,那么WORKDIR
可能是由您正在使用的基础镜像设置的。
因此,为了避免在未知目录中进行意外操作,最佳做法是显式设置WORKDIR
。
ARG
ARG <name>[=<default value>]
ARG
指令定义了一个变量,用户可以使用--build-arg <varname>=<value>
标志在构建时通过docker build
命令将其传递给构建器。如果用户指定了 Dockerfile 中未定义的构建参数,则构建会输出警告。
[Warning] One or more build-args [foo] were not consumed.
Dockerfile 可能包含一个或多个ARG
指令。例如,以下是有效的 Dockerfile:
FROM busybox
ARG user1
ARG buildno
# ...
GitHub
密钥、用户凭据等机密。使用docker history
命令,镜像的任何用户都可以看到构建时的变量值。
请参阅RUN –mount=type=secret
部分,了解构建镜像时使用密钥的安全方法。
默认值
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
,因为用户名变量是在后续第 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
,因为它是ENV
指令在第 3 行中设置的默认值。
本示例中的变量扩展技术允许您从命令行传递参数,并利用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
变量中敏感身份验证信息的风险。
例如,考虑使用以下命令构建以下 Dockerfile:
--build-arg HTTP_PROXY=http://user:[email protected]
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 后端时可用。
Docker 预定义了一组ARG
变量,其中包含有关执行构建的节点平台(构建平台)和生成镜像的平台(目标平台)的信息。可以使用docker build
上的--platform
标志指定目标平台。
自动设置以下ARG
变量:
变量 | 描述 |
---|---|
TARGETPLATFORM
|
构建目标平台 eg: 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
|
String | 设置可选的缓存 ID 命名空间。 |
BUILDKIT_CONTEXT_KEEP_GIT_DIR
|
Bool | 触发 git context 以保留 .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 起受支持 。 |
Example: 保留 .git
目录
使用 Git 上下文时,.git
目录不会保留在git checkouts
中。如果您想在构建过程中检索 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
指令不能触发FROM
或MAINTAINER
指令。
STOPSIGNAL
STOPSIGNAL signal
STOPSIGNAL
指令设置将给容器发送退出的系统调用信号。该信号可以是格式为SIG<NAME>
的信号名称,例如SIGKILL
,也可以是与内核系统调用表中的位置匹配的无符号数字,例如9
。如果未定义,则默认值为SIGTERM
。
可以使用docker run
和docker create
上的--stop-signal
标志覆盖每个容器镜像的默认stopsignal
。
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 秒数,则认为检查失败。
需要重试,即健康检查连续失败才能将容器视为unhealthy
。
start period 为需要时间引导的容器提供初始化时间。在此期间的探测失败将不计入最大重试次数。 但是,如果在启动期间健康检查成功,则认为容器已启动,并且所有连续失败都将计入最大重试次数。
start interval 是启动过程中健康检查之间的时间间隔。
一个 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}
SHELL ["executable", "parameters"]
SHELL
指令允许覆盖用于 shellform 命令的默认 shell。Linux 上的默认 shell 是["/bin/sh", "-c"]
,Windows 上的默认 shell 是["cmd", "/S", "/C"]
。SHELL
指令必须以 JSON 形式写入 Dockerfile 中。
SHELL
指令在 Windows
上特别有用,其中有两种常用且截然不同的本机 shell:cmd
和powershell
,以及可用的备用 shell(包括sh
)。
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 中使用 shellform 时,以下指令可能会受到SHELL
指令的影响:RUN
、CMD
和ENTRYPOINT
。
以下示例是 Windows 上的常见模式,可以使用SHELL
指令对其进行简化:
RUN powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"
docker 调用的命令将是:
cmd /S /C powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"
这种方法效率低下有两个原因。首先,调用了一个不必要的 cmd.exe 命令处理器(又名 shell)。其次,shellform 中的每个RUN
指令都需要一个额外的powershell
- 命令作为命令前缀。
为了提高效率,可以采用两种机制之一。一种是使用RUN
命令的 JSON 形式,例如:
RUN ["powershell", "-command", "Execute-MyCmdlet", "-param1 \"c:\\foo.txt\""]
虽然 JSON 形式是明确的,并且不使用不必要的 cmd.exe,但它确实需要通过双引号和转义更加冗长。
另一种机制是使用SHELL
指令和 shellform ,为 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
docker/dockerfile:1.4
Here-documents 允许将后续 Dockerfile 行重定向到RUN
或COPY
命令的输入。如果此类命令包含 here-document ,则 Dockerfile 会考虑下一行,直到该行仅包含 here-doc 分隔符作为同一命令的一部分。
Example: 运行多行脚本
# 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
Example: 创建内联文件
在COPY
命令中,源参数可以替换为 here-doc 指示符。使用常规的 here-doc 变量扩展和制表符剥离规则 。
# syntax=docker/dockerfile:1 FROM alpine ARG FOO=bar COPY <<-EOT /app/foo hello ${FOO} EOT
# syntax=docker/dockerfile:1 FROM alpine COPY <<-"EOT" /app/script.sh echo hello ${FOO} EOT RUN FOO=abc ash /app/script.sh
Dockerfile
示例
有关 Dockerfile 的示例,请参阅:
脚注: