容器镜像

1. 高效的容器图像

很容易把 Spring Boot 的 fat jar 打包成 docker 镜像。不过,复制并运行 fat jar 以 docker 镜像形式运行有各种缺点。运行 fat jar 而不解压时总会有一定的开销,在容器化环境中这会很明显。另一个问题是,把应用的代码和所有依赖放在 Docker 镜像的同一层是不理想的。因为你可能比升级 Spring Boot 版本更频繁地重新编译代码,通常最好把内容分开得更清楚一些。如果你把 jar 文件放在应用类之前的层,Docker 通常只需要修改最底层,就能从缓存中获取其他层。spring-doc.cadn.net.cn

1.1. 解压可执行的 JAR

如果你是在容器中运行应用,可以使用可执行的jar,但通常也有利于将其爆破并以不同方式运行。某些PaaS实现还可能选择在运行前解包压缩包。例如,Cloud Foundry就是这样作的。运行解压归档的一种方法是启动相应的Starters,具体如下:spring-doc.cadn.net.cn

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

这实际上在启动时比从未爆炸的压缩包运行稍微快一点(取决于罐子大小)。运行时你不应该期待有任何差异。spring-doc.cadn.net.cn

解压完jar文件后,你还可以通过用“自然”的主方法运行应用,从而提升启动时间,而不是JarLauncher. 例如:spring-doc.cadn.net.cn

$ jar -xf myapp.jar
$ java -cp BOOT-INF/classes:BOOT-INF/lib/* com.example.MyApplication
使用JarLauncher在应用程序的主方法上,还有一个可预测的类路径顺序。jar 包含classpath.idx该文件被JarLauncher在构造类路径时。

1.2. Docker镜像分层

为了简化创建优化的 Docker 镜像,Spring Boot 支持在 jar 中添加图层索引文件。它提供了图层列表以及应包含在其中的 jar 部分。索引中的图层列表根据图层添加到 Docker/OCI 镜像的顺序进行排序。开箱即用时,支持以下图层:spring-doc.cadn.net.cn

以下展示了一个layers.idx文件:spring-doc.cadn.net.cn

- "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

这种分层设计是为了根据代码在不同应用构建间变化的可能性来区分代码。 库代码在构建间变化较小,因此被放在独立的图层中,以便工具重新利用缓存中的图层。 应用代码更可能在不同构建间发生变化,因此它被隔离在一个独立的层级中。spring-doc.cadn.net.cn

Spring Boot 还支持通过layers.idx.spring-doc.cadn.net.cn

关于Maven,请参见包装分层罐或战争部分,了解更多关于如何为档案添加图层索引的细节。 关于 Gradle,请参见 Gradle 插件文档中的分层罐子或战争包装部分spring-doc.cadn.net.cn

2. Dockerfile

虽然可以用Docker文件中几行文字将Spring Boot的胖jar转换成docker镜像,但我们将使用分层功能创建优化的Docker镜像。 当你创建一个包含图层索引文件的jar时,Spring-boot-jarmode-layertoolsjar 会作为依赖添加到你的 jar 中。 有了这个 jar 在类路径上,你可以以特殊模式启动应用,允许引导代码运行与应用完全不同的程序,比如提取层的程序。spring-doc.cadn.net.cn

分层工具模式无法与包含启动脚本的完整可执行 Spring Boot 归档一起使用。 在构建用于分层工具.

以下是你可以用分层工具罐模式:spring-doc.cadn.net.cn

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

这将得到以下输出:spring-doc.cadn.net.cn

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

提取命令可以轻松将应用拆分成多层,添加到 docker 文件中。 这里有一个 Dockerfile 的示例,使用贾莫德.spring-doc.cadn.net.cn

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 构建。,或者可选地指定到你的应用jar的路径,如下例所示:spring-doc.cadn.net.cn

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

这是一个多阶段的 docker 文件。 构建阶段会提取后续需要的目录。 每一个复制命令与 jarmode 提取的层相关。spring-doc.cadn.net.cn

当然,Dockerfile 也可以在不使用 jarmode 的情况下编写。 你可以使用以下几种组合解 压缩MV把东西移到正确的图层,但JARMODE简化了这个过程。spring-doc.cadn.net.cn

3. 云原生构建包

Dockerfile只是构建docker镜像的一种方式。 另一种构建docker镜像的方法是直接从你的Maven或Gradle插件中使用buildpack。 如果你用过像Cloud Foundry或Heroku这样的应用平台,那你很可能用过构建包。 构建包是平台中将你的应用转化为平台实际可运行内容的部分。 例如,Cloud Foundry 的 Java 构建包会注意到你正在推送一个。罐并自动添加一个相关的JRE。spring-doc.cadn.net.cn

通过云原生构建包,你可以创建兼容 Docker 的镜像,随时运行。 Spring Boot 直接支持 Maven 和 Gradle 的构建包。 这意味着你只需输入一个命令,就能快速将一个合理的镜像导入本地运行的 Docker 守护进程。spring-doc.cadn.net.cn

请参阅各个插件的文档,了解如何在 MavenGradle 中使用构建包。spring-doc.cadn.net.cn

Paketo Spring Boot 构建包也已更新以支持layers.idx文件,因此应用到它上的任何自定义都会反映在构建包创建的图像中。
为了实现可重复的构建和容器镜像缓存,构建包可以作应用资源的元数据(例如文件“最后修改”信息)。 你应确保你的应用在运行时不依赖这些元数据。 Spring Boot 可以在提供静态资源时使用这些信息,但可以通过以下方式禁用spring.web.resources.cache.use-last-modified

4. 接下来要读什么

一旦你学会了如何构建高效的容器镜像,就可以阅读如何将应用部署到云平台,比如 Kubernetes。spring-doc.cadn.net.cn