PersistentVolumeClaim 把存储的生命周期与重建的 Pod 分开,但不保证一定能恢复数据。PersistentVolume 描述存储,声明请求存储,由控制器安排绑定或供应。真正的恢复仍取决于后备存储、可用拓扑与备份;有了 StorageClass,可以由 provisioner 动态供应,而不必预先建好每块卷。
声明与卷各自有独立的生命周期。finalizer 可以推迟对象删除,回收策略决定释放后的存储如何处理。维护前应核对两者:处理失误可能留下持续 Terminating 的声明、删掉本想保留的数据,或留下一份没人在用却仍在计费的存储。
声明、卷与绑定
普通匹配会检查容量、访问模式、存储类、volumeMode、选择器及既有归属。不匹配的声明可以停在 Pending,应检查事件与 StorageClass,不能只猜一个原因。绑定不证明拓扑可用:显式指定 PV 时,即使节点亲和性冲突也可能绑定成功,后续调度与挂载仍需各自成功。已释放但尚未回收的 Retain 卷也不会自动成为新声明的可用备件;旧 claimRef 可能仍然存在。
accessModes 没有默认值。ReadWriteOnce 允许单节点读写挂载,该节点上的多个 Pod 仍可能共用它,不是单 Pod 独占。ReadWriteOncePod 才为受支持的 CSI 卷提供单 Pod 约束;访问模式本身并不在挂载后执行写保护。Immediate 不等待消费者就开始绑定或供应,受拓扑限制的存储可能因此无法使用。受支持的 WaitForFirstConsumer 则结合消费 Pod 的调度约束协调存储,之后才完成放置;它不是先完成调度再随意创建卷。
删除顺序与 finalizer
通常先停用 Pod,再处理声明与卷。删除仍在使用的声明会记录 deletionTimestamp,但 kubernetes.io/pvc-protection 可以推迟对象消失。Kubernetes v1.34.0 对普通 PVC 引用的检查只计入已经分配节点的 Pod;仅有未调度 Pod 引用,并不会维持这项保护。这是控制器的引用检查,不是实时挂载探测。因此,持续 Terminating 的声明需要检查已调度消费者、控制器进度和其他 finalizer,不能仅凭状态推断唯一原因。
反方向也有同样的保护。PersistentVolume 带着 kubernetes.io/pv-protection,所以删除一块已绑定到声明的卷会被推迟到它不再被绑定为止。这个顺序只是延迟误删除,并不代表可以撤销已经设置的 deletionTimestamp,也解释了为什么只要声明还在,卷看起来就会无视删除命令。
回收策略:Delete 还是 Retain
声明最终消失后,卷被释放,回收策略接手。存储插件支持时,Delete 异步回收 PV 与后备资产,不保证两者立即或原子地消失。Retain 保留 PV 对象、把它标成 Released,并且刻意不让它被新的声明使用,因为上一个占用者的数据还在上面——回收于是变成一串手工动作:确认归属与备份,再按存储提供方流程有意回收、复用或删除资产,而不是把删除写成唯一结局。
动态供应的卷会继承其 StorageClass 的策略,而后者默认是 Delete。这个默认是否适合数据库,取决于保留和恢复要求,而且它容易被忽略:PVC 与 Pod 清单里都没有一个字提到策略,决定是几个月前在一个集群级对象里做出的,审阅工作负载的人从来看不到。所以在评审一份有状态工作负载时,回收策略应当和容量、访问模式放在一起看。
坏掉的样子
最经典的是删不掉的声明。有人删了 PVC,看到 Terminating,然后开始等——而某个 Pod 仍然挂着它,通常是没缩容到零的 StatefulSet,或者卡在不可达节点上的 Pod。没有报错,没有超时,而这份 PVC 一直占着背后的存储。正确做法是找到那个 Pod,而不是手工删 finalizer;绕过保护可能让仍被工作负载使用的存储过早进入删除流程,并非每个提供程序都会立即执行相同的分离动作。
第二种是 Retain 留下的孤儿。声明没了,Pod 没了,命名空间可能也没了,一块 Released 的 PV 和它的存储资产仍等待有意回收。保留可能本来就符合要求;如果没有资产盘点告警,就容易被遗漏,任何工作负载的清单里也看不到它。反向的错误同样安静:某个类的默认 Delete 策略从未被覆盖,等到有人想找回数据时才发现回收早已把它删掉了。
快速验证
同时看绑定的两侧,以及是谁占着这份声明:
kubectl get pvc -A -o custom-columns=NS:.metadata.namespace,NAME:.metadata.name,STATUS:.status.phase,VOLUME:.spec.volumeName,CLASS:.spec.storageClassName
kubectl get pv -o custom-columns=NAME:.metadata.name,STATUS:.status.phase,POLICY:.spec.persistentVolumeReclaimPolicy,CLAIM:.spec.claimRef.name
kubectl get pvc data -o jsonpath='{.metadata.finalizers}{"\n"}'
kubectl get pods -A -o json | jq -r '.items[] | select(.spec.volumes[]?.persistentVolumeClaim.claimName=="data") | "\(.metadata.namespace)/\(.metadata.name)"'
这些保留的诊断本轮没有执行。保护标记说明清理待完成,不证明控制器健康,也不证明一定还有 Pod。第四条查找所有命名空间内名为 data 的声明,因此必须先核对目标命名空间,再确认某个 Pod 是否是消费者。Released 加 Retain 需要归属与恢复决策,不代表应自动删除;旧 claimRef 也可能仍然存在。
来源
- Persistent Volumes,绑定、访问模式、回收策略、
Released状态与 pvc/pv 保护 finalizer,已核验 2026-10-15。 - Storage Classes,动态供应、
reclaimPolicy与volumeBindingMode,已核验 2026-10-15。 - Persistent Volumes (v1.34),v1.34 预绑定、访问模式边界与受支持的回收行为,已核验 2026-10-18。
- PVC protection implementation,v1.34.0 podUsesPVC 的节点分配及显式引用检查,已核验 2026-10-18。