09 | 持久化存储:PV、PVC 与 StorageClass
系列专栏《深入剖析 Kubernetes》· 基于张磊极客时间专栏思想整理 · 适合 CSDN 发布09 | 持久化存储PV、PVC 与 StorageClass容器本身是用完即焚的——Pod 里的文件系统随容器生命周期而生灭。但数据库、日志、用户上传文件这些必须跨重启存活的数据就需要持久化存储。K8s 用一套解耦的设计把这件事做得很优雅核心就是PV、PVC、StorageClass三个对象。本文回答三个问题为什么要解耦、怎么申请存储、存储从哪来静态 vs 动态。一、PV / PVC 解耦存储与使用想象一个没有抽象的世界开发者写 Pod 时得知道底层是 NFS 还是 Ceph得写对方的 IP、路径、认证。一旦运维换了存储设备所有 YAML 都得改。这正是耦合的痛。K8s 的解法借鉴了存储领域的 Service思想PVPersistentVolume持久卷集群级的存储资源对象描述真实的存储后端容量、访问模式、类型、挂载点。由运维或系统创建是供应侧。PVCPersistentVolumeClaim持久卷声明用户对存储的使用请求好比我要 10G、能读写的盘。由开发者创建是消费侧。绑定Binding系统把合适的 PV 分配给 PVCPod 再通过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真正的网络存储支持ReadWriteManyRWX多节点同时读写是共享盘的首选但需运维维护存储集群。经验法则要极致性能且单实例 → 本地卷要共享/可迁移 → 网络存储。四、为什么 PV/PVC 不是多此一举有人质疑“我直接 Pod 里挂 hostPath 不就行了” 问题在于规模化与职责分离研发/运维解耦开发者写 PVC 表达我需要存储无需知道底层是 NFS 还是云盘运维在集群侧统一管理容量、配额、回收策略。岗位边界清晰YAML 不被存储细节污染。生命周期独立PV 的生命周期Retain/Delete/Recycle与 Pod 解耦数据可独立于应用存在避免误删应用连带删数据。策略统一通过 StorageClass 把造盘速度“加密”备份等策略标准化全公司一套规则。可移植换云厂商只改 StorageClass 的 provisioner业务 PVC 零改动。这正是 K8s “声明式 解耦” 哲学在存储领域的体现。五、FlexVolume 与 CSI 插件机制PV 怎么对接千奇百怪的存储后端K8s 通过存储插件机制扩展。FlexVolume旧早期方案以可执行二进制方式安装在每个节点kubelet 调用它完成 attach/mount。部署麻烦、依赖节点文件系统已被淘汰。CSIContainer 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...)小结 / 核心要点PV 是存储资源、PVC 是使用请求两者通过绑定解耦供应侧与消费侧。静态供给需预创建 PV动态供给由 StorageClass 在 PVC 创建时自动造 PV生产首选。本地卷hostPath/local性能高但绑节点网络存储NFS/Ceph可共享可迁移按需选择。PV/PVC 不是多此一举它实现研发/运维解耦、数据生命周期独立、策略标准化与跨云可移植。存储扩展走 CSI 标准接口取代旧 FlexVolume由 Controller Node 插件对接后端存储。