Appearance
Kubernetes CrashLoopBackOff 时怎么排查
CrashLoopBackOff 是 Kubernetes 里最容易让人紧张的一类告警,因为它看起来像:
- Pod 起不来
- 容器在反复重启
- 服务副本数不断掉
- 发布刚做完,线上就开始红
但它本身不是根因,而是一个结果:
- 应用启动失败
- 配置或依赖缺失
- 探针配置不合理
- 资源限制导致进程被杀
- 启动阶段逻辑太重
真正难的不是知道它在“重启”,而是快速判断:
- 是应用自己退了
- 还是被 K8s 杀掉了
- 是启动期问题
- 还是运行后又进入坏状态
先说结论
- CrashLoopBackOff 的第一目标不是“先修配置”,而是先判断容器为什么退出
- 先区分是进程异常退出、OOMKilled、探针失败还是启动依赖没准备好
- 再区分这是单 Pod 问题、同版本问题,还是发布导致的批量问题
- 很多 CrashLoopBackOff 都不是调度问题,而是镜像、配置、探针、启动脚本和资源配置共同作用的结果
一个典型故障现场
一个典型场景是 Java 服务发版后,Pod 状态很快从 Running 变成:
CrashLoopBackOff- 重启次数快速累加
- readiness 一直过不了
- 同版本多个副本都不稳定
这时现场经常会有人同时做很多动作:
- 重启 Pod
- 扩容副本
- 改探针
- 怀疑节点坏了
但真正更有价值的是先看清:
- 容器上一次退出码是什么
- 是不是 OOMKilled
- 探针失败发生在启动前、中还是后
- 是否只有新版本在出问题
第一阶段:先止血,保护线上可用性
1. 如果是发版后批量出现,优先考虑暂停发布或回滚
这一步远比一边查一边继续放量更重要。
如果同版本副本都在 CrashLoop,说明问题很可能就在:
- 新镜像
- 新配置
- 新探针
- 新启动参数
2. 保住健康副本
如果老副本还活着,要避免:
- 误删旧 Pod
- 继续滚动导致可用副本继续减少
3. 暂时放宽探针或资源时要谨慎
这类动作只能作为诊断手段,不要直接把它当长期修复。
比如 startup 时间明显不够,可以临时放宽启动窗口帮助确认问题是否只是慢启动;但不能因为“放宽后能活”就忽略真正的启动过重问题。
第二阶段:先分清容器是怎么死的
1. 进程自己退出
常见于:
- 配置文件缺失
- 环境变量不对
- 启动脚本报错
- 应用初始化失败
- 依赖连接失败后直接
exit
2. 被 OOMKilled
常见于:
- JVM 参数过大
- 启动时缓存预热、类加载、数据加载过重
- limit 太小
- 容器里还有额外 sidecar 争内存
3. 被 liveness 或 startup probe 反复杀掉
常见于:
- 启动很慢但没配 startup probe
- 探针 URL 本身依赖外部服务
- 探针阈值照抄模板,不符合当前服务冷启动特性
4. 运行后进入坏状态再退出
这种情况最容易迷惑人,因为容器可能不是启动立刻死,而是跑几十秒、一两分钟后挂掉。
这时更要看:
- 发布后是否有流量进入
- 某些初始化任务是不是在启动后异步执行
- 依赖连接是不是在服务接流量后才暴露问题
第三阶段:现场最该看的四样东西
1. 上一次退出原因和退出码
这是最先要看的,因为它决定排查方向。
如果退出码都没看,就开始猜探针、猜节点、猜镜像,效率会很低。
2. 容器最近日志和上一个实例日志
很多 CrashLoop 问题日志就摆在那里:
- 配置读取失败
- 端口冲突
- 连接数据库失败
- 初始化 SQL 报错
- JVM 启动参数不兼容
3. 事件和探针失败信息
如果是探针导致,事件里通常能看到:
- 连不上端口
- HTTP 返回非 200
- 超时
- 启动窗口不足
4. 资源限制和节点资源
如果节点本身紧张、limit 太小、请求配置不合理,也可能让容器在启动阶段就被资源问题击穿。
常见根因画像
1. 配置中心、Secret、ConfigMap 变更不兼容
新版本起来后,读到的新配置和老版本逻辑不兼容,直接启动失败。
2. Java 服务冷启动变慢,但探针没同步调整
这在 Spring Boot、搜索、分析、缓存预热类服务里非常常见。
3. 镜像变更导致启动脚本或依赖缺失
比如 JDK、时区、字体、系统库、证书链、执行权限等问题。
4. 启动阶段依赖外部服务过多
数据库、Redis、注册中心、配置中心、第三方接口任何一个起不来,应用就直接退出。
5. OOM 或 limit 太紧
开发环境能跑,线上因为容器资源限制更严格,结果一启动就被杀。
一个更实用的排查顺序
CrashLoopBackOff 现场我通常按这个顺序看:
- 先确认是单 Pod、单节点、单版本,还是批量现象。
- 看
lastState、退出码、是否 OOMKilled。 - 看当前日志和上一个实例日志。
- 看事件里是否有 probe 失败、挂载失败、拉取失败。
- 再看最近是否发版、改配置、改镜像、改资源参数。
- 最后才去怀疑节点层或集群层问题。
这样能避免把大量应用级问题误判成 Kubernetes 平台问题。
止血后的治理方向
1. 启动探针、存活探针、就绪探针职责分开
不要一个 /health 接口包打天下。
2. 慢启动服务单独评估启动窗口
尤其是 Java、搜索、分析、缓存预热型服务。
3. 发版前增加启动期验证
比如:
- 配置完整性检查
- 依赖连通性检查
- 内存参数检查
- 启动时间基线比对
4. 对外部依赖做“可降级启动”设计
不是所有依赖失败都应该让应用直接 exit。
总结
CrashLoopBackOff 真正要解决的,不是“Pod 一直重启”这个表象,而是先分清:
- 容器是自己退了,还是被杀掉了
- 是启动阶段问题,还是运行阶段问题
- 是应用问题,还是探针和资源配置问题
把“退出原因、日志、探针、资源、版本变更”这五条线先看清,绝大多数 CrashLoopBackOff 都能很快缩小到真正根因。