bigywater.tech
Kubernetes · 57

PV 与 PVC 绑定:匹配规则

PVC 是怎么找到 PV 的?为什么有的 PVC 一直 Pending?PVC 删了数据去哪了?这篇先用手工建的静态 PV 把配对规则逐条验证,再看动态供给的 WaitForFirstConsumer 和 Delete 回收策略。

5Gi
请求 3Gi 的 PVC 在 2Gi、5Gi、10Gi 三个 PV 里被绑定的那个(最小够用)
Released
reclaimPolicy: Retain 的 PV 在 PVC 删除后的状态:claimRef 还在,别人拿不走
WaitForFirstConsumer
动态供给的 PVC 在没有 Pod 使用时的事件:waiting for first consumer

配对规则

实测 · 本机执行输出
  三个 PV(存储类 manual,RWO):pv-10g=10Gi/Available; pv-2g=2Gi/Available; pv-5g=5Gi/Available
  PVC c3g 请求 3Gi:phase=Bound;绑定到 k51-pv-5g(容量 5Gi);pv-10g=10Gi/Available; pv-2g=2Gi/Available; pv-5g=5Gi/Bound→c3g
  PVC c20g 请求 20Gi(没有这么大的 PV):phase=Pending;事件:['storageclass.storage.k8s.io "manual" not found']
  PVC crwx 请求 RWX(现有 PV 都只有 RWO):phase=Pending
  PVC cclass 的 storageClassName 是 other(PV 都是 manual):phase=Pending
  现在加一个 RWX 的 PV 之后 7.4 秒:crwx phase=Bound → k51-pv-rwx(Pending 的 PVC 在有匹配的 PV 出现后自动绑定,PV 控制器是周期性同步的,所以有延迟)

PV 控制器把 PVC 和 PV 配对看四件事:容量够、访问模式够、storageClassName 相同、(如果写了)selector 匹配。三个 PV 2Gi/5Gi/10Gi,请求 3Gi 的 PVC 拿到 5Gi 的——候选里最小的。请求 20Gi、ReadWriteMany(现有 PV 只有 RWO)或不同存储类的 PVC 都保持 Pending;新增一个匹配的 PV 之后 7.4 秒,Pending 的 PVC 自动绑上去(PV 控制器是周期性同步的)。

selector 与预留

实测 · 本机执行输出
  PVC want-gold(selector tier=gold):绑定 k51-pv-labeled
  PVC plain-small(没有 selector,请求 1Gi):绑定 k51-pv-2g
  PV k51-pv-reserved 预先写了 claimRef(只给 k51/reserved-for-me),此时集群里没有别的可用 PV:另一个 PVC someone-else 请求 500Mi → phase=Pending(PV 被预留了,别人拿不走);pv-reserved=1Gi/Available→reserved-for-me
  PVC reserved-for-me 请求 500Mi → phase=Bound k51-pv-reserved

PVC 的 selector.matchLabels 只选带这些标签的 PV(tier=gold 的那个);没有 selector 的 1Gi PVC 拿了 2Gi 的最小 PV。PV 上可以预先写 claimRef,把它只留给指定的 PVC:集群里别的 PV 都用掉后,另一个 PVC 想要它,保持 Pending;预留的 PVC 一创建就绑定。

删除之后

实测 · 本机执行输出
  persistentVolumeReclaimPolicy=Retain 的静态 PV:删除 PVC 之后 phase=Released;spec.claimRef 还在:c1(数据和绑定记录都保留着)
  新建 PVC c2(1Gi):phase=Pending(Released 的 PV 不会被别人拿去用)
  手动去掉 PV 的 claimRef 之后 4.5 秒:c2 phase=Bound;PV phase=Bound(Released → Available → Bound)

persistentVolumeReclaimPolicy: Retain 的静态 PV,删掉 PVC 后变成 Released,spec.claimRef 还指着已不存在的 PVC——数据和绑定记录都保留,新 PVC 拿不走它(c2 一直 Pending)。要重新使用得手动把 claimRef 去掉,4.5 秒后 c2 就绑定了。动态供给的 StorageClass 默认是 Delete:PVC 一删,PV 和底层数据都没了。

动态供给

实测 · 本机执行输出
  动态供给的 StorageClass standard:reclaimPolicy=Delete volumeBindingMode=WaitForFirstConsumer provisioner=rancher.io/local-path
  动态 PVC dyn 创建后立刻 phase=Pending(WaitForFirstConsumer:没有 Pod 用它就不供给);事件:['WaitForFirstConsumer: waiting for first consumer to be created before binding']
  创建用它的 Pod 之后:dyn phase=Bound,自动创建了 PV pvc-7e8d30e7-d16…,所在节点 ['kx-sched-worker3']
  删除 PVC dyn 之后:PV pvc-7e8d30e7-d16… 还在吗:False(reclaimPolicy=Delete:PV 随 PVC 一起被删除)

kind 的 standard 存储类用 rancher.io/local-path、volumeBindingMode: WaitForFirstConsumer:PVC 创建后保持 Pending,事件 waiting for first consumer to be created before binding;创建了使用它的 Pod 之后才供给——自动建出的 PV 带节点亲和,落在 Pod 被调度到的那个节点上(worker3);之后 Pod 只能回到这个节点。删除 PVC,PV 随之被删除(reclaimPolicy: Delete)。

分步演示

PV 与 PVC 的配对
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / kind(Docker 里的单节点或多节点真实集群,节点是 Docker Desktop 虚拟机里的容器)/ kubectl;用空的 kubeconfig 之外的独立 kubeconfig,不碰用户自己的集群;计时用 date +%s%N / kubectl 事件时间戳,数字每次运行会有小幅抖动。独立的 5 节点 kind 集群 kx-sched(Kubernetes v1.37.0);静态 PV 用 hostPath(/tmp/<名字>,节点内),storageClassName 写的是 manual(集群里并没有这个 StorageClass 对象,PVC 的事件因此是 storageclass … "manual" not found,但静态绑定不受影响);动态供给用 kind 自带的 standard(local-path-provisioner);第 1 节新增 PV 后的绑定延迟与第 3 节去掉 claimRef 后的绑定延迟取自 0.5 秒间隔轮询(另一次运行里 4 秒还没绑定,所以不要假设是即时的);实验后删除了所有 PV 与命名空间;每种情形各一次
    • 没有验证:Block 模式卷;PV 的 nodeAffinity 与调度;volumeName 预绑定(PVC 直接指定 PV);PV/PVC 保护 finalizer 与删除顺序(见 Finalizer 一篇);存储类的 allowVolumeExpansion(下一篇之后);跨 StorageClass 的默认存储类(没有写 storageClassName 时使用默认类);CSI 驱动的供给流程
    京ICP备2026067502号-1京公网安备11010502063487号