Docker 通过从一个 Dockerfile
文本文件中读取指令来自动构建镜像,该文本文件按顺序包含构建给定镜像所需的所有命令。Dockerfile
遵循特定的格式和指令集。
Docker 镜像由只读层组成,每个只读层代表一个 Dockerfile 指令。这些层是堆叠的,每个层都是与上一层相比变化的增量。
例如:
1 | FROM ubuntu:18.04 |
每条指令创建一层:
FROM
从ubuntu:18.04
Docker 镜像创建一个图层。COPY
从 Docker 客户端的当前目录添加文件。RUN
使用make
构建您的应用程序。CMD
指定在容器中运行什么命令。
运行图像并生成容器时,可以 在基础层之上添加一个新的可写层(“容器层”)。对运行中的容器所做的所有更改(例如写入新文件,修改现有文件和删除文件)都将写入此可写容器层。
FROM
初始化一个新的构建阶段,并为后续指令设置 基本镜像。
有效的 Dockerfile
必须从 FROM
指令开始。
在一个 Dockerfile 中,ARG 指令是唯一可以早于 FROM 指令的指令。
语法
1 | FROM [--platform=<platform>] <image> [AS <name>] |
或
1 | FROM [--platform=<platform>] <image>[:<tag>] [AS <name>] |
或
1 | FROM [--platform=<platform>] <image>[@<digest>] [AS <name>] |
示例
FROM 使用 ARG 指令定义的变量
1 | ARG CODE_VERSION=latest |
RUN
运行指令
语法
shell 形式,命令在 shell 中运行,默认情况下在 Linux
上是 /bin/sh -c
或 Windows
上使用 cmd /S /C
运行
1 | RUN <command> |
或
exec 方式执行
1 | RUN ["executable", "param1", "param2"] |
示例
在 shell 形式中,您可以使用\
(反斜杠)将一条 RUN 指令继续到下一行。例如:
1 | RUN /bin/bash -c 'source $HOME/.bashrc; \ |
等价于
1 | RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME' |
使用’/ bin / sh’以外的其他 shell,请使用 exec 形式传入所需的shell。例如
1 | RUN ["/bin/bash", "-c", "echo hello"] |
注意
EXEC 形式被解析为一个 JSON array,这意味着必须使用双引号(“)而不是单引号(’)。
与 shell 形式不同, exec 方式不会替换变量,例如 RUN [ "echo", "$HOME" ]
将不会对变量 $HOME
进行替换。
如果要进行 shell 处理,则可以使用 shell 形式或直接执行 shelll,例如:RUN [ "sh", "-c", "echo $HOME" ]
。当使用 exec 表单并直接执行 shell 时(例如在 shell 表单中),是由 shell 进行环境变量扩展,而不是 docker。
注意
在 JSON 格式中,必须转义反斜杠。在 Windows 中,反斜杠是路径分隔符,这一点尤其重要。以下 shell 形式将被视为无效的 JSON ,并以意外的方式失败:
1 RUN ["c:\windows\system32\tasklist.exe"]此示例的正确语法为:
1 RUN ["c:\\windows\\system32\\tasklist.exe"]
CMD
CMD命令设置容器启动后默认执行的命令及其参数,但 CMD 设置的命令能够被 docker run
命令后面的命令行参数替换。一个 Dockerfile 只能有一条 CMD 指令。如果存在多个,则只有最后一个 CMD 指令生效
语法
exec 形式,这是首选形式
1 | CMD ["executable","param1","param2"] |
或
1 | CMD ["param1","param2"] |
或
1 | CMD command param1 param2 |
示例
1 | FROM ubuntu |
LABEL
标签。 LABEL
指令将元数据添加到 image
标签是键值对
语法
1 | LABEL <key>=<value> <key>=<value> <key>=<value> ... |
示例
以下用法都可以:
1 | LABEL "com.example.vendor"="ACME Incorporated" |
查看 LABER
设置的元数据
1 | docker inspect <container> |
MAINTAINER [已过时]
设置生成图像的作者.
LABEL
指令比此版本的灵活得多,您应该改用它,因为它可以设置所需的任何元数据,并且可以轻松查看,例如使用 docker inspect
。
语法
1 | MAINTAINER <name> |
使用 Laber 标签的等效语法如下
1 | LABEL maintainer="<name>" |
EXPOSE
EXPOSE
指令通知 Docker 容器在运行时监听指定的网络端口。您可以指定端口是侦听 TCP 还是 UDP,如果未指定协议,则默认值为TCP。EXPOSE
指令实际上并未发布端口。它充当构建镜像的人员和运行容器的人员之间的一种文档类型,有关打算发布哪些端口的信息。要在运行容器时实际发布端口,请使用 docker run -p
映射端口。
示例
默认情况下,EXPOSE
假定为TCP。您还可以指定UDP:
1 | EXPOSE 80/udp |
要同时在 TCP 和 UDP 上公开,请包括以下两行:
1 | EXPOSE 80/tcp |
无论 EXPOSE
设置如何,都可以在运行时使用该 -p
标志覆盖它们。例如
1 | docker run -p 80:80/tcp -p 80:80/udp ... |
ENV
ENV
指令将环境变量<key>
设置为 value <value>
,此值将在构建阶段中所有后续指令的环境中使用。
语法
1 | ENV <key> <value> |
第一种形式,ENV <key> <value>
会将一个变量设置为一个值。第一个空格之后的整个字符串将被视为<value>
-包括空格字符。
第二种形式ENV <key>=<value> ...
允许一次设置多个变量。请注意,第二种形式在语法中使用等号(=),而第一种形式则不使用等号(=)。像命令行解析一样,引号和反斜杠可用于在值中包含空格。
示例
多个环境变量,包含空格
1 | ENV myName="John Doe" myDog=Rex\ The\ Dog \ |
等价于
1 | ENV myName John Doe |
ADD
ADD 指令从 <src>
中复制新文件,目录或远程文件 URL ,并将它们添加到路径 <dest>
的镜像文件系统中。
<src>
可以指定多个资源,但是如果它们是文件或目录,则将其路径解释为相对于构建上下文源的路径。
语法
1 | ADD [--chown=<user>:<group>] <src>... <dest> |
包含空格的路径需要后一种形式。
注意
该
--chown
功能仅在用于构建 Linux 容器的 Dockerfiles 上受支持,而在 Windows 容器上不起作用。
每个 <src>
都可能包含通配符,并且匹配将使用Go的 filepath.Match 规则完成。例如:
要添加所有以“ hom”开头的文件:
1 | ADD hom* /mydir/ |
在下面的示例中,?
被替换为任何单个字符,例如“ home.txt”。
1 | ADD hom?.txt /mydir/ |
<dest>
是一个绝对路径,或相对于 WORKDIR
的相对路径 ,将会把指定的 <src>
在目标容器内进行复制。
下面的示例使用相对路径,并将“ test.txt”添加到 <WORKDIR>/relativeDir/
:
1 | ADD test.txt relativeDir/ |
而此示例使用绝对路径,并向 /absoluteDir/
添加“ test.txt”
1 | ADD test.txt /absoluteDir/ |
如果您的 URL 文件受身份验证保护,则您需要使用RUN wget
, RUN curl
或从容器中使用其他工具,因为该ADD
指令不支持身份验证。
规则
ADD
遵守以下规则:
该
<src>
路径必须在构建上下文内;您不能这样做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>
是目录,则将复制目录的整个内容,包括文件系统元数据。如果
<src>
是以公认的压缩格式(身份,gzip,bzip2或xz)作为本地 tar归档文件,则将其解压缩为目录。来自远程 URL的资源不会被解压缩。复制或解压缩目录时,其行为与tar -x
相同。如果
<src>
是其他类型的文件,则会将其及其元数据一起单独复制。在这种情况下,如果<dest>
以斜杠结尾/
,则将其视为目录,并将<src>
内容写入<dest>/base(<src>)
。如果
<src>
直接或由于使用通配符而指定了多个资源,则该资源<dest>
必须是目录,并且必须以斜杠结尾/
。如果
<dest>
不以斜杠结尾,则将其视为常规文件,并将其内容<src>
写入<dest>
。如果
<dest>
不存在,它将与路径中所有缺少的目录一起创建。
COPY
语法
1 | COPY [--chown=<user>:<group>] <src>... <dest> |
包含空格的路径需要后一种形式
COPY
遵守以下规则:
该
<src>
路径必须在构建上下文内;您不能这样做COPY ../something /something
,因为第一步docker build
是将上下文目录(和子目录)发送到docker守护程序。如果
<src>
是目录,则将复制目录的整个内容,包括文件系统元数据。注意
目录本身不被复制,仅其内容被复制。
如果
<src>
是其他类型的文件,则会将其及其元数据一起单独复制。在这种情况下,如果<dest>
以斜杠结尾/
,则将其视为目录,并将其内容<src>
写入<dest>/base(<src>)
。如果
<src>
直接或由于使用通配符而指定了多个资源,则该资源<dest>
必须是目录,并且必须以斜杠结尾/
。如果
<dest>
不以斜杠结尾,则将其视为常规文件,并将其内容<src>
写入<dest>
。如果
<dest>
不存在,它将与路径中所有缺少的目录一起创建。
ENTRYPOINT
ENTRYPOINT
允许您配置将作为可执行文件运行的容器。
可以使用 –entrypoint 覆盖 ENTRYPOINT
设置,但是只能使用 exec 形式
语法
EXEC 的形式,这是优选的形式:
1 | ENTRYPOINT ["executable", "param1", "param2"] |
shell 形式
1 | ENTRYPOINT command param1 param2 |
VOLUME
VOLUME
指令创建具有指定名称的安装点,并将其标记为保存来自本机主机或其他容器的外部安装的卷。
示例
以下 Dockerfile 生成一个镜像,用 docker run
在 /myvol
处创建一个新的挂载点并将 greeting
文件复制到新创建的卷中。
1 | FROM ubuntu |
WORKDIR
WORKDIR
指令定义的工作目录被 Dockerfile
中在它之后出现的任何 RUN
,CMD
, ENTRYPOINT
,COPY
和ADD
指令使用。如果WORKDIR
不存在,那么即使以后的任何Dockerfile
指令中都没有使用它,也将创建它。
语法
1 | WORKDIR /path/to/workdir |
可以在 Dockerfile
中多次使用 WORKDIR
指令。 如果提供了相对路径,则它将相对于上一条WORKDIR
指令的路径 。例如:
1 | WORKDIR /a |
最终 pwd
命令的输出将是 /a/b/c
。
WORKDIR
指令可以解析先前使用设置的环境变量 ENV
。您只能使用在 Dockerfile
中明确设置的环境变量。例如:
1 | ENV DIRPATH /path |
最终 pwd
命令的输出将是 /path/$DIRNAME
ARG
ARG
指令定义了一个变量,用户可以在构建时 docker build
使用带有--build-arg <varname>=<value>
标志的命令将变量传递给构建器。如果用户指定了未在 Dockerfile 中定义的构建参数,则构建会输出警告。
1 | [Warning] One or more build-args [foo] were not consumed. |
语法
1 | ARG <name>[=<default value>] |
Dockerfile可能包含一个或多个ARG
指令。例如,以下是有效的Dockerfile:
1 | FROM busybox |
不建议使用构建时变量来传递诸如 github 密钥,用户凭据等秘密信息,使用 docker history 命令,任何用户都可以看到构建时变量值。
ARG
指令可以可选地包括一个默认值:
1 | FROM busybox |
如果 ARG
指令具有缺省值,并且在构建时未传递任何值,那么构建器将使用缺省值。
范围
以如下 dockerFile 为例
1 | FROM busybox |
用户通过调用以下命令来构建此文件:
1 | $ docker build --build-arg user=what_user . |
结果是第二行的 USER 被解析为 some_user
,第4行的 USER 被解析为 what_user
。
在通过 ARG
指令定义变量之前 ,对变量的任何使用都会获取一个空字符串。
ARG
指令在它被定义的构建阶段结束推移的范围进行。要在多个阶段使用 ARG
,每个阶段都必须包含 ARG
指令。
1 | FROM busybox |
示例
您可以使用ARG
或ENV
指令来指定RUN
指令可用的变量。使用ENV
指令定义的环境变量始终会覆盖ARG
同名指令。
以如下 dockerFIle 为例
1 | FROM ubuntu |
假定使用以下命令构建:
1 | $ docker build --build-arg CONT_IMG_VER=v2.0.1 . |
在这种情况下,RUN
指令将使用v1.0.0
而不是ARG
用户传递的设置:v2.0.1
。此行为类似于Shell脚本,其中局部作用域的变量从其定义的角度覆盖作为参数传递或从环境继承的变量。
使用上面的示例,但使用不同的ENV
规范,可以在ARG
和ENV
指令之间创建更有用的交互:
1 | FROM ubuntu |
这种情况下 环境变量会获取传入变量,如果没有传入变量,则使用缺省值。
与ARG
指令不同,ENV
值始终保留在生成的镜像中。考虑不带--build-arg
标志的Docker构建:
1 | $ docker build . |
使用此 Dockerfile 示例,CONT_IMG_VER
仍然保留在镜像中,但其值将是指令 ENV
第3行中的默认设置v1.0.0
。
内置 ARG
Docker 具有一组预定义 ARG
变量,您可以在 Dockerfile 中不使用 ARG
相应指令的情况下使用它们。
HTTP_PROXY
http_proxy
HTTPS_PROXY
https_proxy
FTP_PROXY
ftp_proxy
NO_PROXY
no_proxy
要使用这些,只需使用以下标志在命令行中传递它们:
1 | --build-arg <varname>=<value> |
默认情况下,这些预定义变量从 docker history
的输出中排除。排除它们可以减少意外泄露 HTTP_PROXY
变量中的敏感身份验证信息的风险。