什么是Docker构建阶段?

5
据我所知,Docker中的构建阶段是基本的要素,我对它们有实际的了解,但我很难给出一个恰当的定义,也似乎找不到一个。那么,什么是Docker的构建阶段的定义呢?需要注意的是,我并不是在问“如何使用构建阶段?”或者“如何使用多个构建阶段?”这些人们似乎非常渴望回答的问题 :-)。我之所以有这个问题,是因为我在文档中看到了以下句子:
- “FROM指令初始化一个新的构建阶段” - “可以给一个新的构建阶段命名”
这让我想知道:一个构建阶段到底是什么?
5个回答

4
一个阶段是创建一个镜像。在多阶段构建中,您要经历创建多个镜像的过程,但通常只标记一个(例外情况包括多个构建、使用 buildx 等工具构建多架构镜像清单,以及 Docker 在本答案发布后发布的任何其他内容)。
每个阶段都从 Dockerfile 中的“FROM”行开始构建一个不同的镜像。一个阶段不会继承前面步骤中的任何操作,而是基于自己的基础镜像。因此,如果您有以下内容:
FROM alpine as stage1
RUN apk add your_tool

FROM alpine as stage2
RUN your_tool some args

由于第二阶段未安装your_tool,因此您将收到错误。


从构建中输出哪个阶段?默认情况下是最后一个阶段,但您可以使用docker image build --target stage1 .更改它,以构建具有名称stage1的阶段,例如此示例。经典的docker build将从Dockerfile的顶部运行,直到完成目标阶段。Buildkit会构建依赖关系图并同时构建阶段,仅在需要时才构建,因此不要依赖于此排序来控制类似测试工作流程之类的东西在您的Dockerfile中(buildkit可以看到测试阶段中没有任何内容需要在您的发布阶段中使用,并跳过构建测试)。


多阶段的价值在于什么?通常,它用于将构建环境与运行时环境分开。它允许您在docker内执行整个构建。这有两个优点。

首先,您不需要外部Makefile和各种编译器和其他工具安装在主机上来编译二进制文件,然后使用COPY行将其复制到映像中,任何人都可以使用docker构建您的映像。

其次,生成的映像不包括所有编译器或其他构建时间工具,这些工具在运行时不需要,从而导致映像更小且更安全。典型的示例是具有maven和完整JDK的Java应用程序进行构建,具有仅包含jar文件和JRE的运行时。


如果每个阶段都制作一个单独的映像,如何将jar文件从构建阶段传递到运行阶段?这来自COPY命令的新选项--from。一个过度简化的多阶段构建如下:

FROM maven as build
COPY src /app/src
WORKDIR /app/src
RUN mvn install

FROM openjdk:jre as release
COPY --from=build /app/src/target/app.jar /app
CMD java -jar /app/app.jar

通过使用 COPY --from=build,我们能够将在构建阶段生成的构件添加到发布阶段中,而不会包含第一阶段中的任何其他内容(例如JDK或Maven之类的编译工具层不会被添加到我们的第二阶段)。
FROM x as yCOPY --from=y /a /b 如何协同工作?FROM x as y 定义了一个镜像名称,在本次构建过程中持续有效,这里是 y。在 Dockerfile 的后续任何位置,您都可以使用 y 代替镜像名称,并将其作为输入。因此,您可以这样说:
FROM upstream as mybuilder
RUN apk add common_tools

FROM mybuilder as stage2
RUN some_tool arg2

FROM mybuilder as stage3
RUN some_tool arg3

FROM minimal_base as release
COPY --from=stage2 /bin2 /
COPY --from=stage3 /bin3 /

请注意,stage2stage3都是FROM mybuilder,这是第一阶段的输出。 COPY --from=y允许您更改复制内容的上下文,使其成为另一个镜像而不是构建上下文。它不必是另一个阶段。因此,例如,您可以执行以下操作在映像中获取docker二进制文件:
FROM alpine
COPY --from=docker:stable /usr/local/bin/docker /usr/local/bin/

更多关于此内容的文档可在以下链接找到:https://docs.docker.com/develop/develop-images/multistage-build/


2
自17版本起,docker现在支持在docker build执行期间使用多个阶段。
这意味着,您不再需要在docker文件中仅定义一个源图像并在单个运行中完成整个构建,而是可以为每个阶段在Dockerfile中定义具有不同图像的多个阶段,并使用多个FROM定义:
# Build stage
FROM microsoft/aspnetcore
# ..do a build with a dev image for creating ./app artifact

