Dockerfile 官方文档

官方文档地址:https://docs.docker.com/engine/reference/builder/#shell

用法:

docker build 命令从Dockerfile文件和上下文环境(context)中构建Image(image)。
其中构建的上下文环境(context)是一个指定位置的路径(path)或者URL下的一系列文件。
路径(path)是本地文件系统中的一个目录。URL是一个Git仓库的位置。
构建的上下文环境(context)会被递归的处理。所以一个路径(path)包含其下的所有子路径。Url包含对应的仓库以及所有的子模块。
一个简单的构建命令使用当前的目录作为上下文环境(context):

$ docker build .

构建过程运行在Docker的守护进程中而不是通过CLI。
构建过程的第一件事是将上下文入口(entire context)递归的发送给守护进程。大多数情况下,最好是使用一个空的目录作为构建的上下文环境(context)并且保证你的Dockerfile在当前目录中。这会只添加Dockerfile构建需要的文件到Image中。

警告:不要使用你文件系统的根目录(/)作为路径(path),因为它会导致讲硬盘上的所有文件传输到Docker的守护进程中。

要在构建环境中使用文件,Dockerfile引用指令中指定的文件,例如COPY指令. 为了增加构建的性能,可以在构建的上下文环境中添加.dockerignore文件,并在文件中指定需要排除在构建之外的文件和目录,这一点与.gitignore文件的用处类似。
传统上,Dockerfile 位于上下文环境的根目录中。你可以在执行docker build 命令的时候通过添加 -f 标记 指定你文件系统中任何位置的Dockerfile 作为构建脚本使用。

$ docker build -f /path/to/a/Dockerfile .

如果构建成功,你可以-t 标记使用一个仓库和标签保存构建好的Image(image)

$ docker build -t shykes/myapp .

如果需要在构建后将Image标记到多个仓库中,可以在构建时添加多个-t标记在build命令后面:

$ docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .

在Docker守护进程运行Dockerfile中的指令之前,它会对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中指令,必要的时候会将每一条指令提交到新Image中,在结束构建之前会将新Image的Id输出。Docker守护进程会自动的清理你发送的上下文环境。
注意每条指令都会独立的运行,并且会导致创建一个新的Image,所以 RUN cd /tmp 将不会在下一条指令中产生效果。
如果有可能,Docker 将会重新使用中间的Image(cache)来显著的加速docker build 的过程。会通过Using cache 在控制台的输出中标记使用缓存的信息。

$ 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

只有具有本地父Image链的情况下才能够使用缓存构建。这意味着这些Image会通过之前构建的或者使用docker load 命令加载的Image进行构建。如果你希望使用指定的Image作为构建缓存,你可以使用–cache-from 命令。–cache-from命令会从不同的registrie仓库中拉取指定的Image作为构建缓存。

格式:
Dockerfile 的格式如下:

# 注释(Comment)
指令(INSTRUCTION) 参数(arguments)

其中指令是不区分大小写的。然而,按照惯例它们都是大写的这是为了和参数进行区分开来。
Docker 按照顺序运行Dockerfile中的指令。第一个指令必需是FROM,它用来为你的构建指定一个基本的Image。

Docker会将以#开头的行作为注释,除非该行是一个有效的解析器指令(parser directive)。
在一行中的其他位置使用#会被作为一个参数对待。它允许声明成下面这样:

# Comment
RUN echo 'we are running some # of cool things'

注释中不支持行连续字符(\)

解析器指令(Parser directives):
解析器指令是可选的,并且会ImageDockerfile中后续行的处理方式。解析器指令不会添加层到构建中,并且也不会显示为一个构建步骤。解析器指令是以 # directive=value的格式写入指定类型的注释。一个单一指令可能只被使用一次。
一旦一个注释,空行或者构建指令被执行。Docker就不会再寻找解析器指令。相反,它会将任何符合解析器指令的格式视为注释,并且不会去尝试验证它是不是一个解析器指令。因此所有的解析器指令都必需位于Dockerfile的顶部。
解析器指令是不区分大小写的。然而,按照惯例,它们是小写的并且在每个解析器指令后会使用空行分隔。行连续字符(\)也是不被支持的。
无效的行连续字符(\):

# direc \
tive=value

出现两次也是无效的:

# directive=value1
# directive=value2
FROM ImageName

出现在构建指令后会被作为注释对待:

FROM ImageName
# directive=value

出现在注释后也会被作为注释对待:

# About my dockerfile
FROM ImageName
# directive=value

未知的指令会被作为注释对待,不会被承认。出现在其后的已知有效指令也会被作为注释对待。

# unknowndirective=value
# knowndirective=value

解析器指令允许非断行空格,因此以下行会使用相同的处理:

#directive=value
# directive =value
#   directive= value
# directive = value 

下面的解析器指令是被支持的:

  • escape

Escape

# escape=\ (backslash)
或者
# escape=` (backtick)
escape 用来设定在Dockerfile中作为转义字符的字符,如果不设置,默认使用\作为转义字符。
escape 有两种用法,一种是作为转义字符,为行中的字符进行转义,另一种是进行换行。这允许Dockerfile指令使用多行编写。注意,不管escape 是否作为解析器指令包含在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指令的目标和目录。因为Windows系统环境中路径语法看起来不是很自然,所以这种语法最多只会令人困惑,最坏的是,在Windows环境中不是所有的命令都支持 / 作为路径分隔符的。

通过添加escape 解析器指令,下面的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/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 C:\John>

改变运行环境(Environment replacement)

环境变量(使用 ENV 声明)也可以在Dockerfile中的某些指令作为参数使用。还可以处理转义,将字符串中的类似变量的语法包含在语句中。
在Dockerfile 中使用 varibalename {variable_name}的方式使用环境变量。它们的效果是一样的,并且大括号的使用方式通常用来解决不带空格使用变量的问题,比如: ${foo}_bar
${variable_name}语法也支持下面一些标准的bash 修饰符:

  • ${variable:-word} 表示如果variable被设置,则结果使用varibale的值,如果没有设置则使用word作为值.
  • ${variable:+word} 表示如果variable被设置,则结果使用word作为值,如果没有设置则使用空字符串作为值.

word可以是任何字符串包括其它的环境变量。

可以通过在变量前面添加\转义符,来作为别名,例如:$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
  • LABEL
  • USER
  • WORKDIR
  • VOLUME
  • STOPSIGNAL
    以及:
  • ONBUILD (与上述指令结合使用时)

注意:在1.4之前,ONBUILD指令不支持环境变量,即使结合上述任何指令。

环境变量替换将在整个命令中为每个变量使用相同的值。换句话说,在这个例子中:

ENV abc=hello
ENV abc=bye def=$abc
ENV ghi=$abc

结果将是def的值是hello而不是bye。然而,ghi的值将是bye,因为它不是将abc赋值为bye命令的一部分。

.dockerignore file

在docker CLI将上下文发送到docker守护程序之前,它会在上下文的根目录中查找名为.dockerignore的文件。如果此文件存在,则CLI修改上下文(context)以排除与其中的模式匹配的文件和目录。这有助于避免不必要地将大型或敏感的文件和目录发送到守护程序,并可能将其添加到使用ADD或COPY的Image。
CLI将.dockerignore文件解释为类似于Unix shell的文件globs的换行列表。为了匹配,上下文(context)的根目录被认为是工作和根目录。例如,模版 /foo/bar 和 foo/bar 两个都会排除一个foo目录下名为bar的文件或者目录。或者是Git仓库中同一路径。而不会排除其它文件和目录。
如果.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 消除.和..元素,预处理后面的空白行将会被忽略。

Docker还扩展了 Go的filepath.Match规则,Docker还支持一个特殊的通配符**,它匹配任意数量的目录(包括零)。例如,** / *。go将排除所有目录中找到的.go结尾的所有文件,包括构建上下文的根目录。

使用!开始的行,将不会被排除。如下:

   *.md
    !README.md

除了README.md 以外的所有Markdown文件都会被排除。
使用!开始的行排除的文件会收到后续行的影响,.dockerfile的最后一行指定的文件决定了它是否应该包含或者排除,请考虑下面的情况:

*.md
    !README*.md
    README-secret.md

除了README-secret.md 文件以外的名字以README开始的Markdown文件都不会排除,其它的Markdown文件都会被排除.

现在考虑下面这个例子:

    *.md
    README-secret.md
    !README*.md

所有的README文件都包含在内。中间那行没有任何效果,因为最后一行的 !README * .md与README-secret.md匹配。

