ref: https://docs.docker.com/engine/reference/builder/#usage

Dockerfile

Docker可以通过读取Dockerfile指令自动生成镜像 。Dockerfile文件是一个文本文档,其中包含用户可以在命令行上调用以构建镜像的所有命令。通过使用docker build, 用户可以创建一个自动构建,它会连续执行Dockerfile文件定义的命令行指令。

本教程描述了你可以在Dockerfile中使用的所有命令。阅读本页面后,请参阅Dockerfile最佳实践

用法

docker build命令将会根据一个Dockerfile文件和上下文构来建镜像。构建的上下文是在指定位置的文件集合,可以是PATHURLPATH是本地文件系统上的一个目录。URL是一个Git仓库的位置。

上下文会被递归地处理。所以,PATH 可以包括任何子目录,且URL可以包含存储库及其子模块。这个例子展示了一个使用当前目录作为上下文的构建命令:

$ docker build .
Sending build context to Docker daemon  6.51 MB
...

构建由Docker守护程序运行,而不是由CLI运行。构建过程所做的第一件事是将整个上下文(递归地)发送到守护进程。在大多数情况下,最好以空目录作为上下文,并将Dockerfile保存在该目录中。仅添加构建Dockerfile所需的文件。

警告:不要用你的根目录,/ 作为 PATH 因为它会导致将你的硬盘驱动器的全部内容挂载到Docker守护进程,这是不安全的。

若想要在构建上下文中使用某个文件,可以通过 Dockerfile 引用指令来指定文件,例如COPY指令。为了增加构建的性能,可以通过将.dockerignore文件添加到上下文目录来排除文件和目录。有关如何创建.dockerignore 文件的信息,请参阅此页面上的文档。

传统上,Dockerfile被称为Dockerfile并位于上下文的根目录。您也可以使用 -f 参数指定 docker build指向文件系统中任何位置的Dockerfile 文件。

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

如果构建成功,您可以指定一个存储库(镜像名)和标签来保存新镜像:

$ docker build -t shykes/myapp .

若想要在构建完成后将镜像标记到多个存储库中,请在运行build命令时添加多个 -t 参数:

$ 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中的指令,在必要时将每条指令的结果提交给新镜像,最后输出新镜像的ID 。Docker守护进程将自动清理您发送的上下文。

请注意,每条指令都是独立运行的,并且会创建一个新的镜像,所以RUN cd /tmp不会对下一条指令产生任何影响。

只要有可能,Docker将重新使用中间镜像(缓存),以显着加速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

构建缓存仅用于具有本地父链(父镜像)的镜像。这意味着这些镜像是由以前的版本创建的,或者是整个镜像链通过docker load加载的。如果您希望使用特定镜像的构建缓存,则可以使用--cache-from选项指定它。通过--cache-from指定的镜像不需要有父链,它可以从其他registries中提取。

当你完成你的构建时,你已经准备好将其push到registries中了。

格式

这里是Dockerfile文件的格式:

# Comment
INSTRUCTION arguments

指令不区分大小写。但是为了方便将指令和参数区分开,约定指令为大写。

Docker 按顺序运行Dockerfile 中的指令。一个Dockerfile 文件必须从FROM指令开始。该FROM指令指定了您正在构建的基础镜像。FROM只能在该行所有参数之前。

Docker 将会把以#开始的行当做注释,除非该行是一个有效的解析器指令。在指令行中其他任何地方的#标记都被视为参数。这允许像这样的语句:

# 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

# escape=\ (backslash)

要么

# escape=` (backtick)

escape指令用于设置 Dockerfile中的转义字符。如果未指定,则默认转义字符为\

转义字符既用于转义一行中的字符,也用于转义换行符。这允许Dockerfile指令跨越多行。请注意,无论escape解析器指令是否包含在Dockerfile 中,转义都不会在RUN命令中执行,除非在行的末尾。

