Skip to content

存储基础

没有 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 复制卷里。

各对象的角色

对象级别谁创建做什么
VolumePod 内部用户在 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 删除后
RetainPV 和后端数据保留,管理员手动清理
DeletePV 和后端数据一起删除
Recycle已废弃

生产数据盘用 Retain——PVC 误删时后端数据还在,重新建 PVC 绑定即可。Delete 适合测试环境和临时卷——PVC 删了,云盘/NFS 子目录跟着删,中间没有回收站。

后端存储选型

后端模式优点风险
NFSRWX部署简单,共享目录直接可见单点、随机 IO 性能一般、权限配置容易踩
Ceph RBDRWO块存储,适合数据库类单节点运维成本高,需要维护 Ceph 集群
CephFSRWX分布式文件系统,多节点共享同样要维护 Ceph
云盘RWO云平台托管,稳定依赖云、可用区限制、不支持跨可用区挂载
Local PVRWO延迟低,IO 性能好Pod 和节点绑定,节点故障时数据无法迁移
Longhorn/OpenEBSRWO/RWX小集群部署方便,UI 管理本身也要监控和维护,数据面在集群内

实验和通用共享场景用 NFS 最直观——showmount -els 就能看数据。数据库类服务用 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: /data

Pod 挂载后,容器里 /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: WaitForFirstConsumer

WaitForFirstConsumer 表示等 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 和后端存储上验证。