Dockerfile主要用于构建Docker镜像,构建镜像时自动读取,每一行Dockerfile指令,Dockerfile是一个文本文件,它包含所有命令在里面。
一个 Docker 镜像由只读层组成,每个层代表一个 Dockerfile 指令。这些层是堆叠的,每一层都是前一层变化的增量。

使用

  • 使用上下文构建(从当前目录读取Dockerfile)

    1. docker build .

    构建由 Docker 守护程序运行,而不是由 CLI 运行。构建过程所做的第一件事是将整个上下文(递归)发送到守护进程,大多数的案例,使用空目录作为Dockerfile存储的Dockerfile。

    不要使用根目录 / 作为构建上下文的 PATH,因为它会导致构建将硬盘驱动器的全部内容传输到 Docker 守护程序。

  • 使用绝对路径构建

    1. docker build -f /path/to/Dockerfile
  • 示例

    Dockerfile Reference

    Docker 可以通过读取 Dockerfile 中的指令来自动构建镜像。 Dockerfile 是一个文本文档,其中包含用户可以在命令行上调用以组装图像的所有命令。使用 docker build 用户可以创建一个连续执行多个命令行指令的自动构建。

    指令格式

    指令不区分大小写。但是,约定是大写的,以便更容易地将它们与参数区分开来。
    Docker 按顺序在 Dockerfile 中运行指令。 Dockerfile 必须以 FROM 指令开头,也有可能是parser directives, comments,和全局性指令ARGs之后,FROM指令,指定基础镜像构建。

    1. # Comment
    2. INSTRUCTION arguments

    Parse directives

    解析器指令是可选的,并且会影响 Dockerfile 中后续行的处理方式。解析器指令不会将层添加到构建中,并且不会显示为构建步骤。解析器指令以#directive=value 的形式编写为一种特殊类型的注释。一个指令只能使用一次。
    一旦处理了注释、空行或构建器指令,Docker 就不再寻找解析器指令。相反,它将任何格式化为解析器指令的内容视为注释,并且不会尝试验证它是否可能是解析器指令。因此,所有解析器指令都必须位于 Dockerfile 的最顶部。
    解析器指令不区分大小写。但是,约定是小写的。约定也是在任何解析器指令之后包含一个空行。解析器指令不支持换行符。
    官方支持的解析器指令

  • syntax

  • excape

    syntax

    1. # syntax=[remote image reference]
    示例:
    1. # syntax=docker/dockerfile:1
    2. # syntax=docker.io/docker/dockerfile:1
    3. # syntax=example.com/user/repo:tag@sha256:abcdef...

    此功能仅在使用 BuildKit 后端时可用,在使用经典构建器后端时被忽略。

语法指令定义用于构建 Dockerfile 的 Dockerfile 语法的位置。 BuildKit 后端允许无缝使用作为 Docker 镜像分发并在容器沙箱环境中执行的外部实现。

escape

  1. # escape=\ (backslash)

  1. # escape=` (backtick)

转义指令设置用于转义 Dockerfile 中字符的字符。如果未指定,则默认转义字符为 \。
转义字符既用于转义一行中的字符,也用于转义换行符。这允许 Dockerfile 指令跨越多行。请注意,无论转义解析器指令是否包含在 Dockerfile 中,都不会在 RUN 命令中执行转义,除非在行尾。
将转义字符设置为 在 Windows 上特别有用,其中 \ 是目录路径分隔符。 与 Windows PowerShell 一致。
考虑以下示例,该示例在 Windows 上会以不明显的方式失败。第二行末尾的第二个 \ 将被解释为换行符的转义,而不是第一个 \ 的转义目标。类似地,第三行末尾的 \ 会,假设它实际上是作为指令处理的,会导致它被视为行继续。这个 dockerfile 的结果是第二行和第三行被认为是一条指令:

  1. FROM microsoft/nanoserver
  2. COPY testfile.txt c:\\
  3. RUN dir c:\

