1. Efficient Container Images

可以很容易地将一个Spring Boot胖罐子打包成一个码头镜像。然而,复制和运行胖罐子也有各种各样的缺点,就像码头上的图像一样。在不解包的情况下运行一个大罐子时,总会有一定的开销,在集装箱化的环境中,这一点很明显。另一个问题是,将应用程序的代码及其所有依赖项放在Docker映像的一个层中并不是最优的。由于重新编译代码的频率可能比升级您使用的Spring Boot版本更频繁,因此通常更好的做法是将代码稍微分开一些。如果您将JAR文件放在应用程序类之前的层中,Docker通常只需要更改最底层,就可以从缓存中提取其他文件。

1.1. Unpacking the Executable JAR

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

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

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

解压缩JAR文件后,您还可以使用其“自然”的main方法而不是JarLauncher运行应用程序,从而进一步缩短启动时间。例如:

$ jar -xf myapp.jar
$ java -cp BOOT-INF/classes:BOOT-INF/lib/* com.example.MyApplication
            
            
Using the JarLauncher over the application’s main method has the added benefit of a predictable classpath order. The jar contains a classpath.idx file which is used by the JarLauncher when constructing the classpath.

1.2. Layering Docker Images

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

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

  • SpringBoot-Loader(适用于org/springframework/ot/loader下的所有内容)

  • 快照依赖项(用于快照依赖项)

  • 应用程序(用于应用程序类和资源)

以下是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的帮助下对WAR文件进行分层。

对于Maven,请参阅打包分层JAR或WAR部分,了解有关向归档文件添加层索引的更多详细信息。有关Gradle,请参阅Gradle插件文档的打包分层JAR或WAR部分。

2. Dockerfiles

虽然只需在Dockerfile中使用几行代码就可以将Spring Boot胖JAR转换为docker镜像,但我们将使用分层功能创建优化的docker镜像。当您创建包含层索引文件的JAR时,Spring-boot-jarmode-layertoolsJAR将作为依赖项添加到您的JAR中。有了类路径上的这个JAR,您可以在一种特殊的模式下启动您的应用程序,该模式允许引导代码运行与您的应用程序完全不同的东西,例如,提取层的东西。

The layertools mode can not be used with a fully executable Spring Boot archive that includes a launch script. Disable launch script configuration when building a jar file that is intended to be used with layertools.

下面介绍如何使用layerToolsJAR模式启动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

提取命令可用于轻松地将应用程序拆分为要添加到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 .
           
           

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

当然,可以在不使用jarmode的情况下编写Dockerfile。您可以使用解压缩mv的某种组合来将内容移动到正确的层,但jarmode简化了这一过程。

3. Cloud Native Buildpacks

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

使用Cloud Native BuildPack,您可以创建可在任何地方运行的Docker兼容映像。Spring Boot包括对Maven和Gradle的构建包直接支持。这意味着您只需输入一条命令,即可在本地运行的Docker守护程序中快速获取一个合理的图像。

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

The Paketo Spring Boot buildpack has also been updated to support the layers.idx file so any customization that is applied to it will be reflected in the image created by the buildpack.
In order to achieve reproducible builds and container image caching, Buildpacks can manipulate the application resources metadata (such as the file "last modified" information). You should ensure that your application does not rely on that metadata at runtime. Spring Boot can use that information when serving static resources, but this can be disabled with spring.web.resources.cache.use-last-modified

4. What to Read Next

一旦您了解了如何构建高效的容器镜像,您就可以阅读将应用程序部署到云平台,例如Kubernetes。