构建最佳实践
使用多阶段构建
多阶段构建可以通过在镜像构建和最终输出之间创建更清晰的分离来减小最终镜像的大小。将 Dockerfile 指令拆分为不同的阶段,以确保最终输出只包含运行应用程序所需的文件。
使用多阶段还可以通过并行执行构建步骤来提高构建效率。
有关更多信息,请参阅多阶段构建。
创建可重用阶段
如果您有多个具有很多共同点的镜像,请考虑创建一个包含共享组件的可重用阶段,并将您的独特阶段基于该阶段。Docker 只需构建一次公共阶段。这意味着您的派生镜像可以更有效地利用 Docker 主机上的内存,并更快地加载。
维护一个公共基础阶段(“不要重复自己”)也比拥有多个执行类似事情的不同阶段更容易。
选择正确的基础镜像
实现安全镜像的第一步是选择正确的基础镜像。选择镜像时,请确保它来自受信任的来源并保持其小巧。
Docker 官方镜像是经过精选的集合,它们具有清晰的文档,遵循最佳实践,并定期更新。它们为许多应用程序提供了可靠的起点。
经过验证的发布者镜像是与 Docker 合作的组织发布和维护的高质量镜像,Docker 会验证其仓库中内容的真实性。
Docker 赞助的开源镜像是通过开源计划由 Docker 赞助的开源项目发布和维护的。
选择基础镜像时,请注意指示该镜像属于这些程序的徽章。

从 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 文件。
创建临时容器
您的 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.21,则 3.21 将解析为 3.21 的最新补丁版本。
# syntax=docker/dockerfile:1
FROM alpine:3.21在某个时间点,3.21 标签可能指向镜像的 3.21.1 版本。如果您在 3 个月后重建镜像,相同的标签可能指向不同的版本,例如 3.21.4。这种发布工作流是最佳实践,大多数发布者都使用这种标签策略,但它并未强制执行。
这带来的缺点是,您无法保证每次构建都获得相同的结果。这可能导致破坏性更改,并且意味着您也没有确切使用过的镜像版本的审计追踪。
为了完全确保您的供应链完整性,您可以将镜像版本锁定到特定的摘要。通过将镜像锁定到摘要,您保证始终使用相同的镜像版本,即使发布者用新镜像替换了标签。例如,以下 Dockerfile 将 Alpine 镜像锁定到与之前相同的标签 3.21,但这次也包含摘要引用。
# syntax=docker/dockerfile:1
FROM alpine:3.21@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c使用此 Dockerfile,即使发布者更新了 3.21 标签,您的构建仍将使用锁定的镜像版本:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c。
虽然这有助于您避免意外更改,但每次您想要更新基础镜像版本时都必须手动查找和包含镜像摘要,这更繁琐。而且您选择放弃自动化安全修复,这很可能是您想要获得的。
Docker Scout 默认的最新基础镜像策略会检查您正在使用的基础镜像版本是否确实是最新版本。此策略还会检查 Dockerfile 中锁定的摘要是否与正确版本对应。如果发布者更新了您已锁定的镜像,策略评估将返回不合规状态,表明您应该更新您的镜像。
Docker Scout 还支持自动化修复工作流,以保持您的基础镜像最新。当有新的镜像摘要可用时,Docker Scout 可以自动在您的仓库中发起拉取请求,以更新您的 Dockerfile 以使用最新版本。这比使用自动更改版本的标签更好,因为您可以控制,并且您可以拥有更改发生的时间和方式的审计追踪。
有关使用 Docker Scout 自动更新基础镜像的更多信息,请参阅修复。
在 CI 中构建和测试镜像
当您将更改提交到源代码管理或创建拉取请求时,使用GitHub Actions或其他 CI/CD 管道来自动构建和标记 Docker 镜像并对其进行测试。
Dockerfile 指令
请遵循以下关于如何正确使用 Dockerfile 指令的建议,以创建高效且可维护的 Dockerfile。
提示为了改进 Visual Studio Code 中 Dockerfile 的 linting、代码导航和漏洞扫描,请参阅 Docker VS Code 扩展。
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-document 来运行多个命令,而无需使用管道运算符将它们链接起来
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 语句中结合 RUN apt-get update 和 apt-get install。例如
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 nginxDocker 将初始指令和修改后的指令视为相同,并重用先前步骤中的缓存。因此,apt-get update 不会被执行,因为构建使用了缓存版本。由于 apt-get update 未运行,您的构建可能会获取过时的 curl 和 nginx 包版本。
使用 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 > /numberDocker 使用 /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 的镜像(如
dashshell)的情况下,请考虑使用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,随时可以使用。CMD 很少与 ENTRYPOINT 结合使用 CMD ["param", "param"] 的方式,除非您和您的预期用户已经非常熟悉 ENTRYPOINT 的工作原理。
有关 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
ADD 和 COPY 功能相似。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 指令是最佳选择。ADD 比手动使用 wget 和 tar 等工具添加文件更好,因为它能确保更精确的构建缓存。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有关 ADD 或 COPY 的更多信息,请参阅以下内容
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 archive/tar 包在处理稀疏文件时存在一个未解决的 bug,在 Docker 容器内尝试创建具有非常大 UID 的用户可能会导致磁盘耗尽,因为容器层中的
/var/log/faillog会被 NULL (\0) 字符填满。一个解决方法是向useradd传递--no-log-init标志。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-onbuild 或 ruby:2.0-onbuild。
将 ADD 或 COPY 放入 ONBUILD 时要小心。如果新构建的上下文缺少要添加的资源,`onbuild 镜像会灾难性地失败。如上所述添加单独的标签有助于缓解此问题,允许 Dockerfile 作者做出选择。
有关 ONBUILD 的更多信息,请参阅 Dockerfile 参考中的 ONBUILD 指令。