解决上述问题的一种方法是使用 / 作为 COPY 指令和 dir 的目标。但是,这种语法充其量是令人困惑的,因为它对于 Windows 上的路径并不自然,而且在最坏的情况下,由于并非 Windows 上的所有命令都支持 / 作为路径分隔符,因此容易出错。
通过添加转义解析器指令,以下 Dockerfile 使用自然平台语义在 Windows 上的文件路径按预期成功:

  1. # escape=`
  2. FROM microsoft/nanoserver
  3. COPY testfile.txt c:\
  4. RUN dir c:\

ENV

环境变量使用ENV指令声明,环境变量在 Dockerfile 中用 $variable_name 或 ${variable_name} 表示。它们被同等对待,大括号语法通常用于解决没有空格的变量名问题,例如 ${foo}_bar。
${variable_name}语法还支持一些标准 bash 修饰符,如下所示:

  • ${variable:-word}表示如果设置了变量,那么结果就是那个值。如果未设置变量,则结果将是 word。
  • ${variable:+word}表示如果设置了变量,则结果为 word,否则为空字符串。

Dockerfile 中的以下指令列表支持环境变量:

  • ADD
  • COPY
  • ENV
  • EXPOSE
  • FROM
  • LABEL
  • STOPSIGNAL
  • USER
  • VOLUME
  • WORKDIR
  • ONBUILD (when combined with one of the supported instructions above)
    1. ENV abc=hello
    2. ENV abc=bye def=$abc
    3. ENV ghi=$ab

    def=hello,ghi=bye,原因是ENV abc=bye def=$abc在同一指令下,def的值将等于ENV abc=hello

.dockerignore

