Spring Boot Docker入门模板与4个最佳实践
在本博客中,您将学习一些主要针对 Spring Boot 应用程序的 Docker 最佳实践。您将通过将这些实践应用到示例应用程序来学习这些实践。享受!
入门模板
将用作入门模板起点的 Dockerfile 如下:
FROM eclipse-temurin:17.0.5_8-jre-alpine@sha256:02c04793fa49ad5cd193c961403223755f9209a67894622e05438598b32f210e |
这个 Dockerfile 执行以下操作:
- FROM:以eclipse-temurin:17Java Docker镜像为基础镜像;
- WORKDIR:设置/opt/app为工作目录;
- RUN:创建系统组和系统用户;
- ARG:提供一个参数JAR_FILE,这样就不必将 jar 文件名硬编码到 Dockerfile 中;
- COPY:将jar文件复制到Docker镜像中;
- RUN:将 的所有者更改WORKDIR为之前创建的系统用户;
- USER:确保使用之前创建的系统用户;
- ENTRYPOINT:启动 Spring Boot 应用程序。
在接下来的部分中,您将更改此 Dockerfile 以遵循最佳实践。每个段落生成的 Dockerfile 可在 git 存储库的Dockerfiles目录中找到。在每个段落的末尾,将在适用的情况下提及相应的最终 Dockerfile 的名称。
本博客中使用的代码可在GitHub上找到。
1、健康检查
将运行状况检查添加到 Dockerfile 中,以暴露容器的运行状况。根据此状态,可以重新启动容器。这可以通过HEALTHCHECK命令来完成。添加以下健康检查:
EALTHCHECK --interval=30s --timeout=3s --retries=1 CMD wget -qO- http://localhost:8080/actuator/health/ | grep UP || exit 1 |
- interval间隔:每 30 秒执行一次愈合检查。对于生产应用,最好选择 5 分钟这样的间隔。为了进行一些测试,选择一个较小的值会比较容易。这样就不必每次都等待五分钟。
- timeout超时:执行健康检查的三秒超时。
- retries重试:表示在健康状态发生变化之前必须连续执行的检查次数。默认值为 3,这在生产中是个不错的数字。出于测试目的,可将其设置为一次,这意味着在一次不成功的检查后,健康状态将变为不健康。
- command命令:Spring 激励器端点将用作健康检查。获取响应并将其导入 grep,以验证健康状态是否为 UP。建议不要为此目的使用 curl,因为并非每个镜像都有 curl。你需要在镜像中额外安装 curl,这会使镜像增大数 MB。
2、Docker Compose
Docker Compose 为您提供了用一条命令同时启动多个容器的机会。除此之外,它还能让你记录你的服务,即使你只有一个服务需要管理。Docker Compose 过去是与 Docker 分开安装的,但如今它已成为 Docker 本身的一部分。你需要编写一个包含该配置的 compose.yml 文件。让我们看看在健康检查中使用的两个容器是如何配置的。
services: |
配置了两个服务(读作:容器)。一个用于 dockerbestpractices 镜像,一个用于 autoheal 镜像。自动修复镜像会在重启后重新启动,它定义了一个环境变量,并挂载了一个卷。
在可以找到 compose.yml 文件的目录下执行以下命令:
$ docker compose up
在日志记录中,你会看到两个容器都已启动。打开另一个终端窗口,导航到可以找到 compose.yml 的目录。很多命令都可以与 Docker Compose 结合使用。
3、多阶段构建
有时,在 Docker 容器中构建应用程序会很方便。这样做的好处是,你不需要在系统中安装完整的开发环境,而且可以更方便地交换开发环境。不过,在容器内构建应用程序也有一个问题。尤其是当您想使用同一个容器运行应用程序时。源代码和完整的开发环境将出现在生产容器中,从安全角度来看,这不是一个好主意。
您可以编写单独的 Dockerfile 来规避这个问题:一个用于构建,一个用于运行应用程序。但这相当麻烦。解决办法是使用多阶段构建。
使用多阶段构建,可以将构建阶段与运行阶段分开。Dockerfile 文件如下:
FROM maven:3.8.6-eclipse-temurin-17-alpine@sha256:e88c1a981319789d0c00cd508af67a9c46524f177ecc66ca37c107d4c371d23b AS builder |
如您所见,该 Dockerfile 包含两条 FROM 语句。第一条用于构建应用程序:
- FROM:包含 Maven 和 Java 17 的 Docker 镜像,这是构建应用程序所需的;
- WORKDIR:设置工作目录;
- COPY:将当前目录复制到容器中的工作目录;
- RUN:构建 jar 文件的命令。
FROM 语句中还添加了其他内容。最后,添加 AS 生成器。这样,这个容器就有了标签,可以用来构建运行应用程序的镜像。第二部分与之前的 Dockerfile 完全相同,除了两行。
删除以下两行:
ARG JAR_FILE
COPY target/${JAR_FILE} app.jar
这几行确保将本地构建的 jar 文件复制到镜像中。这几行被替换为下面一行:
COPY --from=builder /build/target/mydockerbestpracticesplanet-0.0.1-SNAPSHOT.jar app.jar
通过这一行,您可以表明要将文件从生成容器复制到新镜像中。
当你构建这个 Dockerfile 时,你会发现构建容器会执行构建,最后创建了用于运行应用程序的镜像。在构建镜像的过程中,你还会注意到所有 Maven 依赖项都已下载。
生成的 Dockerfile 可在 git 仓库中找到,名称为 7-Dockerfile-multi-stage-build。
4、Spring Boot Docker 层
- Docker 镜像由层组成。
- Dockerfile 中的每一条命令都会产生一个新层。
- 当你初始化 Docker 镜像时,所有层都会被检索和存储。
- 如果你更新了 Docker 镜像,但只更改了 jar 文件,那么其他层就不会被重新检索。
这样,Docker 镜像的存储效率会更高。
但是,在使用 Spring Boot 时,会创建一个胖 jar。
也就是说,当你只修改了部分代码时,就会创建一个新的胖jar,其依赖关系保持不变。
因此,每次创建新的 Docker 镜像时,都会在新的层中添加几兆字节,而没有任何必要。
简而言之,Spring Boot 可以将胖 jar 分割成多个目录:
- /dependencies
- /spring-boot-loader
- /snapshot-dependencies
- /application
应用程序代码将存放在 application 目录中,而依赖项则存放在 dependencies 目录中。
为此,您将使用多阶段构建。
第一阶段将把 jar 文件复制到 JDK Docker 镜像中,然后提取胖 jar。
FROM eclipse-temurin:17.0.4.1_1-jre-alpine@sha256:e1506ba20f0cb2af6f23e24c7f8855b417f0b085708acd9b85344a884ba77767 AS builder |
第二部分将把分割的目录复制到新的镜像中。COPY 命令会替换 jar 文件。
FROM eclipse-temurin:17.0.4.1_1-jre-alpine@sha256:e1506ba20f0cb2af6f23e24c7f8855b417f0b085708acd9b85344a884ba77767 |
构建并运行容器。在运行容器时,你不会注意到任何不同之处。主要优势在于 Docker 镜像的存储方式。
生成的 Dockerfile 位于 git 仓库,名称为 8-Dockerfile-spring-boot-docker-layers。