什么是Docker构建阶段?

时间:2018-04-17 10:07:34

标签: docker dockerfile

据我所知,Docker中的构建阶段是基本的东西,我对它们有实际的理解,但是我无法找到合适的定义,而且我似乎也找不到它。

那么:Docker 构建阶段的定义是什么?

编辑:我询问“我如何使用构建阶段?”或“我如何使用多构建阶段?”人们似乎非常渴望回答: - )

我有这个问题的原因是因为我在the docs中看到了以下句子:

  • “FROM指令初始化新的构建阶段”
  • “可以将名称赋予新的构建阶段”

这让我想知道:究竟是什么是一个构建阶段?

5 个答案:

答案 0 :(得分:1)

从第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. 一旦一切顺利通过,使用最小尺寸,优化的硬化图像来捕获最终的生产工件
  5. 详细了解multistage-build

答案 1 :(得分:1)

我不认为 Docker构建阶段 会有严格的定义,因为构建阶段通常是理论上的:

  • 可以由您定义
  • 取决于您的案例(语言/图书馆)

在这个问题中:Difference between build and deploy?其中一个答案是......

  

构建表示编译项目

我想你也可以这样看。 构建阶段 生成某些可以在以后使用和使用的过程。

使用docker多阶段构建的想法是:

  1. 生成您需要的东西
  2. 留下您不需要的内容并以更轻量级的方式使用第1步的产品
  3. 如果您已阅读过文档,Alex Ellis有一个很好的例子,其中发生了相同的逻辑:

    1. 他以golang图片开头,添加库,构建他的应用程序(Go生成二进制可执行文件)
    2. 之后,他不需要golang和库运送/运行它,他选择alpine图像,添加步骤1中的可执行文件,并将他的应用程序与图像一起发送到更小的尺寸。

答案 2 :(得分:1)

构建阶段始于FROM语句,结束于下一条FROM语句之前的步骤

答案 3 :(得分:1)

阶段是创建图像。在多阶段构建中,您将经历创建多个映像的过程,但是通常只标记一个映像(例外是多个构建,使用buildx之类的工具构建多体系结构映像清单,以及其他泊坞窗在此答案后释放)。


每个阶段(从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构建将从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/

答案 4 :(得分:0)

  

阶段 | steɪdʒ|
  名词
  流程或开发中的一个点,期间或步骤

举一个实际的例子:你想要构建一个包含生产就绪的Web服务器的图像,其中Typescript文件被编译为Javascript。您希望在Docker容器中构建该Typescript以简化依赖关系管理。所以你需要:

  • 的node.js
  • 打字稿
  • 编译所需的任何依赖项
  • Webpack或其他什么
  • 的nginx /阿帕奇/不管

在你的最终图像中,你只需要编译的.js文件,比如nginx。但要到达那里,你首先需要所有其他东西。当您上传最终图像时,它将包含所有中间图层,即使它们对最终产品不是必需的。

Docker构建阶段现在允许您实际将这些阶段步骤分离为单独的图像,同时仍然只使用一个Dockerfile而不需要将多个Dockerfiles粘合在一起使用外部shell脚本等。 E.g:

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共享数据。

相关问题