Kubernetes CrashLoopBackOff 排查全流程:从 describe 到日志到 exit code
线上发个版,Pod 状态突然变成CrashLoopBackOff,重启次数噌噌往上涨,服务 502。这几乎是每个用 K8s 的人都会撞上的场景。很多人第一反应是kubectl delete pod让它重建——但如果是配置或代码问题,重建再多次也是白搭,反而把现场删没了。
这篇按「看状态 → 看事件 → 看日志 → 看退出码」的顺序,把排查流程走一遍,每一步该敲什么命令、看什么关键字,讲清楚。
先搞懂 CrashLoopBackOff 是什么
它不是一种错误,而是一种状态:容器启动 → 崩溃退出 → kubelet 按退避策略(10s、20s、40s……最长 5min)等一会儿再重启 → 又崩。BackOff指的就是这个越来越长的重启间隔。
所以 CrashLoopBackOff 只告诉你「容器起来就挂」,真正的原因藏在崩溃的那一下。排查的核心是抓住崩溃瞬间的信息。
第一步:kubectl get + describe 看全貌
# 看重启次数和状态,RESTARTS 高且在涨就是它kubectl get pod-nmyns# 关键:describe 看 Events 和上一次容器的退出状态kubectl describe pod my-app-7d9f-nmynsdescribe输出里盯三个地方:
Last State: Terminated Reason: Error Exit Code: 1 # ← 退出码是最重要的线索 Started: ... Finished: ... State: Waiting Reason: CrashLoopBackOff底部的Events区域也要看,像镜像拉不下来(ErrImagePull)、探针失败(Liveness probe failed)、OOM 都会在这里留痕。
第二步:退出码直接缩小范围
Exit Code是判断方向最快的信号,几个高频值背下来:
| Exit Code | 含义 | 常见原因 |
|---|---|---|
| 0 | 正常退出 | 容器主进程跑完就退了(比如把一次性脚本当常驻服务) |
| 1 | 应用异常 | 代码抛未捕获异常、配置读不到 |
| 137 | 被 SIGKILL(128+9) | OOMKilled内存超限,或 liveness 探针杀的 |
| 143 | 被 SIGTERM(128+15) | 优雅关闭,通常是被正常调度掉 |
| 126/127 | 命令不可执行/找不到 | entrypoint 路径错、脚本没加执行权限 |
看到 137 别急着改代码,先确认是不是内存的事:
kubectl describe pod my-app-7d9f-nmyns|grep-ioom# 输出 Reason: OOMKilled 就实锤了内存超限第三步:看日志,重点是「上一个」容器的日志
这是最容易踩的坑。Pod 已经重启了,kubectl logs默认给你的是当前这个容器的日志——它可能刚起来还没崩,啥也看不到。真正有价值的是崩掉的那个容器:
# -p / --previous 看上一个已终止容器的日志,这才是崩溃现场kubectl logs my-app-7d9f-nmyns--previous# 多容器 Pod 要指定容器名kubectl logs my-app-7d9f-nmyns-capp--previous崩溃日志里通常直接写着原因:数据库连不上、环境变量缺失、端口被占、配置文件解析失败。绝大多数 Exit Code 1 都能在--previous日志里找到根因。
第四步:如果日志也是空的
有些容器崩得太快,--previous也捞不到东西(比如 entrypoint 直接报错、进程秒退)。这时换思路,让容器别崩,进去手动跑:
# 临时把 command 覆盖成 sleep,让 Pod 能起来供你调试apiVersion:v1kind:Podmetadata:name:debug-appspec:containers:-name:appimage:myrepo/my-app:v1.2.3# 用出问题的同一个镜像command:["sh","-c","sleep 3600"]# 覆盖原 entrypoint,先撑住kubectl apply-fdebug-app.yaml kubectlexec-itdebug-app --sh# 进去后手动执行原来的启动命令,报错就直接打在你眼前/app/entrypoint.sh这招能把「容器秒崩看不到日志」变成「命令行里直接报错」,屡试不爽。
高频根因对照
排查多了会发现,CrashLoopBackOff 翻来覆去就那么几类:
- 配置缺失:ConfigMap/Secret 没挂上,或 key 名写错,应用读不到必需配置直接退。
kubectl describe里Environment和Mounts能对一遍。 - 依赖没就绪:启动时强连数据库/Redis,连不上就 panic。正确做法是加重试或用 initContainer 探测依赖。
- liveness 探针太激进:应用启动慢(比如 JVM 预热),但
initialDelaySeconds给太短,还没起来就被探针判死重启。表现是日志正常却反复重启,Events里有Liveness probe failed。改大initialDelaySeconds或改用startupProbe。 - OOMKilled(137):
limits.memory给低了,或应用真漏内存。先把 limit 调合理,再排查是不是真泄漏。 - 把一次性任务当服务跑(Exit 0):脚本跑完就退,Deployment 又拉起来。这种应该用 Job 而不是 Deployment。
小结
CrashLoopBackOff 排查记住这条链路:
kubectl get pod确认重启在涨 →kubectl describe pod看Exit Code和Events。- 退出码先分方向:137 查 OOM,1 查应用异常,126/127 查启动命令。
kubectl logs --previous才是崩溃现场,别看当前容器日志。- 日志也捞不到,就用
command: sleep覆盖 entrypoint,进容器手动跑复现。
一句话记忆点:别急着 delete pod,先 describe 看退出码、logs 加--previous看崩溃现场——删了就没现场了。