您甚至可以使用.dockerignore文件来排除Dockerfile和.dockerignore文件。这些文件仍然被发送到守护进程,因为它需要它们来完成它的工作。但ADD和COPY命令不会将它们复制到Image。

最后,你可能需要指定要在上下文中包含的文件,而不是要排除哪些文件。要实现这一点,请指定*作为第一个模式,其次是一个或多个!异常模式。

注意: 因为历史原因,模式 . 会被忽略。

FROM

FROM <image>

或者

FROM <image>:<tag>

或者

FROM <image>@<digest>

FROM指令为后续指令设置了基础Image,后续的所有指令操作都是基于这个Image开始的。因此一个有效的Dockerfile必需使用FROM作为第一个指令。
基础Image可以是任何有效的Image。它可以非常容易从公开的Image仓库中获取。

  • FROM 必需是Dockerfile中第一个非注释指令

  • FROM 可以在同一个Dockerfile中出现多次顺序的创建多个Image。在每个新的FROM命令之前,简单地记录通过提交输出的最后一个ImageID。

  • tag 或者 digest的值是可选的。你如果省略其中任何一个,则默认情况下,构建器假定使用最新的Image。如果构建器与tag值不匹配,则返回错误。

RUN

RUN 命令有两种形式:

  • RUN (shell形式,命令在shell中运行,默认情况下在linux中是 /bin/sh -c 或者windows中是 cmd /S /C)

  • RUN [“executable”,”param1”,params2] (exec 形式)

RUN指令将在当前Image顶部的新层中执行任何命令,并提交结果。生成的提交Image将作为Dockerfile中的下一个指令使用。

分层运行指令和生成提交符合Docker的核心概念,其中提交很廉价,并且容器可以从Image历史中的任何点创建,就像源代码控制一样。

exec形式可以避免使用shell字符串,并使用不包含指定shell可执行文件的基本Image进行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:\windows\system32\tasklist.exe”] 此示例的正确语法是:RUN [“c:\windows\system32\tasklist.exe“]

RUN指令的缓存在下次构建中不会失效。用于诸如RUN apt-get dist-upgrade -y之类的指令的缓存将在下一次构建期间重新使用。RUN指令的缓存可以通过使用–no-cache标志(例如docker build –no-cache)来使其无效。

已知的问题(RUN)

  • Issue 783 是关于在使用AUFS文件系统时可能发生的文件权限问题。您可能会在尝试使用文件时注意到这一点。例如:对于具有最新aufs版本的系统(即,可以设置dirperm1安装选项),docker将尝试通过使用dirperm1选项安装层来自动修复问题。有关dirperm1选项的更多详细信息,请参见aufs手册页 如果您的系统不支持dirperm1,则该问题将描述一种解决方法。

CMD

CMD指令有三种形式:

  • CMD [“executable”,”param1”,”param2”] (exec 形式,这是首选形式)

  • CMD “params1”,”param2”

  • CMD command param1 param2 (shell 形式)

Docker文件中只能有一个CMD指令。如果列出多个CMD,那么只有最后一个CMD才会生效。

CMD的主要目的是为执行容器提供默认值。这些默认值可以包括一个可执行文件,或者它们可以省略可执行文件,在这种情况下你也必须指定一个ENTRYPOINT指令。

注意:如果使用CMD为ENTRYPOINT指令提供默认参数,CMD和ENTRYPOINT指令都应使用JSON数组格式指定.

注意: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指令设置运行Image时要执行的命令。

如果您使用CMD的shell形式,那么将在/bin/sh -c中执行:

FROM ubuntu
CMD echo "This is a test." | wc -

如果要运行您的而不使用shell,则必须将该命令表达为JSON数组,并提供可执行文件的完整路径。这个数组形式是CMD的首选格式。任何其他参数必须单独表示为数组中的字符串.

FROM ubuntu
CMD ["/usr/bin/wc","--help"]

如果您希望容器每次运行相同的可执行文件,那么您应该考虑使用ENTRYPOINT与CMD组合。

如果用户指定docker运行的参数,则它们将覆盖CMD中指定的默认值。

注意:不要将RUN与CMD混淆。 RUN实际上运行命令并提交结果; CMD在构建时不执行任何操作,而是指定Image的预期命令

LABEL

LABEL <key>=<value> <key>=<value> <key>=<value> ...

LABEL指令将元数据添加到Image中。 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."

