09 | 持久化存储:PV、PVC 与 StorageClass
2026/7/15 3:44:12 网站建设 项目流程

系列专栏《深入剖析 Kubernetes》· 基于张磊极客时间专栏思想整理 · 适合 CSDN 发布

09 | 持久化存储:PV、PVC 与 StorageClass

容器本身是"用完即焚"的——Pod 里的文件系统随容器生命周期而生灭。但数据库、日志、用户上传文件这些必须跨重启存活的数据,就需要持久化存储。K8s 用一套"解耦"的设计把这件事做得很优雅:核心就是PVPVCStorageClass三个对象。

本文回答三个问题:为什么要解耦、怎么申请存储、存储从哪来(静态 vs 动态)。

一、PV / PVC 解耦"存储"与"使用"

想象一个没有抽象的世界:开发者写 Pod 时,得知道底层是 NFS 还是 Ceph,得写对方的 IP、路径、认证。一旦运维换了存储设备,所有 YAML 都得改。这正是耦合的痛。

K8s 的解法借鉴了"存储领域的 Service"思想:

  • PV(PersistentVolume,持久卷):集群级的"存储资源对象",描述真实的存储后端(容量、访问模式、类型、挂载点)。由运维或系统创建,是"供应侧"。
  • PVC(PersistentVolumeClaim,持久卷声明):用户对存储的使用请求,好比"我要 10G、能读写的盘"。由开发者创建,是"消费侧"。
  • 绑定(Binding):系统把合适的 PV 分配给 PVC,Pod 再通过persistentVolumeClaim把 PVC 挂进容器。
+--------+ 1. 申请 +-------+ 2. 绑定 +-------------+ | Pod | --------------------> | PVC | <----------------- | PV | | 声明 | volumeClaim | (需求) | 系统匹配 | (真实存储) | | 挂载 | +-------+ +-------------+ +--------+ 3. 挂载 | | | | 4. 使用 v +-------------------------------------------------------> NFS/Ceph/本地盘

PVC 之于存储,就像 Service 之于 Pod:使用者只管"我要什么",不用关心"背后是谁"。开发者永远只碰 PVC,运维在 PV/StorageClass 层管理真实设备,两者通过"声明—匹配"解耦。

二、Static vs Dynamic Provisioning

静态供给(Static Provisioning)

运维预先手工创建一批 PV(比如 5 块 NFS 卷),开发者创建 PVC 时,系统从现成 PV 里挑一个满足条件的绑定。

# 运维预先创建:一个 NFS 类型的 PVapiVersion:v1kind:PersistentVolumemetadata:name:pv-nfs-01spec:capacity:storage:10GiaccessModes:-ReadWriteOnce# RWO:单节点读写persistentVolumeReclaimPolicy:Retain# 回收策略:保留nfs:server:10.0.0.10# NFS 服务器地址path:"/exports/data01"
# 开发者创建:PVC 请求存储apiVersion:v1kind:PersistentVolumeClaimmetadata:name:my-pvcspec:accessModes:-ReadWriteOnceresources:requests:storage:10Gi

问题:PV 要人工预创建,数量/容量难匹配,容易"有 PVC 没 PV"或浪费。于是有了动态供给。

动态供给(Dynamic Provisioning)

开发者只写 PVC,系统在 PVC 创建时自动按需求创建 PV(后端真正分配磁盘)。这靠StorageClass驱动。

# StorageClass:定义"如何造盘"apiVersion:storage.k8s.io/v1kind:StorageClassmetadata:name:fast-ssdprovisioner:kubernetes.io/aws-ebs# 由哪个插件造盘parameters:type:gp3reclaimPolicy:Delete# PVC 删则 PV 自动删allowVolumeExpansion:true# 支持扩容
# 开发者只声明需求,PV 自动生成apiVersion:v1kind:PersistentVolumeClaimmetadata:name:my-pvcspec:storageClassName:fast-ssd# 指定按哪个模板造盘accessModes:["ReadWriteOnce"]resources:requests:storage:20Gi

