CCE Pod 一直 Pending 或 CrashLoopBackOff:按调度、镜像、探针三层排查的 7 步
一、背景与问题
在华为云云容器引擎 CCE 里部署一个新版本后,最常见的两种“卡住”状态是 Pending 和 CrashLoopBackOff:前者是 Pod 一直等不到节点来运行,后者是容器反复启动、退出、再启动。两种状态的表象很像“服务起不来”,但根因完全不在同一层。直接删 Pod、重启 Deployment 往往只能让计数器归零,几分钟后又回到原样。
正确的做法是先分层定位:Pending 先查调度层,CrashLoopBackOff 先查镜像与应用层,最后再查探针与资源层。本文按这个顺序给出 7 步排查法,每一步都有可执行的命令和“做完算通过”的标准。
二、前置条件
- 已开通 CCE 集群,集群状态正常,节点处于 Ready。
- 本地已安装 kubectl,并已在 CCE 控制台的集群详情页下载 kubeconfig,完成
kubectl cluster-info能正常返回。 - 知道出问题的工作负载所在的命名空间和名称,下文用
<namespace>、<deployment>、<pod>代替,请替换为实际值。 - 操作前先确认这是一次可回滚的变更:Deployment 的镜像、副本数、资源请求可以改回去,不要直接删生产命名空间。
三、分步操作
第 1 步:看清现状,先看事件再动手
kubectl get pods -n <namespace> -o wide
kubectl get events -n <namespace> --sort-by=.lastTimestamp | tail -n 30
先记下三件事:Pod 的 STATUS、RESTARTS 次数、所在 NODE。RESTARTS 在短时间内快速增长,基本是应用或探针问题;NODE 一直为空,基本是调度问题。事件(Events)里经常直接写着原因,例如 FailedScheduling、Failed to pull image、BackOff,先读事件能省掉一半弯路。
第 2 步:Pending——查节点资源与调度约束
kubectl describe pod <pod> -n <namespace>
kubectl get nodes
kubectl top nodes
kubectl describe node <node-name>
在 describe pod 的 Events 末尾找调度失败的原因,常见有三类:节点 CPU 或内存不足、Pod 的 resources.requests 要得太多、节点选择器或污点把所有节点都排除了。先看 kubectl top nodes 确认节点真实余量,再看 describe node 的 Allocated resources 一栏。
处理办法按原因区分:资源不足就先降低 requests 或给集群扩一个节点;requests 是照抄模板写大的,改回与实际用量接近的值;用了 nodeSelector 或 affinity,先确认标签确实打在节点上,命令是 kubectl get nodes --show-labels。
第 3 步:Pending——查存储与镜像拉取
如果调度已经成功但容器迟迟不进入 Running,还要查两处:持久卷和镜像。
kubectl get pvc -n <namespace>
kubectl describe pod <pod> -n <namespace> | grep -A 5 -i "volume\|mount"
PVC 一直处于 Pending,说明存储类或容量配额有问题,先不要反复重启 Pod。镜像拉取失败会在 Events 里出现 ImagePullBackOff,多半是镜像地址、标签写错,或私有镜像仓库的访问凭证不对。先用 kubectl get pods -n <namespace> 确认状态,再到 CCE 控制台核对镜像地址,改镜像要用 kubectl set image 而不是手改 Pod。
第 4 步:CrashLoopBackOff——先看日志,再看上一次的日志
kubectl logs <pod> -n <namespace>
kubectl logs <pod> -n <namespace> --previous
kubectl describe pod <pod> -n <namespace>
容器已经退出时,logs 可能什么都没有,这时 --previous 是关键,它看的是上一个已经退出容器的日志。日志里的第一条报错往往就是根因:配置文件路径不对、环境变量缺失、数据库连不上、端口被占用。describe pod 的 Last State 一栏会写退出码,退出码 1 多半是应用报错,137 多半是被 OOMKilled,也就是内存超了。
第 5 步:CrashLoopBackOff——查镜像与启动命令
kubectl set image deployment/<deployment> <container>=<image>:<tag> -n <namespace>
kubectl rollout status deployment/<deployment> -n <namespace>
排查重灾区是三处:镜像标签写成了不存在的版本、镜像里根本没有要启动的命令、启动命令依赖的文件没有打进镜像。先在本地或测试环境用同一条镜像手动跑一次,确认它能启动;再核对 Deployment 里 command 和 args 是否与镜像的入口一致。改镜像后用 rollout status 观察滚动更新是否完成,不要同时改多个变量。
第 6 步:查探针与资源限制
探针配得太急,是“应用明明启动了、Pod 却一直重启”的常见原因。先把探针的配置读出来:
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
如果应用启动本身就要 20 秒以上,而 initialDelaySeconds 只给了 5 秒,探针会在应用还没准备好时就判定失败并重启容器。先把 initialDelaySeconds 放宽到大于应用实测启动时间,再进容器里亲手验证探针地址:
kubectl exec -it <pod> -n <namespace> -- sh
curl -v http://localhost:8080/healthz
curl 返回 200 才算探针地址真实可用;返回 404 或连接拒绝,说明探针路径或端口写错了。资源限制也要同时看:limits.memory 设得比应用峰值还小,容器会被 OOMKilled,表现为退出码 137,这时要调大 limits,而不是反复重启。
第 7 步:确认修复并做滚动验证
kubectl apply -f deployment.yaml
kubectl rollout status deployment/<deployment> -n <namespace>
kubectl get pods -n <namespace> -w
修复后不要只看一次结果:先等 rollout status 显示成功,再用 -w 观察至少 3 分钟,确认新 Pod 持续 Running、RESTARTS 不再增长、旧 Pod 已被正常回收。随后用业务方式验证一次,例如访问 Service 的 ClusterIP 或通过负载均衡访问业务接口,返回正常才算完成。
四、验证方法
完成后的状态应当同时满足以下四条,缺一不可:
kubectl get pods -n <namespace>里目标 Pod 的 STATUS 是 Running,READY 是 1/1 或 2/2。- 观察 3 分钟以上,RESTARTS 不再增长。
kubectl get events -n <namespace> --sort-by=.lastTimestamp里不再出现新的 FailedScheduling、ImagePullBackOff、BackOff 事件。- 通过 Service 或负载均衡访问业务接口一次,返回符合预期的结果。
五、常见坑
- 只删 Pod 不改根因。Deployment 会按模板重建一个一模一样的 Pod,问题原样复现。
- 同时改镜像、资源、探针三个变量。改完好了也不知道是哪一个起的作用,下次还会踩同一个坑。
- 探针地址只在本地验证过。容器内的监听地址、端口、路径只要有一处不同,探针就会误判。
- 把 requests 和 limits 混为一谈。requests 决定能不能调度上去,limits 决定会不会被杀掉,两者要分别配。
- 忽略事件的时间。Events 只保留一段时间,排障时先看最新事件,不要拿昨天的事件套今天的问题。
- 私有镜像凭证过期。镜像地址没变,突然拉取失败时,先核对镜像凭证的有效期和命名空间里的 Secret 是否还在。
六、小结清单
- Pending:先读 Events,再看节点余量、requests、节点选择器与污点,最后看 PVC 与镜像拉取。
- CrashLoopBackOff:先看本次与上一次日志,再看退出码与镜像、启动命令,最后看探针与内存限制。
- 探针:initialDelaySeconds 要大于应用实测启动时间,探针地址要在容器内用 curl 亲手验证。
- 改动一次只动一个变量,改完用 rollout status 与 3 分钟观察确认,业务访问正常才算完成。
只要按“调度、镜像、探针”三层顺序走,大多数 Pending 与 CrashLoopBackOff 都能在半小时内定位到具体原因,而不是靠反复重启碰运气。
- 点赞
- 收藏
- 关注作者
评论(0)