12. 容器镜像

Spring Boot 应用程序可以使用 Dockerfile进行容器化,或者使用 Cloud Native Buildpack 来创建可在任何地方运行的优化的 docker 兼容容器映像,例如Kubernetes。

12.1. 高效的容器镜像

可以轻松地将 Spring Boot uber jar 打包为 docker 镜像。然而,像在 docker 镜像中那样复制和运行 uber jar 存在各种缺点。在不解压的情况下运行 uber jar 总是会产生一定量的开销,并且在容器化环境中这可能会很明显。另一个问题是,将应用程序的代码及其所有依赖项放在 Docker 映像的一层中并不是最佳选择。由于您重新编译代码的频率可能比升级所使用的 Spring Boot 版本的频率高,因此最好将各个部分分开一些。如果将 jar 文件放在应用程序类之前的层中,Docker 通常只需要更改最底层,就可以从其缓存中选取其他层。

12.1.1. 对 Docker 镜像进行分层

为了更轻松地创建优化的 Docker 镜像,Spring Boot 支持向 jar 添加层索引文件。它提供了层列表以及应包含在其中的 jar 部分。索引中的层列表根据层应添加到 Docker/OCI 映像的顺序进行排序。开箱即用,支持以下层:

  • dependencies(对于定期发布的依赖项)

  • spring-boot-loader(对于以下所有内容org/springframework/boot/loader

  • snapshot-dependencies(对于快照依赖项)

  • application(对于应用程序类和资源)

下面显示了一个layers.idx文件的示例:

- "dependencies":
  - BOOT-INF/lib/library1.jar
  - BOOT-INF/lib/library2.jar
- "spring-boot-loader":
  - org/springframework/boot/loader/launch/JarLauncher.class
  - ... <other classes>
- "snapshot-dependencies":
  - BOOT-INF/lib/library3-SNAPSHOT.jar
- "application":
  - META-INF/MANIFEST.MF
  - BOOT-INF/classes/a/b/C.class

这种分层旨在根据应用程序构建之间更改的可能性来分离代码。库代码在构建之间不太可能发生更改,因此将其放置在自己的层中,以允许工具重用缓存中的层。应用程序代码更有可能在构建之间发生更改,因此它被隔离在单独的层中。

Spring Boot 还借助layers.idx.

对于 Maven,请参阅打包分层 jar 或 war 部分,了解有关向存档添加层索引的更多详细信息。对于 Gradle,请参阅Gradle 插件文档的打包分层 jar 或 war 部分。

12.2. Dockerfile

虽然只需在 Dockerfile 中添加几行代码就可以将 Spring Boot uber jar 转换为 docker 镜像,但我们将使用分层功能来创建优化的 docker 镜像。当您创建包含图层索引文件的 jar 时,spring-boot-jarmode-layertoolsjar 将作为依赖项添加到您的 jar 中。将此 jar 放在类路径中,您可以在特殊模式下启动应用程序,该模式允许引导代码运行与应用程序完全不同的东西,例如提取层的东西。

Layertools 模式不能与包含启动脚本的完全可执行的 Spring Boot 存档一起使用。构建打算与 Layertools一起使用的 jar 文件时,禁用启动脚本配置。

以下是如何使用layertools jar 模式启动 jar 的方法:

$ java -Djarmode=layertools -jar my-app.jar

这将提供以下输出:

Usage:
  java -Djarmode=layertools -jar my-app.jar

Available commands:
  list     List layers from the jar that can be extracted
  extract  Extracts layers from the jar for image creation
  help     Help about any command

extract命令可用于轻松地将应用程序拆分为多个层以添加到 dockerfile 中。以下是使用 jarmode的 Dockerfile 示例。

FROM eclipse-temurin:17-jre as builder
WORKDIR application
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} application.jar
RUN java -Djarmode=layertools -jar application.jar extract

FROM eclipse-temurin:17-jre
WORKDIR application
COPY --from=builder application/dependencies/ ./
COPY --from=builder application/spring-boot-loader/ ./
COPY --from=builder application/snapshot-dependencies/ ./
COPY --from=builder application/application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]

假设以上Dockerfile内容位于当前目录中,则可以使用docker build . 构建 docker 映像,或者可以选择指定应用程序 jar 的路径,如以下示例所示:

$ docker build --build-arg JAR_FILE=path/to/myapp.jar .

这是一个多阶段的 dockerfile。构建器阶段提取稍后需要的目录。每个COPY命令都与 jarmode 提取的层相关。

当然,不使用jarmode也可以编写Dockerfile。您可以使用unzipmv的某种组合将内容移动到正确的层,但 jarmode 简化了这一点。

12.3. 云原生构建包

Dockerfile 只是构建 Docker 镜像的一种方式。构建 docker 镜像的另一种方法是使用 buildpacks 直接从 Maven 或 Gradle 插件。如果您曾经使用过 Cloud Foundry 或 Heroku 等应用程序平台,那么您可能使用过构建包。Buildpack 是平台的一部分,它接受您的应用程序并将其转换为平台可以实际运行的内容。例如,Cloud Foundry 的 Java buildpack 会注意到您正在推送.jar文件并自动添加相关的 JRE。

借助 Cloud Native Buildpack,您可以创建可在任何地方运行的 Docker 兼容映像。Spring Boot 包括直接对 Maven 和 Gradle 的构建包支持。这意味着您只需键入一个命令,即可快速将合理的镜像添加到本地运行的 Docker 守护进程中。

请参阅有关如何将构建包与MavenGradle结合使用的各个插件文档。

Paketo Spring Boot 构建包支持layers.idx文件,因此应用于该文件的任何自定义都将反映在该构建包创建的映像中。

为了实现可重复的构建和容器映像缓存,Buildpacks 可以操作应用程序资源元数据(例如文件“上次修改”信息)。您应该确保您的应用程序在运行时不依赖该元数据。Spring Boot 在提供静态资源时可以使用该信息,但可以使用spring.web.resources.cache.use-last-modified.

12.4. 接下来读什么

一旦您了解了如何构建高效的容器映像,您就可以阅读有关将应用程序部署到云平台(例如 Kubernetes)的信息。

最后更新于