CCE 中 Pod 反复重启排查实战:从 CrashLoopBackOff 到 OOMKilled 的定位思路

举报
yd_229207802 发表于 2026/10/27 10:06:50 2026/10/27
【摘要】 在云容器引擎 CCE 上跑业务,最常见也最让人头疼的告警之一就是“Pod 反复重启”:kubectl get pods 里 RESTARTS 不停往上涨,STATUS 在 Running、Error、CrashLoopBackOff 之间来回切换。很多人第一反应是把副本数调大或者直接重新部署,结果问题照旧。本文把我在 CCE 集群里排查此类问题的过程整理成一套固定顺序,按步骤走,大多数情况都...

在云容器引擎 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、应用异常四类分别处理 → 观察并补上告警。按照这个顺序排查,比盲目重启或扩容要高效得多。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。