构建最佳实践

使用多阶段构建

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

使用多个阶段还可以让您通过执行 并行构建步骤。

有关详细信息,请参阅 Multi-stage builds 信息。

创建可重用的阶段

如果您有多个有很多共同点的镜像,请考虑创建一个可重用的 阶段,并将您的独特阶段基于 那。Docker 只需要构建一次 common stage。这意味着您的衍生镜像会占用内存 在 Docker 主机上更高效、加载速度更快。

维护一个共同的基础阶段也更容易(“Don't repeat yourself”), 而不是让多个不同的阶段做类似的事情。

选择正确的基础镜像

实现安全镜像的第一步是选择合适的基础 镜像。选择镜像时,请确保它是从受信任的来源构建的,并保留 它很小。

  • Docker 官方镜像是 Docker Hub 上最安全可靠的镜像之一。通常 Docker 官方镜像很少或没有包含 CVE 的软件包,并且 由 Docker 和项目维护人员进行全面审查。

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

  • Docker 赞助的开源由 Docker 赞助的开源项目发布和维护 通过开源程序

选择基础镜像时,请注意指示 image 是这些程序的一部分。

Docker Hub Official and Verified Publisher images

从 Dockerfile 构建自己的镜像时,请确保选择最小基础 符合您要求的镜像。较小的基础镜像不仅提供 便携性和快速下载,但也缩小了镜像的大小和 最大限度地减少通过依赖项引入的漏洞数量。

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

经常重建镜像

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

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

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

以下 Dockerfile 使用24.04标记的ubuntu镜像。随着时间的推移, 该标签可能会解析为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 文件

创建临时容器

Dockerfile 定义的镜像应生成如下容器 尽可能短暂。Ephemeral 表示容器可以停止 并销毁,然后重建并替换为绝对最低的设置和 配置。

请参阅 12 因素应用程序方法下的 流程 ,了解在此类 无状态时尚。

不要安装不必要的软件包

避免仅仅因为它们可能很好就安装额外或不必要的包。例如,您不需要在数据库镜像中包含文本编辑器。

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

解耦应用程序

每个容器应该只有一个关注点。将应用程序解耦为 多个容器可以更轻松地进行水平扩展和重用容器。 例如,Web 应用程序堆栈可能由三个独立的 容器,每个容器都有自己唯一的镜像,用于管理 Web 应用程序, database 和内存中缓存。

将每个容器限制为一个进程是一个很好的经验法则,但这不是 硬性规定。例如,容器不仅可以使用 init 进程生成, 某些程序可能会自行生成其他进程。为 实例,Celery 可以生成多个 worker 进程,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 中,按指定的顺序执行每个 Dockerfile。对于每个指令,Docker 检查是否可以重用 build cache 中的指令。

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

固定基础镜像版本

图片标签是可变的,这意味着发布者可以更新标签以指向新的 镜像。这非常有用,因为它允许发布者更新要指向的标记 镜像的较新版本。作为镜像消费者,这意味着您 当您重新构建镜像时自动获取新版本。

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

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

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

这样做的缺点是,不能保证每个 建。这可能会导致中断性变更,并且意味着您也没有 您正在使用的确切镜像版本的审核跟踪。

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

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

使用此 Dockerfile,即使发布者更新了3.19标签中,您的 build 仍将使用固定的镜像版本:13b7e62e8df80264dbb747995705a986aa530415763a6c58f84a3ca8af9a5bcd.

虽然这有助于避免意外更改,但这样做也更加乏味 要手动查找并包含基本镜像版本的镜像摘要,请执行以下作 时间更新它。而且您选择退出自动安全修复, 这可能是你想要得到的东西。

Docker Scout 的默认 Up-to-Date Base Images 策略检查 您使用的基础镜像版本实际上是最新版本。此政策还 检查 Dockerfile 中的固定摘要是否对应于正确的版本。 如果发布者更新了您固定的图片,则政策评估 返回 non-compliant 状态,指示您应该更新镜像。

Docker Scout 还支持自动修复工作流,以保持 base 镜像为最新版本。当新的镜像摘要可用时,Docker Scout 可以 自动在您的仓库上提出拉取请求以更新 Dockerfile 使用最新版本。这比使用 更改版本,因为您拥有控制权,并且您有一个 更改发生的时间和方式的审计跟踪。

有关使用 Docker 自动更新基础镜像的更多信息 侦察兵,请参阅 补救

在 CI 中构建和测试镜像

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

Dockerfile 说明

请遵循以下有关如何正确使用 Dockerfile 说明创建高效且可维护的 Dockerfile 的建议。

只要有可能,请使用当前的官方镜像作为 镜像。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

拆分 long 或 complexRUN多行语句,以 反斜杠使您的 Dockerfile 更具可读性和可理解性,以及 维护。

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

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

默认情况下,反斜杠会转义换行符,但你可以使用 这escape命令.

您还可以使用 here 文档来运行多个命令,而无需链接它们 使用 Pipeline Operator:

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

有关RUN,请参阅 RUN 指令的 Dockerfile 参考

apt-get

一个常见的用例RUN基于 Debian 的镜像中的说明是安装 软件使用apt-get.因为apt-get安装软件包时,RUN apt-getCommand 有几个违反直觉的行为需要注意。

始终合并RUN apt-get updateapt-get install在同一RUN陈述。例如:

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

apt-get update独自一人在RUN语句导致缓存问题,并且 随后的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 将初始和修改后的指令视为相同,并重用 cache 的 cache 中。因此,apt-get update未执行 ,因为 build 使用缓存的版本。因为apt-get update不是 run 时,您的构建可能会获得过时的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.*

版本固定会强制 build 检索特定版本,而不管 缓存中的内容。此技术还可以减少由于意外更改而导致的故障 在必需的软件包中。

下面是一个格式正确的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/*

s3cmdargument 指定版本1.1.*.如果之前的镜像 使用旧版本时,指定新版本会导致缓存apt-get update并确保安装新版本。列出包 每行还可以防止包重复中的错误。

此外,当您通过删除/var/lib/apt/lists它 减小镜像大小,因为 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 -cinterpreter 中,只有 评估管道中最后一个作的退出代码以确定成功。 在上面的示例中,此构建步骤成功并生成新镜像 作为wc -l命令成功,即使wget命令失败。

如果您希望命令由于管道中任何阶段的错误而失败,那么 前置set -o pipefail &&为确保意外错误可以防止 build 从无意中成功。例如:

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

注意

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

在诸如dashshell 打开 基于 Debian 的镜像,请考虑使用 exec 形式的RUN显式 选择支持pipefail选择。例如:

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

CMD

CMD指令来运行包含在 image 以及任何参数。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 中,准备就绪。CMD很少应以CMD ["param", "param"]在 结合ENTRYPOINT除非 您和您的预期用户已经非常熟悉ENTRYPOINT工程。

有关CMD,请参阅 Dockerfile 参考以获取 CMD 指令

暴露

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

对于外部访问,您的用户可以执行docker run带有一个标志,指示 如何将指定的端口映射到他们选择的端口。 对于容器链接,Docker 为 将接收者容器返回到源(例如MYSQL_PORT_3306_TCP).

有关EXPOSE,请参阅 Dockerfile 参考以获取 EXPOSE 指令

环境

为了使新软件更易于运行,您可以使用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指令 自动更新容器中的软件版本。

ENVline 创建一个新的中间层,就像RUN命令。这 意味着即使您在 future layer 中取消设置环境变量,它 仍然存在于该层中,其值可以被转储。您可以通过以下方式进行测试 创建一个如下所示的 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

为了防止这种情况,并真正取消设置环境变量,请使用RUN命令 使用 shell 命令,在单个层中设置、使用和取消设置变量。 您可以使用 或 分隔命令。如果使用第二种方法, 并且其中一个命令失败,则;&&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 指令

添加或复制

ADDCOPY在功能上相似。COPY支持基本复制 文件导入到容器中,从构建上下文多阶段构建的阶段中。ADD支持从远程 HTTPS 和 Git URL 获取文件的功能,以及 从 build 上下文添加文件时自动提取 tar 文件。

您主要希望使用COPY用于将文件从一个阶段复制到另一个阶段 多阶段构建。如果您需要将构建上下文中的文件添加到 容器临时执行RUNinstruction,您通常可以替代 这COPY指令替换为 bind 挂载。例如,临时 添加requirements.txt文件RUN pip install指令:

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

绑定挂载比COPY用于包含构建中的文件 context 中。请注意,仅添加绑定挂载的文件 临时为单个RUNinstruction,并且不要坚持在最后 镜像。如果需要在最终镜像中包含来自生成上下文的文件, 用COPY.

ADD说明最适合需要下载远程工件的情况 作为构建的一部分。ADD比使用 类似wgettar,因为它可确保更精确的构建缓存。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是设置镜像的 main 命令,允许 image 以运行该命令,然后使用CMD作为 default 标志。

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

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

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

$ docker run s3cmd

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

$ docker run s3cmd ls s3://mybucket

这很有用,因为镜像名称可以兼作对Binaries的引用 如上面的命令所示。

ENTRYPOINTinstruction 也可以与帮助程序结合使用 脚本,使其能够以与上述命令类似的方式运行,甚至 启动时,该工具可能需要多个步骤。

例如,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 "$@"

此脚本使用execBash 命令,以便最终运行的应用程序成为容器的 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,请参阅 ENTRYPOINT 指令的 Dockerfile 参考

您应该使用VOLUME用于公开任何数据库存储区域的指令, configuration 存储,或者由 Docker 容器创建的文件和文件夹。你 强烈建议使用VOLUME对于 mutable 或 user-serviceable 的任意组合 部分。

有关VOLUME,请参阅 VOLUME 指令的 Dockerfile 参考

用户

如果服务可以在没有权限的情况下运行,请使用USER更改为非根 用户。首先在 Dockerfile 中创建用户和组 如以下示例所示:

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

注意

考虑显式 UID/GID。

镜像中的用户和组被分配了一个不确定的 UID/GID,因为 无论镜像是否重建,都会分配“下一个”UID/GID。所以,如果它是 critical,则应分配显式 UID/GID。

注意

由于 Go archive/tar 包对稀疏文件的处理,尝试创建用户 在 Docker 容器中具有非常大的 UID 可能会导致磁盘 穷竭,因为/var/log/faillog在容器层中填充了 NULL (\0) 字符。解决方法是将--no-log-initflag 设置为 useradd 的Debian / Ubuntuadduserwrapper 不支持此标志。

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

最后,为了减少层数和复杂性,请避免切换USER来回 经常。

有关USER,请参阅 Dockerfile 参考以获取 USER 指令

WORKDIR (工作目录)

为了清晰可靠,您应该始终对WORKDIR.此外,您应该使用WORKDIR而不是增殖指令 喜欢RUN cd … && do-something,这些内容难以阅读、排查问题和 保持。

有关WORKDIR,请参阅 WORKDIR 指令的 Dockerfile 参考

构建

ONBUILD命令在当前 Dockerfile 构建完成后执行。ONBUILD在派生的任何子镜像中执行FROM当前镜像。想 的ONBUILDcommand 作为父 Dockerfile 给出的指令 添加到子 Dockerfile 中。

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

ONBUILD对于将要构建的镜像非常有用FROM一个给定的 镜像。例如,您将使用ONBUILD对于语言堆栈镜像, 在 Dockerfile,如红宝石的ONBUILD变种.

使用ONBUILD应该得到一个单独的标签。例如ruby:1.9-onbuildruby:2.0-onbuild.

放置时要小心ADDCOPYONBUILD.onbuild 镜像 如果新构建的上下文缺少正在 添加。如上所述,添加单独的标签有助于缓解这种情况 允许 Dockerfile 作者做出选择。

有关ONBUILD,请参阅 Onbuild 指令的 Dockerfile 参考