deployment 是 java 项目在 kubernetes 中的实际部署入口,需对齐 jvm 参数(如 -xmx)、容器 memory limits 和探针初始延迟,否则易因 oomkilled、crashloopbackoff 或镜像拉取失败等问题导致线上故障。

Deployment 是部署 Java 项目的实际入口,不是 kubectl run 或直接起 Pod。
Kubernetes 不关心你写的是 Java 还是 Python,它只调度容器。但 Java 应用在容器里容易出问题——JVM 看不见 cgroups 限制、内存 OOM 被 kill、CPU 配额和线程数错配、启动慢被探针干掉……这些不是 Kubernetes 的 bug,是没对齐 JVM 行为导致的。
镜像必须带可执行 JAR,别用 WAR + Tomcat 组合
Spring Boot 默认打包成可执行 jar,这是最简路径。WAR 包扔进 Tomcat 容器,等于套娃:外层 Docker 限制资源,内层 Tomcat 再自己管线程池和内存,JVM 更难感知真实可用资源。
常见错误现象:
-
java.lang.OutOfMemoryError: Java heap space却看到容器 RSS 只用了 1.2Gi —— 因为 JVM 没被告知最大堆,按宿主机内存算的 - Pod 反复 CrashLoopBackOff,日志只有
Killed—— Linux OOM killer 杀的,不是应用崩溃
实操建议:
- 用
FROM openjdk:17-jdk-slim(别用alpine+glibc兼容问题多) - Dockerfile 中显式设 JVM 参数:
ENTRYPOINT ["java", "-Xms512m", "-Xmx512m", "-XX:MaxMetaspaceSize=128m", "-jar", "app.jar"] - 确保
-Xmx≤ 容器resources.limits.memory,留至少 128Mi 给 Metaspace、CodeCache、native memory
resources.limits.memory 和 -Xmx 必须对齐
Kubernetes 的 memory limit 是 cgroup v2 的 memory.max,Linux kernel 强制截断超限分配。JVM 如果没被告知上限,会按宿主机内存(比如 64Gi)设堆,一申请就触发 OOM kill。
为什么不能只靠 -Xmx?因为现代 JDK(8u191+ / 10+)已支持自动读取 cgroup 限制,但前提是:容器 runtime 启用了 cgroup v2,且未禁用 JVM 自动检测。
实操建议:
- 在
Deployment中写死limits.memory: 1Gi,同时 JVM 加-Xmx768m(留 256Mi 给非堆) - 加
-XX:+UseContainerSupport(JDK8u191+ 默认开启,但显式写上更稳) - 避免用
-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap(已废弃) - 验证方式:
kubectl exec -it <pod> -- java -XshowSettings:vm -version 2>&1 | grep MaxHeapSize</pod>,看输出是否接近 768m
livenessProbe 初始延迟必须大于 JVM 启动时间
Spring Boot 应用冷启动常需 15–40 秒(尤其带数据库连接、Liquibase、Elasticsearch client)。默认 initialDelaySeconds: 30 很可能不够,livenessProbe 一上来就失败,Kubernetes 反复重启 Pod,永远进不了 ready 状态。
常见错误现象:
- Pod 处于
Running但READY 0/1,kubectl logs显示应用明明在跑 -
describe pod里有反复的Liveness probe failed和Restart count increased
实操建议:
- 先本地用
time java -jar app.jar测真实启动耗时,再加 10 秒 buffer -
livenessProbe.initialDelaySeconds设为 60~90,readinessProbe.initialDelaySeconds设为 40~60 -
readinessProbe路径用/actuator/health/readiness(Spring Boot 3.x),别用/actuator/health(包含 liveness 检查) - 如果用自定义健康端点,确保它不依赖未就绪的下游服务(如 DB 连接池未建好就报 503)
别忽略 imagePullSecrets 和私有仓库认证
用 Harbor、阿里云 ACR、腾讯云 TCR 等私有镜像仓库时,Kubernetes 默认无法拉取镜像。错误现象是 ImagePullBackOff 或 ErrImagePull,describe pod 显示 Failed to pull image "...": rpc error: code = Unknown desc = unauthorized: authentication required。
实操建议:
- 用
kubectl create secret docker-registry regcred --docker-server=core.harbor.domain --docker-username=admin --docker-password=Harbor12345创建 Secret - 在
Deployment.spec.template.spec下加:imagePullSecrets: [{name: "regcred"}] - Secret 必须和 Deployment 在同一 namespace;跨 namespace 需复制或用 ServiceAccount 绑定
- Harbor 用户密码别用 admin 密码硬编码,生产环境应走 Robot Account 或 OIDC
JVM 在容器里的行为和裸机不同,关键不是“能不能跑”,而是“会不会在压力下意外退出”。很多问题不会在测试环境暴露,直到流量上来、GC 压力增大、cgroup throttle 触发——那时再调参就晚了。把 -Xmx、limits.memory、initialDelaySeconds 这三处对齐,能挡住 80% 的线上诡异重启。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











