Docker 和 Java:高级应用容器化

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

Docker 和 Java:高级应用容器化

\n\n

Java 应用的容器化已成为现代开发中的事实标准。然而,对 Docker 的基础了解(从 openjdk:11 创建镜像并运行容器)已无法满足生产环境的要求。在高级层面,必须考虑 JVM 在容器内运行的特性、优化镜像大小、管理内存以及配置监控。本文将深入探讨一些关键技术,帮助您为 Java 应用创建高效且安全的 Docker 镜像。

\n\n

1. 多阶段构建:最小化镜像大小

\n\n

经典的“一个 Dockerfile 一个镜像”方法常常导致最终产物臃肿。构建工具(Maven、Gradle、编译器)等运行时不需要的组件也会被打包进镜像。解决方案是多阶段构建。我们将流程分为两个阶段:构建阶段和运行阶段。

\n\n

使用 Maven 的 Spring Boot 应用的 Dockerfile 示例:

\n\n
# 构建阶段\nFROM maven:3.8.6-openjdk-17 AS builder\nWORKDIR /app\nCOPY pom.xml .\nRUN mvn dependency:go-offline -B\nCOPY src ./src\nRUN mvn clean package -DskipTests\n\n# 运行阶段\nFROM eclipse-temurin:17-jre-alpine\nWORKDIR /app\nCOPY --from=builder /app/target/*.jar app.jar\nEXPOSE 8080\nENTRYPOINT ["java", "-jar", "app.jar"]
\n\n

最终镜像大小约为 150 MB,而不是 600+ MB。重要提示:使用 jre 镜像(而非 jdk)和最小化发行版(Alpine、Slim)。对于 Java 17+ 应用,可以考虑使用 jlink 创建仅包含必要模块的自定义 JRE。

\n\n

2. 配置 JVM 以在容器内运行

\n\n

长期以来,JVM 无法正确识别为容器设置的内存和 CPU 限制。从 Java 8u191 和 Java 10 开始,支持 cgroups v1(Java 15 开始支持 cgroups v2)。但不应完全依赖自动检测。

\n\n

生产环境的关键参数:

\n
    \n
  • -XX:+UseContainerSupport — 启用容器支持(Java 10+ 默认启用)。
  • \n
  • -XX:MaxRAMPercentage=75.0 — 将堆内存使用限制为容器可用内存的 75%。这为元空间、线程栈和系统进程预留了内存。
  • \n
  • -XX:ActiveProcessorCount=2 — 如果自动检测失败,显式指定可用的处理器数量。
  • \n
\n\n

完整的 ENTRYPOINT 命令示例:

\n\n
ENTRYPOINT ["java", \\\n    "-XX:+UseContainerSupport", \\\n    "-XX:MaxRAMPercentage=75.0", \\\n    "-XX:+ExitOnOutOfMemoryError", \\\n    "-jar", "app.jar"]
\n\n

重要提示: 在 Docker 环境中避免使用固定的 -Xmx 和 -Xms 值。它们在复制或扩展时无法适应容器限制的变化。

\n\n

3. 优化层和构建缓存

\n\n

Dockerfile 中的每条指令都会创建一个新层。Docker 会缓存未更改的层。为了加速重复构建,请将指令从最不易变到最易变排列。

\n\n

正确的 Dockerfile 结构:

\n
    \n
  1. 安装操作系统依赖项(apk add、apt-get)。
  2. \n
  3. 复制构建工具配置文件(pom.xml、build.gradle)。
  4. \n
  5. 下载依赖项(mvn dependency:go-offline)。
  6. \n
  7. 复制源代码。
  8. \n
  9. 构建并复制产物。
  10. \n
\n\n

此外,可以在 CI/CD 中使用 Docker 层缓存。例如,在 GitHub Actions 或 GitLab CI 中,在构建之间保存 Maven 缓存:

\n\n
# GitLab CI 示例\ncache:\n  key: ${CI_COMMIT_REF_SLUG}\n  paths:\n    - .m2/repository/\n    - .gradle/caches/
\n\n

同时,考虑使用

博客

书籍推荐