PVC 卡在 Pending 是很多运维同学排查 Kubernetes 存储问题时最先碰到的拦路虎。表面上看只是一个状态字段没变化,背后却牵扯到 StorageClass、动态供给控制器、云存储后端、CSI 驱动以及节点 kubelet 等一整条链路,任何一环出问题都会让 PVC 停在 Pending。本文按实际排查顺序,把这条链路拆开讲清楚。

先弄懂 PVC 从创建到绑定的完整流程
一个 PVC 要变成 Bound 状态,通常有两条路。第一条是动态供给:PVC 里声明了 storageClassName,对应的 StorageClass 又配有 provisioner,控制器监听到这个 PVC 后会调用云厂商接口或 CSI 插件创建一块真实存储,自动生成一个 PV 并和 PVC 绑定。第二条是静态绑定:集群管理员预先创建了若干 PV,Kubernetes 的卷控制器会按照容量、访问模式、StorageClass 名字等条件做匹配,找到合适的 PV 就绑定。
理解这个流程之后,Pending 的排查思路就很清晰了:要么是动态供给这条路没走通,要么是静态匹配没找到对象,要么是 PV 创建出来了但节点挂载阶段失败又被回退。排查的第一步永远是看事件:
kubectl get pvc my-pvc -o yaml kubectl describe pvc my-pvc kubectl get sc kubectl get pv
kubectl describe 输出底部的 Events 部分是关键,绝大多数情况下故障原因会直接写在事件里,比如 no persistent volumes available、storageclass.storage.k8s.io not found 或者 Failed to provision volume。先看事件再动手,能少走很多弯路。
场景一:动态供给失败导致 PVC Pending
这是生产环境最常见的类型。典型的事件信息是 failed to provision volume with StorageClass xxx,后面跟着具体报错。排查时先确认几件事:PVC 里写的 StorageClass 名字是否真的存在,注意大小写和连字符;该 StorageClass 的 provisioner 对应的控制器是否在正常运行;云账号是否有足够权限和配额去创建磁盘。
比如在自建集群里使用 local-path 或 NFS 类 provisioner 时,控制器 Pod 本身可能没装或者崩了,可以查一下:
kubectl get pods -n kube-system | grep provisioner kubectl logs -n kube-system deploy/local-path-provisioner
在云托管集群(如 ACK、TKE、EKS)中,更多见的是后端限制问题:可用区没有库存、单账号磁盘数量达到上限、或者磁盘类型在目标可用区不支持。这类报错会出现在 controller-manager 或 CSI 控制器的日志里,事件描述通常包含 Quota、Insufficient、NotAvailable 之类的字样,按提示换可用区、换磁盘类型或者提工单释放配额即可。
另一个容易忽略的坑是 StorageClass 的 volumeBindingMode。如果设置为 WaitForFirstConsumer,PVC 在没有 Pod 使用它之前就会一直保持 Pending,这是设计如此而非故障。新手常误以为出了问题,实际上只要创建一个引用该 PVC 的 Pod,绑定流程才会被触发。建议在排查前先确认这个字段:
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: fast-ssd provisioner: diskplugin.csi.alibabacloud.com parameters: type: cloud_essd volumeBindingMode: WaitForFirstConsumer reclaimPolicy: Delete
场景二:静态 PV 匹配不上
如果集群没有用动态供给,而是手工创建 PV,那 Pending 的原因基本就是匹配条件不满足。Kubernetes 匹配 PV 时会检查三点:容量要大于等于 PVC 的请求值;访问模式要兼容;如果 PVC 指定了 storageClassName,PV 的字段必须完全一致,PV 里没写则视为空字符串,和 PVC 指定的名字对不上也不会绑定。
事件里出现 no persistent volumes available for this claim and no storage class is set 时,先核对 PV 是否处于 Available 状态。已经被其他 PVC 绑定或者处于 Released、Failed 状态的 PV 不会参与匹配。可以用下面的命令快速核对:
kubectl get pv -o custom-columns=NAME:.metadata.name,CAPACITY:.spec.capacity.storage,MODES:.spec.accessModes,SC:.spec.storageClassName,STATUS:.status.phase
还有一个隐蔽的坑:手动创建 PV 时忘了设置 persistentVolumeReclaimPolicy,默认是 Retain,PV 被释放后会一直停在 Released,即使删除对应 PVC 也不会自动复用,需要管理员手动清理后重建。此外,如果 PV 用了 claimRef 指向某个已删除的 PVC,也会卡在 Released 状态,删掉 claimRef 或者重建 PV 才能恢复。
场景三:PVC 已 Bound 但 Pod 挂载失败又被误判为 Pending
有一类情况容易被误诊:PVC 其实已经 Bound,但 Pod 一直 ContainerCreating,仔细看会发现事件里有 FailedAttachVolume 或 FailedMount。这已经不属于 PVC Pending,而是节点挂载阶段的问题,但很多人把两者混在一起排查。
这类问题的常见原因包括:节点没有安装对应的 CSI Node 组件(比如用了云盘类型的 StorageClass 但某些节点上 CSI Node Pod 没调度上去);PV 的拓扑限制导致 Pod 无法调度到能访问该存储的节点;文件系统损坏导致挂载命令报错。排查时看两处日志:
kubectl describe pod my-pod | grep -A 10 Events kubectl get pods -n kube-system -o wide | grep csi journalctl -u kubelet --since "10 min ago" | grep -i volume
kubelet 日志里的 MapVolume.MapPodVolume failed 一般会带出具体错误,比如格式化失败、设备路径不存在、权限拒绝等。云盘场景下还要注意可用区问题:云盘通常只能挂载到同可用区的节点,如果 Pod 被调度到别的可用区,就会出现反复挂载失败。解决办法是给 PV 打上拓扑标签,或者让工作负载通过 nodeSelector、podAffinity 约束调度范围。
一套实用的排查顺序总结
把上面的内容整理成固定的排查顺序,遇到问题按步骤走即可:第一步 kubectl describe pvc 看事件,确定是供给失败还是匹配失败;第二步检查 StorageClass 存在性、provisioner 状态和 volumeBindingMode;第三步如果是静态绑定,核对 PV 的容量、访问模式、状态和 reclaimPolicy;第四步如果 PVC 已 Bound,转向排查节点侧的 CSI 驱动和 kubelet 日志;第五步针对云存储,确认可用区、配额和权限。
日常预防方面有几点建议:给所有 StorageClass 显式声明 volumeBindingMode,避免误解;给 PVC 加上合理的容量规划,避免频繁触发配额上限;在监控里对 Pending 超过一定时间的 PVC 做告警,通常超过五分钟还没绑定就值得人工介入了。把这些环节理顺之后,绝大多数 PVC Pending 问题都能在几分钟内定位到根因。
Kubernetes PVC PendingPersistentVolumeClaim 排查StorageClass 动态供给修改时间:2026-09-08 16:51:15