Docker可以通过阅读a的指示自动构建图像Dockerfile
。Dockerfile
是一个文本文档,其中包含用户可以在命令行上调用以组装图像的所有命令。使用docker build
用户可以创建一个自动构建,连续执行几条命令行指令。
该页面描述了您可以在Dockerfile中使用的命令。阅读本页后,请参阅Dockerfile
面向提示的指南的最佳做法。
用法
docker build命令从Dockerfile和上下文构建一个映像。 构建的上下文是指定位置PATH或URL中的文件集。 PATH是本地文件系统上的一个目录。 该URL是一个Git存储库位置。
上下文是递归处理的。因此, PATH
包含任何子目录,且URL
包含存储库及其子模块。此示例显示了一个使用当前目录作为上下文的构建命令:
$ docker build .Sending build context to Docker daemon 6.51 MB...
构建由Docker守护进程运行,而不是通过CLI运行。构建过程所做的第一件事是将整个上下文(递归地)发送到守护进程。在大多数情况下,最好以空目录作为上下文开始,并将Dockerfile保存在该目录中。仅添加构建Dockerfile所需的文件。
警告:不要用你的根目录下,
/
作为PATH
因为它会导致生成到您的硬盘驱动器的全部内容传输到码头工人守护进程。
要在构建上下文中使用文件,Dockerfile引用指令中指定的文件,例如COPY指令。 要增加构建的性能,请通过将.dockerignore文件添加到上下文目录来排除文件和目录。 有关如何创建.dockerignore文件的信息,请参阅此页面上的文档。
传统上,Dockerfile
被称为Dockerfile
并位于上下文的根部。您可以使用该-f
标志docker build
指向文件系统中任何位置的Dockerfile。
$ docker build -f /path/to/a/Dockerfile .
如果构建成功,您可以指定一个存储库和标记来保存新映像:
$ docker build -t shykes/myapp .
要在构建之后将图像标记到多个存储库中,请在-t
运行此build
命令时添加多个参数:
$ docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .
在Docker守护进程运行Dockerfile中的指令之前,它会执行Dockerfile的初步验证,并在语法不正确时返回错误:
$ docker build -t test/myapp .Sending build context to Docker daemon 2.048 kB Error response from daemon: Unknown instruction: RUNCMD
Docker守护进程会Dockerfile
逐个运行指令,在必要时将每条指令的结果提交给新映像,最终输出新映像的标识。Docker守护进程将自动清理您发送的上下文。
请注意,每条指令都是独立运行的,并会创建一个新图像 - 所以RUN cd /tmp
不会对下一条指令产生任何影响。
只要有可能,Docker将重新使用中间映像(缓存),以docker build
显着加速该过程。这由Using cache
控制台输出中的消息指示。(有关更多信息,请参阅Dockerfile
最佳做法指南中的“构建缓存”部分):
$ docker build -t svendowideit/ambassador .Sending build context to Docker daemon 15.36 kB Step 1/4 : FROM alpine:3.2 ---> 31f630c65071 Step 2/4 : MAINTAINER SvenDowideit@home.org.au ---> Using cache ---> 2a1c91448f5f Step 3/4 : RUN apk update && apk add socat && rm -r /var/cache/ ---> Using cache ---> 21ed6e7fbb73 Step 4/4 : CMD env | grep _TCP= | (sed 's/.*_PORT_([0-9]*)_TCP=tcp://(.*):(.*)/socat -t 100000000 TCP4-LISTEN:1,fork,reuseaddr TCP4:2:3 &/' && echo wait) | sh ---> Using cache ---> 7ea8aef582cc Successfully built 7ea8aef582cc
构建缓存仅用于具有本地父链的图像。这意味着这些图像是由以前的版本创建的,或者是整个图像链加载的docker load
。如果您希望使用特定映像的构建缓存,则可以使用--cache-from
选项指定它。指定的图像--cache-from
不需要有父链,可以从其他注册表中提取。
当你完成你的构建时,你已经准备好考虑将存储库推送到它的注册表。
格式
以下是Dockerfile
格式:
# Comment INSTRUCTION arguments
该指令不区分大小写。然而,约定是为了更容易将它们与争论区分开来。
Docker按顺序在Dockerfile中运行指令。 Dockerfile必须以FROM指令开始。 FROM指令指定您正在构建的基本映像。 FROM只能在一个或多个ARG指令之后,这些指令声明在Dockerfile的FROM行中使用的参数。
Docker treats 把该行开始与#
一个评论,除非该行是一个合法的解析器指令。#
一行中其他任何地方的标记都被视为参数。这允许像这样的语句:
# Comment RUN echo 'we are running some # of cool things'
注释中不支持换行符。
解析器指令
解析器指令是可选的,并影响处理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
以下解析器指令受支持:
-
escape
转义
# escape= (backslash)
或是
# escape=` (backtick)
该escape
指令设置用于转义字符的字符Dockerfile
。如果未指定,则默认转义字符为。
转义字符既用于转义一行中的字符,又用于转义换行符。这允许Dockerfile
指令跨越多行。请注意,无论escape
解析器指令是否包含在a中Dockerfile
,转义都不在 RUN
命令中执行,除了在行的末尾。
将转义字符设置`
为特别有用Windows
,其中是目录路径分隔符。
`
与Windows PowerShell一致。
考虑下面的例子,它会以非显而易见的方式失败Windows
。第二,在第二行的端部将被解释为用于换行的逃逸,而不是从第一逸出的目标
。同样,
假设它实际上是作为一条指令处理的,那么在第三行的末尾会导致它被视为连续行。这个dockerfile的结果是第二行和第三行被认为是一条单独的指令:
FROM microsoft/nanoserver COPY testfile.txt c:\ RUN dir c:
结果是:
PS C:John> 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 C:John>
上述的一个解决方案将是/
用作COPY
指令和目标dir
。然而,这种语法充其量是令人困惑的,因为路径并不是自然的Windows
,并且最坏的情况是容易出错,因为并非所有Windows
支持的命令都/
作为路径分隔符。
通过添加转义解析器指令,在Windows上使用自然平台语义对文件路径使用以下Dockerfile可以成功:
# escape=` FROM microsoft/nanoserver COPY testfile.txt c: RUN dir c:
结果是:
PS C:John> 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.txt10/05/2016 02:22 PM <DIR> Program Files10/05/2016 02:14 PM <DIR> Program Files (x86)10/28/2016 11:18 AM 62 testfile.txt10/28/2016 11:20 AM <DIR> Users10/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 C:John>
环境更换
环境变量(用ENV语句声明)也可以在某些指令中用作由Dockerfile解释的变量。 转义也被处理,从字面上将类似于变量的语法包含到语句中。
使用$ variable_name或$ {variable_name}在Dockerfile中记录环境变量。 它们被等价处理,括号语法通常用于解决变量名称中没有空格的问题,如$ {foo} _bar。
该${variable_name}
语法还支持一些标准bash
修饰符,如下所示:
-
$ {variable:-word}表示如果设置了变量,那么结果就是该值。 如果变量没有设置,则字将是结果。
-
$ {variable:+ word}表示如果设置了变量,那么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
(当与上述支持的指令之一结合使用时)
注意:在1.4之前,
ONBUILD
说明不支持环境变量,即使与上面列出的任何指令结合使用。
整个指令中的环境变量替换将对每个变量使用相同的值。换句话说,在这个例子中:
ENV abc=hello ENV abc=bye def=$abc ENV ghi=$abc
将导致def的值为hello,而不是再见。 然而,ghi将具有再见值,因为它不是将abc设置为再见的同一指令的一部分。
.dockerignore文件
在docker CLI将上下文发送到docker守护程序之前,它会在上下文的根目录中查找名为.dockerignore的文件。 如果此文件存在,CLI会修改上下文以排除与其中的模式匹配的文件和目录。 这有助于避免不必要地将大型或敏感文件和目录发送到守护程序,并可能使用ADD或COPY将它们添加到图像。
CLI将.dockerignore
文件解释为与Unix shell文件大小相似的以换行符分隔的模式列表。为了匹配的目的,上下文的根被认为是工作目录和根目录。例如,这些模式/foo/bar
和foo/bar
两者都不包含位于位于的git存储库bar
的foo
子目录PATH
或根目录中指定的文件或目录URL
。两者都没有排除其他任何东西。
如果.dockerignore
文件中的一行以第一#
列开头,则此行被视为注释,并在被CLI解释之前被忽略。
这是一个示例.dockerignore
文件:
# 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
上下文外,所有减价文件均被排除在外。
!
例外规则的放置会影响行为:.dockerignore
匹配特定文件的最后一行确定是否包含或排除。考虑下面的例子:
*.md !README*.md README-secret.md
除README-secret.md以外的README文件除上下文外不包含任何降价文件。
现在考虑这个例子:
*.md README-secret.md !README*.md
所有的README文件都包含在内。 中间行不起作用,因为!README * .md与README-secret.md匹配并且最后一个。
您甚至可以使用.dockerignore文件来排除Dockerfile和.dockerignore文件。 这些文件仍然被发送到守护进程,因为它需要它们来完成它的工作。 但ADD和COPY指令不会将它们复制到图像上。
最后,您可能需要指定要在上下文中包含哪些文件,而不是要排除的文件。要实现这一点,请指定*
第一个模式,然后指定一个或多个!
异常模式。
注意:由于历史原因,该模式.
被忽略。
FROM
FROM <image> [AS <name>]
或者
FROM <image>[:<tag>] [AS <name>]
或者
FROM <image>[@<digest>] [AS <name>]
该FROM
指令初始化一个新的编译阶段并为后续指令设置基本映像。因此,有效的Dockerfile
必须以FROM
指令开始。图像可以是任何有效的图像 - 通过从公共存储库中拉出图像开始特别容易。
-
ARG是Dockerfile中可能位于FROM之前的唯一指令。 请参阅了解ARG和FROM的交互方式。
-
FROM可以在单个Dockerfile中多次出现以创建多个图像或将一个构建阶段用作另一个构建阶段的依赖关系。 只需在每个新的FROM指令之前记录提交输出的最后一个映像ID即可。 每条FROM指令清除以前指令创建的任何状态。
-
通过将AS名称添加到FROM指令,可以给新的生成阶段赋予一个名称。 该名称可用于后续的FROM和COPY --from = <name | index>指令以引用此阶段中构建的映像。
-
标签或摘要值是可选的。 如果您忽略其中之一,则默认情况下,构建器会采用最新的标记。 如果无法找到标记值,构建器将返回错误。
了解ARG和FROM如何交互
FROM
说明支持由ARG
第一个之前发生的任何指令声明的变量FROM
。
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有两种形式:
-
RUN <command>(shell形式,该命令在shell中运行,默认情况下是Linux上的/ bin / sh -c或Windows上的cmd / S / C)
-
RUN [“executable”,“param1”,“param2”](exec形式)
该RUN
指令将在当前图像顶部的新图层中执行任何命令并提交结果。由此产生的提交图像将用于下一步Dockerfile
。
分层RUN
指令和生成提交符合Docker的核心概念,其中提交很便宜,容器可以从图像历史中的任意点创建,就像源代码控制一样。
在EXEC形式使得能够避免壳串改写(munging),且RUN
使用不包含指定壳可执行基本图像命令。
shell表单的默认shell 可以使用该SHELL
命令进行更改。
在shell 形式中,您可以使用(反斜杠)将单个RUN指令继续到下一行。例如,考虑这两行:
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
它们一起等同于这一行:
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
注:要使用不同于'/ bin / sh' 的shell ,请使用传递给所需shell 的exec表单。例如,
RUN ["/bin/bash", "-c", "echo hello"]
注意:exec表单被解析为JSON数组,这意味着您必须在单词(而非单引号('))周围使用双引号(“)。注意:与shell形式不同,exec形式不会调用命令shell。这意味着正常的shell处理不会发生。例如,RUN [ "echo", "$HOME" ]
不会对变量进行替换$HOME
。如果您想要进行shell处理,请使用shell窗体或直接执行shell,例如:RUN [ "sh", "-c", "echo $HOME" ]
。当使用exec表单并直接执行一个shell时(如shell格式的情况),它是在执行环境变量扩展的shell,而不是docker。 注意:在JSON形式中,必须避免反斜杠。这在反斜杠是路径分隔符的Windows上尤其重要。由于不是有效的JSON,以下行将被视为shell形式,并以意外的方式失败:RUN ["c:windowssystem32 asklist.exe"]
此示例的正确语法为:RUN ["c:\windows\system32\tasklist.exe"]
RUN指令缓存在下一次构建期间不会自动失效。 像RUN运行apt-get dist-upgrade -y这样的指令的缓存将在下一次构建时重用。 RUN指令缓存可以通过使用--no-cache标志失效,例如docker build --no-cache。
有关Dockerfile
更多信息,请参阅最佳实践指南。
说明的高速缓存RUN
可能会因ADD
指令而失效。详情请参阅下文。
已知问题(RUN)
-
问题783是关于使用AUFS文件系统时可能发生的文件权限问题。 例如,您可能会在尝试制作文件时注意到它。对于具有最新aufs版本的系统(即可以设置dirperm1安装选项),docker将尝试通过使用dirperm1选项安装图层来自动修复问题。 有关dirperm1选项的更多详细信息,请参阅aufs手册页。如果您的系统不支持dirperm1,则说明问题是解决方法。
CMD
该CMD
指令有三种形式:
-
CMD ["executable","param1","param2"]
(执行形式,这是首选形式) -
CMD ["param1","param2"]
(作为进入点的默认参数) -
CMD command param1 param2
(壳形式)
只能有一条CMD
指令Dockerfile
。如果列出多个,CMD
则只有最后一个CMD
会生效。
CMD的主要目的是为执行容器提供默认值。 这些默认值可以包含可执行文件,或者可以省略可执行文件,在这种情况下,您还必须指定ENTRYPOINT指令。
注:如果
CMD
用于为ENTRYPOINT
指令提供缺省参数,则应该使用JSON数组格式指定CMD
和ENTRYPOINT
指令。注意:exec表单被解析为JSON数组,这意味着您必须在单词(而非单引号('))周围使用双引号(“)。注意:与shell形式不同,exec形式不会调用命令shell。这意味着正常的shell处理不会发生。例如,CMD [ "echo", "$HOME" ]
不会对变量进行替换$HOME
。如果您想要进行shell处理,请使用shell窗体或直接执行shell,例如:CMD [ "sh", "-c", "echo $HOME" ]
。当使用exec表单并直接执行一个shell时(如shell格式的情况),它是在执行环境变量扩展的shell,而不是docker。
当以shell或exec格式使用时,CMD
指令设置运行映像时要执行的命令。
如果你使用的是shell的形式CMD
,那么<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
。见入口点。
如果用户指定码头运行的参数,那么它们将覆盖CMD中指定的默认值。
注意:不要将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建议尽可能将标签结合到单个LABEL指令中。 每个LABEL指令都会产生一个新图层,如果您使用多个标签,则会导致图像效率低下。 这个例子导致一个图像层。
LABEL multi.label1="value1" multi.label2="value2" other="value3"
以上内容也可以写成:
LABEL multi.label1="value1" multi.label2="value2" other="value3"
标签是添加的,包括图像中的LABEL
s FROM
。如果Docker遇到已存在的标签/键,则新值将覆盖任何以前具有相同键的标签。
要查看图像的标签,请使用该docker inspect
命令。
"Labels": { "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 <name>
MAINTAINER指令设置生成图像的Author字段。 LABEL指令是一个非常灵活的版本,您应该使用它,因为它可以设置您需要的任何元数据,并且可以很容易地查看,例如使用docker检查。 要设置与MAINTAINER字段对应的标签,您可以使用:
LABEL maintainer="SvenDowideit@home.org.au"
这将从docker inspect
其他标签中可见。
EXPOSE
EXPOSE <port> [<port>...]
EXPOSE指令通知Docker,该容器在运行时侦听指定的网络端口。 EXPOSE不会使主机可以访问容器的端口。 要做到这一点,您必须使用-p标志来发布一系列端口或使用-P标志来发布所有暴露的端口。 您可以公开一个端口号并在另一个号码外部发布它。
要在主机系统上设置端口重定向,请参阅使用-P标志。Docker网络功能支持创建网络,而无需公开网络中的端口,有关详细信息,请参阅此功能的概述)。
ENV
ENV <key> <value>ENV <key>=<value> ...
ENV指令将环境变量<key>设置为值<value>。 该值将处于所有“后代”Dockerfile命令的环境中,并且可以在许多内联中被替换。
ENV指令有两种形式。 第一种形式ENV <key> <value>将把单个变量设置为一个值。 第一个空格之后的整个字符串将被视为<value> - 包括诸如空格和引号之类的字符。
第二种形式ENV <key> = <value> ...允许一次设置多个变量。 请注意,第二种形式在语法中使用等号(=),而第一种形式不使用。 与命令行解析一样,引号和反斜杠可用于包含值中的空格。
例如:
ENV myName="John Doe" myDog=Rex The Dog myCat=fluffy
且如下:
ENV myName John Doe ENV myDog Rex The Dog ENV myCat fluffy
将在最终图像中产生相同的净结果,但首选形式是首选,因为它会生成单个缓存层。
使用ENV设置的环境变量将在从结果图像运行容器时保留。 您可以使用docker inspect查看这些值,并使用docker run --env <key> = <value>更改它们。
注意:环境持久性可能会导致意想不到的副作用。例如,设置
ENV DEBIAN_FRONTEND noninteractive
可能会将apt-get用户混淆在基于Debian的映像上。要为单个命令设置值,请使用RUN <key>=<value> <command>
。
ADD
ADD有两种形式:
-
ADD <src>... <dest>
-
ADD ["<src>",... "<dest>"]
(此表单对于包含空格的路径是必需的)
ADD指令从<src>中复制新文件,目录或远程文件URL,并将它们添加到路径<dest>中图像的文件系统中。
<src>
可以指定多个资源,但如果它们是文件或目录,则它们必须与正在构建的源目录(构建的上下文)相关。
每个<src>
可能包含通配符,匹配将使用Go的filepath.Match规则完成。例如:
ADD hom* /mydir/ # adds all files starting with "hom"ADD hom?.txt /mydir/ # ? is replaced with any single character, e.g., "home.txt"
这<dest>
是一个绝对路径或相对路径,WORKDIR
源将在目标容器中复制到该路径中。
ADD test relativeDir/ # adds "test" to `WORKDIR`/relativeDir/ADD test /absoluteDir/ # adds "test" to /absoluteDir/
添加包含特殊字符(例如[
和]
)的文件或目录时,您需要按照Golang规则转义这些路径,以防止它们被视为匹配模式。例如,要添加一个名为的文件arr[0].txt
,请使用以下内容;
ADD arr[[]0].txt /mydir/ # copy a file named "arr[0].txt" to /mydir/
所有新文件和目录都是使用UID和GID创建的。
在<src>是远程文件URL的情况下,目标将具有600的权限。如果正在检索的远程文件具有HTTP Last-Modified标头,则将使用来自该标头的时间戳来设置目的地上的mtime 文件。 但是,与在ADD期间处理的任何其他文件一样,mtime不会包含在文件是否已更改以及缓存应该更新的确定中。
注意:如果通过STDIN(docker build - <somefile)传递Dockerfile来构建,则不存在构建上下文,因此Dockerfile只能包含基于URL的ADD指令。 您还可以通过STDIN传递一个压缩归档文件:(docker build - <archive.tar.gz),归档文件根目录下的Dockerfile以及归档文件的其余部分将用作构建的上下文。 注意:如果您的URL文件使用身份验证进行保护,则需要使用RUN wget,RUN curl或使用容器内的其他工具,因为ADD指令不支持身份验证。 注意:如果<src>的内容已更改,则第一次遇到的ADD指令将使Dockerfile中所有后续指令的缓存无效。 这包括使RUN指令的缓存无效。 有关更多信息,请参阅Dockerfile最佳实践指南。
ADD
遵守以下规则:
-
<src>路径必须位于构建的上下文中; 你不能添加../something / something,因为docker build的第一步是将上下文目录(和子目录)发送到docker守护进程。
-
如果<src>是一个URL并且<dest>不以结尾的斜杠结尾,那么文件将从URL下载并复制到<dest>。
-
如果<src>是一个URL,并且<dest>以尾部的斜线结尾,则从URL中推断出该文件名,并将该文件下载到<dest> / <filename>。 例如,添加http://example.com/foobar /会创建文件/ foobar。 该URL必须有一个不平凡的路径,以便在这种情况下可以找到适当的文件名(http://example.com将不起作用)。
-
如果<src>是一个目录,则复制该目录的全部内容,包括文件系统元数据。
注意:目录本身不被复制,只是它的内容。
-
如果
<src>
是以可识别的压缩格式(身份,gzip,bzip2或xz)的本地 tar归档文件,则将其解压缩为目录。来自远程 URL的资源不被解压缩。当一个目录被复制或解压缩时,它的行为tar -x
与结果相同,结果是:
1. Whatever existed at the destination path and2. The contents of the source tree, with conflicts resolved in favor of “2.” on a file-by-file basis.
注意:文件是否被识别为可识别的压缩格式完全是基于文件的内容而不是文件的名称。例如,如果一个空文件恰好结束,.tar.gz
这将不会被识别为一个压缩文件,并且不会生成任何类型的解压缩错误消息,而是将该文件简单地复制到目标。
-
如果<src>是任何其他类型的文件,则将其与其元数据一起单独复制。 在这种情况下,如果<dest>以结尾斜杠/结尾,则它将被视为一个目录,<src>的内容将写入<dest> / base(<src>)。
-
如果指定了多个<src>资源(直接或由于使用通配符),则<dest>必须是目录,并且必须以斜杠/结尾。
-
如果<dest>不以结尾的斜杠结尾,则它将被视为常规文件,<src>的内容将写入<dest>。
-
如果<dest>不存在,则会在其路径中创建所有缺少的目录。
COPY
COPY有两种形式:
-
COPY <src>... <dest>
-
COPY ["<src>",... "<dest>"]
(此表单对于包含空格的路径是必需的)
该COPY
指令复制新文件或目录<src>
并将其添加到路径中容器的文件系统<dest>
。
<src>
可以指定多个资源,但它们必须相对于正在构建的源目录(构建的上下文)。
每个<src>
可能包含通配符,匹配将使用Go的filepath.Match规则完成。例如:
COPY hom* /mydir/ # adds all files starting with "hom"COPY hom?.txt /mydir/ # ? is replaced with any single character, e.g., "home.txt"
这<dest>
是一个绝对路径或相对路径,WORKDIR
源将在目标容器中复制到该路径中。
COPY test relativeDir/ # adds "test" to `WORKDIR`/relativeDir/COPY test /absoluteDir/ # adds "test" to /absoluteDir/
在复制包含特殊字符(如[
和]
)的文件或目录时,您需要按照Golang规则转义这些路径,以防止它们被视为匹配模式。例如,要复制一个名为arr[0].txt
的文件,请使用以下内容;
COPY arr[[]0].txt /mydir/ # copy a file named "arr[0].txt" to /mydir/
所有新文件和目录都是使用UID和GID创建的。
注意:如果使用STDIN(
docker build - < somefile
)构建,则没有构建上下文,因此COPY
无法使用。
可选地,COPY接受一个flag --from = <name | index>,它可以用来将源位置设置为以前的构建阶段(使用FROM .. AS <name>创建),而不是由 用户。 该标志还接受为以FROM指令开始的所有以前的构建阶段分配的数字索引。 如果无法找到具有指定名称的构建阶段,则尝试使用具有相同名称的图像。
COPY
遵守以下规则:
-
<src>路径必须位于构建的上下文中; 你不能COPY ../something / something,因为docker build的第一步是将上下文目录(和子目录)发送到docker守护进程。
-
如果<src>是一个目录,则复制该目录的全部内容,包括文件系统元数据。
注意:目录本身不被复制,只是它的内容。
-
如果<src>是任何其他类型的文件,则将其与其元数据一起单独复制。 在这种情况下,如果<dest>以结尾斜杠/结尾,则它将被视为一个目录,<src>的内容将写入<dest> / base(<src>)。
-
如果指定了多个<src>资源(直接或由于使用通配符),则<dest>必须是目录,并且必须以斜杠/结尾。
-
如果<dest>不以结尾的斜杠结尾,则它将被视为常规文件,<src>的内容将写入<dest>。
-
如果<dest>不存在,则会在其路径中创建所有缺少的目录。
ENTRYPOINT
ENTRYPOINT有两种形式:
-
ENTRYPOINT ["executable", "param1", "param2"]
(exec form, preferred) -
ENTRYPOINT command param1 param2
(shell form)
一个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或运行命令行参数,但具有缺点,即您的ENTRYPOINT将作为/ bin / sh -c的子命令启动,该命令不传递信号。 这意味着可执行文件不会是容器的PID 1 - 并且不会接收Unix信号 - 所以您的可执行文件将不会从docker stop <container>收到SIGTERM。
只有最后一条ENTRYPOINT
指令Dockerfile
才会起作用。
Exec form 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.05Threads: 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(即as PID 1
):
FROM debian:stable RUN apt-get update && apt-get install -y --force-yes apache2 EXPOSE 80 443VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]
如果您需要为单个可执行文件编写启动脚本,则可以通过使用exec
和gosu
命令确保最终的可执行文件接收到Unix信号:
#!/usr/bin/env bashset -eif [ "$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 COMMAND10035 root {run.sh} /bin/sh /run.sh 123 cmd cmd210054 root /usr/sbin/apache2 -k start10055 33 /usr/sbin/apache2 -k start10056 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)。 注意:exec表单被解析为JSON数组,这意味着您必须在单词(而非单引号('))周围使用双引号(“)。 注意:与shell形式不同,exec形式不会调用命令shell。 这意味着正常的shell处理不会发生。 例如,ENTRYPOINT [“echo”,“$ HOME”]不会在$ HOME上进行变量替换。 如果您想要进行shell处理,那么可以使用shell窗体或直接执行shell,例如:ENTRYPOINT [“sh”,“-c”,“echo $ HOME”]。 当使用exec表单并直接执行一个shell时(如shell格式的情况),它是在执行环境变量扩展的shell,而不是docker。
Shell form 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添加到入口点的开头:
FROM ubuntu ENTRYPOINT top -b CMD --ignored-param1
然后你可以运行它(给它下一步的名字):
$ docker run -it --name test top --ignored-param2 Mem: 1704184K used, 352484K free, 0K shrd, 0K buff, 140621524238337K cached CPU: 9% usr 2% sys 0% nic 88% idle 0% io 0% irq 0% sirq Load average: 0.01 0.02 0.05 2/101 7 PID PPID USER STAT VSZ %VSZ %CPU COMMAND 1 0 root S 3168 0% 0% /bin/sh -c top -b cmd cmd2 7 1 root R 3164 0% 0% top -b
您可以从输出中top
看到指定ENTRYPOINT
的不是PID 1
。
如果你然后运行docker stop test,那么容器不会干净地退出 - 在超时之后,stop命令将被迫发送一个SIGKILL:
$ docker exec -it test ps aux PID USER COMMAND 1 root /bin/sh -c top -b cmd cmd2 7 root top -b 8 root ps aux $ /usr/bin/time docker stop test test real 0m 10.19s user 0m 0.04s sys 0m 0.03s
了解CMD和进入点如何交互
两者CMD
和ENTRYPOINT
指令都定义了运行容器时执行的命令。有几条规则描述了他们的合作。
-
Dockerfile应至少指定一个
CMD
或ENTRYPOINT
命令。 -
ENTRYPOINT
应该在使用容器作为可执行文件时定义。 -
CMD
应该用作为ENTRYPOINT
命令定义默认参数或在容器中执行ad-hoc命令的一种方式。 -
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 “p1_cmd”, “p2_cmd” |
p1_cmd p2_cmd |
/bin/sh -c exec_entry p1_entry |
exec_entry p1_entry p1_cmd p2_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 |
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运行的映像,在/ myvol处创建新的挂载点,并将问候语文件复制到新创建的卷中。
有关指定卷的说明
记住Dockerfile中的卷的以下几点。
-
基于Windows的容器上的卷:使用基于Windows的容器时,容器内的卷的目标必须是以下之一:
- a non-existing or empty directory- a drive other than `C:`
-
在Dockerfile中更改卷:如果任何构建步骤在声明后更改了卷内的数据,则这些更改将被丢弃。
-
JSON格式:列表被解析为JSON数组。您必须用双引号(
"
)括住单词而不是单引号('
)。 -
主机目录在容器运行时声明:主机目录(挂载点)本质上是与主机相关的。这是为了保持图像的可移植性。因为无法保证给定的主机目录在所有主机上都可用。因此,您无法从Dockerfile中挂载主机目录。该
VOLUME
指令不支持指定host-dir
参数。您必须在创建或运行容器时指定安装点。
USER
USER <user>[:<group>] or USER <UID>[:<GID>]
USER指令设置用户名(或UID)和可选的用户组(或GID),以在运行映像时以及在Dockerfile中执行后续的任何RUN,CMD和ENTRYPOINT指令。
警告:当用户确实没有主组时,图像(或下一个指令)将与
root
组一起运行。
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
最终的输出pwd
命令Dockerfile
将是/a/b/c
。
WORKDIR指令可以解析先前使用ENV设置的环境变量。 您只能使用在Dockerfile中显式设置的环境变量。 例如:
ENV DIRPATH /path WORKDIR $DIRPATH/$DIRNAME RUN pwd
此Dockerfile中最终pwd命令的输出将是/ path / $ DIRNAME
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命令的图像的任何用户都是可见的。
默认值
ARG
指令可以可选地包括一个默认值:
FROM busybox ARG user1=someuser ARG buildno=1...
如果ARG
指令具有默认值,并且在构建时没有值传递,那么构建器将使用默认值。
Scope
一个ARG
变量定义从它在其中定义的行开始生效,而Dockerfile
不是来自参数在命令行或其他地方使用的行。例如,考虑这个Dockerfile:
1 FROM busybox2 USER ${user:-some_user}3 ARG user4 USER $user...
用户通过调用以下命令构建该文件:
$ docker build --build-arg user=what_user .
第2行的用户评估为some_user,因为用户变量是在后续行3上定义的。第4行的用户评估为用户定义的what_user,并在命令行上传递了what_user值。 在通过ARG指令定义之前,任何使用变量都会导致一个空字符串。
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。
1 FROM ubuntu2 ARG CONT_IMG_VER3 ENV CONT_IMG_VER v1.0.04 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指令之间创建更多有用的交互:
1 FROM ubuntu2 ARG CONT_IMG_VER3 ENV CONT_IMG_VER ${CONT_IMG_VER:-v1.0.0}4 RUN echo $CONT_IMG_VER
与ARG指令不同,ENV值始终保留在构建的映像中。 考虑一个没有--build-arg标志的docker build:
$ 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
要使用这些,只需使用标志在命令行上传递它们即可:
--build-arg <varname>=<value>
默认情况下,这些预定义的变量将从Docker历史记录的输出中排除。 将它们排除在外可以减少在HTTP_PROXY变量中意外泄漏敏感身份验证信息的风险。
For example, consider building the following Dockerfile using --build-arg HTTP_PROXY=http://user:pass@proxy.lon.example.com
FROM ubuntu RUN echo "Hello World"
在这种情况下,HTTP_PROXY变量的值在Docker历史记录中不可用,并且不会被缓存。 如果您要更改位置,并且您的代理服务器更改为http:// user:pass@proxy.sfo.example.com,则后续构建不会导致缓存未命中。
如果您需要重写此行为,则可以通过在Dockerfile中添加ARG语句来执行此操作,如下所示:
FROM ubuntu ARG HTTP_PROXY RUN echo "Hello World"
构建此Dockerfile时,HTTP_PROXY将保留在Docker历史记录中,并且更改其值可使构建缓存失效。
对构建缓存的影响
作为ENV变量,ARG变量不会保留在构建的映像中。 但是,ARG变量确实会以类似的方式影响构建缓存。 如果一个Dockerfile定义了一个ARG变量,它的值与以前的版本不同,那么第一次使用时会发生“缓存未命中”,而不是其定义。 尤其是,遵循ARG指令的所有RUN指令都隐式使用ARG变量(作为环境变量),因此可能导致缓存未命中。 除非在Dockerfile中存在匹配的ARG语句,否则所有预定义的ARG变量都可以免于缓存。
例如,考虑这两个Dockerfile:
1 FROM ubuntu2 ARG CONT_IMG_VER3 RUN echo $CONT_IMG_VER
1 FROM ubuntu2 ARG CONT_IMG_VER3 RUN echo hello
如果在命令行中指定--build-arg CONT_IMG_VER = <value>,则在这两种情况下,第2行上的规范不会导致缓存未命中; 第3行确实导致缓存未命中.ARG CONT_IMG_VER导致RUN行被识别为与运行CONT_IMG_VER = <value> echo hello相同,因此如果<value>发生更改,则会导致缓存未命中。
考虑同一命令行下的另一个示例:
1 FROM ubuntu2 ARG CONT_IMG_VER3 ENV CONT_IMG_VER $CONT_IMG_VER4 RUN echo $CONT_IMG_VER
在此示例中,第3行发生高速缓存未命中。发生未命中是因为ENV中的变量值引用了ARG变量,并且该变量通过命令行进行了更改。 在此示例中,ENV命令会使图像包含该值。
如果ENV
指令覆盖ARG
同名的指令,就像这个Dockerfile一样:
1 FROM ubuntu2 ARG CONT_IMG_VER3 ENV CONT_IMG_VER hello4 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指令完成并且构建继续照常。
-
触发器在执行后从最终图像中清除。 换句话说,它们不会被“grand-children”构建遗传。
例如,你可能会添加如下内容:
[...]ONBUILD ADD . /app/src ONBUILD RUN /usr/local/bin/python-build --dir /app/src[...]
警告:不允许使用ONBUILD ONBUILD链接ONBUILD指令。 警告:ONBUILD指令可能不会触发FROM或MAINTAINER指令。
STOPSIGNAL
STOPSIGNAL signal
该STOPSIGNAL
指令设置将被发送到容器的系统呼叫信号以退出。这个信号可以是一个有效的无符号数字,与内核syscall表中的位置相匹配,例如9,或者SIGNAME格式的信号名称,例如SIGKILL。
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
) -
--retries=N
(默认值:3
)
运行状况检查将首先在容器启动后的间隔秒内运行,然后在每次前一次检查完成后再次间隔几秒。
如果单次运行检查花费的时间超过了超时秒数,那么检查将被视为失败。
需要重试连续的容器运行状况检查失败才会被视为不健康。
启动期为需要时间启动的容器提供初始化时间。在此期间探测失败不会计入最大重试次数。但是,如果在启动期间运行状况检查成功,则认为容器已启动,并且所有连续的故障都将计入最大重试次数。
Dockerfile中只能有一个HEALTHCHECK指令。 如果您列出多个,则只有最后一个HEALTHCHECK将生效。
CMD
关键字后面的命令可以是shell命令(例如HEALTHCHECK CMD /bin/check-running
)或exec阵列(与其他Dockerfile命令一样;请参阅ENTRYPOINT
详细信息)。
该命令的退出状态指示容器的健康状态。可能的值是:
-
0:success - 容器健康并且可以使用
-
1:unhealthy - 容器工作不正常
-
2:reserved - 不要使用此退出代码
例如,要每五分钟检查一次,网络服务器能够在三秒钟内为网站的主页面提供服务:
HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost/ || exit 1
为了帮助调试失败的探测器,命令在stdout或stderr上写入的任何输出文本(UTF-8编码)都将存储在健康状态中,并且可以使用它进行查询docker inspect
。这样的输出应该保持很短(目前仅存储前4096个字节)。
当容器的健康状况发生变化时,health_status
会生成一个新状态的事件。
该HEALTHCHECK
功能添加到Docker 1.12中。
SHELL
SHELL ["executable", "parameters"]
SHELL指令允许覆盖用于shell命令形式的默认shell。 Linux上的默认shell为[“/ bin / sh”,“-c”],在Windows上为[“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 defaultRUN echo default# Executed as cmd /S /C powershell -command Write-Host defaultRUN 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
当它们的shell形式在Dockerfile中使用时,以下指令可能受到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命令处理器(aka shell)。 其次,shell格式中的每条RUN指令都需要在命令前加上一个额外的powershell命令。
为了提高效率,可以采用两种机制之一。一种是使用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:exampleExecute-MyCmdlet -sample 'hello world'
导致:
PS E:dockeruildshell> 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:exampleExecute-MyCmdlet 'hello world' ---> Running in be6d8e63fe75 hello world ---> 8e559e9bf424 Removing intermediate container be6d8e63fe75 Successfully built 8e559e9bf424 PS E:dockeruildshell>
SHELL指令也可以用来修改shell的运行方式。 例如,在Windows上使用SHELL cmd / S / C / V:ON | OFF,可以修改延迟的环境变量扩展语义。
SHELL指令也可用于Linux,如果需要备用shell,如zsh,csh,tcsh等。
该SHELL
功能添加到Docker 1.12中。
Dockerfile示例
下面你可以看到一些Dockerfile语法的例子。如果您对更真实的东西感兴趣,请查看Dockerization示例列表。
# Nginx # # VERSION 0.0.1FROM ubuntu LABEL Description="This image is used to start the foobar executable" Vendor="ACME Products" Version="1.0"RUN apt-get update && apt-get install -y inotify-tools nginx apache2 openssh-server
# Firefox over VNC # # VERSION 0.3FROM ubuntu # Install vnc, xvfb in order to create a 'fake' display and firefox RUN apt-get update && apt-get install -y x11vnc xvfb firefox RUN mkdir ~/.vnc # Setup a password RUN x11vnc -storepasswd 1234 ~/.vnc/passwd # Autostart firefox (might not be the best way, but it does the trick)RUN bash -c 'echo "firefox" >> /.bashrc'EXPOSE 5900CMD ["x11vnc", "-forever", "-usepw", "-create"]
# Multiple images example # # VERSION 0.1FROM ubuntu RUN echo foo > bar # Will output something like ===> 907ad6c2736f FROM ubuntu RUN echo moo > oink # Will output something like ===> 695d7793cbe4 # You'll now have two images, 907ad6c2736f with /bar, and 695d7793cbe4 with# /oink.