Kubernetes集群中由管理员供给的持久化存储卷资源
PersistentVolume(PV) 是集群级别的存储资源,代表一块真实存在的存储 —— 可能是 NFS 共享目录、云厂商云盘、Ceph RBD 镜像或本地磁盘。它由管理员预先创建(静态供给),或由 StorageClass 自动创建(动态供给),生命期独立于任何 Pod。
PV 与 PVC 是「供给」与「消费」的两端:PV 描述「我有什么」,PVC 描述「我要什么」,两者由控制器按容量、访问模式、存储类、标签等条件撮合绑定。这种解耦让存储的生命周期与业务 Pod 彻底分开 —— Pod 删了数据还在,PV 删了才是真的没了。
PV 是集群级资源,不属于任何命名空间;而 PVC 是命名空间级的。这是两者最容易混淆的地方,也是 kubectl get pv -n xxx 看不到命名空间列的原因。
Available 空闲,尚未被任何 PVC 绑定
Bound 已绑定到某个 PVC
Released PVC 已删除,但 PV 尚未被回收(数据仍在)
Failed 自动回收失败,需要人工介入清理
Retain 保留数据,PV 变为 Released,需管理员手工清理才能重新使用(生产环境推荐)
Delete 删除 PV 的同时删除底层存储(AWS EBS、GCE PD、Ceph RBD 等动态供给的默认值)
Recycle 已废弃,等价于在卷上执行 rm -rf,不要使用
ReadWriteOnce (RWO) 单节点读写,同一节点上的多个 Pod 可同时挂载
ReadOnlyMany (ROX) 多节点只读
ReadWriteMany (RWX) 多节点读写,要求 NFS、CephFS 等共享文件系统
ReadWriteOncePod (RWOP) 单 Pod 读写,1.22+ 引入,需 CSI 驱动支持
访问模式是存储后端能力的声明,不是访问控制。写了一个后端不支持的模式,PV 照样能创建,但 PVC 会一直绑定不上或挂载失败。
kubectl get pv [名称] [选项]
kubectl describe pv [名称]
kubectl delete pv [名称]
kubectl patch pv [名称] -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'
NFS 静态卷,支持多节点读写,是最常见的自建存储方案:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-01
labels:
env: production
tier: gold
spec:
capacity:
storage: 100Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs-slow
mountOptions:
- hard
- nfsvers=4.1
nfs:
server: 192.168.1.10
path: /data/nfs/pv-nfs-01
local 卷,性能接近本地盘,但必须配合 nodeAffinity 与 WaitForFirstConsumer:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-local-01
spec:
capacity:
storage: 200Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: /mnt/disks/ssd1
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- node-01
云厂商云盘(AWS EBS 示例,通常由动态供给自动创建,这里展示静态形态):
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-aws-ebs-01
spec:
capacity:
storage: 500Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
storageClassName: gp3
csi:
driver: ebs.csi.aws.com
volumeHandle: vol-0123456789abcdef0
fsType: ext4
PVC 与 PV 的绑定条件必须全部满足,任一不符都会导致 Pending:
1. PV 的 capacity 大于等于 PVC 的 requests.storage
2. PV 的 accessModes 覆盖 PVC 的 accessModes
3. storageClassName 完全一致(两边都为空也视为一致)
4. volumeMode 一致(Filesystem / Block)
5. 若 PVC 指定了 volumeName,则必须精确匹配该 PV
6. 若 PVC 指定了 selector,则 PV 的标签必须匹配
绑定成功后,PV 的 spec.claimRef 会写入对应 PVC 的信息,status.phase 变为 Bound:
kubectl get pv pv-nfs-01 -o jsonpath='{.spec.claimRef}'
kubectl get pv pv-nfs-01 -o jsonpath='{.status.phase}'
# 查看所有 PV 与绑定情况
kubectl get pv
kubectl get pv -o wide
# 自定义列,快速看清容量、模式、存储类
kubectl get pv -o custom-columns=\
NAME:.metadata.name,CAP:.spec.capacity.storage,ACCESS:.spec.accessModes,\
RECLAIM:.spec.persistentVolumeReclaimPolicy,SC:.spec.storageClassName,\
STATUS:.status.phase,CLAIM:.spec.claimRef.name
# 查看详情,关注 Events
kubectl describe pv pv-nfs-01
# 修改回收策略(常用于把动态供给的 Delete 改为 Retain)
kubectl patch pv pv-nfs-01 -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'
# 删除 PV(底层数据是否保留取决于回收策略)
kubectl delete pv pv-nfs-01
# 强制删除卡在 Terminating 的 PV
kubectl patch pv pv-nfs-01 -p '{"metadata":{"finalizers":null}}'
Retain 策略下删除 PVC 后,PV 会停在 Released 状态,且无法被新 PVC 绑定 —— 因为它仍记着旧的 claimRef:
# 1. 删除 PVC 后查看 PV 状态
kubectl get pv
# NAME CAPACITY ... STATUS CLAIM
# pv-nfs-01 100Gi ... Released default/old-claim
# 2. 清除 claimRef 让 PV 回到 Available
kubectl patch pv pv-nfs-01 -p '{"spec":{"claimRef":null}}'
# 3. 确认状态已变为 Available
kubectl get pv pv-nfs-01
# 4. 底层数据仍在,需要手工清理后再交给新的使用方
# 例如在 NFS 服务器上:rm -rf /data/nfs/pv-nfs-01/*
# 1. PVC Pending,列出所有可绑定 PV 做对比
kubectl get pv
kubectl describe pvc app-data
# 2. 检查容量与访问模式是否匹配
kubectl get pv -o custom-columns=\
NAME:.metadata.name,CAP:.spec.capacity.storage,MODE:.spec.accessModes,SC:.spec.storageClassName
# 3. 检查 storageClassName 大小写与拼写
kubectl get pv pv-nfs-01 -o jsonpath='{.spec.storageClassName}'
kubectl get pvc app-data -o jsonpath='{.spec.storageClassName}'
# 4. local 卷的 nodeAffinity 是否与调度到的节点一致
kubectl get pv pv-local-01 -o jsonpath='{.spec.nodeAffinity}'
kubectl get pod <pod> -o jsonpath='{.spec.nodeName}'
# 5. PV 卡在 Terminating
kubectl get pv pv-nfs-01 -o jsonpath='{.metadata.finalizers}'
# 6. NFS 挂载失败(最常见的是服务端未导出该路径)
showmount -e 192.168.1.10
kubectl get pv -n kube-system 与 kubectl get pv 结果完全一样,命名空间参数会被忽略。Released 状态的 PV 不能直接被新 PVC 使用。必须手工清空 spec.claimRef 才能回到 Available,这一步经常被忽略,导致「明明有闲置 PV 却一直 Pending」。Delete 时,删除 PVC 会连底层云盘一起删掉。动态供给的 PV 默认就是 Delete,核心数据务必在 StorageClass 或单个 PV 上改成 Retain。local 卷必须配合 nodeAffinity 使用,否则 Pod 可能被调度到没有该磁盘的节点上,卷挂载直接失败。同时 volumeBindingMode 应为 WaitForFirstConsumer,让调度器先决定节点再绑定卷。hostPath 不适合生产环境。它把 Pod 与特定节点绑死,且几乎没有隔离,节点故障即数据丢失,多租户场景下还存在越权访问节点文件系统的风险。capacity 只用于匹配,不做配额。写 100Gi 但底层实际只有 10Gi 也能绑定成功,超用后才会在写入时报错。capacity 不可修改。需要更大容量只能新建 PV 并迁移数据,或依赖 CSI 驱动的扩容能力直接扩 PVC。volumeName 或 selector。kubectl — Kubernetes集群管理工具pvc — 消费PV的持久卷声明storageclass — 定义PV的动态供给策略namespace — PVC的作用域边界kubeadm — Kubernetes集群安装与生命周期管理工具