CCE 中 Pod 反复重启排查实战:从 CrashLoopBackOff 到 OOMKilled 的定位思路
在云容器引擎 CCE 上跑业务,最常见也最让人头疼的告警之一就是“Pod 反复重启”:kubectl get pods 里 RESTARTS 不停往上涨,STATUS 在 Running、Error、CrashLoopBackOff 之间来回切换。很多人第一反应是把副本数调大或者直接重新部署,结果问题照旧。本文把我在 CCE 集群里排查此类问题的过程整理成一套固定顺序,按步骤走,大多数情况都能在半小时内定位到根因。
一、先看清重启的“死因”
不要急着看日志,先用 describe 确认容器上一次退出的原因和退出码:
kubectl get pod -n demo -o wide
kubectl describe pod order-svc-7d9f8c6b5-x2kq4 -n demo
重点关注 Last State 下的 Reason 和 Exit Code,以及最底部的 Events。常见组合大致可以这样判断:
OOMKilled/ 137:内存超过 limits,被内核 OOM 杀掉;Error/ 1 或其他非 0:应用自己异常退出,多数是配置、依赖或代码问题;Completed/ 0:主进程正常结束了,常见于启动脚本把服务放到后台运行;- Events 中出现
Liveness probe failed:容器本身没崩,是被存活探针判定失败后重启的。
只要先把这四类分开,后面的排查方向就不会跑偏。
二、看“上一次”的日志而不是当前日志
CrashLoopBackOff 时当前容器往往刚启动或者还在等待,直接 logs 看到的内容很少。要加 --previous 查看上一个已经退出的容器:
kubectl logs order-svc-7d9f8c6b5-x2kq4 -n demo --previous --tail=200
如果一个 Pod 里有多个容器(比如带了 sidecar),记得用 -c 指定容器名。我遇到过不少次“数据库连接被拒绝”“配置文件找不到”“环境变量为空”之类的报错,都是在 previous 日志的最后几十行里直接给出答案。CCE 集群如果开启了云日志服务 LTS 采集,也可以在 LTS 里按 Pod 名称检索,适合日志已经被轮转掉的情况。
三、OOMKilled:区分“真不够”和“设小了”
退出码 137 时,先对比一下实际用量和 limits:
kubectl top pod -n demo --containers
kubectl get pod order-svc-7d9f8c6b5-x2kq4 -n demo -o jsonpath='{.spec.containers[*].resources}'
Java 服务是重灾区:容器 limits 设了 1Gi,但 JVM 参数里 -Xmx 也写了 1g,再加上元空间、线程栈和堆外内存,必然超限。建议用 -XX:MaxRAMPercentage=70 让堆大小跟随容器 limits 自动计算,给非堆内存留出余量。如果确认是业务高峰期正常增长,再结合 CCE 控制台的监控曲线把 requests、limits 调整到合理值,或者配置 HPA 按内存利用率扩容,而不是简单地无限加大 limits。
四、探针配置不当导致的“被动重启”
Events 里出现大量 Liveness probe failed 时,先确认应用启动需要多久。很多 Spring Boot 服务冷启动要 40 秒以上,而存活探针 initialDelaySeconds 只设了 10 秒,应用还没起来就被判定死亡,于是陷入“启动—被杀—再启动”的循环。推荐改法是增加 startupProbe,把启动阶段和运行阶段分开:
startupProbe:
httpGet:
path: /actuator/health
port: 8080
periodSeconds: 5
failureThreshold: 30
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
这样启动阶段最多可以等待 150 秒,启动成功后才由 livenessProbe 接管。另外,存活探针的接口不要去检查数据库、Redis 等外部依赖,否则下游一抖动,所有 Pod 会被一起重启,反而放大故障。依赖检查更适合放在 readinessProbe 里,只影响是否接收流量。
五、退出码 0:主进程提前结束
如果 Reason 是 Completed,基本可以断定是镜像入口的问题,例如启动脚本里写了 nohup java -jar app.jar &,脚本执行完主进程就退出了,Kubernetes 认为容器已结束并按策略重启。修复方法是让业务进程作为前台进程运行,在脚本最后使用 exec java -jar app.jar,同时也能让进程正确接收 SIGTERM,实现优雅停机。
六、验证与收尾
修改完成后,用以下命令持续观察重启次数是否稳定:
kubectl rollout status deployment/order-svc -n demo
kubectl get pod -n demo -w
建议在 CCE 中为“容器重启次数”配置告警规则,比如 10 分钟内重启超过 3 次就通知值班人员,避免问题在夜里悄悄发生、第二天才发现。最后把这次的根因、改动和验证结果记录到团队的故障手册里,下次遇到同类现象可以直接对照。
总结一下排查顺序:describe 看退出原因 → logs --previous 看最后的报错 → 按 OOM、探针、退出码 0、应用异常四类分别处理 → 观察并补上告警。按照这个顺序排查,比盲目重启或扩容要高效得多。
- 点赞
- 收藏
- 关注作者
评论(0)