构建最佳实践

使用多阶段构建

多阶段构建允许您通过在镜像构建和最终输出之间创建更清晰的分离来减小最终镜像的大小。将您的 Dockerfile 指令拆分为不同的阶段,以确保生成的输出仅包含运行应用程序所需的文 件。

使用多个阶段还可以通过并行执行构建步骤来提高效率。

请参阅 多阶段构建 以了解更多信息。

创建可重用阶段

如果您有多个具有很多共同点的镜像,请考虑创建一个包含共享组件的可重用阶段,并以此为基础创建您的唯一阶段。Docker 只需要构建一次公共阶段。这意味着您的派生镜像更有效地使用 Docker 主机上的内存,并且加载速度更快。

维护一个通用的基础阶段(“不要重复自己”)也比让多个不同的阶段执行类似的操作更容易。

选择合适的基镜像

实现安全镜像的第一步是选择正确的基镜像。选择镜像时,请确保它来自可信来源并保持较小。

  • Docker 官方镜像 是 Docker Hub 上最安全、最可靠的镜像之一。通常,Docker 官方镜像几乎不包含或不包含包含 CVE 的软件包,并且会由 Docker 和项目维护者进行彻底审查。

  • 已验证的发布者 镜像是由与 Docker 合作的组织发布和维护的高质量镜像,Docker 会验证其存储库中内容的真实性。

  • Docker 赞助的开源项目 通过开源项目计划发布和维护。

选择基础镜像时,请注意指示镜像属于这些程序的徽章。

Docker Hub Official and Verified Publisher images

当从 Dockerfile 构建您自己的镜像时,请确保选择满足您需求的最小基础镜像。较小的基础镜像不仅提供可移植性和快速下载,而且还可以缩小镜像大小并最大限度地减少通过依赖项引入的漏洞数量。

您还应该考虑使用两种类型的基础镜像:一种用于构建和单元测试,另一种(通常更精简)用于生产。在开发的后期阶段,您的镜像可能不需要构建工具,如编译器、构建系统和调试工具。具有最少依赖项的小型镜像可以大大降低攻击面。

经常重建您的镜像

Docker 镜像是不可变的。构建镜像是在此刻拍摄该镜像的快照。这包括您在构建中使用的任何基础镜像、库或其他软件。为保持镜像最新和安全,请确保经常使用更新的依赖项重新构建镜像。

为了确保在构建中获得最新版本的依赖项,您可以使用--no-cache选项来避免缓存命中。

$ docker build --no-cache -t my-image:my-tag .

下面的 Dockerfile 使用ubuntu镜像的24.04标签。随着时间的推移,该标签可能会解析为ubuntu镜像的不同底层版本,因为发布者会使用新的安全补丁和更新的库重新构建镜像。使用--no-cache,您可以避免缓存命中并确保重新下载基础镜像和依赖项。

# syntax=docker/dockerfile:1
FROM ubuntu:24.04
RUN apt-get -y update && apt-get install -y --no-install-recommends python3

另请考虑固定基础镜像版本

使用 .dockerignore 排除文件

要排除与构建无关的文件,无需重构源代码库,请使用.dockerignore文件。此文件支持与.gitignore文件类似的排除模式。

例如,要排除所有扩展名为.md的文件

*.md

有关创建.dockerignore文件的信息,请参阅Dockerignore 文件

创建临时容器

您的 Dockerfile 定义的镜像应生成尽可能短暂的容器。短暂意味着可以停止和销毁容器,然后重建并替换为具有绝对最小设置和配置的容器。

请参考进程,了解以这种无状态方式运行容器的动机,这属于十二要素应用方法。

不要安装不必要的软件包

避免仅仅因为某些软件包可能很好用就安装额外的或不必要的软件包。例如,数据库镜像不需要包含文本编辑器。

当避免安装额外或不必要的软件包时,您的镜像将具有降低的复杂性、降低的依赖项、降低的文件大小和降低的构建时间。

解耦应用程序

每个容器都应该只关注一个方面。将应用程序解耦到多个容器中,可以更容易地进行水平扩展和重用容器。例如,Web 应用程序堆栈可能包含三个单独的容器,每个容器都有其自己的唯一镜像,以便以解耦的方式管理 Web 应用程序、数据库和内存中缓存。

