bigywater.tech
Kubernetes · 31

CrashLoopBackOff:重启退避的时间序列

CrashLoopBackOff 不是 Pod 的一种失败状态,而是 kubelet 在两次重启之间的等待。这篇让一个容器每秒崩溃一次,观察将近 6 分钟,把每次重启前真正等了多久量出来,再看 STATUS 列、多容器 Pod 和重建 Pod 时的表现。

14 → 26 → 45 → 83 → 161 秒
第 2~6 次重启前实测的等待时间(约 10、20、40、80、160 秒加开销)
336 秒
第 6 次重启发生在 Pod 第一次启动后的时间
0 → 14 秒
删除重建同名 Pod 后,第 1、2 次重启前的等待:退避从头开始

等待时间翻倍

实测 · 本机执行输出
  Pod crash(容器 sleep 1 后 exit 1,restartPolicy 默认 Always)共观察 345 秒,restartCount 到 6:
    第 1 次重启:上一个容器 运行 1 秒后崩溃,等了 1 秒才启动新容器(新容器启动时刻 距 Pod 第一次启动 2 秒)
    第 2 次重启:上一个容器 运行 1 秒后崩溃,等了 14 秒才启动新容器(新容器启动时刻 距 Pod 第一次启动 17 秒)
    第 3 次重启:上一个容器 运行 1 秒后崩溃,等了 26 秒才启动新容器(新容器启动时刻 距 Pod 第一次启动 44 秒)
    第 4 次重启:上一个容器 运行 1 秒后崩溃,等了 45 秒才启动新容器(新容器启动时刻 距 Pod 第一次启动 90 秒)
    第 5 次重启:上一个容器 运行 1 秒后崩溃,等了 83 秒才启动新容器(新容器启动时刻 距 Pod 第一次启动 174 秒)
    第 6 次重启:上一个容器 运行 1 秒后崩溃,等了 161 秒才启动新容器(新容器启动时刻 距 Pod 第一次启动 336 秒)
  容器 state 的变化(每秒读一次,只记变化):waiting:ContainerCreating(+0s) → running(+1s) → terminated:Error(+6s) → running(+19s) → terminated:Error(+20s) → running(+46s) → terminated:Error(+48s) → running(+92s) → terminated:Error(+93s) → running(+175s) → terminated:Error(+176s) → waiting:CrashLoopBackOff(+254s) → running(+338s) → terminated:Error(+339s)
  kubectl get pod 的 STATUS 列的变化:ContainerCreating(+0s) → Running(+1s) → Error(+6s) → Running(+19s) → Error(+20s) → Running(+46s) → Error(+48s) → Running(+92s) → Error(+93s) → Running(+175s) → Error(+176s) → CrashLoopBackOff(+254s) → Running(+338s) → Error(+339s)
  事件 BackOff:count=8;Back-off restarting failed container c in pod crash_k25(459cd0d9-b031-44df-bf91-f7d7ceee42d7)

容器 sleep 1 后 exit 1。第 1 次重启等了 1 秒(几乎立刻),之后每次崩溃等待翻倍:14、26、45、83、161 秒,约等于 10、20、40、80、160 秒再加 1~5 秒的启动与同步开销;第 6 次重启发生在 Pod 第一次启动后 336 秒,而每个容器实例只运行了 1 秒。再往后会封顶在 5 分钟。

kubectl get 的 STATUS 大部分时间是 Error(上一个容器的终止原因),容器刚起来的那 1 秒是 Running,CrashLoopBackOff 只在第 254 到 338 秒出现了一次;事件 BackOff 的 count 累计到 8。所以别指望总能在 STATUS 里看到这几个字,判断崩溃循环要看 restartCount、lastState 和事件。

每个容器各管各的

实测 · 本机执行输出
  Pod mixed(good 一直运行,bad 立刻 exit 2):phase=Running;kubectl get 的 STATUS=Error;READY=1/2
    容器 bad:restartCount=6 state=terminated ready=False
    容器 good:restartCount=0 state=running ready=True

多容器 Pod 里 bad 立刻崩溃(restartCount=6),good 一直 running(restartCount=0),Pod 的 phase 仍是 Running,READY 是 1/2,STATUS 显示 Error。退避是按容器计的。

Never 没有退避

实测 · 本机执行输出
  Pod never:phase=Failed;restartCount=0;state=terminated(exitCode=1);kubectl get STATUS=Error

restartPolicy: Never 的 Pod 崩溃后 phase=Failed、restartCount=0,不会重启,所以不存在 CrashLoopBackOff;STATUS 只是 Error。

重建 Pod

实测 · 本机执行输出
  重新创建的 Pod crash 前 50 秒:第 1 次重启前等了 0 秒;第 2 次重启前等了 14 秒;第 3 次重启前等了 27 秒(又是从最短的等待开始,不是接着之前的 5 分钟)

删掉 crash 再创建同名 Pod:前 50 秒第 1 次重启等 0 秒,第 2 次 14 秒,第 3 次 27 秒——退避重新从最短开始,不是接着之前的 5 分钟。想让崩溃的容器立刻重试,把 Pod 重建一次就行;但根因不修,它只是把同样的退避重走一遍。

分步演示

重启退避
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / kind(Docker 里的单节点或多节点真实集群,节点是 Docker Desktop 虚拟机里的容器)/ kubectl;用空的 kubeconfig 之外的独立 kubeconfig,不碰用户自己的集群;计时用 date +%s%N / kubectl 事件时间戳,数字每次运行会有小幅抖动。三节点 kind 集群 kx-main(Kubernetes v1.37.0);容器 busybox:1.36,命令 sh -c "echo started; sleep 1; exit 1";每 1 秒读一次 Pod 状态,重启等待取自 lastState.terminated.finishedAt 到新容器 state.running.startedAt 的差,共观察 345 秒;STATUS 取自 kubectl get pod 的第 3 列;每种情形各一次(同一个脚本的另一次运行得到的等待是 12、26、48、84、164 秒)
    • 没有验证:5 分钟封顶(只观察到 161 秒这一档,下一档 320 会被封顶在 300 秒);容器连续运行 10 分钟后退避重置的规则;kubelet 的 CrashLoopBackOff 最大值参数(--crash-loop-back-off-max 一类的新特性);镜像拉取失败的退避(ImagePullBackOff,另一篇);init 容器的退避(上一篇里出现过 Init:CrashLoopBackOff);节点重启后退避状态是否保留
    京ICP备2026067502号-1京公网安备11010502063487号