将转义字符设置为```````在Windows非常有用,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上的所有命令都支持/作为路径分隔符,所以容易出错。

通过添加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>

##环境替换

环境变量(用ENV语句声明)也可以在某些指令中用作由Dockerfile解释的变量。转义也会被处理,从字面上包含类似于变量的语法。

Dockerfile 中的环境变量用 $variable_nameor 或者 ${variable_name} 来标记。他们会被同等对待,大括号语法通常用于解决变量名称没有空白的问题,如${foo}_bar

${variable_name}语法还支持一些标准bash 修饰符,如下所示:

  • ${variable:-word}表示如果变量variable被设置,则结果将是该值。如果变量variable没有设置,那么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 (当与以上支持的指令结合使用时)

注意:在1.4之前,ONBUILD指令不支持环境变量,即使与上面列出的任何指令结合在一起。

##.dockerignore文件
在docker客户端(CLI)将上下文发送到docker守护程序之前,它会查找上下文根目录中的.dockerignore文件。如果此文件存在,CLI会修改上下文以排除与.dockerignore文件中的模式匹配的文件和目录。这有助于避免不必要地将大型或敏感文件和目录发送到守护程序,而是尽可能地使用ADDCOPY将其添加到镜像。

CLI将.dockerignore文件解释为一个以换行符分隔的模式列表,类似于Unix shell的文件格式。为了匹配的目的,上下文的根被认为是工作目录和根目录。例如,模式 /foo/barfoo/bar会在PATH路径下的foo子目录或位于URL地址的git存储库的根目录中排除名为bar的文件或目录,但是不会排除其他任何东西。

如果.dockerignore文件中的某一行以#开头,则此行被视为注释,并在被CLI解释之前被忽略。

这是一个示例.dockerignore文件:

# comment
*/temp*
*/*/temp*
temp?

此.dockerignore文件将导致以下构建行为:

规则行为
# 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之外的所有md(markdown)文件都将从上下文中排除。

放置! 的异常规则会影响行为:.dockerignore 的最后一行负责匹配特定文件并确定是否包含或排除。 思考下面的例子:

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

除了README-secret.md之外的其他README文件,上下文中不包括任何md文件。
现在思考这个例子:

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

所有的README文件都包含在内。 中间行(README-secret.md)不起作用,因为!README * .md匹配README-secret.md并且是最后一行。

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

最后,您可能需要指定哪些文件包含在上下文中,而不是要排除的文件。 要实现这一点,请将*指定为第一个模式,然后再指定一个或多个! 例外模式。

FROM

FROM <image> [AS <name>]

Or

FROM <image>[:<tag>] [AS <name>]

Or

FROM <image>[@<digest>] [AS <name>]

FROM指令初始化一个新的构建过程并为后续指令设置基础映像。 因此,有效的Dockerfile 文件必须以FROM指令开始。 镜像可以是任何有效的镜像 - 可以很容易通过从公共存储库中拉出镜像。

  • ARG 可能是Dockerfile中位于FROM之前的唯一指令。 请参阅了解ARG和FROM的交互方式。

  • FROM可以在单个Dockerfile文件中多次出现以创建多个镜像或将一个构建阶段用作另一个构建阶段的依赖关系。 只需在每个新的FROM指令之前记录提交输出的最后一个镜像ID。 每条FROM指令清除以前指令创建的任何状态。

  • 通过将AS名称添加到FROM指令,可以给新的构建过程赋予一个名称。 该名称可用于后续的FROMCOPY --from = <name | index>指令以引用此阶段中构建的镜像。

  • 标签或摘要值是可选的。 如果您忽略其中之一,则默认情况下,构建器会采用latest的标记。 如果无法找到标记值,构建器将返回错误。

了解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

An ARG declared before a FROM is outside of a build stage, so it can’t be used in any instruction after a FROM. To use the default value of an ARG declared before the first FROM use an ARG instruction without a value inside of a build stage:

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中的下一步。

分层运行指令和生成提交符合Docker的核心概念,其中提交很方便,容器可以从镜像历史中的任意点创建,与源代码控制非常相似。

exec形式可以避免shell字符串的转换,并且可以使用不包含指定的可执行shell的基础映像运行命令。

对于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。

RUN指令缓存在下一次构建期间不会自动失效。 像RUN apt-get dist-upgrade -y这样的指令的缓存将在下一次构建时重用。 RUN指令缓存可以通过使用--no-cache标志失效,例如docker build --no-cache

有关更多信息,请参阅Dockerfile最佳实践指南

通过ADD指令可以使RUN指令缓存失效。 详情请参阅下文。

CMD

CMD指令有三种形式:

CMD ["executable","param1","param2"](exec形式,这是首选形式)
CMD ["param1","param2"](作为默认参数ENTRYPOINT)
CMD command param1 param2(shell形式)

Dockerfile中只能有一个CMD指令。 如果列出多个CMD,则只有最后一个CMD才会生效。

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

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

当以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"]

如果你希望你的容器每次都运行相同的可执行文件,那么你应该考虑将ENTRYPOINTCMD结合使用。

如果用户指定docker run运行的参数,那么它们将覆盖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."

一个镜像可以有多个label。您可以在一行中指定多个标签。在Docker 1.10之前,这能减少了最终镜像的大小,但现在不再这样了。您仍然可以选择使用在单条指令中指定多个标签:

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

包含在基础镜像或父镜像(FROM 行指定的镜像)中的标签由镜像继承。如果标签已经存在但使用不同的值,则最近应用的值将覆盖任何先前设置的值。

要查看镜像的标签,请使用该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指令设置生成镜像的作者字段。LABEL指令是一个比MAINTAINER指令更灵活的版本,你应该使用它,因为它可以设置所需的任何元数据,并且可以很容易地查看,例如docker inspect。要实现MAINTAINER指令相似的功能你可以使用 label指令指定对应的字段 :

LABEL maintainer="SvenDowideit@home.org.au"

通过 docker inspect 指令可以看见所有label数据。

EXPOSE

EXPOSE <port> [<port>/<protocol>...]

EXPOSE通知Docker,该容器在运行时监听指定的网络端口。你可以指定端口是TCP还是UDP,如果未指定协议,则默认为TCP。

EXPOSE指令并不实际发布该端口。它用作构建镜像的人与运行容器的人之间的关于哪些端口打算发布的文档。要在运行容器时(docker run )指定实际发布端口,请使用-p标志发布和映射一个或多个端口,或者使用-P标志发布所有公开的端口并将它们映射到高阶端口。

要在主机系统上设置端口重定向,请参阅使用-P标志docker network命令支持为容器之间的通信创建网络,而无需公开或发布特定端口,因为连接到网络的容器可以通过任何端口相互通信。有关详细信息,请参阅此功能的概述)。

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 [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"] (此形式对于包含空格的路径是必需的)

注意:--chown功能仅在用于构建Linux容器的Dockerfiles上被支持,并不适用于Windows容器。由于用户和所属组概念不能在Linux和Windows之间进行转换,因此使用/etc/passwd/etc/group将用户名和组名转换为ID会将此功能限制为仅适用于基于Linux操作系统的容器。

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/

除非使用可选的--chown标志指定用户名,组名或UID / GID组合来请求所添加内容的特定所有权,否则所有新文件和目录均使用UID和GID 0创建。该--chown标志的格式允许使用用户名和组名字符串,或以任意组合形式直接使用整数UID和GID。提供没有组名的用户名或没有GID的UID将使用与GID相同的数字UID。如果提供了用户名或组名,容器的根文件系统/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 标志中使用了用户名或组名,则构建将在ADD操作上失败。使用数字标识不需要查找,也不依赖于容器根文件系统内容。

如果<src>是远程文件URL,则目标将具有600的权限。如果正在检索的远程文件具有HTTP Last-Modified标头,则将使用该标头的时间戳来设置目标文件上的mtime时间戳。但是,与在ADD期间处理的任何其他文件一样,mtime将不会包含在文件是否已更改以及缓存应该更新的确定中。

注意:如果通过STDIN传递Dockerfiledocker build - < somefile)来构建,则没有构建上下文,因此Dockerfile 只能包含基于URL的ADD指令。你还可以通过STDIN传递一个压缩文档:(docker build - < archive.tar.gz),压缩文档根目录的Dockerfile和其余部分将用作构建的上下文。

注意:如果你的url文件使用了认证保护,由于ADD指令不支持认证, 因此你需要使用RUN wgetRUN curl或其它工具。

注意:如果<src>的内容已更改,则第一次遇到的ADD指令将使缓存中的所有后续指令无效。这包括使RUN指令的缓存无效。有关更多信息,请参阅Dockerfile最佳实践指南

ADD 遵守以下规则:

  • <src>路径必须位于构建的上下文中(即<src>的路径必须在Dockerfile文件根目录或其子目录中); 你不能ADD ../something /something,因为docker build的第一步是将上下文目录(和子目录)发送到docker守护进程。

  • 如果<src>是一个URL并且<dest>不以斜杠结尾,那么文件将从URL下载并复制到<dest>

  • 如果<src>是一个URL,并且<dest>以斜杠结尾,则从URL中推断出该文件名,并将该文件下载到<dest>/<filename>。 例如,添ADD http://example.com/foobar /会创建文件/foobar。 该URL必须有一个有效的路径,以便找到适当的文件名(http://example.com将不起作用)。

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

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

  • 如果<src>是一个可识别的本地压缩归档文件(identity,gzip,bzip2或xz),那么它将被解压缩为一个目录。 来自远程URL的资源不被解压缩。 当一个目录被复制或解压时,它与tar -x具有相同的行为,结果是:
       1.无论在目的地路径和目的地存在什么
       2.源代码树的内容,在逐个文件的基础上解决冲突,支持“2.”。

注意:文件是否被识别为压缩格式完全是基于文件的内容而不是文件的名称。例如,如果空文件恰好以.tar.gz结尾,则不会将其识别为压缩文件,也不会生成任何类型的解压缩错误消息,而只是将文件复制到目标路径。

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

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

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

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

COPY

COPY有两种形式:

COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"] (此形式对于包含空格的路径是必需的)

注意:--chown特性仅在用于构建Linux容器的Dockerfiles上受支持,并且不适用于Windows容器。

COPY指令从<src>中复制新的文件或目录,并将它们添加到容器文件系统中的路径<dest>处。

可以指定多个<src>资源,但文件和目录的路径将被解释为相对于构建的上下文来源(上下文根目录,即Dockerfile文件所属的目录)。

每个<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/

除非使用可选的--chown标志指定用户名,组名或UID / GID组合来请求所添加内容的特定所有权,否则所有新文件和目录均使用UID和GID 0创建。该--chown标志的格式允许使用用户名和组名字符串,或以任意组合形式直接使用整数UID和GID。提供没有组名的用户名或没有GID的UID将使用与GID相同的数字UID。如果提供了用户名或组名,容器的根文件系统/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/

如果容器根文件系统不包含任何/etc/passwd/etc/group文件,并且在该--chown 标志中使用了用户名或组名,则构建将在ADD操作上失败。使用数字标识不需要查找,也不依赖于容器根文件系统内容。

如果<src>是远程文件URL,则目标将具有600的权限。如果正在检索的远程文件具有HTTP Last-Modified标头,则将使用该标头的时间戳来设置目标文件上的mtime时间戳。但是,与在ADD期间处理的任何其他文件一样,mtime将不会包含在文件是否已更改以及缓存应该更新的确定中。

注意:如果通过STDIN传递Dockerfiledocker build - < somefile)来构建,则没有构建上下文,因此COPY不能使用。

COPY可以接受一个标志参数--from=<name|index>,该参数可用于将源位置设置为之前的构建阶段(使用创建的FROM .. AS <name>),这将用于代替用户发送的构建上下文。该标志还接受为以FROM指令开始的所有以前的构建阶段分配的数字索引。 如果无法找到具有指定名称的构建阶段,则尝试使用具有相同名称的镜像。

COPY 遵守以下规则:

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

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

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

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

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

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

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

ADD和COPY的区别

相比ADDCOPY 更加直接了当,只复制文件或者目录到容器里。COPY不支持URL,也不会特别对待压缩文件。如果build 上下文件中没有指定解压的话,那么就不会自动解压,只会复制压缩文件到容器中。COPYADD的一种简化版本,目的在于满足大多数人“复制文件到容器”的需求。Docker 团队的建议是在大多数情况下使用COPY(除非你明确你需要ADD)

VOLUME

VOLUME ["/data"]

VOLUME指令创建一个具有指定名称的挂载点,并将其标记为从本机主机或其他容器中存储外部安装的卷。 该值可以是JSON数组,VOLUME ["/var/log/"]或具有多个参数的纯字符串,例如VOLUME /var/logVOLUME /var/log /var/db。 有关通过Docker客户端的更多信息/示例和安装说明,请参阅通过卷文档共享目录

docker run命令使用基础镜像中指定位置存在的任何数据初始化新创建的卷。 例如,请考虑以下Dockerfile代码片段:

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

Dockerfile会生成一个镜像,导致docker run/myvol处创建新的挂载点,并将greeting文件复制到新创建的卷中。

有关指定卷的说明

记住Dockerfile中的卷的以下几点。

  • 基于Windows的容器上的卷:使用基于Windows的容器时,容器内的卷的目标必须是以下之一:

  • 一个不存在的或空的目录

  • C以外的驱动器

  • 在Dockerfile中更改卷:如果任何构建步骤在声明后更改了卷内的数据,则这些更改将被丢弃。

  • JSON格式:列表被解析为JSON数组。您必须用双引号(")括住单词而不是单引号(’)。

  • 主机目录在容器运行时声明:主机目录(挂载点)本质上取决于主机。 这是为了保持镜像的可移植性,因为无法保证给定主机目录在所有主机上都可用。 因此,你不能在Dockerfile中挂载一个主机目录。 VOLUME指令不支持指定host-dir参数。 你必须在创建或运行容器时指定挂载点。

USER

USER <user>[:<group>] or
USER <UID>[:<GID>]

USER指令设置用户名(或UID)和可选的用户组(或GID),以在运行镜像时以及在Dockerfile中执行后续的任何RUNCMDENTRYPOINT指令。

警告:当用户没有主组时,镜像(或下一个指令)将与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中后面的任何RUNCMDENTRYPOINTCOPYADD指令设置工作目录。 如果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

持续更新中 …

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