docker 多阶段构建
与Java开发人员直接相关的DockerCon 2017上的两个公告是:
- Docker多阶段构建
- Docker Store中的Oracle JRE
该博客解释了Docker多阶段构建的目的,并提供了示例说明它们如何帮助我们生成更小,更高效的Java Docker映像。
只是给我看代码: github.com/arun-gupta/docker-java-multistage 。
有什么问题
为Java应用程序构建Docker映像通常涉及构建应用程序并将生成的工件打包到映像中。 Java开发人员可能会使用Maven或Gradle来构建JAR或WAR文件。 如果您使用Maven基本映像来构建应用程序,则它将从配置的存储库中下载所需的依赖项并将它们保留在映像中。 本地存储库中的JAR数量可能会很大,具体取决于pom.xml
的依赖项数量。 这可能会在图像中留下很多杂物。
让我们看一个示例Dockerfile:
FROM maven:3.5-jdk-8
COPY src /usr/src/myapp/src
COPY pom.xml /usr/src/myapp
RUN mvn -f /usr/src/myapp/pom.xml clean package
ENV WILDFLY_VERSION 10.1.0.Final
ENV WILDFLY_HOME /usr
RUN cd $WILDFLY_HOME && curl http://download.jboss.org/wildfly/$WILDFLY_VERSION/wildfly-$WILDFLY_VERSION.tar.gz | tar zx && mv $WILDFLY_HOME/wildfly-$WILDFLY_VERSION $WILDFLY_HOME/wildfly
RUN cp /usr/src/myapp/target/people-1.0-SNAPSHOT.war $WILDFLY_HOME/wildfly/standalone/deployments/people.war
EXPOSE 8080
CMD ["/usr/wildfly/bin/standalone.sh", "-b", "0.0.0.0"]
在此Dockerfile中:
-
maven:3.5-jdk-8
用作基础映像 - 应用程序源代码已复制到图像
- Maven用于构建应用程序工件
- 已下载并安装WildFly
- 生成的工件被复制到WildFly的
deployments
目录 - 终于,WildFly开始了
这种流程有几个问题:
- 将
maven
用作基本映像限制了映像中可用的功能。 这要求WildFly被明确下载和配置。 - 构建工件会下载所有Maven依赖项。 这些保留在映像中,并且在运行时不需要。 这会在运行时导致图像大小不必要的膨胀。
- 更改WildFly版本将需要更新Dockerfile。 如果我们可以
jboss/wildfly
使用jboss/wildfly
基本映像,这会容易得多。 - 另外,单元测试可以在打包工件和图像创建之后的集成测试之前运行。 再次不需要测试依赖项和结果来将其存在于生产映像中。
还有其他构建Docker映像的方法。 例如,将Dockerfile分为两个文件。 然后,第一个文件将构建工件,并使用卷映射将工件复制到公共位置。 然后,第二个文件将拾取生成的工件,然后使用精简基础图像。 这种方法还存在需要分别维护多个Dockerfile的问题。 另外,两个Dockerfile之间存在带外切换。
让我们看看如何通过多阶段构建解决这些问题。
什么是Docker多阶段构建?
多阶段构建允许在Dockerfile中使用多个FROM
语句。 每个FROM
语句之后直到下一个语句的指令都会创建一个中间映像。 最终的FROM
语句是最终的基础映像。 可以使用COPY --from=<image-number>
复制中间阶段的工件,对于第一个基本图像,从0开始。 未复制的工件将被丢弃。 这样可以使最终图像保持精简,并且仅包含相关的伪像。
FROM
语法已更新为使用as <stage-name>
指定阶段名称。 例如:
FROM maven:3.5-jdk-8 as BUILD
这允许使用阶段名称而不是带有--from
选项的数字。
让我们看一个示例Dockerfile:
FROM maven:3.5-jdk-8 as BUILD
COPY src /usr/src/myapp/src
COPY pom.xml /usr/src/myapp
RUN mvn -f /usr/src/myapp/pom.xml clean package
FROM jboss/wildfly:10.1.0.Final
COPY --from=BUILD /usr/src/myapp/target/people-1.0-SNAPSHOT.war /opt/jboss/wildfly/standalone/deployments/people.war
在此Dockerfile中:
- 有两个
FROM
指令。 这意味着这是一个两阶段的构建。 -
maven:3.5-jdk-8
是第一个构建的基础映像。 这用于为应用程序构建WAR文件。 第一阶段称为BUILD
。 -
jboss/wildfly:10.1.0.Final
是构建的第二个也是最终的基础映像。 使用COPY --from
语法将第一阶段生成的WAR文件复制到此阶段。 该文件直接复制到WildFly部署目录中。
让我们看一下这种方法的一些优点。
Docker多阶段构建的优势
- 一个Dockerfile定义了整个构建过程。 不需要单独的Dockerfile,然后使用卷映射协调“构建” Dockerfile和“运行” Dockerfile之间的工件转移。
- 可以适当选择最终映像的基础映像,以满足运行时需求。 这有助于减小运行时映像的整体大小。 此外,在中间阶段将废弃构建时产生的碎片。
- 使用标准WildFly基本映像,而不是手动下载和配置发行版。 如果发布了更新的标签,这将使更新映像变得更加容易。
使用单个Dockerfile构建的映像大小为816MB。 相比之下,使用多阶段构建构建的映像大小为584MB。
因此,使用多阶段有助于创建更小的图像。
当然,可以使用maven:jdk-8-alpine
图像。 但是随后,您必须创建或找到使用jdk-8-alpine
或类似工具构建的WildFly映像。 但是问题仍然存在,例如maven存储库,两个Dockerfile,使用卷映射或其他类似技术共享工件。
阅读PR#31257中的更多讨论。
如前所述,完整的代码可在github.com/arun-gupta/docker-java-multistage上找到 。
注册Docker Online Meetup以获取DockerCon 2017回顾。
docker 多阶段构建