使用Docker多阶段构建来创建多个映像

时间:2019-06-18 17:32:20

标签: docker build docker-compose dockerfile devops

我有一个包含多个子项目的项目,并且我想为每个子项目运送一个单独的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添加到所有Dockerfiles

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原则(每个Dockerile的第一部分反复进行重复)。

最佳实践?

有更好的方法吗?最好甚至可以使用一个Dockerfile吗?

1 个答案:

答案 0 :(得分:2)

我遇到了同样的问题:几个项目共享一些共同的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并提供了更多详细信息。