Image可以有多个Label。要指定多个Label,Docker建议在可能的情况下将Label组合到单个LABEL指令中。每个LABEL指令都会产生一个新的层,如果使用很多Label,则可能会导致效率低下。该示例导致单个Image层。

LABEL multi.label1="value1" multi.label2="value2" other="value3"

以上也可以写成:

LABEL multi.label1="value1" \
      multi.label2="value2" \
      other="value3"

Label是附加的,包括FROM图像中的LABEL。如果Docker遇到已经存在的标签/键,则新值会覆盖任何先前具有相同键的Label。

要查看Image的Label,请使用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 <name>

MAINTAINER指令设置生成的Image的Author字段。 LABEL指令是一个更灵活的版本,您应该使用它,因为它可以设置所需的任何元数据,并且可以轻松查看,例如docker检查。要设置对应于MAINTAINER字段的Label,可以使用:

LABEL maintainer "SvenDowideit@home.org.au"

这将在docker inspect 显示。

EXPOSE

EXPOSE <port> [<port>...]

EXPOSE指令通知Docker容器在运行时侦听指定的网络端口。 EXPOSE不会使主机的端口可以访问。要做到这一点,您必须使用-p标记来发布一系列端口或-P标记来随机发布所有暴露的端口。您可以公开一个端口号,并在外部发布另一个端口号。 要在主机系统上设置端口重定向,请参阅使用-P标记。

ENV

ENV <key> <value>
ENV <key>=<value> ...

ENV指令将环境变量设置为值。该值将在所有“后代”Dockerfile命令的环境中有效,并且可以被内联替换(replaced inline)。

ENV指令有两种形式。第一个形式ENV 将单个变量设置为一个值。第一个空格后的整个字符串将被视为包括空格和引号等字符。

第二种形式ENV = …允许一次设置多个变量。请注意,第二种形式在语法中使用等号(=),而第一种形式则不使用等号。像命令行解析一样,引号和反斜杠可用于在值中包含空格。

例如:

ENV myName="John Doe" myDog=Rex\ The\ Dog \
    myCat=fluffy

以及:

ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy

这在最终的Image中产生相同的结果,但是第一种形式是优选的,因为它产生单个缓存层。
当从生成的Image运行容器时,使用ENV设置的环境变量将保持不变。您可以使用docker inspect 查看值,并使用docker run –env = 进行更改。

注意:环境持久性可能会导致意外的副作用。例如,设置ENV DEBIAN_FRONTEND非交互式可能会将apt-get用户混淆在基于Debian的Image上。要设置单个命令的值,请使用RUN = 。

ADD

ADD 有两种形式:

  • ADD …

  • ADD [“”,… “”] (包含空格的路径形式必须使用这种形式)

ADD指令从复制新文件,目录或远程文件URL,并将它们添加到路径的Image文件系统。
可以指定多个资源,但如果它们是文件或目录,则它们必须相对于正在构建的源目录(构建的上下文)。
每个可能包含通配符,并使用Go语言的filepath.Match规则进行匹配。例如:

ADD hom* /mydir/        # 添加所有hom开头的文件
ADD hom?.txt /mydir/    # ?可以替换单字符比如 home.txt、homa.txt等

< dest>是一个绝对路径,或相对于WORKDIR的路径,源文件将被复制到目标容器内。

ADD test relativeDir/          # 添加 "test" 到 `WORKDIR`/relativeDir/
ADD test /absoluteDir/         # 添加 "test" 到 /absoluteDir/

所有新文件和目录都使用UID和GID为0创建。
在是远程文件URL的情况下,目标将具有600的权限。如果正在检索的远程文件具有HTTP Last-Modified标头,则该标题的时间戳将用于设置目的地的mtime文件。然而,像在ADD期间处理的任何其他文件一样,在确定文件是否已更改并且缓存应该被更新的情况下,mtime将不会被包含。

注意:如果通过STDIN(docker build - < somefile)传递一个Dockerfile构建,那么就没有构建上下文,所以Dockerfile只能包含基于URL的ADD指令。您还可以通过STDIN:(docker build - < archive.tar.gz)传递压缩的归档文件,存档的根目录下的Dockerfile将作为构建的上下文使用。
注意:如果您的URL文件使用身份验证进行保护,则ADD指令不支持身份验证,则需要使用RUN wget,RUN curl或从容器内使用另一个工具。

