【问题标题】:Using Docker multistage build to create multiple images使用 Docker 多阶段构建创建多个镜像
【发布时间】:2019-06-18 17:32:20
【问题描述】:

我有一个包含多个子项目的项目,我想为每个子项目提供一个单独的 docker 映像。为了提高效率,我想使用multistage builds,并正在寻找一种最佳实践模式,如何以最有效和最直观的方式完成这项工作。到目前为止,我发现了两种可能性,都有缺点:

多个 Dockerfile 和构建调用

我可以为构建器镜像制作一个 Dockerfile

FROM maven as builder

COPY . /build
WORKDIR /build
RUN mvn -e clean install

并为每个子项目单独的 Dockerfiles

FROM my_builder as builder

FROM openjdk:jre-slim as proj1

COPY --from=builder /build/proj1.jar /somewhere/
CMD ["java", "-jar","/somewhere/proj1.jar"]

这可行,但缺点是我必须分多个步骤构建我的镜像,并且子项目的 Dockerfile 不能自己构建:

docker build -t my_builder .
docker build proj1/
docker build proj2/

使用 docker-compose 文件

我可以通过使用 docker-compose 文件来解决这个问题:

version: "3.4"

services:
   builder:
      build:
         context: ./
   proj1:
     build:
       target: proj1
       context: ./proj1
     depends_on:
       - builder
   proj2:
     build:
       target: proj2
       context: ./proj2
     depends_on:
       - builder

这具有能够使用单个命令运行构建的优势

docker-compose build

但有一个缺点,即为项目中不需要的 docker-compose 创建了一个不需要的和人为的依赖项。

在所有子项目中构建整个项目

我还可以将 buildstage 添加到所有 Dockerfile 中

FROM maven as builder

COPY . /build
WORKDIR /build
RUN mvn -e clean install

FROM openjdk:jre-slim as proj1

COPY --from=builder /build/proj1.jar /somewhere/
CMD ["java", "-jar","/somewhere/proj1.jar"]

这样的好处是我可以自己构建每个项目的容器

docker build proj1/

另一方面,它的效率较低并且违反了 DRY 原则(每个 Dockerfile 的第一部分一遍又一遍地重复)。

最佳实践?

有没有更好的方法来做到这一点?最好是一个可以使用单个 Dockerfile 的?

【问题讨论】:

  • 您是否考虑过使用 maven、gradle 或其他构建工具来封装流程?
  • @YuriG. 我做到了。一个简单的 bash 脚本就足够了。但这是一个肮脏的 hack,类似于 docker 引入多阶段构建之前的构建器模式。我真的很想避免这种情况并找到一个干净的 docker-only 解决方案。
  • 那么,有什么问题吗?创建一个带有简单错误处理的简单 bash 脚本,在此处发布,社区将给予反馈

标签: docker build docker-compose dockerfile devops


【解决方案1】:

我遇到了同样的问题:几个项目共享一些通用的 Dockerfile 行,但有一些差异。有几种方法可以使用单个 Dockerfile 解决此问题。

首先,你可以做一个扇出方法:

FROM ubuntu:18.04 as base
RUN echo "base" >> /history.txt
CMD cat /history.txt


FROM base as variant0
RUN echo "variant0" >> /history.txt

FROM base as variant1
RUN echo "variant1" >> /history.txt

然后在构建过程中,您只需使用--target 选择您想要的:

docker build --file=fan-out.dockerfile --target=variant0 --tag=fan-out/variant0 ./

或者,有时您的项目在结尾而不是开头有共享步骤。你可以这样做,我称之为扇入方法:

ARG variant

FROM ubuntu:18.04 as variant0
RUN echo "variant0" >> /history.txt

FROM ubuntu:18.04 as variant1
RUN echo "variant1" >> /history.txt

FROM ubuntu:18.04 as variant2
RUN echo "variant2" >> /history.txt

FROM $variant as join
# pass, do nothing

FROM ubuntu:18.04 as final
COPY --from=join /history.txt /
RUN echo "final" >> /history.txt
CMD cat /history.txt

并使用--build-arg 构建它:

docker build --file=fan-in.dockerfile --target=final --build-arg="variant=variant1" --tag=fan-in/variant1 ./

在这两种方法中,您可能需要一个 makefile 或 shell 脚本来跟踪每个变体的命令。

我写了一个blog post 并提供了更多细节。

【讨论】:

  • 这有点像 hack,但还是不错的 hack。如果您正在运行第三阶段,则第一和第二阶段也将运行。但是第二个会生成一个未使用的图像,对输出没有影响。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-29
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
相关资源
最近更新 更多