在 kind 集群里让容器启动后立即退出,复现 CrashLoopBackOff 并练习读退出码

演练目标

环境准备

安装 Docker、kind 和 kubectl。使用独立集群,避免影响已有工作负载。

kind create cluster --name lab-crash
kubectl get nodes
kubectl create namespace lab-crash
kubectl config set-context --current --namespace=lab-crash

注入故障

创建一个启动后打印一行日志并 exit 1 的 Deployment:

kubectl apply -f - <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: crash-lab
spec:
  replicas: 1
  selector:
    matchLabels:
      app: crash-lab
  template:
    metadata:
      labels:
        app: crash-lab
    spec:
      containers:
      - name: app
        image: busybox:1.36
        command: ["sh", "-c", "echo starting; exit 1"]
EOF

观察现象

kubectl get pods -l app=crash-lab
sleep 20
kubectl get pods -l app=crash-lab
kubectl describe pod -l app=crash-lab | sed -n '/State:/,/Events:/p'
kubectl logs -l app=crash-lab --previous
kubectl get pod -l app=crash-lab \
  -o jsonpath='{.items[0].status.containerStatuses[0].lastState.terminated.exitCode}{"\n"}'
kubectl get events --sort-by=.lastTimestamp | tail -20

预期 Pod 先短暂 Running,随后变成 Error,最后显示 CrashLoopBackOff;RESTARTS 从 0 递增,重启间隔按 10 秒、20 秒、40 秒拉长;describe 的 Last State 显示 Terminated、Exit Code: 1;--previous 日志能看到 starting。

判断与定位

  1. 退出码是第一线索:1 通常是应用错误;137 常见于 OOMKilled 或 SIGKILL;126/127 是命令不可执行或找不到;143 是收到 SIGTERM。
  2. 先看 kubectl logs --previous,当前容器可能还没产生日志,上一次容器的日志才有价值。
  3. 看 describe 的 Last State、Reason 和 Events:OOMKilled 要查内存 limit,Error 要查应用日志,探针失败会显示 Liveness probe failed。
  4. 容易误判:CrashLoopBackOff 本身不是根因,只是 kubelet 在退避重启。另一个常见误判是退出码为 0 仍反复重启;如果 restartPolicy: Always,容器正常结束也会被重启,最终同样显示 CrashLoopBackOff。
  5. initContainer 失败、镜像入口错误和配置文件缺失也会表现为容器反复退出,需要结合 init 日志和挂载检查。

修复

把命令改成长期运行的进程,模拟修复应用启动逻辑:

kubectl patch deployment crash-lab --type=json \
  -p '[{"op":"replace","path":"/spec/template/spec/containers/0/command","value":["sh","-c","echo fixed; sleep 3600"]}]'
kubectl rollout status deployment/crash-lab --timeout=120s
kubectl get pods -l app=crash-lab
kubectl logs -l app=crash-lab

Pod 应保持 Running 且重启次数不再增长。真实修复要针对退出码处理:补齐环境变量和 Secret、修应用启动异常、调大内存 limit、放宽 startupProbe,或修正镜像入口命令。

清理与回滚

kubectl delete deployment crash-lab --ignore-not-found
kubectl delete namespace lab-crash --ignore-not-found
kind delete cluster --name lab-crash

如果使用现有集群,只删除 crash-lab Deployment 和 lab-crash 命名空间即可。

复盘

延伸阅读

读完自测

  1. 容器退出码 137 通常意味着什么?
  2. 为什么排查 CrashLoopBackOff 要看 --previous 日志?
  3. 容器退出码为 0 也可能进入 CrashLoopBackOff 吗?
答案
  1. 通常是被 SIGKILL 杀死,常见于 OOMKilled,也可能是其他强制终止。
  2. 当前容器可能刚启动就退出,日志为空;上一次容器的日志才包含退出前的错误信息。
  3. 会。如果 restartPolicy: Always,即使容器正常结束,kubelet 也会重启它,反复循环后进入退避状态。