20220524 Container Images

前言

文档地址

Spring Boot 应用程序可以 使用 Dockerfiles 进行容器化,或者 使用 Cloud Native Buildpacks 创建您可以在任何地方运行的优化的 docker 兼容容器镜像

1. 高效的容器镜像

很容易将 Spring Boot fat jar 打包为 docker 镜像。然而,在 docker 镜像中复制和运行 fat jar 有很多缺点。在不解压的情况下运行 fat jar 总是有一定的开销,在容器化环境中这可能很明显。另一个问题是,将应用程序的代码及其所有依赖项放在 Docker 映像的一层中是次优的。由于您可能比升级您使用的 Spring Boot 版本更频繁地重新编译您的代码,因此通常最好将它们分开一点。如果你把 jar 文件放在你的应用程序类之前的层,Docker 通常只需要改变最底层,就可以从它的缓存中提取其他的。

1.1. 解压 fat jar

如果您从容器运行应用程序,则可以使用可执行 jar ,但分解它并以不同的方式运行它通常也是一个优势。某些 PaaS 实现也可能会选择在运行前解压缩。例如,Cloud Foundry 就是这样运作的。运行解压的一种方法是启动适当的启动器,如下所示:

$ jar -xf myapp.jar
$ java org.springframework.boot.loader.JarLauncher

这实际上在启动时(取决于 jar 的大小)比从未解压的存档中运行要快一些。在运行时,您不应期望有任何差异。

解压 jar 文件后,您还可以通过使用其“自然”主方法而不是 JarLauncher 。例如:

$ jar -xf myapp.jar
$ java -cp BOOT-INF/classes:BOOT-INF/lib/* com.example.MyApplication

使用 JarLauncher 而不是应用程序的 main 方法具有可预测的类路径顺序的额外好处。jar 包含一个在构建类路径时 JarLauncher 使用的 classpath.idx 文件。

1.2. 分层 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/JarLauncher.class
  - org/springframework/boot/loader/jar/JarEntry.class
- "snapshot-dependencies":
  - BOOT-INF/lib/library3-SNAPSHOT.jar
- "application":
  - META-INF/MANIFEST.MF
  - BOOT-INF/classes/a/b/C.class

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

layers.idx 的帮助下,Spring Boot 还支持 war 文件的分层。

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

2. Dockerfiles

虽然只需 Dockerfile 中的几行代码就可以将 Spring Boot fat jar 转换为 docker 镜像,但我们将使用 分层功能 来创建优化的 docker 镜像。当您创建一个包含层索引文件的 jar 时,spring-boot-jarmode-layertools jar 将作为依赖项添加到您的 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:11-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:11-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.JarLauncher"]

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

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

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

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

3. Cloud Native Buildpacks

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

使用 Cloud Native Buildpacks ,您可以创建可以在任何地方运行的 Docker 兼容镜像。Spring Boot 直接包含对 Maven 和 Gradle 的 buildpack 支持。这意味着您只需键入一个命令,就可以快速将合理的镜像放入本地运行的 Docker 守护程序中。

请参阅单独的插件文档,了解如何将 buildpacks 与 MavenGradle 一起使用。

Paketo Spring Boot buildpack 也已更新以支持 layers.idx 文件,因此应用到它的任何自定义都将反映在 buildpack 创建的映像中。

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

posted @ 2022-06-08 07:58  流星<。)#)))≦  阅读(42)  评论(0编辑  收藏  举报