在 Docker 中构建多模块 Maven 项目

1、概览

本文将带你了解如何利用 Docker 的多阶段构建功能高效地为多模块 Maven 项目构建 Docker 镜像,,以充分利用 Docker 的缓存机制。

然后,还会介绍 Google Jib Maven 插件,用于在没有 DockerfileDocker 的情况下构建 Docker 镜像。

2、多模块 Maven 项目

多模块 Maven 应用由不同功能的独立模块组成。Maven 通过管理依赖关系来构建应用,并将这些模块组装成一个可部署的单元。

在本文的代码示例中,我们将使用一个包含两个 Maven 模块的基本 Spring Boot 项目,这两个模块分别代表应用程序的 Domain 和 API。

Maven 项目的结构如下:

+-- parent
   +-- api
   |   `-- src
   |   `-- pom.xml
   +-- domain
   |   `-- src
   |   `-- pom.xml
    `-- pom.xml

查看父模块的 pom.xml 文件,就会发现它继承了 spring-boot-starter-parent,并包含了 domainapi 模块:

<project>
    <groupId>com.baeldung.docker-multi-module-maven</groupId>
    <artifactId>parent</artifactId>
    <packaging>pom</packaging>
    <version>0.0.1-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.2</version>
        <relativePath />
    </parent>

    <!-- 模块 -->
    <modules>
        <module>api</module>
        <module>domain</module>
    </modules>

    <!--  other configuration  -->
</project>

此外,我们还遵循了简洁架构原则,确保所有源码的依赖方向都是正确的。简而言之,确保 api 模块依赖于 module 模块,而不是相反。

3、多阶段 Docker 构建

Docker 中的多阶段构建允许我们在单个 Docker 文件中使用多个 FROM 指令来创建更小、更高效的镜像。每个阶段都可用于不同的目的,如编译代码或打包应用,只有最后阶段才会包含在最终镜像中。

例如,我们的示例可以使用三个阶段:提取依赖、构建应用程序和准备运行时环境。

创建包含这三个不同部分的 Dockerfile

# 预取依赖
FROM maven:3.8.5-openjdk-17 AS DEPENDENCIES

# 构建 jar
FROM maven:3.8.5-openjdk-17 AS BUILDER

# 准备运行时环境
FROM openjdk:17-slim

3.1、预取依赖项

DEPENDENCIES 阶段将为我们的应用预先获取和缓存 Maven 依赖项。

先选择 Maven 镜像,然后复制三个 pom.xml 文件:

FROM maven:3.8.5-openjdk-17 AS DEPENDENCIES

WORKDIR /opt/app
COPY api/pom.xml api/pom.xml
COPY domain/pom.xml domain/pom.xml
COPY pom.xml .

然后,需要使用 maven-dependency-plugin 及其 go-offline goal 来解析和下载 pom.xml 文件中指定的所有依赖。此外,还通过指定 “-B” 选项以非交互模式运行命令,并通过 “-e” 提示所有错误:

RUN mvn -B -e org.apache.maven.plugins:maven-dependency-plugin:3.1.2:go-offline -DexcludeArtifactIds=domain

最后还添加了 excludeArtifactIds 属性,以防止 Maven 下载特定的组件。在本例中,它排除了 domain 组件。因此,domain JAR 将在本地构建,而不是从 Repository 中获取。

这个命令确保在下一个阶段运行构建过程时,所有依赖项都将在本地可用,无需再次下载。

3.2、构建镜像

要构建镜像,首先需要确保所有必要的依赖都已预先获取,并且源代码可用。在 BUILDER 阶段,我们首先要从 DEPENDENCIES 阶段复制必要的资源:

FROM maven:3.8.5-openjdk-17 AS BUILDER

WORKDIR /opt/app
COPY --from=DEPENDENCIES /root/.m2 /root/.m2
COPY --from=DEPENDENCIES /opt/app/ /opt/app
COPY api/src /opt/app/api/src
COPY domain/src /opt/app/domain/src

接下来,运行 mvn clean install 来编译代码并构建 domainapi JAR 文件。由于测试可能已经在之前运行过了,我们可以使用 -DskipTests 跳过测试来加快编译过程:

RUN mvn -B -e clean install -DskipTests

3.3、准备运行环境

Dockerfile 的最后阶段,我们要为应用设置最基本的运行环境。

将选择应用将要运行的基础镜像,复制前一阶段的 JAR 文件,并定义启动应用的入口点:

FROM openjdk:17-slim

WORKDIR /opt/app
COPY --from=BUILDER /opt/app/api/target/*.jar /app.jar
EXPOSE 8080

ENTRYPOINT ["java", "-jar", "/app.jar"]

3.4、运行应用

最后,我们就可以构建并运行镜像了。还可以添加 from-dockerfile 标签来区分该镜像:

docker build -t baeldung-demo:from-dockerfile .

docker run -p 8080:8080 baeldung-demo:from-dockerfile

此时,请求 localhost:8080/api/countries,就可以获取到响应。

如你所见,多阶段 Dockerfile 通过将构建依赖与最终运行环境隔离开来,简化了依赖管理。此外,它还能帮助我们减少最终镜像的大小,因为它只复制了构建阶段的必要工件,从而提高了效率和安全性。

4、使用 Jib 构建项目

我们还可以使用 Jib 等专用工具构建 Docker 镜像。Jib Maven 插件是一种工具,可直接从我们的 Maven 构建中为 Java 应用程序构建优化的 Docker 镜像,而无需 DockerfileDocker 守护进程。

Jib 需要配置几个关键属性:

  • Java 基础镜像
  • 生成的 Docker 镜像的名称
  • 应用的入口
  • 暴露的端口

maven-jib-plugin 添加到 API 模块的 pom.xml 中:

<plugin>
    <groupId>com.google.cloud.tools</groupId>
    <artifactId>jib-maven-plugin</artifactId>
    <version>3.4.0</version>
    <configuration>
        <from>
            <image>openjdk:17-slim</image>
        </from>
        <to>
            <image>baeldung-demo:from-jib</image>
        </to>
        <container>
            <mainClass>com.baeldung.api.Application</mainClass>
            <ports>
                <port>8080</port>
            </ports>
        </container>
    </configuration>
</plugin>

之后,就可以使用 Maven 来构建镜像:

mvn compile jib:dockerBuild

最后,Jib 成功地构建了 Docker 镜像,现在我们可以使用 docker run 命令运行应用程序了:

docker run -p 8080:8080 baeldung-demo:from-jib

5、总结

本文介绍了如何使用 Docker 的多阶段构建功能为多模块的 Maven 项目构建 Docker 镜像,以及如何使用 Jib Maven 插件来构建 Docker 镜像,而无需 Dockerfile。


Ref:https://www.baeldung.com/docker-maven-build-multi-module-projects