创建 PVC 后kubectl get pv会看到一张自动生成的 PV,名字形如pvc-xxxx,这正是动态供给的痕迹。

三、本地卷 vs 网络存储

类型代表优点缺点适用
本地卷hostPath/local零网络开销、延迟极低与节点绑定、Pod 漂移需配合调度高 IOPS 本地 DB
网络存储NFS / Ceph RBD / 云盘可跨节点、可迁移有网络开销、需额外组件共享/弹性存储
  • hostPath:直接挂宿主机目录,最简单但完全绑定节点,Pod 被调度走就丢了,仅适合单机测试/daemon 场景。
  • local:比 hostPath 规范,需在 PV 上声明nodeAffinity指定节点,且有 External 健康检测,但仍绑定单节点。
  • NFS/Ceph:真正的网络存储,支持ReadWriteMany(RWX,多节点同时读写),是"共享盘"的首选,但需运维维护存储集群。

经验法则:要极致性能且单实例 → 本地卷;要共享/可迁移 → 网络存储。

四、为什么 PV/PVC 不是多此一举

有人质疑:“我直接 Pod 里挂 hostPath 不就行了?” 问题在于规模化与职责分离:

  1. 研发/运维解耦:开发者写 PVC 表达"我需要存储",无需知道底层是 NFS 还是云盘;运维在集群侧统一管理容量、配额、回收策略。岗位边界清晰,YAML 不被存储细节污染。
  2. 生命周期独立:PV 的生命周期(Retain/Delete/Recycle)与 Pod 解耦,数据可独立于应用存在,避免误删应用连带删数据。
  3. 策略统一:通过 StorageClass 把"造盘速度"“加密”"备份"等策略标准化,全公司一套规则。
  4. 可移植:换云厂商只改 StorageClass 的 provisioner,业务 PVC 零改动。

这正是 K8s “声明式 + 解耦” 哲学在存储领域的体现。

五、FlexVolume 与 CSI 插件机制

PV 怎么对接千奇百怪的存储后端?K8s 通过存储插件机制扩展。

  • FlexVolume(旧):早期方案,以可执行二进制方式安装在每个节点,kubelet 调用它完成 attach/mount。部署麻烦、依赖节点文件系统,已被淘汰。
  • CSI(Container Storage Interface,容器存储接口):当前标准。一套与 K8s 无关的标准接口,任何存储厂商实现 CSI 驱动即可接入 K8s、甚至跨编排系统复用。架构上分三部分:
    • CSI Controller 插件(通常是 Deployment):负责"造盘/删盘"(CreateVolume/DeleteVolume)。
    • CSI Node 插件(DaemonSet):在每个节点负责"挂盘/卸盘"(NodePublishVolume)。
    • external-provisioner / external-attacher等 sidecar:把 CSI 调用翻译成 K8s 的 PV 生命周期事件。
Pod --> PVC --> PV --> StorageClass(provisioner) | +------+------+ | CSI Driver | | Controller | CreateVolume (造盘) | NodePlugin | NodePublish (挂到节点/Pod) +------+------+ v 实际存储后端 (EBS/Ceph/NFS...)

小结 / 核心要点

  1. PV 是"存储资源"、PVC 是"使用请求",两者通过绑定解耦供应侧消费侧
  2. 静态供给需预创建 PV;动态供给由 StorageClass 在 PVC 创建时自动造 PV,生产首选。
  3. 本地卷(hostPath/local)性能高但绑节点;网络存储(NFS/Ceph)可共享可迁移,按需选择。
  4. PV/PVC 不是多此一举:它实现研发/运维解耦、数据生命周期独立、策略标准化与跨云可移植。
  5. 存储扩展走 CSI 标准接口(取代旧 FlexVolume),由 Controller + Node 插件对接后端存储。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询