注意:如果的内容已更改,第一个遇到的ADD指令会使Dockerfile中所有以下指令的缓存失效。这包括使RUN指令无效。

ADD遵守以下规则:

  • < src>路径必须位于构建的上下文之内;你不能ADD ../something/ something,因为docker构建的第一步是将上下文目录(和子目录)发送到docker守护程序。

  • 如果< src>是URL,并且不以尾部斜杠结尾,则从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具有相同的行为。

文件是否被识别为可识别的压缩格式是完全基于文件的内容而不是文件的名称完成的。例如,如果空文件以.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/        # 拷贝所有以hom开头的文件
COPY hom?.txt /mydir/    # ? 可以替换成任意单个字符

< dest>是一个绝对路径,或相对于WORKDIR的路径,源文件将被复制到目标容器内。

COPY test relativeDir/   # 复制 "test" 到 `WORKDIR`/relativeDir/
COPY test /absoluteDir/  # 复制 "test" 到 /absoluteDir/

所有新文件和目录都使用UID和GID为0创建。

注意:如果使用STDIN(docker build - < somefile)构建,则没有构建上下文,因此不能使用COPY。

COPY遵守以下规则:

  • < src>路径必须位于构建的上下文之内;你不能COPY ../something / something,因为docker构建的第一步是将上下文目录(和子目录)发送到docker守护程序。

  • 如果< src>是目录,则会复制目录的全部内容,包括文件系统元数据。

注意:目录本身不被复制,只是其内容。

  • 如果< src>是任何其他类型的文件,它将与其元数据一起单独复制。在这种情况下,如果< dest>以斜线/尾部结尾,则将被视为目录,< src>的内容将被写入< dest> / base(< src>)。

  • 如果指定了多个< src>资源,直接或由于使用通配符,则< dest>必须是一个目录,它必须以斜杠/结尾。

  • 如果< dest>不以尾部斜杠结尾,则将被视为常规文件,< src>的内容将被写入< dest>。

  • 如果< dest>不存在,则会在其路径中创建所有缺少的目录。

ENTRYPOINT

ENTRYPOINT 有两种形式:

  • ENTRYPOINT [“executable”, “param1”, “param2”] (exec 形式, 首选形式)

  • ENTRYPOINT command param1 param2 (shell 形式)

ENTRYPOINT允许您配置将作为可执行文件运行的容器。
例如,以下将使用默认内容启动nginx,在端口80上侦听:

docker run -i -t --rm -p 80:80 nginx

docker运行的命令行参数将被附加在exec形式ENTRYPOINT中的所有元素之后,并且将覆盖使用CMD指定的所有元素。这允许将参数传递到入口点,即docker运行 -d将-d参数传递到入口点。您可以使用docker run –entrypoint标志覆盖ENTRYPOINT指令。

shell形式防止任何CMD或run 命令行参数被使用,但是有一个缺点是您的ENTRYPOINT将作为未传递信号的/ bin / sh -c的子命令启动。这意味着可执行文件不会是容器的PID 1,并且不会收到Unix信号,因此您的可执行文件将不会从docker stop 接收到SIGTERM。(意思就是说,如果使用shell形式可以防止 CMD参数或者 run 命令行参数被用户使用,但是如果使用这种形式,那么ENTRYPOINT指定的应用程序就会作为/bin/sh -c 的子命令启动,这样如果用户通过docker stop 的形式停止容器的时候,你的应用就不会收到停止信号,就会变成异常终止,类似于宕机.)

只有Docker文件中的最后一个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

并且您可以优雅地请求top关闭使用docker停止测试。
以下Docker文件显示使用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信号:

\#!/bin/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运行此Image,则运行-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(不使用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 形式 ENTRYPOINT 例子

你可以为ENTRYPOINT指定一个纯字符串,并在/bin/sh -c中执行。这个形式将使用shell处理来替换shell环境变量,并且将忽略任何CMD或docker运行命令行参数。为了确保docker stop 信号可以正常发出,任何长时间运行的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

并且可以正常的退出:

$ /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

然后你正常的运行这个Image(给它指定一个名字,为了方便在下一步使用):

$ 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进程的PID并不是1.

如果你运行docker stop test ,容器不会正常的退出,stop 命令会在超时的时候强制发送 SIGKILL信号给top进程。

$ 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的相互作用

