本文为官方文档直译版本。原文链接
Spring Boot 应用程序可以使用 Dockerfiles 进行容器化,也可以使用 Cloud Native Buildpacks 创建优化的 docker 兼容容器映像,以便在任何地方运行。
将 Spring Boot uber jar 打包成 docker 镜像是很容易实现的。不过,在 docker 镜像中复制并运行 uber jar 有各种弊端。在未解压缩的情况下运行 uber jar 时,总会有一定的开销,在容器化环境中这一点会很明显。另一个问题是,将应用程序的代码及其所有依赖项放在 Docker 镜像的一个层中并不理想。由于重新编译代码的频率可能比升级 Spring Boot 版本的频率还要高,因此最好将它们分开一些。如果将 jar 文件放在应用程序类之前的层中,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
的帮助下对 war 文件进行分层。
关于 Maven,请参阅打包分层 jar 或 war 部分,了解在归档文件中添加层索引的更多详情。对于 Gradle,请参阅 Gradle 插件文档中的打包分层 jar 或 war 部分。
虽然只需在 Dockerfile 中添加几行代码,就能将 Spring Boot uber jar 转换为 docker 镜像,但我们将使用分层功能来创建优化的 docker 镜像。当你创建一个包含分层索引文件的 jar 时,spring-boot-jarmode-layertools
jar 将作为依赖关系添加到你的 jar 中。有了类路径上的这个jar,你就可以以一种特殊的模式启动你的应用程序,这种模式允许引导代码运行与你的应用程序完全不同的东西,例如,提取层(layers)的东西。
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 .
或指定应用程序 jar 的路径来构建你的 docker 镜像,如下例所示:
$ docker build --build-arg JAR_FILE=path/to/myapp.jar .
这是一个多阶段的 Dockerfile
。生成器阶段提取了后面需要的目录。每个 COPY
命令都与 jarmode 提取的层有关。
当然,也可以不使用 jarmode 来编写 Dockerfile。你可以使用 unzip
和 mv
的组合将东西移到正确的层,但 jarmode 简化了这一过程。
Dockerfile 只是构建 docker 镜像的一种方法。另一种构建 docker 镜像的方法是直接从 Maven 或 Gradle 插件中使用构建包。如果你曾经使用过 Cloud Foundry 或 Heroku 等应用平台,那么你可能使用过构建包。构建包是平台的一部分,它将您的应用程序转换为平台可以实际运行的内容。例如,Cloud Foundry 的 Java 构建包会注意到您推送的是 .jar
文件,并自动添加相关的 JRE。
有了云原生构建包,您就可以创建可在任何地方运行的 Docker 兼容映像。Spring Boot 包含对 Maven 和 Gradle 的直接构建包支持。这意味着您只需键入一条命令,就能快速将合理的映像导入本地运行的 Docker 守护进程。
有关如何在 Maven 和 Gradle 中使用构建包,请参见各个插件的文档。
Paketo Spring Boot 构建包支持
layers.idx
文件,因此应用于该文件的任何定制都将反映在构建包创建的镜像中。
为了实现可重现的构建和容器映像缓存,构建包可操作应用程序资源元数据(如文件的 “最后修改” 信息)。您应确保您的应用程序在运行时不依赖该元数据。Spring Boot 可以在提供静态资源时使用该信息,但可以使用
spring.web.resources.cache.use-last-modified
将其禁用。