跳转至

Java / Spring Boot 容器镜像制作

运维制作镜像的目标是:制品可追溯、运行环境固定、非 root 运行、配置外置、日志可收集,并能在受限资源下优雅启动和停止。

基础运行镜像

FROM eclipse-temurin:21-jre

RUN groupadd --system app \
    && useradd --system --gid app --home-dir /app app

WORKDIR /app
COPY --chown=app:app app.jar /app/app.jar

USER app:app
EXPOSE 8080

ENV JAVA_TOOL_OPTIONS="-XX:MaxRAMPercentage=70 -XX:+ExitOnOutOfMemoryError -Dfile.encoding=UTF-8"
ENTRYPOINT ["java", "-jar", "/app/app.jar"]

21 只是示例,必须与应用声明的 Java 版本一致。生产中进一步固定经过验证的镜像标签或 digest,避免同一 Dockerfile 在不同时间拉到不同基础镜像。

构建与运行

docker build -t registry.example.com/team/app:1.4.0 .
docker run --rm \
  --name app \
  --memory 1g \
  --cpus 1.5 \
  -p 8080:8080 \
  -e SPRING_PROFILES_ACTIVE=prod \
  -v /srv/app/config:/app/config:ro \
  registry.example.com/team/app:1.4.0 \
  --spring.config.additional-location=file:/app/config/

镜像内只放 JAR 和固定运行依赖;环境配置、密码、证书和可增长日志不烘焙进镜像。

镜像检查清单

项目 要求
基础镜像 与 Java 版本和 CPU 架构一致,固定版本/digest,定期扫描漏洞
运行用户 使用非 root 用户,数据与临时目录提前授权
制品 JAR 版本、Git SHA 和镜像标签可对应,CI 中校验来源
配置 通过只读挂载和环境变量注入,Secret 不进入 Layer
日志 默认写 stdout/stderr,由容器平台收集
资源 设置容器 CPU/内存,并为非 Heap 内存留余量
停止 应用正确响应 SIGTERM,编排平台给予合理优雅退出时间
健康检查 区分进程存活、应用就绪和外部依赖状态

分层镜像为什么有用

Spring Boot Fat JAR 中依赖库变化通常比业务代码少。使用 Spring Boot 的分层 JAR/工具模式把 dependencies、loader 和 application 分成不同镜像层,可以在只修改业务代码时复用依赖层缓存,减少构建和推送量。

是否采用分层取决于开发构建产物是否支持。运维不要自行解压未知 JAR 改内容,应让 Maven/Gradle 构建和 Dockerfile 形成一套经过验证的流程。

健康检查

如果应用启用了 Actuator:

/actuator/health/liveness   进程是否仍应运行
/actuator/health/readiness  是否可以接收流量

健康端点应限制暴露内容和访问范围。不要把“数据库短暂不可用”不加区分地当成 liveness 失败,否则容器会被反复重启,放大依赖故障。

常见问题

现象 优先检查
UnsupportedClassVersionError JAR 构建版本高于运行镜像 Java 版本
容器立即退出 docker logs、入口命令、JAR 路径、文件权限、配置缺失
写日志/临时文件报权限错误 非 root 用户对挂载目录和 java.io.tmpdir 是否有权限
容器 OOMKilled 但无 Java OOM 容器总内存不足,Heap 外内存或进程被内核直接杀死
镜像很大 是否误用完整构建镜像、复制 Maven 缓存或未使用 .dockerignore

官方参考:Spring Boot Container Images