CMD和ENTRYPOINT指令都定义了运行容器时执行的命令。描述他们协作的规则很少。

  1. Dockerfile应该指定至少一个CMD或ENTRYPOINT命令。
  2. 使用容器作为可执行文件时,应定义ENTRYPOINT
  3. 应使用CMD作为定义ENTRYPOINT命令的默认参数或在容器中执行ad-hoc命令的方式。
  4. 使用备用参数运行容器时,CMD将被覆盖。

下表显示了对不同ENTRYPOINT / CMD组合执行的命令:

|No ENTRYPOINT| ENTRYPOINT exec_entry p1_entry | ENTRYPOINT [“exec_entry”, “p1_entry”]
————|————|————|————
No CMD|错误,不允许|/bin/sh -c exec_entry p1_entry|exec_entry p1_entry
CMD [“exec_cmd”, “p1_cmd”]| exec_cmd p1_cmd| /bin/sh -c exec_entry p1_entry|exec_entry p1_entry exec_cmd p1_cmd
CMD [“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 run 命令使用存在于基本Image中指定位置的任何数据初始化新创建的卷。例如,考虑以下Dockerfile片段:

FROM ubuntu
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
VOLUME /myvol

该Docker文件导致导致docker运行的Image,在/myvol创建一个新的安装点,并将greeting文件复制到新创建的卷中。

关于指定卷的注意事项

关于Dockerfile中的卷,请注意以下事项。

  • 基于Windows容器的卷:当使用基于Windows的容器时,容器内卷的目的地必须是以下之一:
    • 一个不存在或空的目录
    • C以外的驱动器:
  • 从Dockerfile中更改卷:如果任何构建步骤在声明后更改卷中的数据,那么这些更改将被丢弃。
  • JSON格式:将列表解析为JSON数组。您必须用双引号(“)而不是单引号(’)括起单词。
  • 主机目录在容器运行时被声明:主机目录(mountpoint)本质上是与主机相关的。这是为了保持图像可移植性。因为给定的主机目录不能保证在所有主机上可用。因此,您无法从Dockerfile中挂载主机目录。 VOLUME指令不支持指定host-dir参数。创建或运行容器时,必须指定安装点。

USER

USER daemon

USER指令为Dockerfile中运行的所有RUN,CMD,ENTRYPOINT设置运行Image时使用的用户名或UID。

WORKDIR

WORKDIR /path/to/workdir

WORKDIR指令为Dockerfile中的所有RUN,CMD,ENTRYPOINT,COPY和ADD指令设置工作目录。如果WORKDIR不存在,即使在任何后续的Dockerfile指令中没有使用它,也将创建它。
它可以在一个Docker文件中多次使用。如果提供相对路径,它将相对于先前WORKDIR指令的路径。例如:

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

此Dockerfile中的最终pwd命令的输出将为/a/b/c(类似于cd)

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
...

Dockerfile作者可以可选地为ARG指令指定默认值:

FROM busybox
ARG user1=someuser
ARG buildno=1
...

如果ARG指定的变量具有默认值,并且如果在构建时没有传递任何值,则构建器将使用默认值。

一个ARG变量定义从Dockerfile中定义的一行开始生效,而不是在命令行或其他地方使用参数。例如,考虑这个Dockerfile:

1 FROM busybox
2 USER ${user:-some_user}
3 ARG user
4 USER $user
...

用户通过调用以下方式构建此文件:

$ docker build --build-arg user=what_user Dockerfile

第2行的USER的值为some_user,因为在后续第三行中定义了用户变量。第4行的USER的值为用户定义的what_user,并在命令行中传递了what_user值。在通过ARG指令定义之前,对变量的任何使用都将导致空字符串。(意思就是说,你可以在声明变量之前使用这个变量,但是这个变量不会被赋值,值始终是空字符串)

警告:不建议使用构建时变量来传递秘密,如github的密钥,用户凭证等。使用docker history命令,图像的任何用户都可以看到构建时变量值。

您可以使用ARG或ENV指令来指定可用于RUN指令的变量。使用ENV指令定义的环境变量始终覆盖相同名称的ARG指令。考虑这个Dockerfile中的ENV和ARG指令。

1 FROM ubuntu
2 ARG CONT_IMG_VER
3 ENV CONT_IMG_VER v1.0.0
4 RUN echo $CONT_IMG_VER

然后,假设这个Image是用这个命令构建的:

$ docker build --build-arg CONT_IMG_VER=v2.0.1 Dockerfile

在这种情况下,RUN指令使用v1.0.0而不是由用户传递的ARG设置的v2.0.1,从它的定义上来看这种行为类似于shell脚本,其中本地作用域变量将覆盖作为参数传递或从环境继承的变量。 使用上述示例,但不同于ENV规范,您可以在ARG和ENV指令之间创建更有用的交互:

1 FROM ubuntu
2 ARG CONT_IMG_VER
3 ENV CONT_IMG_VER ${CONT_IMG_VER:-v1.0.0}
4 RUN echo $CONT_IMG_VER

与ARG指令不同,ENV值始终保留在内置Image中。考虑一个没有–build-arg标志的docker构建:

$ docker build Dockerfile

使用此Dockerfile示例,CONT_IMG_VER仍然保留在Image中,但其值将为v1.0.0,因为它是ENV指令的第3行默认设置。

此示例中的变量扩展技术允许您从命令行传递参数,并通过使用ENV指令将它们持久保留在最终Image中。只有一组有限的Dockerfile指令才支持变量扩展。

Docker有一组预定义的ARG变量,您可以在Dockerfile中使用而不需要使用ARG指令声明。

  • HTTP_PROXY
  • http_proxy
  • HTTPS_PROXY
  • https_proxy
  • FTP_PROXY
  • ftp_proxy
  • NO_PROXY
  • no_proxy

要使用这些,只需在命令行中使用标志:

--build-arg < varname>=< value>

Impact on build caching

和ENV变量相比,ARG变量不会保留在内置Image中。但是,ARG变量会以类似的方式影响构建缓存。如果一个Dockerfile定义了一个值与前一个版本不同的ARG变量,那么在它的第一次使用时就会出现一个“cache miss”,而不是在它的定义时。具体来说,ARG指令之后的所有RUN指令会隐晦的使用ARG变量(作为环境变量),因此导致高速缓存未命中。

例如,考虑这两个Dockerfile:

1 FROM ubuntu
2 ARG CONT_IMG_VER
3 RUN echo $CONT_IMG_VER
1 FROM ubuntu
2 ARG CONT_IMG_VER
3 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 ubuntu
2 ARG CONT_IMG_VER
3 ENV CONT_IMG_VER $CONT_IMG_VER
4 RUN echo $CONT_IMG_VER

在这个例子中,高速缓存未命中发生在第3行。由于ENV中的变量的值引用ARG变量并且该变量通过命令行进行更改,因此出现未命中。在此示例中,ENV命令使Image包含该值。

如果ENV指令覆盖相同名称的ARG指令,就像这个Dockerfile:

1 FROM ubuntu
2 ARG CONT_IMG_VER
3 ENV CONT_IMG_VER hello
4 RUN echo $CONT_IMG_VER

第3行不会导致缓存未命中,因为CONT_IMG_VER的值为常数(hello)。因此,在RUN(第4行)上使用的环境变量和值在构建之间不会改变。

ONBUILD

ONBUILD [INSTRUCTION]

当Image作为另一个构建脚本的基础Image时,ONBUILD指令会被添加到Image中在下一次构建时触发。触发器将在下次构建的上下文中执行,就好像它被插入到下次构建的Dockerfile中的FROM指令之后一样。(翻译不太好,其实这个意思就是说,一个Image如果声明了ONBUILD指令,那么如果这个Image被作为一个FROM指令引用的Image也就是被作为基础Image时,在构建的时候,ONBULD指令会被插入到Dcokerfile的FROM指令之后,FROM指令执行完会立即执行这个ONBUILD指令)

任何构建指令都可以注册为触发器。

如果您正在构建将用作构建其他Image的基础的Image(例如可以使用用户特定配置定制的应用程序构建环境或守护程序),这将非常有用。

例如,如果您的Image是可重用的Python应用程序构建器,则需要将应用程序源代码添加到特定目录中,并且可能需要在之后调用构建脚本。您现在无法调用ADD和RUN,因为您还没有访问应用程序源代码,并且每个应用程序构建都会有所不同。您可以简单地为应用程序开发人员提供一个样板Docker文件,将其复制粘贴到应用程序中,但这是低效率,容易出错,难以更新的,因为它与应用程序特定的代码混合在一起。

解决方案是在下一个构建阶段使用ONBUILD来注册提前说明以便稍后运行。

以下是它的工作原理:

  • 当遇到ONBUILD指令时,构建器将为正在构建的Image的元数据添加触发器。该指令不会影响当前版本。
  • 在构建结束时,所有触发器的列表都存储在Image清单中的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指令将发送到容器运行的系统中调用停止信号退出。该信号可以是与内核的系统调用表中的位置相匹配的有效无符号数,(例如 9)或者SIGNAME格式的信号名称,例如SIGKILL。

HEALTHCHECK

HEALTHCHECK指令有两种形式:

  • HEALTHCHECK [OPTIONS] CMD command(通过在容器内运行命令来检查容器的健康状况)
  • HEALTHCHECK NONE (禁用从基本图像继承的任何healthcheck)

HEALTHCHECK指令告诉Docker如何去测试一个容器是否仍然工作。这样可以检测出一系列无限循环的Web服务器,无法处理新连接的情况。

当容器指定了健康检查时,除了正常状态之外,它还具有健康状态。这个状态最初是开始的。每当健康检查通过时,它变得健康(无论以前是什么状态)。经过一定数量的连续失败后,会变得不健康。

CMD之前可以出现的选项如下:

  • –interval=DURATION (default: 30s)
  • –timeout=DURATION (default: 30s)
  • –retries=N (default: 3)

运行状况检查将首先运行容器,启动后间隔一段时间(秒)进行检查,然后在每次上一次检查完成后再次间隔一段时间(秒)。

如果单次检查的运行时间超过了超时秒数,则该检查被认为失败。

如果连次多次检查失败,将会视为不健康的状态。

Docker文件中只能有一个HEALTHCHECK指令。如果您列出多于一个,那么只有最后一个HEALTHCHECK才能生效。

CMD关键字之后的命令可以是一个shell命令(例如,HEALTHCHECK CMD /bin/ check-running)或一个exec数组。

命令的退出状态表示容器的健康状态。可能的值是:

  • 0:成功 - docker 健康,准备使用
  • 1:不健康 - 容器运行不正常
  • 2:保留 - 不要使用此退出代码

例如,要每隔五分钟左右检查一次,网络服务器能够在三秒钟内提供站点的主页面:

HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost/ || exit 1

为了调试和探测错误,命令会将在stdout或stderr中输出的所有文本(UTF-8编码)存储在运行状况中,可以通过docker inspect进行查看。这种输出应该保持比较小的字符量(仅存储前4096个字节)

当容器的运行状况更改时,将生成一个具有新状态的health_status事件。

Docker 1.12中添加了HEALTHCHECK功能。

SHELL

SHELL ["executable", "parameters"]

SHEELL指令允许默认的shell形式被命令形式覆盖。默认shell形式在 Linux 上是 [“/bin/sh”, “-c”], 在 Windows上是 [“cmd”, “/S”, “/C”]。SHELL指令必须用Dockerfile中的JSON格式写入。SHELL指令在Windows上特别有用,其中有两个常用的和完全不同的本机shell:cmd和powershell,以及包括sh的备用shell。
SHELL指令可以出现多次。每个SHELL指令都会覆盖所有以前的SHELL指令,并影响所有后续指令。例如:

FROM microsoft/windowsservercore

# 以 cmd /S /C 执行 echo default
RUN echo default

# 用 cmd /S /C 作为 powershell -command 执行 Write-Host default
RUN powershell -command Write-Host default

# 作为 powershell -command 执行 Write-Host hello
SHELL ["powershell", "-command"]
RUN Write-Host hello

# 作为 cmd /S /C 执行 echo hello
SHELL ["cmd", "/S"", "/C"]
RUN echo hello

当使用SHELL指令时,Dockerfile中后续的 RUN、CMD和ENTRYPOINT指令如果以shell形式执行,就会被它影响。

以下示例是在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形式中的每个RUN指令都需要一个额外的powershell -command命令前缀。

虽然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操作的方式。例如,在Windows上使用SHELL cmd /S /C /V:ON | OFF,可以修改延迟的环境变量扩展语义。

如果需要一个替代的shell(如zsh,csh,tcsh等),SHELL指令也可以在Linux上使用。

Docker 1.12中添加了SHELL功能。

Dockerfile examples

# 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.
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值