Appearance
存储基础
没有 PV/PVC 时,Pod 里写的文件存在容器层,Pod 删除后数据跟着消失。要想数据比 Pod 活得久,需要把存储挂载到 Pod 外面。
Kubernetes 存储不是 PV 和 PVC 两个名词。真正的链路是:Pod 挂载 PVC,PVC 绑定 PV,PV 指向真实后端存储,动态供给则由 StorageClass 调用 CSI provisioner 在后端自动创建卷。
从 Pod 到真实存储的链路
PV 不是磁盘。PV 是 K8s 资源对象,描述一块可用存储。真实数据可能在 NFS 目录、Ceph RBD 块设备、CephFS 文件系统、云盘、本地目录或 Longhorn 复制卷里。
各对象的角色
| 对象 | 级别 | 谁创建 | 做什么 |
|---|---|---|---|
| Volume | Pod 内部 | 用户在 Pod spec 里写 | 声明 Pod 要挂载什么(PVC 或直接引用存储) |
| PVC | 命名空间级 | 用户 | 申请一块存储——"我要 5Gi RWO" |
| PV | 集群级 | 管理员或 provisioner | 集群里一块可用的存储资源 |
| StorageClass | 集群级 | 管理员 | 动态创建 PV 的模板——什么后端、什么参数、回收策略 |
| CSI Driver | 集群组件 | 管理员部署 | 和真实存储后端交互——创建卷、挂载、卸载、快照 |
静态供给:管理员先创建 PV,用户创建 PVC 绑定到已有 PV。适合存储后端固定、容量预先分配的场景。PV 不够用时 PVC 一直 Pending。
动态供给:用户只建 PVC,StorageClass 对接的 provisioner 自动创建 PV 和后端卷。适合存储按需分配的日常场景。没有默认 StorageClass 时,不写 storageClassName 的 PVC 无法动态供给。
访问模式
| 模式 | 含义 | 常见后端 |
|---|---|---|
ReadWriteOnce (RWO) | 单节点读写 | 云盘、Ceph RBD、本地盘 |
ReadOnlyMany (ROX) | 多节点只读 | NFS、CephFS |
ReadWriteMany (RWX) | 多节点读写 | NFS、CephFS、部分分布式文件系统 |
ReadWriteOncePod (RWOP) | 单 Pod 独占读写 | 新 CSI 能力,需要 CSI 和存储后端支持 |
访问模式不是 PVC 里写什么就得到什么——最终看后端存储是否支持。多数云盘只支持 RWO,不支持多节点同时挂载。把 RWO 的 PV 挂给多个 Pod 时,Pod 会卡在 ContainerCreating——Event 里能看到 Multi-Attach error。
回收策略
| 策略 | PVC 删除后 |
|---|---|
Retain | PV 和后端数据保留,管理员手动清理 |
Delete | PV 和后端数据一起删除 |
Recycle | 已废弃 |
生产数据盘用 Retain——PVC 误删时后端数据还在,重新建 PVC 绑定即可。Delete 适合测试环境和临时卷——PVC 删了,云盘/NFS 子目录跟着删,中间没有回收站。
后端存储选型
| 后端 | 模式 | 优点 | 风险 |
|---|---|---|---|
| NFS | RWX | 部署简单,共享目录直接可见 | 单点、随机 IO 性能一般、权限配置容易踩 |
| Ceph RBD | RWO | 块存储,适合数据库类单节点 | 运维成本高,需要维护 Ceph 集群 |
| CephFS | RWX | 分布式文件系统,多节点共享 | 同样要维护 Ceph |
| 云盘 | RWO | 云平台托管,稳定 | 依赖云、可用区限制、不支持跨可用区挂载 |
| Local PV | RWO | 延迟低,IO 性能好 | Pod 和节点绑定,节点故障时数据无法迁移 |
| Longhorn/OpenEBS | RWO/RWX | 小集群部署方便,UI 管理 | 本身也要监控和维护,数据面在集群内 |
实验和通用共享场景用 NFS 最直观——showmount -e 和 ls 就能看数据。数据库类服务用 Ceph RBD、云盘或专门的数据库方案——NFS 的锁机制和随机 IO 性能不适合数据库事务型负载。
静态 PV/PVC 示例
管理员先创建 PV,指向真实 NFS 目录:
yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-demo
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs-static
nfs:
server: 192.168.10.20
path: /data/nfs/k8s-demo用户创建 PVC 申请存储。PV 不存在时 PVC 一直 Pending:
yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-nfs-demo
namespace: demo
spec:
accessModes:
- ReadWriteMany
storageClassName: nfs-static # 必须和 PV 的 storageClassName 一致
resources:
requests:
storage: 5Gi # 小于 PV 容量即可绑定PVC Bound 之后,Pod 才能挂载:
yaml
volumes:
- name: data
persistentVolumeClaim:
claimName: pvc-nfs-demo
containers:
- name: app
volumeMounts:
- name: data
mountPath: /dataPod 挂载后,容器里 /data 就是 NFS 服务器的 /data/nfs/k8s-demo 目录。在容器里写文件,NFS 服务器上直接可见。
动态供给
动态供给场景下,管理员只需建 StorageClass,provisioner 自动创建 PV 和后端卷:
yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast
provisioner: example.com/csi-driver
parameters:
type: ssd
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumerWaitForFirstConsumer 表示等 Pod 调度完成后再创建卷——在云盘和多可用区场景下很重要。不设这个参数(默认 Immediate)时,PVC 创建后立刻在后端创建卷,如果卷在 A 可用区,Pod 被调度到 B 可用区,挂载就会失败。
没有默认 StorageClass 时,PVC 不写 storageClassName 会一直 Pending。查看集群的 StorageClass:
bash
kubectl get storageclass
# 看哪个是 default——有 (default) 标记的PVC Pending 排查
bash
kubectl get pvc -A
kubectl describe pvc <pvc-name> -n <namespace>
kubectl get pv
kubectl get storageclass| 现象 | 方向 |
|---|---|
| 没有可用 PV | 静态供给——PV 不存在或 storageClassName/容量/访问模式不匹配 |
| StorageClass 不存在 | storageClassName 名称写错 |
| provisioner 不工作 | CSI Driver Pod 异常 |
| 访问模式不支持 | 后端不支持 PVC 请求的 RWO/RWX 模式 |
| 容量不匹配 | PVC 请求容量大于所有 PV |
WaitForFirstConsumer 正常等待 | PVC 等 Pod 被调度后才创建卷——不是故障 |
PVC Bound 不等于存储可用。绑定只说明 PV 和 PVC 匹配上了——挂载是否成功、权限是否正确、后端 IO 是否正常,要分别在 Pod Events 和后端存储上验证。