# Publish - use a hardened, production image
FROM alpine:latest
CMD ["./app"]  

这样做有利于您将图像构建过程分解为针对某个阶段进行优化的任务 - 例如,这些阶段可能包括:
  1. 使用具有额外 linting 依赖项的图像来检查源代码
  2. 使用已安装所有开发依赖项的 dev-image 来构建源代码
  3. 使用包含测试框架的另一个图像在生成物上运行各种测试
  4. 一旦一切顺利通过,就可以使用最小化、优化、强化的图像来捕获最终生成物以供生产使用
详细了解多阶段构建:

谢谢。我明白多个构建阶段是如何工作的。但这仍然没有回答我的问题:什么是构建阶段? - Niels Bom

2
我认为Docker构建阶段不会有严格的定义,因为构建阶段通常是一些理论上的东西,它:
- 可以由您定义 - 取决于您的情况(语言/库)
在这个问题中:Difference between build and deploy? 其中一个答案说...
“Build”意味着编译项目。
我认为您也可以这样看待。构建阶段是生成可以稍后使用的内容的任何过程。
Docker多阶段构建的想法是:
1. 生成您将要使用的内容。 2. 放弃您不需要的内容,并以更轻量级的方式使用第1步的产品。
如果您已经阅读了文档,Alex Ellis有一个很好的例子,其中使用了相同的逻辑:
1. 他从golang镜像开始,添加库,构建应用程序(Go生成二进制可执行文件)。 2. 然后,他不需要golang和库来运行它,所以他选择了alpine镜像,从第1步中添加可执行文件,并使用大小更小的镜像来运行他的应用程序。

好的,我认为我的问题的答案在你的句子中:“构建阶段是生成某些东西的任何过程”。这是一个相当通用的答案,但我认为它已经足够了。关于多阶段构建的部分对于我的问题来说是无关紧要的。 - Niels Bom

1
一个构建阶段从一个FROM语句开始,到下一个FROM语句之前的步骤结束。

0
阶段 | ˈsteɪdʒ |
名词
指过程或发展中的一个点、时期或步骤。
举个实际例子:您想构建一个包含生产就绪的 Web 服务器和已编译为 JavaScript 的 TypeScript 文件的映像。您想在 Docker 容器中构建该 TypeScript,以简化依赖项管理。因此,您需要:
  • node.js
  • TypeScript
  • 任何编译所需的依赖项
  • Webpack 或其他工具
  • nginx/Apache/其他
在最终映像中,您实际上只需要编译后的 .js 文件和 nginx 等。但是,在到达那里之前,您首先需要所有这些其他内容。当您上传最终映像时,它将包含所有中间层,即使它们对于最终产品来说是不必要的。
现在,Docker 构建阶段允许您将这些“阶段”或“步骤”分成单独的映像,同时仍然使用一个 Dockerfile,而无需使用外部 shell 脚本等将多个 Dockerfile 粘合在一起。例如:
FROM node as builder
RUN npm install ...
# whatever you need to build your files

FROM nginx as production
COPY --from=builder /final.js /var/www/html

这个 Dockerfile 的最终结果是一个基于 nginx 的小型镜像,只包含最终的 .js 文件,不包含所有不必要的东西,如 node.js 和 npm 依赖项。

builder 是第一阶段,production 是第二阶段。在这种情况下,第一阶段将在过程结束时被丢弃,但您也可以选择使用 docker build --target=builder 构建特定的阶段。一个新的 FROM 引入了一个新的、独立的阶段。它们本质上是独立的 Dockerfiles,但可以使用 COPY --from 共享数据。


你说得没错 :-) 你基本上给出了与@muratiakos相同的答案; 解释了多阶段构建的工作原理以及它们的用途。并且:你也没有给出“构建阶段”是什么的定义,只是说明了如何使用它们。 - Niels Bom
还能更明确吗?我正在定义“stage”这个词,并描述它如何应用于Docker构建过程。你还期望有什么其他类型的定义呢? - deceze
更多。我知道"stage"这个单词的英文含义,但在Docker上下文中不知道"build stage"的意思。正如我在问题中所述:我知道如何使用它们,只是不知道其中一个的定义。我认为@tgogos的回答更好。 - Niels Bom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接