# 12. 容器镜像

Spring Boot 应用程序可以[使用 Dockerfile](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#container-images.dockerfiles)进行容器化，或者[使用 Cloud Native Buildpack 来创建可在任何地方运行的优化的 docker 兼容容器映像](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#container-images.buildpacks)，例如Kubernetes。

#### 12.1. 高效的容器镜像

可以轻松地将 Spring Boot uber jar 打包为 docker 镜像。然而，像在 docker 镜像中那样复制和运行 uber jar 存在各种缺点。在不解压的情况下运行 uber jar 总是会产生一定量的开销，并且在容器化环境中这可能会很明显。另一个问题是，将应用程序的代码及其所有依赖项放在 Docker 映像的一层中并不是最佳选择。由于您重新编译代码的频率可能比升级所使用的 Spring Boot 版本的频率高，因此最好将各个部分分开一些。如果将 jar 文件放在应用程序类之前的层中，Docker 通常只需要更改最底层，就可以从其缓存中选取其他层。

**12.1.1. 对 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`.

对于 Maven，请参阅[打包分层 jar 或 war 部分，](https://docs.spring.io/spring-boot/docs/3.2.0/maven-plugin/reference/htmlsingle/#repackage-layers)了解有关向存档添加层索引的更多详细信息。对于 Gradle，请参阅Gradle 插件文档的[打包分层 jar 或 war 部分。](https://docs.spring.io/spring-boot/docs/3.2.0/gradle-plugin/reference/htmlsingle/#packaging-layered-archives)

#### 12.2. Dockerfile

虽然只需在 Dockerfile 中添加几行代码就可以将 Spring Boot uber jar 转换为 docker 镜像，但我们将使用[分层功能](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#container-images.efficient-images.layering)来创建优化的 docker 镜像。当您创建包含图层索引文件的 jar 时，`spring-boot-jarmode-layertools`jar 将作为依赖项添加到您的 jar 中。将此 jar 放在类路径中，您可以在特殊模式下启动应用程序，该模式允许引导代码运行与应用程序完全不同的东西，例如提取层的东西。

> `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 .` 构建 docker 映像，或者可以选择指定应用程序 jar 的路径，如以下示例所示：

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

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

当然，不使用jarmode也可以编写Dockerfile。您可以使用`unzip`和`mv`的某种组合将内容移动到正确的层，但 jarmode 简化了这一点。

#### 12.3. 云原生构建包

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

借助 Cloud Native Buildpack，您可以创建可在任何地方运行的 Docker 兼容映像。Spring Boot 包括直接对 Maven 和 Gradle 的构建包支持。这意味着您只需键入一个命令，即可快速将合理的镜像添加到本地运行的 Docker 守护进程中。

[请参阅有关如何将构建包与Maven](https://docs.spring.io/spring-boot/docs/3.2.0/maven-plugin/reference/htmlsingle/#build-image)和[Gradle](https://docs.spring.io/spring-boot/docs/3.2.0/gradle-plugin/reference/htmlsingle/#build-image)结合使用的各个插件文档。

> Paketo [Spring Boot 构建包](https://github.com/paketo-buildpacks/spring-boot)支持`layers.idx`文件，因此应用于该文件的任何自定义都将反映在该构建包创建的映像中。
>
> 为了实现可重复的构建和容器映像缓存，Buildpacks 可以操作应用程序资源元数据（例如文件“上次修改”信息）。您应该确保您的应用程序在运行时不依赖该元数据。Spring Boot 在提供静态资源时可以使用该信息，但可以使用`spring.web.resources.cache.use-last-modified`.

#### 12.4. 接下来读什么

一旦您了解了如何构建高效的容器映像，您就可以阅读有关[将应用程序部署到云平台](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#deployment.cloud.kubernetes)（例如 Kubernetes）的信息。

<br>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://doc.shiker.tech/spring-boot-can-kao-wen-dang/12.-rong-qi-jing-xiang.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