在 docker CLI 将上下文发送到 docker 守护进程之前,它会在上下文的根目录中查找名为 .dockerignore 的文件。如果此文件存在,CLI 会修改上下文以排除与其中的模式匹配的文件和目录。这有助于避免不必要地将大型或敏感文件和目录发送到守护程序,并可能使用 ADD 或 COPY 将它们添加到图像中。
CLI 将 .dockerignore 文件解释为以换行符分隔的模式列表,类似于 Unix shell 的文件 glob。为了匹配的目的,上下文的根被认为是工作目录和根目录。例如,模式 /foo/bar 和 foo/bar 都排除了 PATH 的 foo 子目录或位于 URL 的 git 存储库的根目录中名为 bar 的文件或目录。都不排除其他任何东西。
如果 .dockerignore 文件中的一行在第 1 列中以 # 开头,则该行被视为注释,并在 CLI 解释之前被忽略。
示例:

  1. # comment
  2. */temp*
  3. */*/temp*
  4. temp?
Rule Behaivor
#comment 注释
/temp 在根的任何直接子目录中排除名称以 temp 开头的文件和目录。例如,普通文件 /somedir/temporary.txt 被排除在外,目录 /somedir/temp 也是如此。
//temp* 从根以下两级的任何子目录中排除以 temp 开头的文件和目录。例如,/somedir/subdir/temporary.txt 被排除在外。
temp? 排除根目录中名称为 temp 的单字符扩展名的文件和目录。例如,/tempa 和 /tempb 被排除在外。

以 ! 开头的行(感叹号)可用于排除例外情况。以下是使用此机制的示例 .dockerignore 文件:

  1. *.md
  2. !README.md

排除所有Markdown文件,但README.md除外

FROM

FROM指定一个基础镜像,给随后运行的指令

  • ARG是唯一可以运行在FROM指令之前的指令。
  • FROM 可以在单个 Dockerfile 中出现多次以创建多个映像或使用一个构建阶段作为另一个构建阶段的依赖项。只需在每个新的 FROM 指令之前记下提交的最后一个图像 ID 输出。每个 FROM 指令都会清除先前指令创建的任何状态。
  • 可选名称可以在一个新构建阶段,通过增加 as name,该名称可以使用在下个FROM COPY --from=<name>指向该镜像。
  • tagdisest是可选项,如果不指定镜像tag,Docker默认拉取latest版本。

如果 FROM 引用多平台映像,可选的 —platform 标志可用于指定映像的平台。例如,linux/amd64、linux/arm64 或 windows/amd64。默认情况下,使用构建请求的目标平台。全局构建参数可以用在这个标志的值中,例如自动平台 ARGs 允许你强制一个阶段到本地构建平台 (—platform=$BUILDPLATFORM),并使用它来交叉编译到内部的目标平台阶段。

ARG和FROM关系

FROM 指令支持在第一个 FROM 之前出现的任何 ARG 指令声明的变量。

  1. ARG MAJOR_VERSION=latest
  2. FROM alpine:${MAJOR_VERSION}
  3. FROM extract:${MAJOR_VERSION}

在 FROM 之前声明的 ARG 不在构建阶段,因此它不能在 FROM 之后的任何指令中使用。要使用在第一个 FROM 之前声明的 ARG 的默认值,请在构建阶段使用没有值的 ARG 指令:

  1. ARG VERSION=latest
  2. FROM busybox:$VERSION
  3. ARG VERSION
  4. RUN echo $VERSION > image_version

RUN

RUN指令有两种格式:

  1. RUN shell 风格的命令行模式
  2. RUN [‘excutable’,’pams1’,’pams2’] exec 风格命令模式

RUN 指令将在当前图像之上的新层中执行任何命令并提交结果。生成的提交图像将用于 Dockerfile 中的下一步。
将 RUN 指令分层并生成提交符合 Docker 的核心概念,其中提交很简单,并且可以从映像历史的任何点创建容器,就像源代码控制一样。
exec 形式可以避免 shell 字符串修改,并使用不包含指定 shell 可执行文件的基本映像运行命令。
可以使用 SHELL 命令更改 shell 形式的默认 shell。
在 shell 形式中,您可以使用 \(反斜杠)将单个 RUN 指令继续到下一行。

  1. RUN /bin/bash -c 'source $HOME/.bashrc; \
  2. echo $HOME'

exec 风格:

  1. RUN ["/bin/bash", "-c", "echo hello"]

exec 格式被解析为 JSON 数组,这意味着您必须在单词周围使用双引号 (“) 而不是单引号 (‘)。

与 shell 形式不同,exec 形式不调用命令 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 这样的指令的缓存将在下一次构建期间重复使用。可以使用 —no-cache 标志使 RUN 指令的缓存无效,例如 docker build —no-cache。

CMD

CMD指令有三种模式:

  • CMD ["executable","param1","param2"] (exec form, this is the preferred form)
  • CMD ["param1","param2"](as default parameters to ENTRYPOINT)
  • CMD command param1 param2(shell form)

一个Dockerfile中仅有一个CMD指令,当有多个CMD指令时,默认最后一个有效。
CMD指令主要目的是提供给容器一个默认的执行命令,这个默认可以包含一个可执行文件,也可以忽略可执行文件,在包含一个可执行文件案例下,必须提供一个ENTRYPOINT文件。
如果CMD提供默认参数给ENTRYPONT文件,CMDENTRYPOINT 都要指定_Json_数组格式。
shell风格:

  1. FROM ubuntu:latest
  2. CMD echo "FFFFFF"

exec风格:

  1. FROM ubuntu:latest
  2. CMD ["/bin/sh -c","echo","FFFFFFF"]

LABEL

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

LABEL指令给镜像提供元数据信息,可以有多个LABEL,以键值对的形式出现。

  1. LABEL "com.example.vendor"="ACME Incorporated"
  2. LABEL com.example.label-with-value="foo"
  3. LABEL version="1.0"
  4. LABEL description="This text illustrates \
  5. that label-values can span multiple lines."

EXPOSE

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

EXPOSE 指令通知 Docker 容器在运行时侦听指定的网络端口。可以指定端口监听 TCP 还是 UDP,如果不指定协议,则默认为 TCP。
EXPOSE 指令实际上并不发布端口。它充当构建映像的人和运行容器的人之间的一种文档类型,关于打算发布哪些端口。要在运行容器时实际发布端口,请在 docker run 上使用 -p 标志来发布和映射一个或多个端口,或者使用 -P 标志来发布所有暴露的端口并将它们映射到高阶端口。

  1. EXPOSE 80/tcp
  2. EXPOSE 80/udp
  3. EXPOSE 443/tcp

ADD

ADD可以有两种形式:

  • ADD [—chown=:]
  • ADD [—chown=:] [““,… ““]

包含空格需要使用第二种模式

—chown 功能仅在用于构建 Linux 容器的 Dockerfile 上受支持,不适用于 Windows 容器。由于用户和组所有权概念不能在 Linux 和 Windows 之间转换,因此使用 /etc/passwd 和 /etc/group 将用户名和组名转换为 ID 会限制此功能仅适用于基于 Linux 操作系统的容器。

ADD规约:

  • 文件必须在 build 的上下文目录内
  • 如果 是 URL 并且 不以斜杠结尾,则从 URL 下载文件并复制到
  • 如果 是 URL 并且 确实以斜杠结尾,则从 URL 推断文件名并将文件下载到 /。例如,添加 http://example.com/foobar / 将创建文件 /foobar。 URL 必须有一个重要的路径,以便在这种情况下可以找到适当的文件名(http://example.com 将不起作用)。
  • 如果是一个目录,则将会拷贝整个目录内容及文件系统的元数据
  • 如果 是可识别压缩格式(identity、gzip、bzip2 或 xz)的本地 tar 存档,则将其解压缩为目录。来自远程 URL 的资源不会被解压缩。当一个目录被复制或解压时,它的行为与 tar -x 相同,结果是以下的并集:
  1. 无论目标路径上存在什么,
  2. 源代码树的内容,冲突已解决,有利于“2”。逐个文件。

    Note: 文件是否被识别为可识别的压缩格式完全取决于文件的内容,而不是文件的名称。例如,如果一个空文件碰巧以 .tar.gz 结尾,这将不会被识别为压缩文件,也不会生成任何类型的解压缩错误消息,而是将文件简单地复制到目标位置。

  • 如果 是任何其他类型的文件,它会与元数据一起单独复制。在这种情况下,如果 以斜杠 / 结尾,则将其视为目录,并且 的内容将写入 /base()。
  • 如果指定了多个 资源,无论是直接指定还是由于使用通配符,那么 必须是目录,并且必须以斜杠 / 结尾。
  • 如果 不以斜杠结尾,它将被视为常规文件,并且 的内容将写入
  • 如果 不存在,它会连同其路径中所有缺失的目录一起创建。

    COPY

    COPY指令两种形式:

  • COPY [—chown=:]

  • COPY [—chown=:] [““,… ““]

包含空格需要后一种形式。
COPYADD 两种都属于拷贝文件,不同之处在于ADD指定可以指定URL和自动解压功能。

ENTRYPOINT

ENTRYPOINT两种形式:
exec 形式

  1. ENTRYPOINT ["executable", "param1", "param2"]

shell 形式

  1. ENTRYPOINT command param1 param2

ENTRYPOINT 允许您配置将作为可执行文件运行的容器。

VOLUME

  1. VOLUME ["/data"]

VOLUME 指令创建一个具有指定名称的挂载点,并将其标记为保存来自本机主机或其他容器的外部挂载卷。该值可以是 JSON 数组、VOLUME [“/var/log/“] 或具有多个参数的纯字符串,例如 VOLUME /var/log 或 VOLUME /var/log /var/db。
docker run 命令使用基本映像中指定位置存在的任何数据初始化新创建的卷。例如,考虑以下 Dockerfile 片段:

  1. FROM ubuntu
  2. RUN mkdir /myvol
  3. RUN echo "hello world" > /myvol/greeting
  4. VOLUME /myvol

此 Dockerfile 生成一个图像,该图像导致 docker run 在 /myvol 创建一个新的挂载点,并将问候文件复制到新创建的卷中。

关于指定卷的注意事项

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

    USER

    1. USER <user>[:<group>]
    或者
    1. USER <UID>[:<GID>]
    USER 指令设置用户名(或 UID)和可选的用户组(或 GID),以在运行映像时以及 Dockerfile 中跟随它的任何 RUN、CMD 和 ENTRYPOINT 指令使用。

    请注意,在为用户指定组时,用户将仅具有指定的组成员身份。任何其他配置的组成员资格都将被忽略。

当用户没有主组时,图像(或下一条指令)将与根组一起运行。 在 Windows 上,如果用户不是内置帐户,则必须先创建用户。这可以通过调用作为 Dockerfile 一部分的 net user 命令来完成。

WORKDIR

  1. WORKDIR /path/to/workdir

WORKDIR 指令为 Dockerfile 中任何 RUN、CMD、ENTRYPOINT、COPY 和 ADD 指令设置工作目录。如果 WORKDIR 不存在,即使它没有在任何后续 Dockerfile 指令中使用,它也会被创建。
WORKDIR 指令可以在 Dockerfile 中多次使用。如果提供了相对路径,它将相对于前一个 WORKDIR 指令的路径。
WORKDIR 指令可以解析之前使用 ENV 设置的环境变量。您只能使用在 Dockerfile 中显式设置的环境变量。例如:

  1. ENV DIRPATH=/path
  2. WORKDIR $DIRPATH/$DIRNAME
  3. RUN pwd

ARG

  1. ARG <name>[=<default value>]

ARG 指令定义了一个变量,用户可以在构建时通过 docker build 命令使用 —build-arg = 标志将其传递给构建器。如果用户指定了未在 Dockerfile 中定义的构建参数,则构建会输出警告。 [警告] 一个或多个构建参数 [foo] 未使用。
一个 Dockerfile 可能包含一个或多个 ARG 指令。例如,以下是一个有效的 Dockerfile:

  1. FROM busybox
  2. ARG user1
  3. ARG buildno
  4. # ...

Default values

ARG 指令可以选择包含默认值:

  1. FROM busybox
  2. ARG user1=someuser
  3. ARG buildno=1
  4. # ...

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

Scope

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

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

用户构建时调用

  1. docker build --build-arg user=what_user .

第 2 行的 USER 评估为 some_user,因为用户变量在随后的第 3 行中定义。第 4 行的 USER 评估为 what_user,因为用户已定义,并且 what_user 值已在命令行上传递。在通过 ARG 指令定义之前,对变量的任何使用都会导致空字符串。
ARG 指令在定义它的构建阶段结束时超出范围。要在多个阶段中使用 arg,每个阶段都必须包含 ARG 指令。

  1. FROM
  2. Learn more about the "FROM" Dockerfile command.
  3. busybox
  4. ARG SETTINGS
  5. RUN ./run/setup $SETTINGS
  6. FROM busybox
  7. ARG SETTINGS
  8. RUN ./run/other $SETTINGS

您可以使用 ARG 或 ENV 指令来指定可用于 RUN 指令的变量。使用 ENV 指令定义的环境变量总是覆盖同名的 ARG 指令。考虑这个带有 ENV 和 ARG 指令的 Dockerfile。

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

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

  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
  2. ARG CONT_IMG_VER
  3. ENV CONT_IMG_VER=${CONT_IMG_VER:-v1.0.0}
  4. RUN echo $CONT_IMG_VER

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

  1. docker build .

使用此 Dockerfile 示例,CONT_IMG_VER 仍保留在映像中,但其值为 v1.0.0,因为它是 ENV 指令在第 3 行中设置的默认值。
此示例中的变量扩展技术允许您从命令行传递参数,并通过利用 ENV 指令将它们保存在最终图像中。仅有限的一组 Dockerfile 指令支持变量扩展。

预定义的 ARGs

Docker 有一组预定义的 ARG 变量,您可以在 Dockerfile 中没有相应的 ARG 指令的情况下使用这些变量。

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

要使用这些,请使用 —build-arg 标志在命令行上传递它们,例如:

  1. docker build --build-arg HTTPS_PROXY=https://my-proxy.example.com .

默认情况下,这些预定义变量会从 docker 历史记录的输出中排除。排除它们可以降低在 HTTP_PROXY 变量中意外泄露敏感身份验证信息的风险。
例如,考虑使用 —build-arg HTTP_PROXY=http://user:pass@proxy.lon.example.com 构建以下 Dockerfile

  1. FROM ubuntu
  2. RUN echo "Hello World"

在这种情况下,HTTP_PROXY 变量的值在 docker 历史记录中不可用,也不会被缓存。如果您要更改位置,并且您的代理服务器更改为 http://user:pass@proxy.sfo.example.com,则后续构建不会导致缓存未命中。
如果您需要覆盖此行为,则可以通过在 Dockerfile 中添加 ARG 语句来实现,如下所示:

  1. FROM ubuntu
  2. ARG HTTP_PROXY
  3. RUN echo "Hello World"

构建此 Dockerfile 时,HTTP_PROXY 保留在 docker 历史记录中,更改其值会使构建缓存无效。

对构建缓存的影响

ARG 变量不会像 ENV 变量那样持久化到构建的映像中。但是,ARG 变量确实以类似的方式影响构建缓存。如果 Dockerfile 定义了一个 ARG 变量,其值与之前的构建不同,那么在第一次使用时会发生“缓存未命中”,而不是在定义时发生。特别是,在 ARG 指令之后的所有 RUN 指令都隐式使用 ARG 变量(作为环境变量),因此可能会导致缓存未命中。所有预定义的 ARG 变量都免于缓存,除非 Dockerfile 中有匹配的 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=,在这两种情况下,第 2 行的规范都不会导致缓存未命中;第 3 行确实会导致缓存未命中。ARG CONT_IMG_VER 导致 RUN 行被识别为与运行 CONT_IMG_VER= echo hello 相同,因此如果 更改,我们会得到缓存未命中。
考虑同一命令行下的另一个示例:

  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 命令使镜像包含该值。
如果 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

  1. ONBUILD <INSTRUCTION>

ONBUILD 指令向镜像添加了一条触发指令,该指令将在稍后的时间执行,此时镜像用作另一个构建的基础。触发器将在下游构建的上下文中执行,就好像它是在下游 Dockerfile 中的 FROM 指令之后立即插入的一样。
任何构建指令都可以注册为触发器。

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

例如,如果您的镜像是可重用的 Python 应用程序构建器,则需要将应用程序源代码添加到特定目录中,并且之后可能需要调用构建脚本。您现在不能只调用 ADD 和 RUN,因为您还没有访问应用程序源代码的权限,并且每个应用程序构建都会有所不同。您可以简单地为应用程序开发人员提供样板 Dockerfile 以将其复制粘贴到他们的应用程序中,但这效率低下、容易出错且难以更新,因为它与特定于应用程序的代码混合在一起。
解决方案是使用 ONBUILD 注册高级指令,以便稍后在下一个构建阶段运行。
这是它的工作原理:

  1. 当遇到 ONBUILD 指令时,构建器将触发器添加到正在构建的图像的元数据中。该指令不会影响当前的构建。
  2. 在构建结束时,所有触发器的列表存储在映像清单中,位于键 OnBuild 下。可以使用 docker inspect 命令检查它们。
  3. 稍后,可以使用 FROM 指令将映像用作新构建的基础。作为处理 FROM 指令的一部分,下游构建器查找 ONBUILD 触发器,并按照它们注册的相同顺序执行它们。如果任何触发器失败,FROM 指令将被中止,这反过来会导致构建失败。如果所有触发器都成功,则 FROM 指令完成并且构建照常继续。
  4. 触发器在执行后会从最终图像中清除。换句话说,它们不会被“孙子”构建继承。

例如,您可以添加如下内容:

  1. ONBUILD ADD . /app/src
  2. ONBUILD RUN /usr/local/bin/python-build --dir /app/src

链接ONBUILD指令,使用 ONBUILD ONBUILD是不被允许的。 ONBUILD指令不可以触发FROM或MAINTAINER指令。

STOPSIGNAL

  1. STOPSIGNAL signal

STOPSIGNAL 指令设置系统调用信号,该信号将被发送到容器以退出。此信号可以是格式为 SIG 的信号名称,例如 SIGKILL,也可以是匹配内核系统调用表中某个位置的无符号数,例如 9。如果未定义,则默认为 SIGTERM。
可以使用 docker run 和 docker create 上的 —stop-signal 标志来覆盖每个容器的映像的默认停止信号。

HEALTHCHECK

HEALTHCHECK有两种形式:

  1. HEALTHCHECK [OPTIONS] CMD command (容器健康检查,通过在容器内部运行命令)
  2. HEALTHCHECK NONE (关闭任何健康检查,继承来自基础镜像)

HEALTHCHECK 指令告诉 Docker 如何测试容器以检查它是否仍在工作。这可以检测诸如 Web 服务器陷入无限循环并且无法处理新连接的情况,即使服务器进程仍在运行。
当容器指定了健康检查时,除了正常状态外,它还具有健康状态。此状态最初为开始。每当健康检查通过时,它就会变得健康(无论它以前处于什么状态)。在连续失败一定次数后,它变得不健康。
CMD 之前可以出现的选项有:

  • --interval=DURATION (default: 30s)
  • --timeout=DURATION (default: 30s)
  • --start-period=DURATION (default: 0s)
  • --retries=N (default: 3)

健康检查将首先在容器启动后运行间隔秒,然后在每次之前的检查完成后再次运行间隔秒。

如果单次运行检查花费的时间超过 timeout 秒,则认为检查失败。

它需要重试健康检查的连续失败才能将容器视为不健康。

start period 为需要时间引导的容器提供初始化时间。在此期间探测失败将不计入最大重试次数。但是,如果在启动期间健康检查成功,则认为容器已启动,所有连续失败将计入最大重试次数。

Dockerfile 中只能有一条 HEALTHCHECK 指令。如果您列出多个,则只有最后一个 HEALTHCHECK 才会生效。
CMD 关键字后面的命令可以是 shell 命令(例如 HEALTHCHECK CMD /bin/check-running)或 exec 数组(与其他 Dockerfile 命令一样;有关详细信息,请参见例如 ENTRYPOINT)。

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

  • 0: success - the container is healthy and ready for use
  • 1: unhealthy - the container is not working correctly
  • 2:reserved - do not use this exit code

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

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

为了帮助调试失败的探测,该命令在 stdout 或 stderr 上写入的任何输出文本(UTF-8 编码)都将存储在健康状态中,并且可以使用 docker inspect 进行查询。此类输出应保持简短(当前仅存储前 4096 个字节)。
当容器的健康状态发生变化时,会生成一个带有新状态的 health_status 事件。

SHELL

  1. SHELL ["executable", "parameters"]

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

  1. FROM microsoft/windowsservercore
  2. # Executed as cmd /S /C echo default
  3. RUN echo default
  4. # Executed as cmd /S /C powershell -command Write-Host default
  5. RUN powershell -command Write-Host default
  6. # Executed as powershell -command Write-Host hello
  7. SHELL ["powershell", "-command"]
  8. RUN Write-Host hello
  9. # Executed as cmd /S /C echo hello
  10. SHELL ["cmd", "/S", "/C"]
  11. RUN echo hello

以下指令在 Dockerfile 中使用它们的 shell 形式时会受到 SHELL 指令的影响:RUN、CMD 和 ENTRYPOINT。