导语:Pod 卡在 ContainerCreating,一看是 PVC 一直 Pending?别急着删了重建。五步定位:看事件分形态 → 核对 StorageClass → 查空闲 PV → 查供应方日志 → 验证挂载条件,每步都有能当场执行的命令。
部署一个带持久化的应用,Pod 卡在 ContainerCreating 不动,进去一看:kubectl get pvc 里 PVC 的 STATUS 一直是 Pending。这是 K8s 存储问题里最常见的入口故障。很多人第一反应是把 PVC 删了重建,结果有时候好了,有时候越建越多------因为 Pending 的原因有好几种,不动手看事件就重建,纯属碰运气。
正确的做法是按层拆:先看事件分清形态,再核对 StorageClass,再查空闲 PV,再看供应方日志,最后验证挂载条件。五步走完,绝大多数 Pending 都能落到具体原因上。
第 1 步:看事件,先把 Pending 分成四类
PVC 为什么 Pending,答案就写在它自己的 Events 里:
bash
kubectl describe pvc mydata -n prod
重点看 Events 段的原文,常见四类:
- 没等到消费方 :
waiting for a volume to be created, either by external provisioner "xxx" or manually created by system administrator------供应方没响应或者没有供应方。 - 延迟绑定 :
waiting for pod xxx to be scheduled------这不是故障,是volumeBindingMode: WaitForFirstConsumer在等 Pod 先调度。 - SC 不存在 :
storageclass.storage.k8s.io "xxx" not found------PVC 里写的存储类名字不存在。 - 匹配不上 :
no persistent volumes available for this claim and no storage class is set------没有现成 PV 能接住这个申请。
先把形态分清,后面查的方向完全不同:第 2 类不用修,把 Pod 的调度问题解决掉它自己会绑定;第 1、3、4 类才需要往下查。
一个容易忽略的点:PVC 的 Events 只保留最近的,新建 PVC 事件很少时,把 describe 输出拉到最底下看,或者用 kubectl get events -n prod --sort-by=.lastTimestamp | grep mydata 过滤一遍。
第 2 步:核对 StorageClass,名字和默认标注
第 3 类「SC not found」最简单也最高频------PVC 里 storageClassName 拼错了。先看集群里有哪些存储类:
bash
kubectl get sc
kubectl get sc -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.provisioner}{"\t"}{.volumeBindingMode}{"\n"}{end}'
逐项核对三件事:
- 名字对不对 :PVC 的
storageClassName与get sc的名字逐字符比对,fast-ssd和fast-ss差一个字符就报 not found。 - 默认存储类在不在 :看有没有标了
storageclass.kubernetes.io/is-default-class: "true"的 SC。PVC 不写storageClassName时会自动用它;如果默认 SC 被删了或没人标,不写名字的 PVC 就会永远 Pending。 - 绑定模式 :
volumeBindingMode是Immediate还是WaitForFirstConsumer。前者创建 PVC 立刻绑,后者要等 Pod 调度到某个节点才在对应可用区/节点供卷。多可用区集群强烈建议后者,否则跨区调度会挂载失败。
改名字直接 kubectl edit pvc 是不行的------PVC 的 storageClassName 字段不可变,只能删了重建(数据没绑定就不存在丢数据问题)。
第 3 步:查空闲 PV,能不能接住这个申请
如果 PVC 没写 storageClassName(或写了但想手动绑定),就要看有没有现成 PV 能匹配:
bash
kubectl get pv
kubectl get pv -o custom-columns=NAME:.metadata.name,CLAIM:.spec.claimRef.name,STATUS:.status.phase,CAP:.spec.capacity.storage,MODES:.spec.accessModes
逐列核对:
- 状态 :只有
Available的 PV 能被新 PVC 绑定。Bound已被占用;Released已被旧 claim 释放但数据还在,默认策略下不能复用 ,必须手动清掉spec.claimRef才会回到 Available。 - 容量:PV 的 capacity 必须 ≥ PVC 的 requests。申请 20Gi、PV 只有 10Gi,永远不会绑。
- AccessModes :PVC 要
ReadWriteMany、PV 只有RWO,匹配不上。 - StorageClassName :手动绑定也要求两者的
storageClassName一致(或都是空)。
Released 的 PV 想复用,编辑 PV 删掉 spec.claimRef 字段即可;如果 reclaimPolicy 是 Delete,PV 会直接被删掉,别指望它留着。
第 4 步:查供应方,让 provisioner 自己开口
第 1 类「等外部供应方」------PVC 挂在了一个 SC 上,但 SC 对应的 provisioner 没干活。分两看:
先看 SC 的 provisioner 指向谁,再确认这个供应方在集群里活着:
bash
kubectl get sc fast-ssd -o jsonpath='{.provisioner}'
kubectl get pods -A | grep -iE 'provisioner|csi'
- 内置 provisioner(如 hostPath、local-path)由 controller-manager 处理,失败信息直接出现在 PVC Events 里,一般是节点上路径不存在、权限不足。
- 外部 provisioner(nfs-subdir-external-provisioner、CSI 驱动、Harbor 之后的 ceph-csi 等)是独立 Pod,要看它自己的日志:
bash
kubectl logs -f -n kube-system <provisioner-pod> | grep -i mydata
高频失败原因:NFS 服务端不通或导出目录没建 、CSI 驱动的 node plugin 没起来 (kubectl get pods -A -o wide 看是否缺节点)、存储后端配额打满或磁盘满 、SC 的 parameters 写错(比如 ceph 的 clusterID 与 CephCluster 对不上)。报错原文几乎都会点名具体原因,比猜快十倍。
第 5 步:验证挂载条件,绑上了≠能用了
STATUS 变成 Bound 不算完,Pod 经常还是起不来,卡在 ContainerCreating。这时候查三个挂载条件:
bash
kubectl get pv <pv-name> -o jsonpath='{.spec.nodeAffinity}'
kubectl describe pod <pod-name> | grep -A 5 -i mount
- 节点亲和:local PV / WaitForFirstConsumer 绑定的卷带 nodeAffinity,只允许调度到特定节点。Pod 打了节点选择器却选不到那些节点,就会卡住。
- 挂载报错 :Events 里
MountVolume.SetUp failed一类报错,通常是 NFS 服务端拒连、路径不存在、或文件系统类型不支持(节点缺nfs-common就会挂载超时)。 - 权限 :容器内非 root 用户写不了挂载点,补
securityContext.fsGroup;ext4/xfs 的 local PV 还要注意目录属主。
一图总结
- 第一步看事件:describe pvc 把 Pending 分四类,延迟绑定不算故障
- 第二步核 SC:名字逐字符对,默认标注与绑定模式两查
- 第三步找 PV:Available + 容量 + AccessModes 三匹配,Released 要清 claimRef
- 第四步问供应方:provisioner 日志点名报错,配额与参数细核对
- 第五步验挂载:nodeAffinity、Mount 报错、fsGroup 权限
避坑清单
- PVC 的
storageClassName、spec.resources.requests.storage创建后不可改,要改只能删了重建------先确认没 Bound 再删。 - 删 PVC 之前想清楚 reclaimPolicy:
Delete会连 PV 带数据一起删,生产环境重要数据一律Retain。 Released状态的 PV 不能直接复用,手动清spec.claimRef才会回 Available;Delete策略下 PV 已被删。- 不写
storageClassName依赖默认 SC 时,删 SC 前先确认没有 PVC 在等它,否则一批 PVC 集体 Pending。 - WaitForFirstConsumer 模式下 PVC 一直「等 Pod」是正常的,先修 Pod 调度(资源不足、亲和冲突、污点),别去动存储。
- 节点挂 NFS 卷必须装
nfs-common(Debian 系)/nfs-utils(RHEL 系),缺包的表现就是 mount 超时,错误信息里却看不出是缺包。 - 排查 provisioner 时先看它的 Pod 是不是真的在跑:CSI 驱动是 DaemonSet + Deployment 双件套,node plugin 缺节点是隐蔽故障源。
小结
PVC Pending 之所以让人烦躁,是因为「卷没造出来」「卷造了绑不上」「绑上了挂不了」三类完全不同的问题共用一个状态。先用 describe 的 Events 分清形态,再沿「SC → PV → provisioner → 挂载条件」这条链逐层核对,每一步都有明确的验证命令。记住一条:删了重建只是重掷骰子,Events 里的报错原文才是答案。