Kubernetes 基础体系 · 第 36/83 篇。示例基于 Kubernetes 当前稳定 API;弃用、版本偏差、云厂商差异和生产风险会明确说明。
Kubernetes PV 与 PVC:绑定、访问模式、回收策略和生命周期
在 Kubernetes 中,容器的生命周期通常很短,但有状态应用的数据不能随着 Pod 删除而消失。PersistentVolume(PV)和 PersistentVolumeClaim(PVC)将“存储资源”与“应用对存储的需求”分离:
- PV 是集群中的存储资源对象,描述一块已经存在或将要创建的持久化存储。
- PVC 是用户对存储的请求,描述需要多大容量、什么访问模式、什么卷格式等。
- StorageClass 描述动态创建 PV 的方式,例如由哪个 CSI Provisioner 创建云盘、网络文件系统或本地卷。
PV/PVC 不是数据本身,而是 Kubernetes 对外部存储以及使用关系的声明。真正的数据位于云盘、文件系统、SAN、NAS、本地磁盘等后端。
一、先区分四个对象:Pod、PVC、PV 和后端存储
一个典型关系如下:
flowchart LR
Pod["Pod<br/>挂载 volume"] --> PVC["PVC<br/>应用的存储请求"]
PVC --> PV["PV<br/>集群存储资源"]
PV --> Backend["后端存储<br/>云盘/NFS/本地盘等"]
SC["StorageClass<br/>动态供给规则"] --> Provisioner["CSI Provisioner"]
Provisioner --> Backend
Provisioner -.创建并登记.-> PV
Pod 通常不直接引用 PV,而是引用 PVC:
volumes:
- name: data
persistentVolumeClaim:
claimName: app-data
这样做有两个重要后果:
- 应用只关心“我要一个名为
app-data的存储请求”,不需要知道具体使用哪块云盘。 - 管理员或平台负责提供满足 PVC 的 PV,或者由 StorageClass 动态创建 PV。
PV 与 PVC 的关系是一对一绑定:
- 一个 PV 最多绑定一个 PVC;
- 一个 PVC 最终绑定一个 PV;
- 一个 PVC 不能自动拆分到多个 PV;
- 一个 PV 也不能被多个 PVC 共享,即使后端本身支持共享。
如果多个 Pod 需要共享同一个卷,它们应引用同一个 PVC,并且底层卷必须支持相应的访问模式。
二、PV 和 PVC 的核心字段
1. PV 的主要字段
一个 PV 通常包含:
apiVersion: v1
kind: PersistentVolume
metadata:
name: example-pv
spec:
capacity:
storage: 10Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: ""
hostPath:
path: /srv/k8s/example
这些字段分别表示:
capacity.storage:PV 提供的容量。volumeMode:卷以文件系统还是原始块设备形式提供。accessModes:卷允许的访问模式。persistentVolumeReclaimPolicy:PVC 删除后 PV 如何处理。storageClassName:PV 所属的 StorageClass。- 卷插件字段:例如 CSI、NFS、
local、云厂商卷等。
hostPath 只是把节点上的目录暴露给 Pod,适合实验环境,不适合把普通节点目录当作生产持久化存储。节点损坏、Pod 调度到其他节点、目录权限和备份问题都不会由 Kubernetes 自动解决。
2. PVC 的主要字段
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: example-pvc
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 5Gi
storageClassName: ""
PVC 的字段表达的是需求:
resources.requests.storage:请求的最小容量。accessModes:应用需要的访问模式。volumeMode:需要文件系统还是块设备。storageClassName:希望使用的存储类别。selector:可选,用标签进一步选择预先创建的 PV。
PVC 请求 5Gi 不表示一定创建一个精确为 5Gi 的卷。静态绑定时,只要某个候选 PV 的容量至少为 5Gi,就可能满足该请求;动态供给时,Provisioner 通常会按照请求创建容量为 5Gi 或更大、但具体行为由实现决定的后端卷。
三、PVC 如何绑定到 PV
1. 静态供给和动态供给
PV 有两种来源。
静态供给由管理员预先创建 PV:
管理员创建 PV
↓
用户创建 PVC
↓
Kubernetes 查找匹配的 PV
↓
PVC 与 PV 绑定
动态供给由用户创建 PVC,Kubernetes 根据 StorageClass 调用 CSI Provisioner 创建后端卷,并自动创建 PV:
用户创建 PVC
↓
StorageClass 选择 Provisioner
↓
Provisioner 创建后端卷
↓
Provisioner 创建对应 PV
↓
PVC 与 PV 绑定
动态供给依赖 CSI 驱动和 StorageClass。Kubernetes 本身不会凭空创建云盘或网络存储。
2. 绑定匹配条件
设 PVC 的请求为:
- 容量需求为
- 访问模式集合为
- 卷模式为
- StorageClass 为
- 可选标签选择器为
某个 PV 能否成为候选者,至少需要满足:
并且:
如果 PVC 指定了 selector,还需要:
其中:
- 是 PV 容量;
- 是 PV 声明支持的访问模式;
- 是 PV 的
volumeMode; - 是 PV 的 StorageClass;
labels(PV) \models L_r表示 PV 标签满足 PVC 的选择器。
例如:
| 条件 | PV | PVC | 是否满足 |
|---|---|---|---|
| 容量 | 10Gi | 5Gi | 是 |
| 访问模式 | RWO | RWO | 是 |
| volumeMode | Filesystem | Filesystem | 是 |
| StorageClass | "" |
"" |
是 |
| selector | 无 | 无 | 是 |
因此它可以绑定。
但以下情况会导致绑定失败:
- PV 只有
ReadWriteOnce,PVC 请求ReadWriteMany; - PV 是
Block,PVC 请求Filesystem; - PV 属于
fast,PVC 请求slow; - PV 容量小于 PVC 请求;
- PVC selector 不匹配任何 PV;
- PV 已经绑定给另一个 PVC。
容量不是唯一条件。一个 10Gi 的 PV 不一定能绑定 5Gi 的 PVC;访问模式、卷模式和 StorageClass 同样必须匹配。
3. 一个可运行的静态绑定示例
下面的示例用于单节点实验集群。它使用 hostPath,要求 Kubernetes 节点上存在 /srv/k8s/pv-demo。
sudo mkdir -p /srv/k8s/pv-demo
sudo chmod 777 /srv/k8s/pv-demo
创建 PV 和 PVC:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-demo-10gi
spec:
capacity:
storage: 10Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: ""
hostPath:
path: /srv/k8s/pv-demo
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-demo
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 5Gi
storageClassName: ""
应用并观察:
kubectl apply -f pv-pvc.yaml
kubectl get pv pv-demo-10gi
kubectl get pvc pvc-demo
预期状态类似:
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM
pv-demo-10gi 10Gi RWO Retain Bound default/pvc-demo
这里 storageClassName: "" 很重要,它表示“不使用 StorageClass”。如果省略该字段,集群存在默认 StorageClass 时,PVC 可能被默认 StorageClass 处理,而不会绑定这个没有 StorageClass 的静态 PV。
再创建一个挂载 PVC 的 Pod:
apiVersion: v1
kind: Pod
metadata:
name: pvc-demo-pod
spec:
containers:
- name: app
image: busybox:1.36
command: ["/bin/sh", "-c"]
args:
- |
echo "created by pod" >> /data/message.txt
cat /data/message.txt
sleep 3600
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: pvc-demo
验证:
kubectl apply -f pod.yaml
kubectl logs pvc-demo-pod
kubectl exec pvc-demo-pod -- cat /data/message.txt
Pod 删除后重新创建,只要 PVC 和 PV 仍然存在,message.txt 仍应保留。这个结果来自 PV/PVC 的生命周期独立于 Pod,而不是来自 Pod 本身。
四、绑定状态与故障路径
1. PV 的阶段
PV 常见的 status.phase 包括:
Available:PV 尚未绑定 PVC。Bound:已经绑定 PVC。Released:原 PVC 已删除,但 PV 仍引用原来的 claim,尚未重新变为可用。Failed:回收或处理过程失败。
PVC 常见的阶段包括:
Pending:尚未绑定 PV,或者动态供给尚未完成。Bound:已经绑定 PV。Lost:关联的 PV 不再可用或关系异常。
可以使用以下命令查看事件和绑定原因:
kubectl describe pvc pvc-demo
kubectl describe pv pv-demo-10gi
kubectl get events --sort-by=.lastTimestamp
当 PVC 长时间处于 Pending 时,重点检查:
storageClassName是否正确;- 是否有容量足够的 PV;
- 访问模式和
volumeMode是否匹配; - selector 是否过于严格;
- StorageClass 是否存在默认值;
- CSI Provisioner 是否运行;
- 是否存在拓扑约束或可用区约束;
- 动态创建的后端卷是否因配额、权限或 API 错误创建失败。
kubectl describe pvc 的 Events 往往比 kubectl get pvc 更能说明原因。
2. 预绑定
管理员可以在 PV 上预先指定 PVC:
spec:
claimRef:
namespace: default
name: pvc-demo
也可以在 PVC 中指定目标 PV:
spec:
volumeName: pv-demo-10gi
预绑定不是绕过所有约束。即使指定了名称,PV 与 PVC 的容量、访问模式、卷模式等基本条件仍应兼容。预绑定适合迁移、恢复或把特定卷交给特定应用,但会降低普通调度的灵活性。
3. selector 的效果
PVC 可以使用 selector:
spec:
selector:
matchLabels:
tenant: team-a
这表示只接受带有 tenant=team-a 标签的 PV。selector 会限制候选范围,并且通常会阻止动态供给,因为动态供给无法根据任意标签 selector 推断应该创建什么资源。需要动态供给时,不应无意中加入与 Provisioner 不兼容的 selector。
五、访问模式:它描述“怎么访问”,不是应用锁
访问模式表示卷允许怎样被一个或多个节点、Pod 挂载。常见模式如下:
| 模式 | 含义 |
|---|---|
ReadWriteOnce(RWO) |
一个节点以读写方式挂载 |
ReadOnlyMany(ROX) |
多个节点以只读方式挂载 |
ReadWriteMany(RWX) |
多个节点以读写方式挂载 |
ReadWriteOncePod(RWOP) |
只能被单个 Pod 以读写方式使用 |
1. RWO 不等于单个 Pod
ReadWriteOnce 的“Once”指一个节点,不是一个 Pod。
同一个节点上的多个 Pod 可能同时挂载一个 RWO 卷。例如两个 Pod 都在节点 node-a 上,它们可能都能读写同一个卷。调度器和卷插件不应被理解为“自动为应用提供进程级互斥”。
因此:
- RWO 可以约束卷不能被多个节点同时读写;
- RWO 不能保证只有一个 Pod 使用数据;
- RWO 不能替代数据库锁、分布式锁或应用级 leader election。
2. RWOP 的语义更严格
ReadWriteOncePod 表示整个集群中只能有一个 Pod 使用该卷进行读写。它依赖支持该能力的 CSI 驱动和相关组件版本。它适合需要单 Pod 独占语义的场景,但不是所有后端或旧 CSI 驱动都支持。
如果使用 RWOP,应该确认:
kubectl get csidrivers
kubectl describe csidriver <driver-name>
实际兼容性还取决于 CSI Driver、external-provisioner、external-attacher 等组件版本。不能仅因为 API 接受 ReadWriteOncePod,就推断底层存储一定提供了完整的独占保证。
3. RWX 不是“任何磁盘都能共享”
普通云块设备通常不能直接以 RWX 方式安全地挂载到多个节点。RWX 通常需要支持并发网络文件协议或共享文件系统的后端,例如某些 NFS、CephFS 或云文件服务。
“PVC 写了 ReadWriteMany”只是需求声明。最终能否满足,取决于:
- StorageClass 使用的 Provisioner;
- CSI 驱动支持的访问模式;
- 后端存储的数据一致性和锁语义;
- 文件系统或数据库是否适合并发访问。
即使卷支持 RWX,也不意味着任意应用都能安全地并发写入同一个目录。数据库、日志文件和状态文件仍需要应用级并发设计。
4. 访问模式不是权限模型
访问模式不负责:
- Linux 用户和组权限;
- 文件所有者;
- Unix mode;
- SELinux 标签;
- 应用身份认证;
- 读写数据的业务授权。
这些通常由 Pod 的 securityContext、文件系统权限、CSI 挂载参数和应用自身共同决定。
六、volumeMode:文件系统卷与块卷
PV/PVC 可以请求两种卷模式:
1. Filesystem
这是默认模式。Kubernetes 或 CSI 驱动把底层设备格式化并挂载为文件系统,Pod 中看到的是目录:
volumeMounts:
- name: data
mountPath: /var/lib/app
应用可以使用普通文件 API 创建文件和目录。
2. Block
块模式不会把卷挂载成目录,而是以设备形式暴露:
volumeDevices:
- name: data
devicePath: /dev/xvda
对应完整示例:
apiVersion: v1
kind: Pod
metadata:
name: block-consumer
spec:
containers:
- name: app
image: busybox:1.36
command: ["/bin/sh", "-c"]
args: ["ls -l /dev/xvda; sleep 3600"]
volumeDevices:
- name: data
devicePath: /dev/xvda
volumes:
- name: data
persistentVolumeClaim:
claimName: block-pvc
Filesystem PVC 不能绑定 Block PV,反之亦然。块设备通常由数据库、存储引擎或应用自行初始化;如果直接当普通文件目录使用,会得到错误的设备访问方式。
七、回收策略:PVC 删除后数据怎么办
persistentVolumeReclaimPolicy 描述 PVC 删除后,PV 和后端存储的处理方式。
1. Retain
Retain 保留 PV 和后端数据。PVC 删除后,PV 通常进入 Released,不会自动交给其他 PVC 使用。
它适合:
- 生产数据;
- 需要人工确认的删除操作;
- 需要进行数据恢复或审计的卷。
但它也意味着删除 PVC 后,卷可能长期占用云资源和存储配额。
典型恢复流程是:
- 确认原 PVC 确实不再使用;
- 备份或验证后端数据;
- 确认新 PVC 的命名空间、应用和数据归属;
- 清理 PV 上对旧 PVC 的引用;
- 让 PV 重新进入可绑定状态,或创建新的 PV 指向恢复后的数据;
- 创建新 PVC 并验证挂载内容。
手工清理 claimRef 之前必须确认没有旧 Pod 仍在使用该卷。错误地清理引用,可能造成两个应用同时访问同一份数据。
2. Delete
Delete 表示删除 PVC 后,PV 对象以及动态供给的底层资源通常会被删除。具体删除动作由 Kubernetes 控制器和 CSI 驱动完成。
它适合可重建的临时环境,但生产环境必须确认:
- 删除卷是否真的符合数据保留要求;
- CSI 驱动是否正确实现删除;
- 云厂商是否存在快照、回收站或延迟删除;
- 是否有防止误删的权限和审批机制。
动态创建的 PV 往往会继承 StorageClass 的回收策略。不要只根据某个集群的默认值推断所有集群行为,应直接检查:
kubectl get storageclass
kubectl get pv <pv-name> -o jsonpath='{.spec.persistentVolumeReclaimPolicy}{"\n"}'
3. Recycle
Recycle 曾表示通过删除文件清空卷后重新使用,但已经弃用,并不应作为当前方案设计。现代集群应使用 Retain 或 Delete,或者由存储系统提供快照、克隆和安全清理能力。
4. 回收策略不等于备份策略
Retain 不是备份:
- 它不能防止磁盘损坏;
- 不能提供时间点恢复;
- 不能替代跨可用区或跨区域复制;
- 不能自动验证数据是否可恢复。
Delete 也不一定意味着底层数据立即不可恢复,实际行为由后端和 CSI 驱动决定。数据保护应通过快照、复制、备份和恢复演练实现。
八、完整生命周期:从创建到删除
阶段 1:创建 PVC
用户提交 PVC 后:
kubectl get pvc
可能看到:
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS
pvc-demo Pending
Pending 表示尚未完成绑定,不代表 Pod 一定已经失败。
阶段 2:静态匹配或动态供给
控制器会尝试:
- 查找匹配的
AvailablePV; - 或根据 StorageClass 请求动态创建 PV。
在动态供给中,CSI Provisioner 可能先创建云盘,再创建 PV;如果云厂商 API 超时、权限不足或配额耗尽,PVC 会继续处于 Pending,事件中通常会记录失败原因。
阶段 3:绑定
绑定完成后:
PVC: Pending → Bound
PV: Available → Bound
PV 的 claimRef 会记录绑定关系,防止其他 PVC 再次选择同一个 PV。
阶段 4:Pod 调度和挂载
PVC Bound 不代表 Pod 已经成功运行。Pod 仍需要经过:
- 调度到满足资源和拓扑约束的节点;
- 节点上的 kubelet 与 CSI Node Plugin 协作;
- Attach 阶段将卷连接到节点,适用于需要 attach 的后端;
- Mount 阶段把卷挂载到 Pod 的路径;
- 容器启动并访问数据。
因此应分别检查:
kubectl get pvc
kubectl get pod
kubectl describe pod <pod-name>
kubectl get volumeattachments
PVC Bound 但 Pod Pending,可能是调度问题;Pod 已调度但 ContainerCreating,可能是 attach、mount、权限或文件系统问题。
阶段 5:Pod 删除
删除 Pod 通常不会删除 PVC,也不会删除 PV。新 Pod 只要引用同一个 PVC,就可以重新挂载该卷。
这正是 StatefulSet 使用 PVC 保存状态的基础,但它并不自动解决数据库复制、事务一致性或故障恢复问题。
阶段 6:PVC 删除
删除 PVC 后,PV 根据回收策略处理:
PVC 删除
├── Retain → PV 通常进入 Released,数据保留
└── Delete → 删除 PV,并请求删除动态后端资源
如果 CSI 驱动或后端删除失败,PV 可能因为 finalizer 继续存在。此时不应直接强制删除对象绕过 finalizer,因为这可能留下无法管理的云盘、孤儿资源或数据泄漏风险。
九、StorageClass 与动态供给:为什么 PVC 可能不需要预先创建 PV
一个 StorageClass 示例:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: example-csi
provisioner: example.csi.example.com
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
关键字段含义:
provisioner:负责创建卷的 CSI 驱动名称。reclaimPolicy:动态 PV 的默认回收策略。volumeBindingMode:Immediate:PVC 创建后立即供给和绑定;WaitForFirstConsumer:等到有 Pod 使用 PVC 后,再结合 Pod 调度结果决定卷位置。
allowVolumeExpansion:是否允许通过编辑 PVC 请求更大容量。
使用该 StorageClass 的 PVC:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dynamic-pvc
spec:
storageClassName: example-csi
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
如果 volumeBindingMode 是 Immediate,动态卷可能在 Pod 尚未调度前就被创建。如果后端卷带有可用区限制,而 Pod 最终被调度到另一个不可访问该卷的区域,就可能形成调度或挂载问题。
WaitForFirstConsumer 将绑定延迟到 Pod 出现后,使调度器可以同时考虑:
- 节点可用区;
- PV 的 node affinity;
- Pod 的 nodeSelector;
- taint 和 toleration;
- 资源与其他调度约束。
这不是“延迟绑定一定更好”,而是它更适合具有拓扑限制的存储。具体效果取决于 CSI 驱动是否正确提供拓扑信息。
默认 StorageClass 的隐含行为
如果 PVC 没有设置 storageClassName,启用了默认 StorageClass 的集群可能自动填入默认类。若要明确表示不使用 StorageClass,应写:
storageClassName: ""
这是静态绑定示例中常被忽略的差异,也是“同一份 YAML 在不同集群表现不同”的常见原因。
十、扩容:修改 PVC 不等于后端一定能扩容
如果 StorageClass 设置:
allowVolumeExpansion: true
通常可以增加 PVC 的容量请求:
kubectl patch pvc dynamic-pvc \
--type merge \
-p '{"spec":{"resources":{"requests":{"storage":"30Gi"}}}}'
查看结果:
kubectl get pvc dynamic-pvc
kubectl describe pvc dynamic-pvc
扩容通常包含两个层次:
- 后端卷扩容:CSI Controller 调用存储系统,把云盘或后端卷变大;
- 节点文件系统扩容:CSI Node Plugin 或 kubelet 让挂载的文件系统识别新空间。
因此 spec.resources.requests.storage 变大后,status.capacity 可能不会立即变化。最终是否需要重新挂载、重启 Pod 或满足特定文件系统条件,由 CSI 驱动和文件系统实现决定。
扩容通常只支持增加,不支持缩小。直接把 PVC 请求从 30Gi 改成 10Gi 不能安全地缩小已创建的块设备;需要通过备份、创建更小的新卷、迁移数据等方式完成。
扩容失败时,检查:
kubectl describe pvc dynamic-pvc
kubectl get events --sort-by=.lastTimestamp
kubectl logs -n <csi-namespace> <csi-controller-pod>
不要只修改 PV 的 spec.capacity 来“伪造扩容结果”。那可能改变 Kubernetes 的记录,却没有改变后端容量,导致数据写满或挂载状态不一致。
十一、常见误解与反例
误解一:删除 Pod 会删除数据
通常不会。Pod 删除只会删除容器和 Pod 对象;PVC、PV 和后端卷是独立资源。
反例是某些应用把数据写在容器可写层或 emptyDir 中。这样的数据没有经过 PVC,Pod 删除后自然不会保留。
误解二:PVC Bound 就代表应用可写
不代表。PVC 绑定只说明资源关系满足。以下问题仍可能存在:
- 节点无法访问后端;
- CSI 驱动挂载失败;
- 文件系统只读;
- 容器用户没有目录权限;
- Pod 被调度到不支持该卷的节点;
- 后端发生故障。
必须同时检查 Pod、事件、CSI 组件和节点挂载状态。
误解三:RWO 可以防止两个 Pod 同时写入
RWO 只限制节点范围。两个 Pod 如果位于同一节点,可能都能访问卷。需要单 Pod 独占时,应评估 RWOP、StatefulSet 的副本设计以及应用级锁。
误解四:把 PVC 请求写成 RWX 就能获得共享文件系统
访问模式不能改变后端能力。一个只支持块存储的 Provisioner 不会因为 PVC 写了 RWX 就突然获得多节点共享写能力。
误解五:PV 的 Retain 可以防止所有误删
Retain 主要影响删除 PVC 后的回收动作。管理员仍可能直接删除 PV,后端管理员也可能删除云盘或文件系统。它不是权限控制,也不是备份。
误解六:同一个 PVC 可以让多个副本安全共享数据库目录
PVC 能否被多个 Pod 挂载,与应用是否支持多个进程并发操作同一份数据,是两个问题。数据库通常要求明确的单实例、复制协议或分布式存储设计;不能仅凭 RWX 解决数据库一致性。
十二、生产诊断的最短路径
遇到“应用无法使用持久化存储”时,可以按资源边界逐层定位:
# 1. PVC 是否绑定
kubectl get pvc -A
kubectl describe pvc <pvc-name> -n <namespace>
# 2. PV 是否存在、属于哪个 PVC
kubectl get pv
kubectl describe pv <pv-name>
# 3. Pod 是否被调度、是否卡在挂载
kubectl get pod <pod-name> -n <namespace> -o wide
kubectl describe pod <pod-name> -n <namespace>
# 4. StorageClass 和 CSI 驱动是否存在
kubectl get storageclass
kubectl get csidrivers
# 5. 查看最近事件
kubectl get events -A --sort-by=.lastTimestamp
可以把故障分成四类:
- 绑定故障:容量、访问模式、StorageClass、selector 不匹配;
- 供给故障:Provisioner 权限、配额、后端 API 或拓扑失败;
- 调度故障:节点亲和性、可用区、资源和污点不满足;
- 挂载故障:CSI Node Plugin、设备连接、文件系统、权限或网络失败。
这种分类比反复删除并重建 PVC 更安全。删除 PVC 可能触发 Delete 回收策略,导致本来可以恢复的数据和后端卷被清理。
十三、与 StatefulSet 的边界
StatefulSet 常为每个副本生成独立 PVC,例如:
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
副本 db-0、db-1 通常分别拥有:
data-db-0
data-db-1
这样可以获得稳定的存储归属,但需要明确:
- PVC 只保存卷与请求关系;
- StatefulSet 提供稳定 Pod 名称、网络身份和副本管理;
- StatefulSet 不自动提供数据库复制;
- 删除或缩容 StatefulSet 通常不会自动删除其 PVC,数据保留行为还取决于具体控制器字段和集群版本;
- 数据库故障转移、日志复制、脑裂防护和一致性仍由数据库系统负责。
PV/PVC 解决的是“这份数据存在哪里以及如何挂载”,不是“多个副本如何达成一致”。
十四、设计时应明确的边界
一个可靠的 PV/PVC 设计至少要明确以下事实:
- 容量:PVC 请求的是容量下限,实际可用空间还受到文件系统、预留空间和后端限制影响。
- 访问模式:是节点级挂载约束,不是应用锁,也不是权限控制。
- 卷模式:文件系统和块设备不能混用。
- 供给方式:静态 PV 需要管理员管理;动态供给依赖 StorageClass 和 CSI。
- 拓扑:云盘常与可用区、节点或区域绑定,
WaitForFirstConsumer可能影响正确调度。 - 回收策略:
Retain与Delete都有成本,前者可能遗留资源,后者可能造成数据删除。 - 扩容能力:由 StorageClass、CSI 驱动、后端和文件系统共同决定。
- 备份责任:PV/PVC 生命周期不是备份系统,数据保护需要独立设计。
- 应用一致性:卷挂载成功不代表应用具备并发、恢复和复制能力。
PV 是集群层面的存储资源,PVC 是应用层面的存储契约,StorageClass 是动态供给规则,CSI 是连接 Kubernetes 与真实存储后端的实现。理解这四者的边界,才能正确解释“为什么 PVC 一直 Pending”“为什么 Bound 后 Pod 仍不能启动”“为什么删除 PVC 后数据还在”以及“为什么设置 RWX 后仍无法共享写入”。
系列导航与关联阅读
- 系列入口:Kubernetes 完整学习路线:从 Pod 与控制面到安全、运维和 Operator
- 上一篇:Kubernetes 网络排障:DNS、Service、CNI、MTU、Conntrack 和抓包
- 下一篇:StorageClass 与动态供给:Provisioner、Binding、扩容和拓扑
- 延伸:StatefulSet:稳定身份、顺序、PVC、更新和分布式系统边界
官方资料
本文依据 Kubernetes、CNCF 与相关项目官方文档重新梳理;正文和生产清单由 WR BLOG 编写。

评论
0 条讨论