一、理解状态之前,先理解 K8s 的"三层状态模型"
Kubernetes 中并不存在一个单一的"Pod 状态字段"能解释一切。API 对象 Pod 的 status 中同时维护着三套互相独立、互相补充的状态信息:
| 层级 | API 字段路径 | 粒度 | 作用 |
|---|---|---|---|
| 第一层:Pod 阶段(Phase) | status.phase |
Pod 级、宏观生命周期 | 描述 Pod 所处的生命周期大阶段,只有 5 个取值 |
| 第二层:容器状态(Container Statuses) | status.containerStatuses[](含 state、lastState、ready、restartCount) |
容器级 | 描述每个容器的 Waiting / Running / Terminated 状态及原因(reason) |
| 第三层:Pod 状况(Conditions) | status.conditions[] |
Pod 级、面向可用性 | 描述 Pod 是否被调度、是否初始化完成、容器是否就绪、整体是否 Ready |
而你在 kubectl get pod 里看到的 STATUS 列,并不是其中任何一个字段的原样输出,而是 kubectl 根据上述三层信息(外加 metadata.deletionTimestamp)现场推导出来的"显示状态"。这是理解一切的关键。
1.1 第一层:Pod Phase(status.phase)
官方定义的 Phase 只有 5 个取值,且刻意地保持粗粒度,它不试图描述细节,只回答"这个 Pod 大致处在生命周期的哪一段":
-
Pending(挂起)
集群已经接受该 Pod 对象(API 对象已创建),但 Pod 尚未进入 Running:可能还没被调度到节点,或者正在拉取镜像、挂载卷、创建容器。即:从对象创建成功到第一个容器开始运行之前的所有时间,都属于 Pending。
-
Running(运行中)
Pod 已经绑定到某个节点,所有容器均已创建完成,且至少有一个容器正在运行,或正在启动/重启过程中。注意:Running 不等于健康,也不等于对外提供服务(还要看 Ready)。
-
Succeeded(成功)
Pod 中所有容器都已成功终止(退出码 0),且不会再被重启。典型场景:Job、CronJob 的任务 Pod。
-
Failed(失败)
Pod 中所有容器都已终止,且至少有一个容器以失败方式终止(非 0 退出码,或被系统信号强制终止)。被驱逐(Evicted)的 Pod 其 Phase 也是 Failed。
-
Unknown(未知)
由于某种原因无法获取 Pod 的状态,通常是控制面与所在节点(kubelet)通信失败:节点宕机、kubelet 进程异常、网络分区等。
请注意 Phase 的两个重要特征:
- Phase 之间不是严格的状态机,官方文档明确说明 Phase 不保证构成一个严密的状态转移图,它只是"高层概括";
- Phase 不包含
Terminating、CrashLoopBackOff、Completed、Evicted这些天天在 kubectl 里看到的词------它们来自下面两层和推导逻辑。
1.2 第二层:容器状态(containerStatuses\[\].state)
每个容器在任意时刻必然处于三种状态之一:
- Waiting(等待中) :容器尚未运行,正在做启动前的准备工作,或因为某种原因无法启动。
waiting.reason会给出具体原因,这是排查"起不来"问题的第一现场,例如ContainerCreating、ImagePullBackOff、CrashLoopBackOff、CreateContainerConfigError等。 - Running(运行中) :容器正在运行。
startedAt记录启动时间;若配置了启动探针/就绪探针,还会体现到started、ready字段。 - Terminated(已终止) :容器执行完毕或因某种原因退出。
terminated.reason(如Completed、Error、OOMKilled)、exitCode、signal、finishedAt是判断"怎么死的"的核心证据。
此外还有两个极易被忽视但价值极高的字段:
lastState:上一次容器实例的状态。当容器正在 CrashLoopBackOff 循环中、当前 state 是 Waiting 时,lastState.terminated里保存着上一次崩溃的退出码和原因,这是事后验尸(post-mortem)的关键。restartCount:该容器的重启次数,对应 kubectl 的 RESTARTS 列。
1.3 第三层:Pod Conditions(status.conditions)
Conditions 面向"可用性"而非"生命周期",标准条件有 4 个(新版本增加了第 5 个):
| Condition | 含义 |
|---|---|
PodScheduled |
Pod 已被成功调度到某个节点 |
Initialized |
所有 Init 容器已成功完成 |
ContainersReady |
所有容器均已通过就绪检查 |
Ready |
Pod 整体就绪,可以接入 Service 流量 |
DisruptionTarget(较新版本) |
标记 Pod 即将被某种干扰(如驱逐)终止,并携带原因 |
Ready 条件直接决定了 Pod 的 IP 是否被加入 Service 的 Endpoints/EndpointSlice。一个 Pod 可以是 Running 但 Ready=False------此时它活着,但不接流量。这是"服务看起来在跑、接口却 502/无节点"这类诡异问题的根源之一。
1.4 真相:kubectl STATUS 列是"推导"出来的
kubectl 的打印器按如下优先级逻辑合成 STATUS 列(此处描述其与源码等价的语义,便于记忆):
- 初始值取
status.phase(如Pending、Running、Failed); - 若
status.reason非空(如Evicted、NodeLost),则覆盖; - 若存在未完成的 Init 容器,则显示
Init:i/n、Init:Error、Init:CrashLoopBackOff、Init:Signal:x、Init:ExitCode:x等; - Init 完成后、主容器尚未启动时,显示
PodInitializing; - 主容器层面:取最后一个 非正常容器的
waiting.reason(如CrashLoopBackOff、ContainerCreating、ImagePullBackOff)或terminated.reason(如Completed、Error、OOMKilled),无 reason 时显示Signal:x/ExitCode:x; - 若显示为
Completed但仍有容器在 Running,则修正回Running; - 最高优先级 :只要
metadata.deletionTimestamp非空(即对象已被发起删除),一律显示Terminating。
由此可以得出三个重要结论,请务必记住:
Terminating不是 Phase ,它是"删除流程进行中"的显示态,来自deletionTimestamp;Completed不是 Phase,它是 Phase=Succeeded 的显示形式;CrashLoopBackOff、ImagePullBackOff、OOMKilled等不是 Phase ,它们是容器级state.reason,被 kubectl 提升显示到了 STATUS 列。
理解了这套推导逻辑,就不会再被 STATUS 列"牵着走",而是能反向定位到它背后的真实字段,从而精准排障。
二、Pod Phase 五态
2.1 Pending:卡在"出生"之前
典型停留点与成因:
- 调度失败 :资源不足(CPU/内存不满足 requests)、节点亲和性/污点容忍不匹配、拓扑分布约束过严、PV 的节点亲和性与可用节点冲突。Events 中会出现
FailedScheduling。 - 镜像拉取慢或失败:大镜像、跨洋拉取、镜像仓库限流(如 Docker Hub 匿名限流)、凭证错误。
- 卷挂载慢或失败:云盘 attach/mount 超时、NFS 不通、CSI 插件异常。
- 准入/配额拦截:ResourceQuota 超限、LimitRange 默认值注入失败等(这类更多表现为创建对象失败,但部分场景会体现为调度阻塞)。
排查命令:
bash
kubectl describe pod <pod> -n <ns> # 重点看 Events
kubectl get events -n <ns> --sort-by='.lastTimestamp'
kubectl describe node <node> # 看 Allocatable 与已分配量
2.2 Running:活着,但未必健康
Running 只承诺"至少一个容器在跑"。进入 Running 后仍要持续关注:
READY列是否为1/1(就绪探针是否通过);RESTARTS是否在缓慢增长(历史上是否崩溃过,是否存在 flapping);lastState.terminated是否残留 OOMKilled/Error 记录。
一个严谨的巡检习惯 :不要只看 STATUS,要看 STATUS + READY + RESTARTS + AGE 四联指标。RESTARTS 高但 STATUS 是 Running 的 Pod,往往是"带病运行",需要复盘历史崩溃原因。
2.3 Succeeded:任务型 Pod 的圆满结局
所有容器以 0 退出且不会重启。对 Job/CronJob 这是预期状态;但如果一个本应长期服务的 Deployment Pod 变成 Succeeded,反而是严重事故------通常意味着主进程"正常退出"了(例如前台进程意外 return、启动脚本执行完就结束),而重启策略又不是 Always,或镜像 ENTRYPOINT 设计错误。长期服务容器的进程必须"永不返回"。
2.4 Failed:所有容器都已终止且至少一个失败
- 对 Job:可能是业务错误、超过
backoffLimit、超过activeDeadlineSeconds; - 对普通 Pod:可能是全部容器崩溃且重启策略为 Never;
- 被驱逐的 Pod 也是 Failed ,且
status.reason=Evicted,status.message会写明驱逐原因(如 "The node was low on resource: memory")。
2.5 Unknown:失联
控制面在一段时间内收不到 kubelet 上报(节点状态超过 node-monitor-grace-period,默认 40s 未上报则节点被置为 NotReady/Unknown),该节点上的 Pod 会陆续显示 Unknown。后续由基于污点的驱逐机制(默认约 5 分钟)决定是否在其他节点重建。常见根因:节点宕机、kubelet 崩溃、CNI/网络分区、节点资源耗尽导致 kubelet 无响应。
三、容器状态三态与全量 reason 字典
3.1 Waiting 及常见 reason 全表
| reason | 含义 | 严重程度 | 第一响应 |
|---|---|---|---|
ContainerCreating |
正在创建:拉镜像、挂卷、分配网络、创建沙箱 | 低(短暂)/高(长期卡住) | describe 看 Events 卡在哪一步 |
PodInitializing |
Init 容器已完成,主容器正在初始化启动 | 低 | 稍等;长期不动查节点与运行时 |
Init:N/M、Init:Error、Init:CrashLoopBackOff |
Init 容器进行中/失败/崩溃循环 | 中~高 | kubectl logs <pod> -c <init容器名> |
ErrImagePull |
镜像拉取立即失败(名称错、仓库拒绝、凭证错) | 高 | 核对镜像名/tag/secret |
ImagePullBackOff |
拉取失败后进入退避重试 | 高 | 同上,并查节点到仓库的网络 |
InvalidImageName |
镜像引用格式非法 | 高 | 修正镜像地址写法 |
CreateContainerConfigError |
配置类错误:引用了不存在/缺键的 ConfigMap/Secret 等 | 高 | describe 看 message,核对配置对象 |
CreateContainerError |
创建容器失败:如挂载配置冲突、OCI 创建错误 | 高 | describe + 节点运行时日志 |
RunContainerError |
启动容器时出错,常见为卷挂载失败 | 高 | 查 Events 与节点 kubelet/CSI 日志 |
CrashLoopBackOff |
容器启动后反复退出,kubelet 按退避策略反复重启 | 高 | logs --previous + lastState |
3.2 Running 的关键字段
startedAt:本次启动时间,结合 RESTARTS 可推算崩溃频率;ready:是否通过就绪探针;started:是否通过启动探针(配置了 startupProbe 时)。
3.3 Terminated 及常见 reason 全表
| reason | 含义 | 典型退出码/信号 |
|---|---|---|
Completed |
正常执行完毕退出 | 0 |
Error |
以非 0 退出码失败退出 | 1、2、126、127 等 |
OOMKilled |
超出容器 memory limit,被内核 OOM Killer 杀死 | 137(SIGKILL) |
ContainerCannotRun |
容器无法运行:如入口二进制不可执行、运行时错误 | 126/127/128+ |
DeadlineExceeded |
超过 Job 的 activeDeadlineSeconds 被强制终止 |
--- |
| 无 reason,仅有 signal/exitCode | 被信号杀死等 | 128+信号编号 |
退出码速查(容器"死因"的语言):
| 退出码 | 含义 |
|---|---|
| 0 | 正常退出 |
| 1 | 通用错误(业务异常、未捕获异常) |
| 2 | Shell 命令误用(常见于脚本类容器) |
| 126 | 文件存在但不可执行(权限/格式问题) |
| 127 | 命令/二进制不存在(ENTRYPOINT 写错、镜像缺文件) |
| 137 | SIGKILL(9):OOMKilled、liveness 强杀、grace 到期强杀 |
| 139 | SIGSEGV(11):段错误,程序崩溃 |
| 143 | SIGTERM(15):正常优雅终止流程 |
| 128+N | 被信号 N 终止的通用表达 |
记住这张表,排查 CrashLoopBackOff 时看 lastState.terminated.exitCode 就能瞬间缩小根因范围:137 往内存/探针/强杀方向查,127/126 往镜像与启动命令方向查,1 往业务日志方向查。
四、kubectl STATUS 列全状态
以下按"出现频率 × 重要性"排序,每个状态给出:本质、常见成因、诊断、处置、严重度。
4.1 Running(正常态)
本质 :Phase=Running 且无删除时间戳、无异常容器 reason。
注意 :Running ≠ 可用。必须同时确认 READY=1/1。若 READY=0/1 且 STATUS=Running,说明就绪探针未通过或容器 ready=false,此时 Service 不会导流,需检查探针配置、端口、依赖服务连通性。
严重度:低。
4.2 Pending(卡住未启动)
本质 :Phase=Pending,尚未有容器运行。
常见成因 :FailedScheduling(资源/亲和/污点/PV 约束)、镜像拉取中、卷挂载中、Init 未开始。
诊断:
bash
kubectl describe pod <pod> -n <ns> # Events 是核心
kubectl get events -n <ns> --field-selector reason=FailedScheduling
处置 :扩容节点或降低 requests;调整亲和/容忍;检查 StorageClass 与 PV 可用区;清理被占用的资源。
严重度:中(新建 Pod 短暂 Pending 正常,长期 Pending 为故障)。
4.3 ContainerCreating(创建中)
本质 :Waiting,reason=ContainerCreating。kubelet 正在:拉取镜像 → 创建/挂载卷 → 调用 CNI 分配网络 → 创建容器沙箱。
卡住时的定位思路:describe 的 Events 会明确停在哪个动作上------
- 停在 "Pulling image":镜像问题(大、慢、仓库不通、限流);
- 停在 "MountVolume.SetUp failed":卷问题(CSI、NFS、secret 卷);
- 停在 network 相关:CNI 插件故障、IP 池耗尽;
- 停在 sandbox 创建:容器运行时(containerd/docker)异常。
严重度:短暂为低,长期卡住为高。
4.4 Init 系列:Init:N/M、Init:Error、Init:CrashLoopBackOff、PodInitializing
本质 :Init 容器按顺序执行,全部成功后主容器才启动。任何一个 Init 容器失败,Pod 就卡在 Init 阶段。
典型场景 :等待依赖就绪的 init 脚本(如等数据库可连接)写死循环、初始化 SQL 失败、init 镜像拉取失败。
诊断:
bash
kubectl logs <pod> -n <ns> -c <init-container-name> # 当前
kubectl logs <pod> -n <ns> -c <init-container-name> --previous # 上一次
处置 :修复 init 逻辑;为"等待依赖"类 init 容器设置超时与重试上限,避免无限 Pending 式占用。
严重度:中~高(会阻塞整个 Pod 启动)。
4.5 Completed(圆满完成)
本质 :Phase=Succeeded 的显示形式,所有容器以 0 退出且不再重启。
典型载体 :Job/CronJob。
注意:
- Completed 的 Pod 会一直留在集群中直到 TTL 控制器(
ttlSecondsAfterFinished)或人工清理; - 若 Deployment/StatefulSet 的 Pod 出现 Completed,属于设计错误(长期服务不应"执行完就退出"),需检查镜像入口与重启策略。
严重度:对 Job 为低(预期态);对长期服务为高(异常态)。
4.6 Error(失败退出)
本质 :容器以非 0 退出,terminated.reason=Error,通常出现在重启策略为 Never/OnFailure 且已不再重启的 Pod 上,或崩溃瞬间的显示。
诊断 :logs --previous 看业务报错;describe 看 exitCode。
严重度:中~高,视业务而定。
4.7 CrashLoopBackOff(崩溃循环退避)★重点
本质 :容器启动后在短时间内退出,kubelet 不断重启它,且重启间隔按指数退避增长。它是一个 Waiting reason,表示"上一次尝试失败了,正在等待下一次重试"。
退避机制(务必掌握) :重试间隔依次为 10s → 20s → 40s → 80s → 160s → 320s,封顶 5 分钟 ;当容器某次成功持续运行约 10 分钟后,退避计数清零。这就是为什么崩溃越久,看到"back-off restarting failed container"的间隔越长,也是为什么"刚 kubectl logs 看不到东西,等几分钟再看又崩了"。
六大根因族:
- 应用自身崩溃:配置错误、空指针、依赖连接失败(DB/Redis/MQ 地址错或不通)、启动即 panic;exitCode 多为 1。
- OOMKilled 循环 :limit 设置过小或内存泄漏;exitCode=137,
lastState.terminated.reason=OOMKilled。 - liveness 探针误杀循环:探针阈值过严/端口路径错误/应用启动慢但未配 startupProbe,导致 kubelet 反复杀容器→重启→再杀。exitCode 常为 137/143。
- 镜像/入口问题:ENTRYPOINT 命令不存在(127)、不可执行(126)、前台进程立即退出(0 或 1)------"容器没有常驻进程"是新手高频坑。
- 配置注入失败:环境变量引用的 Secret/ConfigMap 键缺失(此类更多表现为 CreateContainerConfigError,但挂载卷失败也可能导致启动即退)。
- 权限/安全上下文问题:非 root 用户写只读文件系统、capabilities 不足、seccomp/SELinux 拦截。
标准排查路径:
bash
# 1. 看上一次崩溃的日志(最关键一步)
kubectl logs <pod> -n <ns> --previous
# 多容器时指定容器
kubectl logs <pod> -n <ns> -c <container> --previous
# 2. 看死因:exitCode / reason / signal
kubectl get pod <pod> -n <ns> -o jsonpath='{.status.containerStatuses[*].lastState}' | jq
# 3. 看事件与探针报错
kubectl describe pod <pod> -n <ns>
# 4. 崩溃太快抓不到日志时:临时改为 sleep 启动或 kubectl debug
kubectl debug -n <ns> <pod> --copy-to=debug-pod --container=xx --image=busybox -- sh
处置 :按根因族对症处理------修配置、修依赖连通性、调大 memory limit、修正探针与 startupProbe、修复镜像入口;若由近期发布引入,优先回滚 (kubectl rollout undo deployment/<name> -n <ns>)。
严重度:高(服务不可用或能力降级)。
4.8 ErrImagePull / ImagePullBackOff / InvalidImageName(镜像三兄弟)
区别 :ErrImagePull 是"本次拉取立即失败";连续失败后进入退避重试即 ImagePullBackOff;InvalidImageName 是镜像引用写法非法(如 tag 含大写字母等不规范字符)。
常见成因:
- 镜像名/tag 写错、镜像已被删除;
- 私有仓库未配置
imagePullSecrets或 secret 过期; - 节点到仓库网络不通、DNS 解析失败、代理/证书问题;
- 公有仓库限流(Docker Hub 对匿名拉取限流)。
处置 :核对 image: 字段;kubectl get secret <regcred> -o yaml 校验凭证;在节点上 crictl pull/ctr pull 复现;建设私有镜像仓库与镜像预热。
严重度:高(新 Pod 完全起不来),但根因通常简单。
4.9 CreateContainerConfigError(配置注入错误)
本质:容器创建前解析配置失败,最经典的是:
couldn't find key "DB_PASSWORD" in ConfigMap "app-config"
secret "app-secret" not found
即 env.valueFrom / envFrom / 卷引用指向了不存在的 ConfigMap/Secret 或其中缺失某个键。
诊断 :describe 的 Events 会直接打印缺失对象与键名。
处置 :补齐配置对象;在 CI/CD 中加入配置清单校验;对可选配置使用 optional: true。
严重度:高但修复快。
4.10 CreateContainerError / RunContainerError(运行时创建/启动错误)
本质 :OCI 运行时创建容器或启动容器时报错。常见于:卷挂载配置冲突(同路径重复挂载)、hostPath 不存在且类型校验失败、运行时(containerd/runc)异常、节点磁盘满导致无法写容器层。
诊断 :describe Events + 节点上 journalctl -u kubelet、journalctl -u containerd。
处置 :修正卷配置;清理节点磁盘;修复运行时。
严重度:高,且常为节点级问题,可能批量影响。
4.11 OOMKilled(内存超限被杀)★重点
本质 :容器内存使用超过 resources.limits.memory,触发 cgroup 级 OOM,内核杀死容器进程,terminated.reason=OOMKilled,exitCode=137。
三个易混淆概念:
- 节点级 OOM:节点整体内存不足,内核 OOM Killer 按 oom_score 杀进程,可能杀任意进程,不一定体现为 OOMKilled reason;
- cgroup 级 OOM(本文的 OOMKilled):只杀超限容器,reason 明确;
- Evicted:kubelet 基于驱逐阈值的主动驱逐,Pod 级事件,Phase=Failed、reason=Evicted。
排查:
bash
kubectl top pod -n <ns> # 需 metrics-server
kubectl describe pod <pod> -n <ns> # Limits 与 lastState
# 监控侧看 container_memory_working_set_bytes 曲线
处置与预防 :按工作集(working set)而非 RSS 估算内存;limit 预留 20%~50% 余量;Java 类应用务必设置堆外与容器感知参数(如 -XX:MaxRAMPercentage);修复内存泄漏;为关键服务设置 Guaranteed/Burstable 的合理 QoS,降低被优先驱逐概率。
严重度:高;若伴随 CrashLoopBackOff(反复 OOM)则为紧急。
4.12 Evicted(被驱逐)
本质:节点资源压力(内存、磁盘、inode、PID)触发 kubelet 驱逐机制,Pod 被终止,Phase=Failed,reason=Evicted,message 指明低压资源。
默认硬阈值(典型值) :memory.available < 100Mi、nodefs.available < 10%、imagefs.available < 15%、nodefs.inodesFree < 5%。
驱逐顺序与 QoS 强相关:BestEffort 最先死,Burstable 次之,Guaranteed 最后(且 Guaranteed 通常只在自己超限时才被杀)。这也是为什么"裸奔"(不写 requests/limits)的 Pod 在节点出事时总是第一批牺牲。
处置 :扩容或削峰;清理节点磁盘(日志、镜像 GC 策略 imageGCHighThresholdPercent);为业务补齐 resources;对关键服务提高 QoS 等级并配合 PDB。
严重度:中~高(单 Pod 被驱逐是保护机制,批量驱逐是容量事故)。
4.13 Terminating(删除进行中)★重点
本质 :metadata.deletionTimestamp 已设置,Pod 进入优雅终止流程。它不是 Phase,而是"正在退场"的显示态。
标准删除时间线:
- 用户/控制器发起删除,API Server 写入
deletionTimestamp(对象进入"只读待删"状态); - 端点控制器将 Pod 从 Service 的 Endpoints 中摘除(停止导流);
- kubelet 执行本地清理:若配置了
preStop钩子先执行它,然后发送 SIGTERM; - 在
terminationGracePeriodSeconds(默认 30s)内等待容器自行退出;到期未退出的容器被 SIGKILL 强杀; - kubelet 确认容器全部终止、卷卸载完成,向 API Server 确认,对象被真正移除。
"大量 Terminating"的两种语义:
- 正常语义:滚动更新、缩容、节点排水时,旧 Pod 批量进入 Terminating,数十秒内消失;
- 异常语义 :Terminating 长时间不消失(分钟级甚至天级),即"卡 Terminating"。
卡 Terminating 的六大根因:
- Finalizers 未清除:某个控制器(或人为误加)持有 finalizer 且未释放,API 对象无法最终删除;
- 卷卸载失败:存储插件异常、远端存储不可达,kubelet 等待 unmount;
- kubelet/节点异常:节点 NotReady、kubelet 挂死,无人执行本地清理;
- 容器处于 D 状态(不可中断睡眠,多为 IO 卡死),SIGKILL 也无法立即生效;
- preStop 钩子阻塞:写了超长 sleep 或死循环的 preStop,占满甚至超过 grace 期;
- 容器运行时异常:containerd 卡死,无法完成 kill/remove。
处置 SOP:
bash
# 1. 看卡在哪:事件与 finalizers
kubectl describe pod <pod> -n <ns>
kubectl get pod <pod> -n <ns> -o jsonpath='{.metadata.finalizers}'
# 2. 节点/kubelet 是否健康
kubectl get node
# 节点上:journalctl -u kubelet -u containerd
# 3. 确认业务已无流量、无数据写入风险后,方可兜底:
# a) 清除 finalizer(仅当确认对应控制器已无需收尾)
kubectl patch pod <pod> -n <ns> -p '{"metadata":{"finalizers":null}}'
# b) 强制删除(API 层立即移除,kubelet 侧异步清理)
kubectl delete pod <pod> -n <ns> --force --grace-period=0
强制删除的风险警告 :对 StatefulSet Pod、挂载了 ReadWriteOnce 云盘的 Pod,强删可能造成"旧容器实际还活着 + 新 Pod 抢挂同一卷"的双写/脑裂风险;对数据库类有状态服务,强删前必须确认节点与运行时状态,必要时先隔离节点。
严重度:滚动更新中的短暂 Terminating 为低;长期卡住为高(占用资源、阻塞 StatefulSet 有序重建、掩盖底层存储/节点故障)。
4.14 Unknown / NodeLost / ContainerStatusUnknown(失联族)
本质 :控制面拿不到 Pod 真实状态。NodeLost 场景下 kubectl 可能显示 Unknown;较新版本中容器级还可能出现 ContainerStatusUnknown(如节点异常时 kubelet 无法确认容器状态)。
根因 :节点宕机、kubelet 崩溃、网络分区、节点负载过高导致上报超时。
处置 :先治节点(重启 kubelet、修复网络、下线坏节点),Pod 侧由控制器在驱逐超时后自动重建;不要对 Unknown Pod 盲目 force delete,先确认节点是否真的"死了"。
严重度:高(通常是节点级故障的信号)。
4.15 Running 但 READY 0/1(就绪未通过)
本质 :容器在跑,但 ready=false,Pod Condition Ready=False,Endpoints 中无此 Pod。
常见成因 :就绪探针端口/路径/协议配错;应用启动慢但 readiness 阈值过严;应用依赖(DB/配置中心)不可用导致健康检查返回非 200;容器内监听地址为 127.0.0.1 而探针走 Pod IP。
影响 :发布时新 Pod 永远不接流 → 滚动更新卡住(deployment waiting for rollout);或运行中依赖抖动导致流量被摘除。
处置 :手动模拟探针请求(进入容器 curl);校准探针参数;区分 startupProbe(管启动慢)与 readinessProbe(管运行期就绪)。
严重度:中~高。
五、重启策略、工作负载与状态行为的矩阵
restartPolicy 决定"容器退出后 kubelet 怎么办",它与工作负载类型强绑定:
| restartPolicy | 容器成功退出(0) | 容器失败退出(非0) | 适用工作负载 |
|---|---|---|---|
| Always(默认) | 重启 | 重启(反复失败则 CrashLoopBackOff) | Deployment/StatefulSet/DaemonSet |
| OnFailure | 不重启 | 重启 | Job |
| Never | 不重启 | 不重启(Pod 停留 Error/Failed) | Job(特殊场景) |
配套机制:
- Deployment 等长期负载只允许 Always,因此它们的 Pod 不会"自然 Completed",出现 Completed 即设计错误;
- Job 的
backoffLimit控制失败重试上限,超限后 Job 标记 Failed;activeDeadlineSeconds控制总时长,超时容器被以 DeadlineExceeded 终止; - RESTARTS 列 是容器级 restartCount 之和;
4 (338d ago)表示累计重启 4 次、最后一次发生在 338 天前------这是判断"老毛病还是新故障"的时间锚点; - CrashLoopBackOff 的退避与 Job 的失败重试是两套独立机制,不要混淆。
六、Ready 机制深潜:为什么"Running ≠ 可用"
一次完整的"可用"判定链条是:
容器进程存活(Running)
→ startupProbe 通过(若配置)→ started=true
→ readinessProbe 通过 → ready=true
→ ContainersReady=True → Ready=True
→ EndpointSlice 纳入该 Pod IP
→ kube-proxy/Service 开始导流
链条上任何一环断裂,都会产生"看起来在跑、实际不服务"或"流量打进来了、实际没准备好"的事故。两个经典反模式:
- liveness 配得像 readiness 一样严:依赖一抖动容器就被杀,人为制造 CrashLoopBackOff。liveness 只应回答"进程是否死锁/不可恢复",不应检查业务依赖;
- 不配 startupProbe 且 readiness 过早开始探测:慢启动应用(JVM、大数据量加载)在启动期被判定不就绪甚至被 liveness 杀掉。正确姿势是 startupProbe 兜底启动期,ready 后再接流。
同时记住删除侧的配合:Pod 被删除时 Endpoints 摘除与 SIGTERM 几乎同时发生,若客户端/网关有连接粘性与缓存,可能出现"Pod 已 Terminating 仍收到流量"的短暂窗口,标准解法是 preStop: sleep 若干秒 或端点更新感知的优雅下线设计。
七、排障方法论:从"看到异常"到"定位根因"
第一原则:先看三层模型,再看显示状态。 即:status.phase 定大方向 → containerStatuses 定死因 → conditions 定可用性 → Events 定过程 → logs 定细节。
标准 SOP(按顺序执行):
bash
# 0. 全局快照与过滤
kubectl get pod -A
kubectl get pod -A --field-selector=status.phase!=Running # 快速捞出非 Running
kubectl get pod -A -o wide # 补节点信息
# 1. 事件(过程证据,注意事件默认只保留 1 小时)
kubectl describe pod <pod> -n <ns>
kubectl get events -n <ns> --sort-by='.lastTimestamp'
# 2. 日志(当前与上一次)
kubectl logs <pod> -n <ns> -c <container> --previous --tail=200
# 3. 结构化死因(退出码/信号/lastState/ready/restartCount)
kubectl get pod <pod> -n <ns> -o yaml | yq '.status.containerStatuses'
# 4. 资源视角(OOM/容量)
kubectl top pod -n <ns>
kubectl top node
kubectl describe node <node> | sed -n '/Allocated resources/,$p'
# 5. 运行时视角(节点上)
journalctl -u kubelet --since "1h ago"
journalctl -u containerd --since "1h ago"
crictl ps -a && crictl logs <container-id>
# 6. 抓不住的崩溃:调试容器
kubectl debug -n <ns> <pod> --copy-to=dbg --image=busybox:1.36 --target=<container> -- sh
经验法则:
- exitCode=1 看业务日志;=137 看内存与探针;=127/126 看镜像入口;
- 批量同状态 → 往"共性变更/共性依赖"方向查(发布、配置中心、DB、网络、节点);
- 单点异常 → 往"个体差异"方向查(所在节点、卷、副本参数);
- RESTARTS 的"最后一次重启时间"是判断故障起点的第一线索。
八、减少异常状态的工程最佳实践
- 探针三件套规范化:startupProbe 兜底慢启动;liveness 只判死锁不判依赖;readiness 真实反映可服务能力;阈值经过压测校准。
- 资源治理:所有 Pod 声明 requests/limits;以监控中的 working set 分位数定容量;关键服务争取更高 QoS 等级。
- 优雅终止 :应用捕获 SIGTERM 做连接排空;
terminationGracePeriodSeconds按真实排空时长设置(而非默认 30s);必要时 preStop 补偿端点摘除窗口。 - 镜像与配置防线 :私有仓库 + 不可变 tag;CI 校验 ConfigMap/Secret 键完整性;杜绝
latest在生产漂移。 - 容量与驱逐防线:节点水位告警;日志/镜像 GC 策略明确;磁盘与内存阈值纳入监控。
- 变更防线:发布分批 + 自动回滚条件(崩溃次数、就绪失败);对"批量 Terminating + 批量 CrashLoopBackOff 同时出现"这类发布事故特征设置组合告警。
- 可观测性 :对
kube_pod_container_status_restarts_total、kube_pod_status_phase、kube_pod_container_status_waiting_reason建立告警,让 CrashLoopBackOff/ImagePullBackOff/Evicted 在 IM 里第一时间被看见,而不是在巡检时才被发现。 - 有状态服务特殊照顾:数据库类 StatefulSet 慎用强删;PDB 保护自愿性驱逐;存储健康独立监控。