本站(springdoc.cn)中的内容来源于 spring.io ,原始版权归属于 spring.io。由 springdoc.cn 进行翻译,整理。可供个人学习、研究,未经许可,不得进行任何转载、商用或与之相关的行为。 商标声明:Spring 是 Pivotal Software, Inc. 在美国以及其他国家的商标。

1. 高效的容器镜像(Efficient container images)

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

1.1. 分层的Docker镜像

为了更容易创建优化的Docker镜像,Spring Boot支持在jar中添加一个层索引文件。 它提供了一个层的列表以及jar中应包含的部分。 索引中的层列表是根据层应该被添加到Docker/OCI镜像中的顺序排列的。 开箱即用,支持以下层。

  • dependencies (用于常规发布的依赖)

  • spring-boot-loader (针对 org/springframework/boot/loader 下的所有内容)

  • snapshot-dependencies (用于snapshot的依赖)

  • application (用于application 类和资源)

The following shows an example of a layers.idx file:

- "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中。有了classpath上的这个jar,你可以在一个特殊的模式下启动你的应用程序,这个模式允许bootstrap代码运行与你的应用程序完全不同的东西,例如,提取层里的东西。

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.JarLauncher"]

假设上述 Dockerfile 在当前目录下,你的docker镜像可以用 docker build . 来构建,也可以选择指定应用程序jar的路径,如下例所示。

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

这是一个多阶段的docker文件。 构建者阶段提取了以后需要的目录。 每个 COPY 命令都与jarmode提取的层有关。

当然,不使用jarmode也可以编写Dockerfile。 你可以使用 unzipmv 的一些组合来把东西移到正确的层,但jarmode简化了这一点。

3. 云原生构建包(Cloud Native Buildpacks)

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

有了Cloud Native Buildpacks,你可以创建Docker兼容的镜像,可以在任何地方运行。 Spring Boot包括对Maven和Gradle的直接构建包支持。 这意味着你只需输入一条命令,就能迅速将一个合理的镜像放入本地运行的Docker守护程序中。

关于如何在 MavenGradle 中使用buildpacks,请参见各个插件的文档。

Paketo Spring Boot buildpack 也已更新,以支持 layer.idx 文件,因此任何应用于该文件的定制都将反映在构建包创建的镜像中。
为了实现可重复的构建和容器镜像缓存,Buildpacks可以操作应用程序的资源元数据(比如文件的 "last modified" 信息)。 你应该确保你的应用程序在运行时不依赖该元数据。 在提供静态资源时,Spring Boot 可以使用这些信息,但可以用 spring.web.resources.cache.use-last-modified 禁用。

4. 接下来读什么

一旦你学会了如何构建高效的容器镜像,你就可以阅读关于将应用程序部署到云平台的内容,如Kubernetes。