你有没有盯着一个 Pending 的 Pod 骂过娘?明明 YAML 没错,就是起不来,然后对着屏幕发呆。老实说,我刚搞 K8s 那阵子,经常被一个 CrashLoopBackOff 整到怀疑人生。
后来我静下心把 Pod 从提交到跑起来的每一步都捋了一遍,画了张流程图,再遇到问题顺着链路一摸一个准。今天把这些东西掰开揉碎讲给你听,全是 SRE 视角的实战经验,版本基于 Kubernetes 1.28(我测试用的集群版本),其它小版本也差不多,核心链路没变。
你需要的背景和准备
这篇文章适合正在和 Pod 状态斗智斗勇的开发、运维、SRE。你要有:
- 一个能用的集群(Minikube、Kind 都行)
kubectl客户端,能执行apply和describe- 对 Pod、Deployment 这些概念有基本认知
光看不练没用,建议你边看边敲。
Pod 诞生五步曲
我把整个过程拆成五个阶段,每个阶段卡住,Pod 状态就会停在某个中间态。下面从 kubectl apply 开始讲起。
1. 准入与持久化------你的 YAML 去哪了?
你敲下 kubectl apply -f pod.yaml 之后,请求第一时间到 API Server 。别以为就直接写 etcd 了,先得过三关:认证 → 授权 → 准入控制。
认证授权基本不会出大问题,问题多半出在准入控制。比如你写了个 privileged: true 的容器,刚好集群开了 PodSecurity 准入,直接给你拒掉,API Server 返一个 forbidden 的错误。我当初就因为这个,在工位上折腾了半小时,事件里才看到 "violates PodSecurity" 字样。
过完这三关,API Server 才把 Pod 对象序列化写入 etcd 。这时候 Pod 只是 etcd 里一条普普通通的记录,没有 nodeName,status.phase 是 Pending。换句话说,Pod 还是个"黑户",连该去哪台机器都不知道。
这步最容易炸的点 :要是你配了 ValidatingWebhook 或 MutatingWebhook,而 webhook 服务挂了或超时,API Server 会一直等,然后报 failed calling webhook。我遇到过一次,集群升级后旧的 webhook 还没适配,新 Pod 全都建不出来。遇到这种,临时踢掉 webhook 就能应急。
小细节:kubectl apply 会把 YAML 干进了 etcd,但 Deployment 之类的控制器还要额外创建 ReplicaSet,那是另一条链路,这里只聚焦 Pod 自己。
2. 调度------Scheduler 该上场了
Scheduler 通过 watch 机制发现了一个 nodeName 为空的 Pod,于是摩拳擦掌开始干活。流程是经典的预选 + 优选 :先把不符合条件的节点踢掉(资源不足、有污点且 Pod 没容忍、节点选择器不匹配等等),再给剩下的节点打分,最后把得分最高的节点写回 Pod 的 spec.nodeName,再经由 API Server 存入 etcd。
调度失败的 Pod 会一直 Pending ,你在 kubectl describe pod 里能看到类似 "0/3 nodes are available: 3 node(s) had untolerated taint" 的提示。我看过太多人给 Pod 设了 8C 16G 的资源请求,但整个集群每个节点才 4C 8G,不 pending 才见鬼。还有那种忘记给 GPU 节点加 toleration 的,一样卡住。
说一个我踩过的坑:集群节点很多(几百台),调度变慢。后来我调了 Scheduler 的 percentageOfNodesToScore 参数,只让部分节点参与优选,速度立马上去,但代价是可能不会选到全局最优节点。这个适合大规模集群,小集群别碰。
3. 容器创建前奏------kubelet 接手,CNI 也来了
一旦 Pod 被钉到某个节点,该节点上的 kubelet 就通过 watch 拿到了属于它的 Pod。kubelet 做事很守规矩,先不急着拉业务镜像,而是调 CRI 创建 Pod Sandbox。
Sandbox 是什么?其实就是给 Pod 搞一个共享的命名空间(网络、IPC 等),本质上是一个 pause 容器。也正是这一步,会触发 CNI 插件给 Pod 分配网络------建网络命名空间、分 IP、配路由。网络不通或者 CNI 插件炸了,Pod 就卡在 ContainerCreating,事件里能看到类似 "network: failed to allocate for range" 这样的错误。
我遇到最坑爹的一次:集群网络插件是 Calico,calico-node 这 Pod 自己挂逼了,结果所有新 Pod 全部停在 ContainerCreating,一查节点上的 calico 日志,发现连不上 etcd。重启 calico-node,世界清净了。
所以,ContainerCreating 很久不结束,不要只看 Pod 事件,顺便看一眼节点上 kubelet 日志和 CNI 组件的健康状况。
4. 拉镜像、建容器------终于到你写的应用了
Sandbox 网络搞通之后,kubelet 才开始拖业务镜像。镜像拉取是这个阶段最熟悉也最烦人的拦路虎:ImagePullBackOff 。原因千奇百怪:镜像名拼错、tag 不存在、私有仓库没配 imagePullSecrets、网络不通...... 我有次把 image: myapp:latest 推到仓库,结果仓库里根本没有 latest tag,一直拉不下来,查了十分钟才发现 tag 没推成功。
拉完镜像,kubelet 调 CRI 创建并启动容器。如果你的 CMD 或 ENTRYPOINT 有问题,容器启动就退出了。kubelet 会根据 restartPolicy 不断地重启,表现出来就是 CrashLoopBackOff。这时候你要做的是:
kubectl describe pod看容器退出码(Exit Code)------非 0 基本就是应用崩了- 用
kubectl logs抓日志,看应用报错 - 如果容器还没启动就死了,可能在挂载卷阶段就炸了
说到挂载卷,CreateContainerConfigError 也是高频错误。ConfigMap 或 Secret 名字写错、不存在,Pod 事件会直接告诉你 "MountVolume.SetUp failed for volume xxx"。我曾经把 secretName 写成 secretname,YAML 里不明显,排查时气得想抽自己。
5. 就绪与健康------探针拍板能不能接流量
容器跑起来,还没完。kubelet 会按你配的探针来检查容器是否真正健康,这才是服务可用的最后一道关卡。
如果你配了 startupProbe,kubelet 会先跑它。成功之后才开始跑 livenessProbe 和 readinessProbe。startupProbe 特别适合 Java 这种慢启动的,我以前给一个 Spring Boot 应用配了长 failureThreshold,避免还没启动完就被 liveness 杀掉。但有一点要注意:startupProbe 失败会导致容器重启,跟 liveness 一样,只是它只在启动阶段有效。
readinessProbe 成功的那一刻,Pod 的状态才变成 Running 且 Ready 为 True,接着才会被 Endpoint Controller 加进 Service 的端点里,流量才真正打进来。如果 readiness 一直失败,Pod 显示 Running 但 ready 是 0/1,线上部署看着 Pod 全绿,实则一个请求都接不住,这是典型的"假 Running",我当年在凌晨上线时被坑惨了。
整个流程走完,Pod 终于可以正常干活了。
怎么验证全流程
你随便搞个 Pod 一 apply,用下面命令看实时事件,一清二楚:
kubectl get events --watch
更细的用:
kubectl describe pod <pod-name>
重点关注 Conditions 和 Events 的时间线,哪一步卡住一目了然。我还习惯看 kubectl get pod -o yaml 里的 status 字段,有时调度信息、网络 IP 分配都在里面。
常见错误速查
|----------------------------|---------------------------------|
| 状态 / 现象 | 可能根因 |
| 一直 Pending,无可用节点 | 资源不够、污点容忍缺失、nodeSelector/亲和性不满足 |
| ContainerCreating 超久 | CNI 插件故障、存储卷挂载不上 |
| ImagePullBackOff | 镜像名写错、secret 未配置、仓库不可达 |
| CrashLoopBackOff | 应用启动崩溃、退出码非 0、startupProbe 失败 |
| CreateContainerConfigError | ConfigMap/Secret 名称不对、引用不存在的卷 |
这些状态本质上就是 Pod 在不同阶段挂掉的体现,你只要照着阶段往回推,很快能定位。
好了,整个链路捋下来了。下次 Pod 抽风,别再盯着屏幕干瞪眼,把这五个阶段当成排查路线图用。你遇到过什么诡异问题没?比如我见过一次调度器死活不把 Pod 分到某个节点,后来发现是节点上报告的内存有个微小的浮点误差,导致资源判定刚好不过...... 评论区来聊聊你的经历。
如果这文章帮你省了点咖啡钱和时间,丢给同事也看看,救人一命。