<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>持久卷 · XB 现场笔记</title><link>https://hk.xbcatm.com/zh/tags/%E6%8C%81%E4%B9%85%E5%8D%B7/</link><description>基于 HTTP、DNS、TLS、Caddy、SSH、发布与恢复的可复现运维现场笔记。</description><generator>Hugo</generator><language>zh-CN</language><atom:link href="https://hk.xbcatm.com/zh/tags/%E6%8C%81%E4%B9%85%E5%8D%B7/index.xml" rel="self" type="application/rss+xml"/><item><title>PVC 为 Pod 提供容器之外的持久存储</title><link>https://hk.xbcatm.com/zh/posts/kubernetes-pvc-storage-lifecycle/</link><pubDate>Fri, 13 Aug 2027 00:00:00 +0000</pubDate><guid>https://hk.xbcatm.com/zh/posts/kubernetes-pvc-storage-lifecycle/</guid><description>持久卷声明绑定后备卷并跨 Pod 重启存续，恢复仍取决于后备存储和拓扑。</description><content:encoded>&lt;p&gt;PersistentVolumeClaim 把存储的生命周期与重建的 Pod 分开，但不保证一定能恢复数据。PersistentVolume 描述存储，声明请求存储，由控制器安排绑定或供应。真正的恢复仍取决于后备存储、可用拓扑与备份；有了 StorageClass，可以由 provisioner 动态供应，而不必预先建好每块卷。&lt;/p&gt;
&lt;p&gt;声明与卷各自有独立的生命周期。finalizer 可以推迟对象删除，回收策略决定释放后的存储如何处理。维护前应核对两者：处理失误可能留下持续 &lt;code&gt;Terminating&lt;/code&gt; 的声明、删掉本想保留的数据，或留下一份没人在用却仍在计费的存储。&lt;/p&gt;
&lt;figure class="technical-diagram"&gt;&lt;img loading="lazy" src="https://hk.xbcatm.com/visuals/posts-kubernetes-pvc-storage-lifecycle.detail.zh.svg" alt="Pod、声明与卷是独立对象。绑定要求各项条件匹配。保护机制可能推迟删除；声明删除后，受支持插件按 Delete 策略异步回收 PV 与存储，Retain 留下 Released 存储等待有意回收。" width="560" height="2960"&gt;&lt;figcaption&gt;保护机制推迟删除，不证明控制器健康。Retain 需要核对归属、备份并按提供方流程决策。保留诊断仍受阻，本轮未重跑。&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id="声明卷与绑定"&gt;声明、卷与绑定&lt;/h2&gt;
&lt;p&gt;普通匹配会检查容量、访问模式、存储类、volumeMode、选择器及既有归属。不匹配的声明可以停在 Pending，应检查事件与 StorageClass，不能只猜一个原因。绑定不证明拓扑可用：显式指定 PV 时，即使节点亲和性冲突也可能绑定成功，后续调度与挂载仍需各自成功。已释放但尚未回收的 Retain 卷也不会自动成为新声明的可用备件；旧 claimRef 可能仍然存在。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;accessModes&lt;/code&gt; 没有默认值。&lt;code&gt;ReadWriteOnce&lt;/code&gt; 允许单节点读写挂载，该节点上的多个 Pod 仍可能共用它，不是单 Pod 独占。&lt;code&gt;ReadWriteOncePod&lt;/code&gt; 才为受支持的 CSI 卷提供单 Pod 约束；访问模式本身并不在挂载后执行写保护。&lt;code&gt;Immediate&lt;/code&gt; 不等待消费者就开始绑定或供应，受拓扑限制的存储可能因此无法使用。受支持的 &lt;code&gt;WaitForFirstConsumer&lt;/code&gt; 则结合消费 Pod 的调度约束协调存储，之后才完成放置；它不是先完成调度再随意创建卷。&lt;/p&gt;
&lt;h2 id="删除顺序与-finalizer"&gt;删除顺序与 finalizer&lt;/h2&gt;
&lt;p&gt;通常先停用 Pod，再处理声明与卷。删除仍在使用的声明会记录 deletionTimestamp，但 &lt;code&gt;kubernetes.io/pvc-protection&lt;/code&gt; 可以推迟对象消失。Kubernetes v1.34.0 对普通 PVC 引用的检查只计入已经分配节点的 Pod；仅有未调度 Pod 引用，并不会维持这项保护。这是控制器的引用检查，不是实时挂载探测。因此，持续 Terminating 的声明需要检查已调度消费者、控制器进度和其他 finalizer，不能仅凭状态推断唯一原因。&lt;/p&gt;
&lt;p&gt;反方向也有同样的保护。&lt;code&gt;PersistentVolume&lt;/code&gt; 带着 &lt;code&gt;kubernetes.io/pv-protection&lt;/code&gt;，所以删除一块已绑定到声明的卷会被推迟到它不再被绑定为止。这个顺序只是延迟误删除，并不代表可以撤销已经设置的 deletionTimestamp，也解释了为什么只要声明还在，卷看起来就会无视删除命令。&lt;/p&gt;
&lt;h2 id="回收策略delete-还是-retain"&gt;回收策略：Delete 还是 Retain&lt;/h2&gt;
&lt;p&gt;声明最终消失后，卷被释放，回收策略接手。存储插件支持时，&lt;code&gt;Delete&lt;/code&gt; 异步回收 PV 与后备资产，不保证两者立即或原子地消失。&lt;code&gt;Retain&lt;/code&gt; 保留 PV 对象、把它标成 &lt;code&gt;Released&lt;/code&gt;，并且刻意不让它被新的声明使用，因为上一个占用者的数据还在上面——回收于是变成一串手工动作：确认归属与备份，再按存储提供方流程有意回收、复用或删除资产，而不是把删除写成唯一结局。&lt;/p&gt;
&lt;p&gt;动态供应的卷会继承其 StorageClass 的策略，而后者默认是 &lt;code&gt;Delete&lt;/code&gt;。这个默认是否适合数据库，取决于保留和恢复要求，而且它容易被忽略：PVC 与 Pod 清单里都没有一个字提到策略，决定是几个月前在一个集群级对象里做出的，审阅工作负载的人从来看不到。所以在评审一份有状态工作负载时，回收策略应当和容量、访问模式放在一起看。&lt;/p&gt;
&lt;h2 id="坏掉的样子"&gt;坏掉的样子&lt;/h2&gt;
&lt;p&gt;最经典的是删不掉的声明。有人删了 PVC，看到 &lt;code&gt;Terminating&lt;/code&gt;，然后开始等——而某个 Pod 仍然挂着它，通常是没缩容到零的 StatefulSet，或者卡在不可达节点上的 Pod。没有报错，没有超时，而这份 PVC 一直占着背后的存储。正确做法是找到那个 Pod，而不是手工删 finalizer；绕过保护可能让仍被工作负载使用的存储过早进入删除流程，并非每个提供程序都会立即执行相同的分离动作。&lt;/p&gt;
&lt;p&gt;第二种是 Retain 留下的孤儿。声明没了，Pod 没了，命名空间可能也没了，一块 &lt;code&gt;Released&lt;/code&gt; 的 PV 和它的存储资产仍等待有意回收。保留可能本来就符合要求；如果没有资产盘点告警，就容易被遗漏，任何工作负载的清单里也看不到它。反向的错误同样安静：某个类的默认 &lt;code&gt;Delete&lt;/code&gt; 策略从未被覆盖，等到有人想找回数据时才发现回收早已把它删掉了。&lt;/p&gt;
&lt;h2 id="快速验证"&gt;快速验证&lt;/h2&gt;
&lt;p&gt;同时看绑定的两侧，以及是谁占着这份声明：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;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=&amp;#39;{.metadata.finalizers}{&amp;#34;\n&amp;#34;}&amp;#39;
kubectl get pods -A -o json | jq -r &amp;#39;.items[] | select(.spec.volumes[]?.persistentVolumeClaim.claimName==&amp;#34;data&amp;#34;) | &amp;#34;\(.metadata.namespace)/\(.metadata.name)&amp;#34;&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这些保留的诊断本轮没有执行。保护标记说明清理待完成，不证明控制器健康，也不证明一定还有 Pod。第四条查找所有命名空间内名为 data 的声明，因此必须先核对目标命名空间，再确认某个 Pod 是否是消费者。Released 加 Retain 需要归属与恢复决策，不代表应自动删除；旧 claimRef 也可能仍然存在。&lt;/p&gt;
&lt;h2 id="来源"&gt;来源&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/concepts/storage/persistent-volumes/"&gt;Persistent Volumes&lt;/a&gt;，绑定、访问模式、回收策略、&lt;code&gt;Released&lt;/code&gt; 状态与 pvc/pv 保护 finalizer，已核验 2026-10-15。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/concepts/storage/storage-classes/"&gt;Storage Classes&lt;/a&gt;，动态供应、&lt;code&gt;reclaimPolicy&lt;/code&gt; 与 &lt;code&gt;volumeBindingMode&lt;/code&gt;，已核验 2026-10-15。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://v1-34.docs.kubernetes.io/docs/concepts/storage/persistent-volumes/"&gt;Persistent Volumes (v1.34)&lt;/a&gt;，v1.34 预绑定、访问模式边界与受支持的回收行为，已核验 2026-10-18。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://raw.githubusercontent.com/kubernetes/kubernetes/v1.34.0/pkg/controller/volume/pvcprotection/pvc_protection_controller.go"&gt;PVC protection implementation&lt;/a&gt;，v1.34.0 podUsesPVC 的节点分配及显式引用检查，已核验 2026-10-18。&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>