容器镜像
1. 高效的容器镜像
很容易将 Spring Boot 胖 jar 打包为 docker 映像。 但是,复制和运行 Docker 镜像中的 fat jar 存在各种缺点。 在不解包的情况下运行 fat jar 时,总会有一定量的开销,在容器化环境中,这可能会很明显。 另一个问题是,将应用程序的代码及其所有依赖项放在 Docker 映像的一层中是次优的。 由于您可能更频繁地重新编译代码,而不是升级您使用的 Spring Boot 版本,因此通常最好将内容分开更多。 如果你把 jar 文件放在应用程序类之前的层中,Docker 通常只需要改变最底层,就可以从它的缓存中获取其他文件。
1.1. 解压缩可执行 JAR
如果您从容器运行应用程序,则可以使用可执行 jar,但分解它并以不同的方式运行它通常也是一个优势。 某些 PaaS 实施也可能选择在运行之前解压缩档案。 例如,Cloud Foundry 以这种方式运行。 运行解压后的档案的一种方法是启动相应的Starters,如下所示:
$ jar -xf myapp.jar
$ java org.springframework.boot.loader.JarLauncher
这实际上在启动时(取决于 jar 的大小)比从未爆炸的存档运行要快一些。 启动后,您不应期望任何差异。
解压缩 jar 文件后,您还可以通过使用其 “natural” main 方法而不是JarLauncher
.例如:
$ jar -xf myapp.jar
$ java -cp BOOT-INF/classes:BOOT-INF/lib/* com.example.MyApplication
使用JarLauncher 应用程序的 main 方法具有可预测的 Classpath Sequences 的额外好处。
该 jar 包含一个classpath.idx 文件,该文件由JarLauncher 在构造 Classpath 时。 |
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
此分层旨在根据应用程序构建之间更改的可能性来分隔代码。 库代码在构建之间不太可能更改,因此它被放置在自己的层中,以允许工具重用缓存中的层。 应用程序代码更有可能在构建之间发生变化,因此它被隔离在单独的层中。
Spring Boot 还支持在layers.idx
.
对于 Maven,请参阅打包分层 jar 或 war 部分,以了解有关向存档添加层索引的更多详细信息。 对于 Gradle,请参阅 Gradle 插件文档的打包分层 jar 或 war 部分。
2. Dockerfile 文件
虽然只需在 Dockerfile 中编写几行代码就可以将 Spring Boot fat jar 转换为 docker 映像,但我们将使用分层功能来创建优化的 docker 映像。
当您创建包含 layers 索引文件的 jar 时,spring-boot-jarmode-layertools
jar 将作为依赖项添加到您的 jar 中。
在 Classpath 上使用此 jar,您可以以特殊模式启动应用程序,该模式允许引导代码运行与应用程序完全不同的东西,例如,提取层的东西。
这layertools mode 不能与包含启动脚本的完全可执行的 Spring Boot 存档一起使用。
在构建旨在用于的 jar 文件时禁用启动脚本配置layertools . |
以下是使用layertools
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
.
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 .
这是一个多阶段 dockerfile。
builder 阶段将提取稍后需要的目录。
每个COPY
命令与 JarMode 提取的图层相关。
当然,可以在不使用 jarmode 的情况下编写 Dockerfile。
您可以使用unzip
和mv
将内容移动到正确的图层,但 JarMode 简化了此作。
3. 云原生 Buildpack
Dockerfile 只是构建 Docker 镜像的一种方式。
构建 docker 镜像的另一种方法是直接从 Maven 或 Gradle 插件使用 buildpacks。
如果您曾经使用过 Cloud Foundry 或 Heroku 等应用程序平台,那么您可能使用过 buildpack。
Buildpack 是平台的一部分,它获取您的应用程序并将其转换为平台可以实际运行的内容。
例如,Cloud Foundry 的 Java buildpack 会注意到您正在推送一个.jar
文件并自动添加相关的 JRE。
使用 Cloud Native Buildpacks,您可以创建可在任何地方运行的 Docker 兼容映像。 Spring Boot 直接包括 Maven 和 Gradle 的 buildpack 支持。 这意味着您只需键入一个命令,即可快速将合理的映像获取到本地运行的 Docker 守护程序中。
Paketo Spring Boot buildpack 支持layers.idx 文件,因此应用于它的任何自定义都将反映在 buildpack 创建的镜像中。 |
为了实现可重现的构建和容器镜像缓存,Buildpack 可以作应用程序资源元数据(例如文件的“last modified”信息)。
您应该确保您的应用程序在运行时不依赖该元数据。
Spring Boot 可以在提供静态资源时使用该信息,但这可以通过spring.web.resources.cache.use-last-modified . |
4. 下一步阅读什么
了解如何构建高效的容器映像后,您可以阅读有关将应用程序部署到云平台(如 Kubernetes)的信息。