将每个容器限制为一个进程是一个很好的经验法则,但它不是一个严格的规则。例如,容器不仅可以使用 init 进程启动,某些程序也可能会自行启动其他进程。例如,Celery可以生成多个工作进程,而Apache可以为每个请求创建一个进程。

请根据您的最佳判断,使容器尽可能保持简洁和模块化。如果容器相互依赖,您可以使用Docker 容器网络来确保这些容器可以通信。

排序多行参数

尽可能按字母数字顺序对多行参数进行排序,以方便维护。这有助于避免软件包重复,并使列表更容易更新。这也使 PR 更易于阅读和审查。在反斜杠 (\) 前添加空格也有帮助。

这是一个来自buildpack-deps 镜像的示例。

RUN apt-get update && apt-get install -y --no-install-recommends \
  bzr \
  cvs \
  git \
  mercurial \
  subversion \
  && rm -rf /var/lib/apt/lists/*

利用构建缓存

构建镜像时,Docker 会逐步执行 Dockerfile 中的指令,并按指定的顺序执行每个指令。对于每个指令,Docker 都会检查它是否可以重用构建缓存中的指令。

了解构建缓存的工作方式以及缓存失效的方式,对于确保更快的构建至关重要。有关 Docker 构建缓存以及如何优化构建的更多信息,请参阅Docker 构建缓存

固定基镜像版本

镜像标签是可变的,这意味着发布者可以更新标签以指向新的镜像。这很有用,因为它允许发布者更新标签以指向镜像的较新版本。作为镜像使用者,这意味着您在重新构建镜像时会自动获得新版本。

例如,如果您在 Dockerfile 中指定FROM alpine:3.19,则3.19将解析为3.19的最新补丁版本。

# syntax=docker/dockerfile:1
FROM alpine:3.19

在某个时间点,3.19标签可能指向镜像的 3.19.1 版本。如果您在 3 个月后重新构建镜像,则相同的标签可能指向不同的版本,例如 3.19.4。此发布工作流是最佳实践,大多数发布者都使用此标记策略,但并未强制执行。

缺点是不能保证每次构建都获得相同的版本。这可能会导致重大更改,这意味着您也没有使用过的确切镜像版本的审计跟踪。

为了完全确保您的供应链完整性,您可以将镜像版本固定到特定的摘要。通过将镜像固定到摘要,即使发布者用新镜像替换标签,您也可以保证始终使用相同的镜像版本。例如,以下 Dockerfile 将 Alpine 镜像固定到与前面相同的标签3.19,但这次也使用了摘要引用。

# syntax=docker/dockerfile:1
FROM alpine:3.19@sha256:13b7e62e8df80264dbb747995705a986aa530415763a6c58f84a3ca8af9a5bcd

使用此 Dockerfile,即使发布者更新了3.19标签,您的构建仍然会使用固定的镜像版本:13b7e62e8df80264dbb747995705a986aa530415763a6c58f84a3ca8af9a5bcd

虽然这有助于避免意外更改,但每次想要更新基础镜像版本时,都必须手动查找并包含镜像摘要,这也很繁琐。而且您选择不使用自动安全修复,这可能是您想要获得的功能。

Docker Scout 的默认最新基础镜像策略会检查您使用的基础镜像版本实际上是否是最新版本。此策略还会检查 Dockerfile 中固定的摘要是否与正确的版本相对应。如果发布者更新了您已固定的镜像,则策略评估将返回不符合状态,表明您应该更新镜像。

Docker Scout 还支持自动修复工作流以保持基础镜像最新。当有新的镜像摘要可用时,Docker Scout 可以自动向您的存储库发出拉取请求,以更新您的 Dockerfile 以使用最新版本。这比使用自动更改版本的标签更好,因为您可以控制更改的时间和方式,并有审计跟踪。

有关使用 Docker Scout 自动更新基础镜像的更多信息,请参阅修复

在 CI 中构建和测试您的镜像

当您签入对源代码控制的更改或创建拉取请求时,请使用GitHub Actions或其他 CI/CD 管道来自动构建和标记 Docker 镜像并对其进行测试。

Dockerfile 指令

遵循以下关于如何正确使用Dockerfile 指令的建议,以创建高效且易于维护的 Dockerfile。

FROM

尽可能地使用当前官方镜像作为您镜像的基础。Docker 推荐使用 Alpine 镜像,因为它受到严格控制,体积小巧(目前小于 6 MB),同时仍然是一个完整的 Linux 发行版。

有关 FROM 指令的更多信息,请参阅 Dockerfile 中 FROM 指令的参考

LABEL

您可以向镜像添加标签,以帮助按项目组织镜像、记录许可信息、辅助自动化或出于其他原因。对于每个标签,请添加一行以LABEL开头,其中包含一个或多个键值对。以下示例显示了不同的可接受格式。内联包含解释性注释。

带有空格的字符串必须用引号括起来,或者必须转义空格。内部引号字符 (") 也必须转义。例如

# Set one or more individual labels
LABEL com.example.version="0.0.1-beta"
LABEL vendor1="ACME Incorporated"
LABEL vendor2=ZENITH\ Incorporated
LABEL com.example.release-date="2015-02-12"
LABEL com.example.version.is-production=""

一个镜像可以有多个标签。在 Docker 1.10 之前,建议将所有标签组合到单个 LABEL 指令中,以防止创建额外的层。这现在不再必要,但仍然支持组合标签。例如

# Set multiple labels on one line
LABEL com.example.version="0.0.1-beta" com.example.release-date="2015-02-12"

上面的示例也可以写成

# Set multiple labels at once, using line-continuation characters to break long lines
LABEL vendor=ACME\ Incorporated \
      com.example.is-beta= \
      com.example.is-production="" \
      com.example.version="0.0.1-beta" \
      com.example.release-date="2015-02-12"

有关可接受的标签键和值准则,请参阅 理解对象标签。有关查询标签的信息,请参考 管理对象上的标签 中与过滤相关的项目。另请参阅 Dockerfile 参考中的 LABEL

RUN

将长或复杂的 RUN 语句拆分成多行,并用反斜杠分隔,以使您的 Dockerfile 更易于阅读、理解和维护。

例如,您可以使用 && 运算符链接命令,并使用转义字符将长命令拆分成多行。

RUN apt-get update && apt-get install -y --no-install-recommends \
    package-bar \
    package-baz \
    package-foo

默认情况下,反斜杠转义换行符,但您可以使用 escape 指令 来更改它。

您还可以使用 here documents 来运行多个命令,而无需使用管道运算符将它们链接起来

RUN <<EOF
apt-get update
apt-get install -y --no-install-recommends \
    package-bar \
    package-baz \
    package-foo
EOF

有关 RUN 的更多信息,请参阅 Dockerfile 中 RUN 指令的参考

apt-get

在基于 Debian 的镜像中,RUN 指令的一个常见用例是使用 apt-get 安装软件。由于 apt-get 安装软件包,因此 RUN apt-get 命令有一些需要特别注意的反直觉行为。

始终将 RUN apt-get updateapt-get install 组合在同一个 RUN 语句中。例如

RUN apt-get update && apt-get install -y --no-install-recommends \
    package-bar \
    package-baz \
    package-foo

单独在 RUN 语句中使用 apt-get update 会导致缓存问题,并导致后续的 apt-get install 指令失败。例如,此问题将出现在以下 Dockerfile 中

# syntax=docker/dockerfile:1

FROM ubuntu:22.04
RUN apt-get update
RUN apt-get install -y --no-install-recommends curl

构建镜像后,所有层都位于 Docker 缓存中。假设您稍后通过添加一个额外的软件包来修改 apt-get install,如下面的 Dockerfile 所示

# syntax=docker/dockerfile:1

FROM ubuntu:22.04
RUN apt-get update
RUN apt-get install -y --no-install-recommends curl nginx

Docker 将初始指令和修改后的指令视为相同,并重用先前步骤中的缓存。结果,apt-get update 未执行,因为构建使用的是缓存版本。由于未运行 apt-get update,因此您的构建可能会获取 curlnginx 软件包的过期版本。

使用 RUN apt-get update && apt-get install -y --no-install-recommends 可确保您的 Dockerfile 安装最新的软件包版本,无需进一步编码或手动干预。此技术称为缓存清除。您还可以通过指定软件包版本来实现缓存清除。这称为版本锁定。例如

RUN apt-get update && apt-get install -y --no-install-recommends \
    package-bar \
    package-baz \
    package-foo=1.3.*

版本锁定强制构建检索特定版本,而不管缓存中有什么。此技术还可以减少因所需软件包的意外更改而导致的失败。

下面是一个格式良好的 RUN 指令,它演示了所有 apt-get 建议。

RUN apt-get update && apt-get install -y --no-install-recommends \
    aufs-tools \
    automake \
    build-essential \
    curl \
    dpkg-sig \
    libcap-dev \
    libsqlite3-dev \
    mercurial \
    reprepro \
    ruby1.9.1 \
    ruby1.9.1-dev \
    s3cmd=1.1.* \
    && rm -rf /var/lib/apt/lists/*

s3cmd 参数指定版本 1.1.*。如果镜像以前使用的是旧版本,则指定新版本会导致 apt-get update 的缓存清除,并确保安装新版本。每行列出软件包还可以防止软件包重复的错误。

此外,当您通过删除 /var/lib/apt/lists 来清理 apt 缓存时,它会减小镜像大小,因为 apt 缓存不会存储在层中。由于 RUN 语句以 apt-get update 开头,因此在 apt-get install 之前始终会刷新软件包缓存。

官方 Debian 和 Ubuntu 镜像 自动运行 apt-get clean,因此不需要显式调用。

使用管道

一些 RUN 命令依赖于能够使用管道字符 (|) 将一个命令的输出传入另一个命令的能力,如下例所示

RUN wget -O - https://some.site | wc -l > /number

Docker 使用 /bin/sh -c 解释器执行这些命令,该解释器仅评估管道中最后一次操作的退出代码以确定成功与否。在上面的示例中,只要 wc -l 命令成功,此构建步骤就会成功并生成一个新镜像,即使 wget 命令失败也是如此。

如果希望命令由于管道中任何阶段的错误而失败,请在前面添加 set -o pipefail && 以确保意外错误不会阻止构建意外成功。例如

RUN set -o pipefail && wget -O - https://some.site | wc -l > /number

注意

并非所有 shell 都支持 -o pipefail 选项。

在基于 Debian 的镜像上的 dash shell 等情况下,请考虑使用 RUN 的 *exec* 形式来显式选择支持 pipefail 选项的 shell。例如

RUN ["/bin/bash", "-c", "set -o pipefail && wget -O - https://some.site | wc -l > /number"]

CMD

CMD 指令应用于运行镜像中包含的软件以及任何参数。CMD 几乎始终应以 CMD ["executable", "param1", "param2"] 的形式使用。因此,如果该镜像用于服务(例如 Apache 和 Rails),则您将运行类似于 CMD ["apache2","-DFOREGROUND"] 的命令。实际上,对于任何基于服务的镜像,都推荐使用此指令形式。

在大多数其他情况下,CMD 应提供交互式 shell,例如 bash、python 和 perl。例如,CMD ["perl", "-de0"]CMD ["python"]CMD ["php", "-a"]。使用此表单意味着当您执行类似于 docker run -it python 的命令时,您将进入一个可用的 shell,随时可以使用。除非您和您的预期用户已经非常熟悉 ENTRYPOINT 的工作方式,否则很少应该以 CMD ["param", "param"] 的方式与 ENTRYPOINT 结合使用 CMD

有关 CMD 的更多信息,请参阅 Dockerfile 中 CMD 指令的参考

EXPOSE

EXPOSE 指令指示容器侦听连接的端口。因此,您应该为您的应用程序使用常见的传统端口。例如,包含 Apache Web 服务器的镜像将使用 EXPOSE 80,而包含 MongoDB 的镜像将使用 EXPOSE 27017,依此类推。

对于外部访问,您的用户可以使用带有标志的 docker run 命令来指示如何将指定的端口映射到他们选择的端口。对于容器链接,Docker 提供了从接收容器返回到源的路径的环境变量(例如,MYSQL_PORT_3306_TCP)。

有关 EXPOSE 的更多信息,请参阅 Dockerfile 中 EXPOSE 指令的参考

ENV

为了使新的软件更容易运行,您可以使用 ENV 更新容器安装的软件的 PATH 环境变量。例如,ENV PATH=/usr/local/nginx/bin:$PATH 可确保 CMD ["nginx"] 正确运行。

ENV 指令也可用于提供您要容器化的服务(例如 Postgres 的 PGDATA)所需的特定环境变量。

最后,ENV 也可用于设置常用的版本号,以便更容易维护版本升级,如下例所示

ENV PG_MAJOR=9.3
ENV PG_VERSION=9.3.4
RUN curl -SL https://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgres &&
ENV PATH=/usr/local/postgres-$PG_MAJOR/bin:$PATH

与在程序中使用常量变量(而不是硬编码值)类似,这种方法允许您更改单个 ENV 指令来自动升级容器中软件的版本。

每个 ENV 行都会创建一个新的中间层,就像 RUN 命令一样。这意味着即使您在将来的层中取消设置环境变量,它仍然会保留在此层中,并且其值可以被转储。您可以通过创建如下所示的 Dockerfile 并构建它来测试这一点。

# syntax=docker/dockerfile:1
FROM alpine
ENV ADMIN_USER="mark"
RUN echo $ADMIN_USER > ./mark
RUN unset ADMIN_USER
$ docker run --rm test sh -c 'echo $ADMIN_USER'

mark

为防止这种情况,并真正取消设置环境变量,请使用带有 shell 命令的 RUN 命令,在一个层中设置、使用和取消设置变量。您可以使用 ;&& 分隔命令。如果您使用第二种方法,并且其中一个命令失败,则 docker build 也会失败。这通常是一个好主意。对于 Linux Dockerfile,使用 \ 作为行延续字符可以提高可读性。您也可以将所有命令放入 shell 脚本中,并让 RUN 命令只运行该 shell 脚本。

# syntax=docker/dockerfile:1
FROM alpine
RUN export ADMIN_USER="mark" \
    && echo $ADMIN_USER > ./mark \
    && unset ADMIN_USER
CMD sh
$ docker run --rm test sh -c 'echo $ADMIN_USER'

有关 ENV 的更多信息,请参阅 Dockerfile 中 ENV 指令的参考

ADD 或 COPY

ADDCOPY 在功能上类似。COPY 支持将文件从 构建上下文 或多阶段构建中的阶段复制到容器中。ADD 支持从远程 HTTPS 和 Git URL 获取文件的功能,并在从构建上下文添加文件时自动解压缩 tar 文件。

在多阶段构建中,你通常会使用COPY指令将文件从一个阶段复制到另一个阶段。如果你需要临时从构建上下文添加文件到容器中以执行RUN指令,你可以使用绑定挂载来代替COPY指令。例如,临时添加requirements.txt文件以执行RUN pip install指令。

RUN --mount=type=bind,source=requirements.txt,target=/tmp/requirements.txt \
    pip install --requirement /tmp/requirements.txt

COPY相比,绑定挂载从构建上下文包含文件到容器更高效。请注意,绑定挂载的文件仅在单个RUN指令中临时添加,不会保留在最终镜像中。如果需要在最终镜像中包含来自构建上下文的文件,请使用COPY指令。

ADD指令最适合在构建过程中需要下载远程构件的情况。与使用wgettar等手动添加文件相比,ADD更好,因为它可以确保更精确的构建缓存。ADD还内置支持对远程资源的校验和验证,以及从Git URL解析分支、标签和子目录的协议。

以下示例使用ADD下载一个.NET安装程序。结合多阶段构建,只有.NET运行时保留在最终阶段,没有中间文件。

# syntax=docker/dockerfile:1

FROM scratch AS src
ARG DOTNET_VERSION=8.0.0-preview.6.23329.7
ADD --checksum=sha256:270d731bd08040c6a3228115de1f74b91cf441c584139ff8f8f6503447cebdbb \
    https://dotnetcli.azureedge.net/dotnet/Runtime/$DOTNET_VERSION/dotnet-runtime-$DOTNET_VERSION-linux-arm64.tar.gz /dotnet.tar.gz

FROM mcr.microsoft.com/dotnet/runtime-deps:8.0.0-preview.6-bookworm-slim-arm64v8 AS installer

# Retrieve .NET Runtime
RUN --mount=from=src,target=/src <<EOF
mkdir -p /dotnet
tar -oxzf /src/dotnet.tar.gz -C /dotnet
EOF

FROM mcr.microsoft.com/dotnet/runtime-deps:8.0.0-preview.6-bookworm-slim-arm64v8

COPY --from=installer /dotnet /usr/share/dotnet
RUN ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet

有关ADDCOPY的更多信息,请参见以下链接:

ENTRYPOINT

ENTRYPOINT指令最适合设置镜像的主命令,使镜像的运行如同该命令一样,然后使用CMD作为默认标志。

以下是命令行工具s3cmd的镜像示例。

ENTRYPOINT ["s3cmd"]
CMD ["--help"]

可以使用以下命令运行镜像并显示命令的帮助信息:

$ docker run s3cmd

或者,可以使用正确的参数来执行命令,例如以下示例:

$ docker run s3cmd ls s3://mybucket

这很有用,因为镜像名称可以像上面命令中显示的那样充当二进制文件的引用。

ENTRYPOINT指令还可以与辅助脚本结合使用,即使启动工具可能需要多个步骤,它也可以像上面的命令一样工作。

例如,官方 Postgres 镜像 使用以下脚本作为其ENTRYPOINT

#!/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 "$@"

此脚本使用exec Bash 命令,以便最终运行的应用程序成为容器的PID 1。这允许应用程序接收发送到容器的任何Unix信号。更多信息,请参见ENTRYPOINT 参考

在下面的示例中,一个辅助脚本被复制到容器中,并在容器启动时通过ENTRYPOINT运行。

COPY ./docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["postgres"]

这个脚本允许你以多种方式与Postgres交互。

它可以简单地启动Postgres。

$ docker run postgres

或者,你可以用它来运行Postgres并将参数传递给服务器。

$ docker run postgres postgres --help

最后,你可以用它来启动一个完全不同的工具,比如Bash。

$ docker run --rm -it postgres bash

有关ENTRYPOINT的更多信息,请参见Dockerfile 中 ENTRYPOINT 指令的参考

VOLUME

你应该使用VOLUME指令来公开任何数据库存储区、配置存储或Docker容器创建的文件和文件夹。强烈建议你对镜像中任何可变或用户可服务的组合部分使用VOLUME

有关VOLUME的更多信息,请参见Dockerfile 中 VOLUME 指令的参考

USER

如果服务可以在没有特权的情况下运行,请使用USER切换到非root用户。首先使用类似以下示例在Dockerfile中创建用户和组:

RUN groupadd -r postgres && useradd --no-log-init -r -g postgres postgres

注意

考虑显式指定UID/GID。

镜像中的用户和组被分配非确定性UID/GID,即分配“下一个”UID/GID与镜像重建无关。因此,如果这至关重要,则应分配显式UID/GID。

注意

由于Go存档/tar包处理稀疏文件中的未解决的bug,尝试在Docker容器中创建具有非常大的UID的用户会导致磁盘耗尽,因为容器层中的/var/log/faillog充满了NULL(\0)字符。解决方法是将--no-log-init标志传递给useradd。Debian/Ubuntu的adduser包装器不支持此标志。

避免安装或使用sudo,因为它具有不可预测的TTY和信号转发行为,这可能会导致问题。如果你绝对需要类似于sudo的功能,例如以root身份初始化守护进程,但以非root身份运行它,请考虑使用“gosu”

最后,为了减少层数和复杂性,避免频繁切换USER

有关USER的更多信息,请参见Dockerfile 中 USER 指令的参考

WORKDIR

为了清晰性和可靠性,你应该始终为WORKDIR使用绝对路径。此外,你应该使用WORKDIR,而不是大量使用诸如RUN cd … && do-something之类的指令,这些指令难以阅读、排查和维护。

有关WORKDIR的更多信息,请参见Dockerfile 中 WORKDIR 指令的参考

ONBUILD

ONBUILD命令在当前Dockerfile构建完成后执行。ONBUILD在任何从当前镜像派生的子镜像中执行。可以将ONBUILD命令视为父Dockerfile给子Dockerfile的指令。

Docker构建在子Dockerfile中的任何命令之前执行ONBUILD命令。

ONBUILD对于将要从给定镜像构建的镜像很有用。例如,你会在语言栈镜像中使用ONBUILD来构建在Dockerfile中用该语言编写的任意用户软件,正如你在Ruby 的 ONBUILD 变体中看到的那样。

使用ONBUILD构建的镜像应该获得一个单独的标签。例如,ruby:1.9-onbuildruby:2.0-onbuild

ONBUILD中放置ADDCOPY时要小心。如果新的构建上下文缺少要添加的资源,则onbuild镜像会灾难性地失败。如上所述,添加单独的标签有助于通过允许Dockerfile作者做出选择来减轻这种情况。

有关ONBUILD的更多信息,请参见Dockerfile 中 ONBUILD 指令的参考