Docker可以通过读取Dockerfile中的指令来自动构建图像。Dockerfile是一个文本文档,它包含用户可以在命令行上调用的所有命令来组装一个映像。使用docker构建,用户可以创建一个连续执行多个命令行指令的自动化构建。
构建镜像
docker build -f /path/Dockerfile .
or
enter Dockerfile文件所在目录
docker build Dockerfile .
比如:
docker build \
-f /opt/mydocker/Dockerfile \
-t image-one:1.0.0 \
-t image-two:1.0.0 \
-t image-three:1.0.0 .
运行结果
Sending build context to Docker daemon 3.072kB
Step 1/4 : FROM busybox
---> 020584afccce
Step 2/4 : ENV foo /opt/test
---> Running in 6c677c13e841
Removing intermediate container 6c677c13e841
---> 92076e59bfec
Step 3/4 : WORKDIR ${foo}
Removing intermediate container a31bc20b2674
---> 63a79fcdd60d
Step 4/4 : ADD . $foo
---> 8d8d15703e5f
Successfully built 8d8d15703e5f
Successfully tagged image-one:1.0.0
Successfully tagged image-two:1.0.0
Successfully tagged image-three:1.0.0
查看构建的镜像:
[root@localhost mydocker]# docker images|grep image
image-one 1.0.0 8d8d15703e5f 28 minutes ago 1.22MB
image-three 1.0.0 8d8d15703e5f 28 minutes ago 1.22MB
image-two 1.0.0 8d8d15703e5f 28 minutes ago 1.22MB
注:从构建的镜像来看我们可以发现用同一个Dockerfile文件构建的的镜像虽然仓库名称不同,镜像id是一致的,类似于堆栈,虽然构建了不同的仓库引用地址是一样的,本质是一样的,只是名字不同
只能位于dockerfile文件内顶部,因为文件内一旦处理了注释、空行或生成器指令,docker便不在寻找解析器指令;
单个指令只能使用一次;
不区分大小写,不过习惯上都是小写的;
不支持延续字符\;
1. 由于行延续而无效
# direc \
tive=value
2. 无效,因为出现两次
# directive=value1
# directive=value2
FROM ImageName
3. 由于出现在生成器指令之后而被视为注释
FROM ImageName
# directive=value
4. 由于出现在非解析器指令的注释之后,所以被视为注释
# About my dockerfile
# directive=value
FROM ImageName
5. 未知指令将被视为注释,因为它不被识别。此外,由于出现在注释之后,已知的指令被视为注释,而注释不是解析器指令
# unknowndirective=value
# knowndirective=value
6. 解析器指令中允许非断行空白。因此,下面的行都是相同的
#directive=value
# directive =value
# directive= value
# directive = value
# dIrEcTiVe=value
escape指令设置用于在Dockerfile中转义字符的字符。如果没有指定,默认的转义字符是\。
转义字符既用于转义行中的字符,也用于转义换行。这允许Dockerfile指令跨越多行。
注意,无论Dockerfile中是否包含escape解析器指令,除了在行尾之外,都不会在RUN命令中执行转义。
将转义字符设置为’在Windows上特别有用,其中\是目录路径分隔符。与Windows PowerShell一致。
escape指令设置用于在Dockerfile中转义字符的字符。如果没有指定,默认的转义字符是\。
转义字符既用于转义行中的字符,也用于转义换行。这允许Dockerfile指令跨越多行。
注意,无论Dockerfile中是否包含escape解析器指令,除了在行尾之外,都不会在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>
上面的一个解决方案是使用/作为复制指令和dir的目标。但是,这种语法在最好的情况下是令人困惑的,因为它对Windows上的路径不是很自然,在最坏的情况下,很容易出错,因为不是所有Windows上的命令都支持/作为路径分隔符。
通过添加escape parser指令,下面的Dockerfile成功地使用了Windows上文件路径的自然平台语义:
# 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/2019 05:04 PM 1,894 License.txt
10/05/2019 02:22 PM Program Files
10/05/2019 02:14 PM Program Files (x86)
10/28/2019 11:18 AM 62 testfile.txt
10/28/2019 11:20 AM Users
10/28/2019 11:20 AM 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要解释的变量。还可以处理转义,将类似变量的语法按字面意思包含到语句中。
环境变量在Dockerfile中用 v a r i a b l e n a m e 或 variable_name或 variablename或{variable_name}标记。它们被同等对待,大括号语法通常用于处理变量名没有空格的问题,比如${foo}_bar。
${variable_name}语法还支持以下指定的一些标准bash修饰符:
${variable:-word}表示如果设置了变量,那么结果将是该值。如果变量没有设置,那么word将是结果。
${variable:+word}表示如果设置了变量,则结果为word,否则结果为空字符串。
在所有情况下,word可以是任何字符串,包括额外的环境变量。
可以通过在变量前面添加一个\来进行转义,例如,oo或${foo}将分别转换为 f o o 和 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
环境变量替换将在整个指令中对每个变量使用相同的值。换句话说,在这个例子中
ENV abc=hello
ENV abc=bye def=$abc
ENV ghi=$abc
将导致def的值为hello,而不是bye。然而,ghi将有一个值bye,因为它不是设置abc为bye的同一指令的一部分。
在docker CLI将上下文发送到docker守护进程之前,它会在根目录t中查找一个名为.dockerignore的文件。这有助于避免不必要地向守护进程发送大型或敏感的文件和目录,并可能使用ADD或COPY将它们添加到映像中。
CLI将.dockerignore文件解释为一个新行分隔的模式列表,类似于Unix shell的文件globs。为了进行匹配,上下文的根被认为是工作目录和根目录。例如,模式/foo/bar和foo/bar都排除了PATH的foo子目录或位于URL的git存储库根目录中名为bar的文件或目录。两者都不排斥其他任何东西。
如果.dockerignore文件中的一行以第1列中的#开始,那么这一行将被视为注释,并在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进行匹配。匹配规则。预处理步骤删除开头和结尾的空白并消除。和. .使用Go的filepath.Clean的元素。预处理后为空的行将被忽略。
去的filepath之外。匹配规则,Docker还支持一个特殊的通配符字符串**,它可以匹配任意数量的目录(包括0)。例如,* * / *。go将排除在所有目录中以.go结尾的所有文件,包括构建上下文的根目录。
行开始!(感叹号)可用于排除的例外情况。下面是一个例子。dockerignore文件使用这个机制:
*.md
!README.md
所有的markdown文件,除了README。md被排除在上下文之外。
放置!异常规则影响行为:.dockerignore中与特定文件匹配的最后一行决定它是被包含还是被排除。考虑下面的例子:
*.md
!README*.md
README-secret.md
除了README-secret.md以外,上下文中不包含任何标记文件。
现在考虑这个例子:
*.md
README-secret.md
!README*.md
所有的自述文件都包括在内。中间的行没有效果,因为!自述*。md README-secret匹配。md,最后一名。
您甚至可以使用.dockerignore文件来排除Dockerfile和.dockerignore文件。这些文件仍然被发送到守护进程,因为守护进程需要它们来完成自己的工作。但是添加和复制指令不会将它们复制到图像。
最后,您可能希望指定要在上下文中包含哪些文件,而不是要排除哪些文件。要实现这一点,将*指定为第一个模式,然后是一个或多个模式!异常模式。
注意:由于历史原因,模式。将被忽略
FROM [AS ]
or
FROM [:] [AS ]
or
FROM [@] [AS ]
FROM指令初始化一个新的构建阶段,并为后续指令设置基本映像。因此,一个有效的Dockerfile必须以FROM指令开始。图像可以是任何有效的图像—从公共存储库中提取图像尤其容易。
ARG是Dockerfile中唯一可能在前面的指令。了解ARG和FROM如何交互。
可以在一个Dockerfile中多次出现,以创建多个图像,或者使用一个构建阶段作为另一个构建阶段的依赖项。只需在每条新的FROM指令之前,通过提交记录最后一个图像ID输出。每个FROM指令清除以前的指令创建的任何状态。
可以通过将AS名称添加到FROM指令中,将名称指定给新的构建阶段,这是可选的。该名称可用于后续的FROM和COPY——FROM =指令,以引用在此阶段构建的映像。
标记或摘要值是可选的。如果省略其中任何一个,构建器默认使用最新的标记。如果生成器找不到标记值,则返回一个错误。
ROM指令支持由第一个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中的下一步。
运行指令并生成提交符合Docker的核心概念,其中提交是廉价的,容器可以从图像历史记录中的任意点创建,非常类似于源代码控制。
exec形式可以避免使用shell字符串,并使用不包含指定的shell可执行文件的基本映像运行命令。
可以使用shell命令更改shell窗体的默认shell。
在shell形式中,您可以使用\(反斜杠)将单个运行指令延续到下一行。例如,考虑这两行:
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”, " H O M E " ] 不 会 对 HOME"]不会对 HOME"]不会对HOME执行变量替换。如果您想要shell处理,那么要么使用shell形式,要么直接执行shell,例如:运行[“sh”, “-c”, “echo $HOME”]。当使用exec形式并直接执行shell时(如shell形式的情况),执行环境变量扩展的是shell,而不是docker。
注意:在JSON格式中,必须转义反斜杠。这在以反斜杠为路径分隔符的窗口中特别相关。下面这行代码由于不是有效的JSON,将被视为shell格式,并以一种意外的方式失败:
RUN["c:\windows\system32\tasklist]。
本例的正确语法是:RUN [“c:\windows\system32\tasklist.exe”]
RUN指令的缓存不会在下一次构建期间自动失效。像RUN apt-get distt -upgrade -y这样的指令的缓存将在下一个构建过程中重用。运行指令的缓存可以通过使用——no-cache标志来失效,例如docker build——no-cache
一个Dockerfile中只能有一条CMD指令。如果你列出一个以上的CMD,那么只有最后一个CMD会生效。
CMD的主要用途是为执行容器提供默认值。这些缺省值可以包括可执行文件,也可以省略可执行文件,在这种情况下,您还必须指定一个入口点指令。
注意:如果CMD用于为ENTRYPOINT指令提供默认参数,那么CMD和ENTRYPOINT指令都应该使用JSON数组格式指定。
注意:exec格式被解析为JSON数组,这意味着必须在单词周围使用双引号("),而不是单引号(’)。
注意:与shell格式不同,exec格式不调用命令shell。这意味着不会发生正常的shell处理。例如,CMD [“echo”, " H O M E " ] 不 会 对 HOME"]不会对 HOME"]不会对HOME执行变量替换。如果您需要shell处理,那么要么使用shell格式,要么直接执行shell,例如:CMD [“sh”, “-c”, “echo $HOME”]。当使用exec格式并直接执行shell时(如shell格式的情况),执行环境变量扩展的是shell,而不是docker。
在shell或exec格式中使用时,CMD指令设置在运行映像时要执行的命令。
如果使用CMD的shell形式,那么
FROM ubuntu
CMD echo "This is a test." | wc -
如果您想运行没有shell的
FROM ubuntu
CMD ["/usr/bin/wc","--help"]
如果希望容器每次都运行相同的可执行文件,那么应该考虑将ENTRYPOINT与CMD结合使用。看到入口点。
如果用户指定docker运行的参数,那么他们将覆盖CMD中指定的默认值。
注意:不要将RUN与CMD混淆。RUN实际运行命令并提交结果;CMD在构建时不执行任何操作,但是为映像指定想要的命令。
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 multi.label1="value1" multi.label2="value2" other="value3"
也可以写成:
LABEL multi.label1="value1" \
multi.label2="value2" \
other="value3"
标签是附加的,包括来自图像的标签。如果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
MAINTAINER指令设置生成的映像的Author字段。LABEL指令是一个灵活得多的版本,您应该使用它,因为它支持设置您需要的任何元数据,并且可以轻松查看,例如使用docker inspect。要设置与MAINTAINER字段对应的标签,可以使用以下命令:
LABEL maintainer="[email protected]"
EXPOSE [/...]
EXPOSE指令通知Docker容器在运行时监听指定的网络端口。您可以指定端口监听TCP还是UDP,如果没有指定协议,则默认为TCP。
EXPOSE指令实际上并不发布端口。它的功能类似于构建映像的人员和运行容器的人员之间的一种文档类型,关于要发布哪些端口。要在运行容器时实际发布端口,可以使用docker run上的-p标志来发布和映射一个或多个端口,或者使用-p标志来发布所有公开的端口并将它们映射到高阶端口。
要在主机系统上设置端口重定向,请参阅使用-P标志。docker network命令支持在容器之间创建通信网络,而不需要公开或发布特定的端口,因为连接到网络的容器可以通过任何端口相互通信。有关详细信息,请参阅此功能的概述)。
ENV
ENV = ...
ENV指令将环境变量设置为值。
此值将位于构建阶段中所有后续指令的环境中,也可以内联地替换许多指令。
ENV指令有两种形式:
注意,第二种形式在语法中使用了等号(=),而第一种形式没有。
与命令行解析一样,引号和反斜杠可用于在值中包含空格。
例如:
ENV myName="John Doe" myDog=Rex\ The\ Dog \
myCat=fluffy
and
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy
产生的结果一样
当从结果映像运行容器时,使用ENV设置的环境变量将保持不变。您可以使用docker inspect查看这些值,
并使用docker run --env
注意:环境持久性可能会导致意外情况。例如,设置ENV DEBIAN_FRONTEND非交互式可能会使基于debian的图像上的apt-get用户感到困惑。要设置单个命令的值,使用RUN
=
ADD有两种形式:
注意:–chown特性只支持用于构建Linux容器的Dockerfiles,而不能用于Windows容器。由于用户和组所有权概念不能在Linux和Windows之间转换,因此使用/etc/passwd和/etc/group将用户名和组名转换为IDs,这限制了该特性只能用于基于Linux os的容器。
ADD 指令从
ADD hom* /mydir/ # 添加所有以hom开头的文件
ADD hom?.txt /mydir/ # 添加所有类似home.txt文件
ADD test relativeDir/ # 添加 "test" to `WORKDIR`/relativeDir/
ADD test /absoluteDir/ # 添加 "test" to /absoluteDir/
在添加包含特殊字符(如[和])的文件或目录时,需要转义那些遵循Golang规则的路径,以防止它们被视为匹配模式。例如,添加一个名为arr[0].txt的文件,使用以下:
ADD arr[[]0].txt /mydir/ # 复制 "arr[0].txt" 到 /mydir/
所有新创建的文件和目录的UID和GID都是0,除非可选的–chown标志指定了一个给定的用户名、groupname或UID/GID组合来请求添加内容的特定所有权。–chown标志的格式允许在任何组合中使用用户名和groupname字符串或直接整数UID和GID。提供没有groupname的用户名或没有GID的UID将使用与GID相同的数字UID。如果提供了用户名或groupname,则将使用容器的根文件系统/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/
如果容器根文件系统不包含/etc/passwd或/etc/group文件,并且在–chown标志中使用了用户名或组名,那么在添加操作时构建将失败。使用数字id不需要查找,也不依赖于容器根文件系统的内容。在
注意:如果您通过STDIN (docker build - < somefile)传递一个Dockerfile来构建,则没有构建上下文,因此Dockerfile只能包含一个基于URL的ADD指令。您还可以通过STDIN (docker build - < archive.tar.gz)传递压缩的归档文件,归档文件根目录下的Dockerfile和归档文件的其余部分将用作构建的上下文
注意:如果您的URL文件使用身份验证进行保护,那么您将需要在容器中使用RUN wget、RUN curl或其他工具,因为ADD指令不支持身份验证。
注意:如果的内容发生了变化,第一次遇到的ADD指令将使Dockerfile中的所有后续指令的缓存失效。这包括使运行指令的缓存无效。有关更多信息,请参阅Dockerfile最佳实践指南。
如果
如果
如果
注意:目录本身不是复制的,只是它的内容。
如果
注意:一个文件是否被识别为一个可识别的压缩格式仅仅是基于文件的内容,而不是文件的名称。例如,如果一个空文件以.tar.gz结尾,那么它将不会被识别为压缩文件,也不会生成任何类型的解压缩错误消息,而只是简单地将该文件复制到目的地。
如果
如果
如果
如果
COPY有两种形式:
注意:–chown特性只支持用于构建Linux容器的Dockerfiles,而不能用于Windows容器。由于用户和组所有权概念不能在Linux和Windows之间转换,因此使用/etc/passwd和/etc/group将用户名和组名转换为IDs,这限制了该特性只能用于基于Linux os的容器。
COPY指令从
COPY hom* /mydir/ # 添加所有以hom开头的文件
COPY hom?.txt /mydir/ # 添加所有类似home.txt文件
是一个绝对路径,或相对于WORKDIR的路径,源文件将被复制到目标容器中。
COPY test relativeDir/ # 添加 "test" to `WORKDIR`/relativeDir/
COPY test /absoluteDir/ # 添加 "test" to /absoluteDir/
在复制包含特殊字符(如[和])的文件或目录时,需要转义那些遵循Golang规则的路径,以防止它们被视为匹配模式。例如,复制一个名为arr[0].txt,使用以下:
COPY arr[[]0].txt /mydir/ # 复制文件名为 "arr[0].txt" 到/mydir/
所有新文件和目录都是用UID和GID 0创建的。
注意:如果使用STDIN (docker build - < somefile)进行构建,则没有构建上下文,因此不能使用COPY。
如果
如果直接指定了多个资源
如果
如果
ENTRYPOINT有两种形式:
ENTRYPOINT 允许您配置一个容器将用作可执行文件的运行。
例如,下面将启动nginx的默认内容,监听端口80:
docker run -i -t --rm -p 80:80 nginx
docker run
shell形式可以防止使用任何CMD或run命令行参数,但是有一个缺点,即您的ENTRYPOINT将作为/bin/sh -c的子命令启动,该命令不传递信号。这意味着可执行文件不是容器的PID 1,也不会收到Unix信号,所以你的可执行文件不会收到来自docker stop
只有Dockerfile中的最后一条ENTRYPOINT指令才会起作用。
您可以使用ENTRYPOINT的exec形式来设置相当稳定的默认命令和参数,然后使用CMD的任何一种形式来设置更可能更改的其他默认值。
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
当你运行容器,你可以看到顶部是唯一的进程:
$ 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(即, as 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 '"
read
# stop service and clean up here
echo "stopping apache"
/usr/sbin/apachectl stop
echo "exited $0"
如果你用docker run -it——rm -p 80:80——name test apache来运行这个镜像,你可以用docker exec或docker top来检查容器的进程,然后让脚本停止apache:
$ docker exec -it test ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.1 0.0 4448 692 ? Ss+ 00:42 0:00 /bin/sh /run.sh 123 cmd cmd2
root 19 0.0 0.2 71304 4440 ? Ss 00:42 0:00 /usr/sbin/apache2 -k start
www-data 20 0.2 0.2 360468 6004 ? Sl 00:42 0:00 /usr/sbin/apache2 -k start
www-data 21 0.2 0.2 360468 6000 ? Sl 00:42 0:00 /usr/sbin/apache2 -k start
root 81 0.0 0.1 15572 2140 ? R+ 00:44 0:00 ps aux
$ docker top test
PID USER COMMAND
10035 root {run.sh} /bin/sh /run.sh 123 cmd cmd2
10054 root /usr/sbin/apache2 -k start
10055 33 /usr/sbin/apache2 -k start
10056 33 /usr/sbin/apache2 -k start
$ /usr/bin/time docker stop test
test
real 0m 0.27s
user 0m 0.03s
sys 0m 0.03s
注意:您可以使用 --entrypoint覆盖ENTRYPOINT设置,但这只能将二进制设置为exec(no sh -c 将会被使用)。
注意:exec形式被解析为JSON数组,这意味着必须在单词周围使用双引号("),而不是单引号(’)。
注意:与shell形式不同,exec形式不调用命令shell。这意味着不会发生正常的shell处理。例如,ENTRYPOINT [“echo”, " H O M E " ] 不 会 对 HOME"]不会对 HOME"]不会对HOME执行变量替换。如果您想要shell处理,那么要么使用shell形式,要么直接执行shell,例如:ENTRYPOINT [“sh”, “-c”, “echo $HOME”]。当使用exec形式并直接执行shell时(如shell表单的情况),执行环境变量扩展的是shell,而不是docker。
您可以为ENTRYPOINT指定一个纯字符串,它将在/bin/sh -c中执行。此形式将使用shell处理来替代shell环境变量,并将忽略任何CMD或docker运行命令行参数。要确保docker停止将信号任何长时间运行的可执行ENTRYPOINT,你需要记住,启动它与exec:
FROM ubuntu
ENTRYPOINT exec top -b
当你运行这个image,你会看到单一的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
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
如果你运行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和ENTRYPOINT指令都定义了在运行容器时执行什么命令。很少有规则描述他们的合作。
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 ["/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处创建一个新的挂载点,并将greeting文件复制到新创建的卷中。
关于Dockerfile中的卷,请记住以下几点:
USER [:] or
USER [:]
USER指令设置用户名(或UID)和可选的用户组(或GID),以便在运行映像时使用,并设置Dockerfile中紧随其后的任何运行、CMD和ENTRYPOINT指令。
警告:当用户没有主组时,图像(或下一个指令)将与根组一起运行。
WORKDIR /path/to/workdir
WORKDIR指令为Dockerfile中的任何运行、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
ARG [=]
ARG指令定义了一个变量,用户可以在构建时通过docker build命令使用–build-arg
[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指令有默认值,并且在构建时没有传递值,那么构建器将使用默认值。
ARG变量定义从Dockerfile中定义它的行开始生效,而不是从参数在命令行或其他地方的使用开始。例如,考虑这个Dockerfile:
FROM busybox
USER ${user:-some_user}
ARG user
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或ENV指令来指定运行指令可用的变量。使用ENV指令定义的环境变量总是覆盖同名的ARG指令。考虑这个带有ENV和ARG指令的Dockerfile。
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER v1.0.0
RUN echo $CONT_IMG_VER
然后,假设这个image是用以下命令构建的:
$ docker build --build-arg CONT_IMG_VER=v2.0.1 .
在本例中,RUN指令使用的是v1.0.0,而不是用户传递的ARG设置:v2.0.1。这种行为类似于shell脚本,其中局部作用域的变量从定义的角度覆盖作为参数传递或从环境中继承的变量。
使用上面的例子,但不同的环境规范,你可以创建更多有用的互动之间的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指令。
Docker有一组预定义的ARG变量,您可以在Dockerfile中使用它们,而不需要相应的ARG指令。
HTTP_PROXY
http_proxy
HTTPS_PROXY
https_proxy
FTP_PROXY
ftp_proxy
NO_PROXY
no_proxy
要使用这些,只需通过命令行使用标志:
--build-arg =
默认情况下,这些预先定义的变量被排除在docker历史记录的输出之外。排除它们可以降低在HTTP_PROXY变量中意外泄漏敏感身份验证信息的风险。
例如,考虑使用–build-arg HTTP_PROXY=http://user:[email protected]
FROM ubuntu
RUN echo "Hello World"
在本例中,HTTP_PROXY变量的值在docker历史记录中不可用,也没有被缓存。如果您要更改位置,并且您的代理服务器更改为http://user:[email protected],则后续的构建不会导致缓存丢失。
如果你需要覆盖这个行为,你可以通过添加一个ARG语句在Dockerfile如下:
FROM ubuntu
ARG HTTP_PROXY
RUN echo "Hello World"
在构建此Dockerfile时,HTTP_PROXY将保留在docker历史中,更改其值将使构建缓存无效。
ARG变量不像ENV变量那样持久化到构建的映像中。但是,ARG变量也会以类似的方式影响构建缓存。如果Dockerfile定义的ARG变量的值与以前的构建版本不同,那么在第一次使用时就会出现“缓存遗漏”,而不是它的定义。特别是,ARG指令之后的所有运行指令都会隐式地使用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=
考虑一下同一命令行的另一个例子:
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER $CONT_IMG_VER
RUN echo $CONT_IMG_VER
在本例中,缓存丢失发生在第3行。之所以会发生miss,是因为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)。因此,运行时使用的环境变量和值(第4行)不会在构建之间更改。
ONBUILD [INSTRUCTION]
ONBUILD指令向映像添加了一条触发器指令,以便在稍后将映像用作另一个构建的基础时执行。触发器将在下游构建的上下文中执行,就好像它是在下游Dockerfile中的FROM指令之后立即插入的一样。
任何构建指令都可以注册为触发器。
如果您正在构建一个映像,该映像将用作构建其他映像的基础,例如一个应用程序构建环境或一个可以使用特定于用户的配置进行自定义的守护进程,那么这是非常有用的。
例如,如果您的映像是一个可重用的Python应用程序构建器,那么它将需要在特定的目录中添加应用程序源代码,并且可能需要在此之后调用构建脚本。您现在不能只调用ADD并运行,因为您还没有访问应用程序源代码的权限,而且每个应用程序的构建版本都不一样。您可以简单地为应用程序开发人员提供一个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指令可能不会从或维护程序指令触发。
用于设置停止容器所要发送的系统调用信号
STOPSIGNAL signal
STOPSIGNAL指令设置将发送到容器以退出的系统调用信号。这个信号可以是与内核的syscall表中的某个位置匹配的有效无符号数字,也可以是格式为SIGNAME的信号名(例如SIGKILL)。
HEALTHCHECK指令有两种形式:
HEALTHCHECK指令告诉Docker如何测试容器,以检查它是否仍在工作。这可以检测某些情况,比如web服务器陷入无限循环,无法处理新连接,即使服务器进程仍在运行。
当容器指定了healthcheck时,除了正常状态外,它还有一个健康状态。此状态最初是启动的。当一个健康检查通过时,它就变成健康的(不管它以前处于什么状态)。经过一定数量的连续失败之后,它就变得不健康了。
可以出现在CMD之前的选项有:
--interval=DURATION (default: 30s)
--timeout=DURATION (default: 30s)
--start-period=DURATION (default: 0s)
--retries=N (default: 3)
健康检查将首先在容器启动后的几秒内运行interval,然后在每次前一次检查完成后的几秒内再次运行interval。
如果检查的单个运行时间超过超时秒,则认为检查失败。
如果容器的健康检查连续失败,则需要重试才能认为是不健康的。
start period为需要时间引导的容器提供初始化时间。在此期间的探测失败将不计入最大重试次数。但是,如果在开始期间的健康检查成功,则认为容器已经启动,所有连续的失败都将被计入最大重试次数。
在一个Dockerfile中只能有一个HEALTHCHECK指令。如果你列出多于一个,那么只有最后一次健康检查才会生效。
CMD关键字后面的命令可以是shell命令(例如HEALTHCHECK CMD /bin/check-running),也可以是exec数组(与其他Dockerfile命令一样;详细信息参见例如:ENTRYPOINT)。
该命令的退出状态指示容器的健康状态。可能的值是:
0:成功-容器是健康的,可以使用了
1:不健康——容器不能正常工作
2:保留-不要使用这个退出码
例如,每隔5分钟左右检查一次,使web服务器能够在3秒钟内为站点的主页提供服务:
HEALTHCHECK --interval=5m --timeout=3s \
CMD curl -f http://localhost/ || exit 1
为了帮助调试失败探测,命令在stdout或stderr上编写的任何输出文本(UTF-8编码)都将存储在健康状态中,并可以通过docker inspect进行查询。这样的输出应该保持简短(当前只存储前4096个字节)。
当容器的健康状态更改时,将使用新的状态生成health_status事件。
在docker1.12中添加了HEALTHCHECK功能。
SHELL ["executable", "parameters"]
SHELL指令允许覆盖用于命令的SHELL形式的默认SHELL。Linux上的默认shell是["/bin/sh", “-c”], Windows上是[“cmd”, “/S”, “/C”]。SHELL指令必须以JSON格式写入Dockerfile中。
SHELL指令在Windows上特别有用,因为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中使用SHELL形式时,以下指令可能会受到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)。其次,shell形式的每条运行指令都需要一个额外的powershell命令作为前缀。
为了提高效率,可以使用两种机制中的一种。一种是使用JSON格式的运行命令,如:
...
RUN ["powershell", "-command", "Execute-MyCmdlet", "-param1 \"c:\\foo.txt\""]
...
而JSON格式是明确的,不使用不必要的cmd.exe,它需要更多的冗余通过双引号和转义。另一种机制是使用SHELL指令和SHELL形式,为Windows用户提供更自然的语法,特别是与escape parser指令结合使用时:
# 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:\docker\build\shell> 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:\docker\build\shell>
SHELL指令还可以用来修改SHELL的操作方式。例如,使用SHELL cmd /S /C /V:ON|OFF在Windows上,可以修改延迟的环境变量扩展语义。
如果需要另一个SHELL,比如zsh、csh、tcsh等,也可以在Linux上使用SHELL指令。
在docker1.12中添加了SHELL特性。
下面你可以看到一些Dockerfile语法的例子
# Nginx
#
# VERSION 0.0.1
FROM 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.3
FROM 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 5900
CMD ["x11vnc", "-forever", "-usepw", "-create"]
# Multiple images example
#
# VERSION 0.1
FROM 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.
ok~.~