第一步:看退出码,它就是错误分类表
`kubectl describe pod` 的 Last State 里有两个关键字段:Reason 与 Exit Code。退出码是现成的错误分类:137 = 被 SIGKILL 杀掉(绝大多数是 OOMKilled);143 = SIGTERM 优雅退出(应用没在期限内完成收尾被杀);1 = 应用自身报错退出(去看日志);0 却在重启 = 进程干完活退出了(常见于一次性任务被当成常驻服务跑)。
先分类再下钻,能避免在最常见的 OOM 问题上绕远路——OOMKilled 的容器日志里通常毫无异常。
kubectl describe pod <pod> -n <ns>
# Last State: Terminated, Reason: OOMKilled, Exit Code: 137第二步:看上一次崩溃的日志
容器已重启,`kubectl logs` 默认只给当前实例的日志——往往太短或为空。加 `--previous` 才是崩溃现场的真实输出:找不到配置、连不上数据库、端口被占用这类启动期错误基本都在这里。
多容器 Pod 记得 `-c <container>` 指定容器;日志被应用写到文件而非 stdout 时(Tomcat、Java 传统应用常见),需要调整日志输出或用 sidecar 收集,否则 kubectl logs 永远看不到。
kubectl logs <pod> -n <ns> --previous
kubectl logs <pod> -n <ns> -c <container> --previous第三步:查探针——被自己人杀死很常见
liveness 探针失败会触发容器重启,表现与真实崩溃一模一样,describe 里 Reason 是探针名(如 Liveness probe failed)。典型误配:把 liveness 也配成强依赖(启动慢的应用在就绪前被判死)、initialDelaySeconds 太短、探针端口/路径写错。
原则:liveness 只检测「死没死」,readiness 才表达「能不能接流量」。启动慢的应用加 startupProbe 或调大 initialDelaySeconds,把「还在启动」与「已经死了」区分开。
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 30 # 启动慢的应用别配太短
periodSeconds: 10第四步:资源限制——OOMKilled 的两处来源
容器内存超过 limits 就会被内核杀掉(退出码 137)。排查两处:① limits 本身设小了——Java 应用要特别注意,容器内 JVM 默认按容器 cgroup 感知内存(JDK 8u191+ / JDK 10+),但 -Xmx 手工设超 limits 依然会被杀;② 应用内存泄漏——表现为运行一段时间后才 OOM,用容器内存监控曲线区分「一开始就顶到限」与「缓慢爬升」。
调 limits 只是止血;泄漏要回到应用侧用堆 dump 分析。requests 与 limits 差距过大还会造成节点超卖驱逐,别只看单容器。
resources:
requests: { memory: "256Mi", cpu: "100m" }
limits: { memory: "512Mi" }第五步:依赖未就绪——启动顺序问题
应用启动时连数据库/Redis,对方还没就绪就直接崩溃重试,日志里是清晰的连接拒绝——这类 CrashLoopBackOff 其实是「重试策略太硬」。与其依赖 K8s 的无限重启,不如应用侧做连接重试与超时,或用 initContainers 挡在主容器前(如 wait-for-db 脚本)。
Docker Compose 转过来的项目最容易踩这里:compose 有 depends_on + condition,K8s 没有直接等价物,需要自己补齐就绪检查逻辑。
initContainers:
- name: wait-for-db
image: busybox
command: ["sh", "-c", "until nc -z db-svc 5432; do sleep 2; done"]收尾:一份排查速查表
describe 看退出码(137=OOM / 1=报错 / 0=正常退出却重启)→ logs --previous 看崩溃现场 → 排查探针误杀 → 核对 limits 与内存曲线 → 补依赖就绪逻辑。多数 CrashLoopBackOff 都能在这五步内闭环;还剩下的往往是镜像本身有 bug,回到本地跑同一镜像复现即可。
kubectl describe pod <pod> -n <ns> # 1 退出码
kubectl logs <pod> -n <ns> --previous # 2 崩溃现场