4、Kubernetes存储基石:PV、PVC、StorageClass、CSI插件机制详解
说实话,Kubernetes的存储体系,是很多初学者最头疼的部分。我自己刚接触那会儿,也被PV、PVC这些概念绕得晕头转向。但后来我发现,只要搞清楚了它们各自扮演的角色,整个存储体系其实非常清晰。
今天我们就来彻底拆解这四个核心组件。我会结合我实际踩过的坑,帮你把这块硬骨头啃下来。
4.1 从问题出发:为什么需要PV和PVC?
先想想看,在Kubernetes里跑一个有状态应用,比如MySQL。Pod是随时可能被调度到不同Node上的。如果数据直接写在Pod里,Pod一重启,数据就丢了。这显然不行。
所以我们需要一个独立于Pod生命周期的存储。这就是持久卷(PersistentVolume,PV)的由来。PV是集群管理员预先准备好的一块存储资源,它可以是NFS、Ceph、云硬盘等等。
但问题又来了:应用开发者(比如写MySQL的YAML的人)并不关心底层存储是NFS还是Ceph。他只知道“我需要一个10GB的存储空间”。
这时候,持久卷声明(PersistentVolumeClaim,PVC)就派上用场了。PVC是用户对存储的请求,它只声明需求(比如“我要10GB,读写模式是ReadWriteOnce”),而不关心底层实现。
核心思想:PV是资源,PVC是请求。PVC和PV的关系,就像Pod和Node的关系——Pod声明资源需求,调度器找到满足条件的Node来运行它。
4.2 PV与PVC的生命周期与绑定机制
PV和PVC是怎么匹配上的?这里有个绑定(Binding)的过程。Kubernetes会寻找一个满足PVC需求的PV(容量、访问模式、存储类等),然后将它们绑定在一起。一旦绑定,这个PVC就只能使用这个PV了。
我记得有一次,我创建了一个PVC,等了半天状态还是Pending。排查后发现,集群里根本没有满足条件的PV。嗯,这里要注意,PVC的绑定是一对一的,一个PV只能被一个PVC使用。
当PVC被删除时,PV的命运取决于它的回收策略(Reclaim Policy):
- Retain(保留):PV保留,数据还在,但需要管理员手动清理才能被再次使用。这是最安全的策略,我建议生产环境用这个。
- Delete(删除):PV和底层存储一起被删除。适合动态创建的云硬盘。
- Recycle(回收):已废弃,不推荐使用。
避坑指南:我曾经在生产环境误删了一个PVC,结果PV的回收策略是Delete,整个云硬盘连带数据都被删了。从那以后,我所有核心业务的PV都强制设置为Retain。
4.3 StorageClass:让存储“动态”起来
手动创建PV太麻烦了。每次开发要个存储,运维都得去云平台创建一个云硬盘,再手动创建PV。这效率太低了。
所以就有了StorageClass。它相当于一个存储的“模板”或“配置类”。你只需要在PVC里指定storageClassName,Kubernetes就会根据StorageClass的配置,自动调用云厂商的API去创建底层存储,并自动生成PV。
说白了,StorageClass让存储实现了动态供给(Dynamic Provisioning)。你想想看,这就像你告诉Kubernetes:“我要一个SSD类型的100GB云盘”,然后它自动帮你创建好,再绑定到你的Pod上。
下面是一个典型的StorageClass定义:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp3
fsType: ext4
reclaimPolicy: Delete
allowVolumeExpansion: true
使用的时候,PVC只需要这样写:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-app-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: fast-ssd
是不是简单多了?开发人员只需要知道存储类的名字,运维人员只需要定义好StorageClass的配置。各司其职。
4.4 CSI插件机制:存储的“万能接口”
在CSI出现之前,Kubernetes内置了很多存储插件(in-tree插件),比如NFS、iSCSI、GCE PD等。但问题是,每增加一种存储,就得修改Kubernetes核心代码。这导致Kubernetes的代码库越来越臃肿,而且新存储的适配周期很长。
CSI(Container Storage Interface)的出现,彻底解决了这个问题。它是一个标准接口,就像USB接口一样。任何存储厂商,只要实现了CSI规范,就能无缝接入Kubernetes。
我个人习惯把CSI理解成一个“存储驱动”。它运行在Kubernetes集群中,通常以DaemonSet或Deployment的形式部署。当Kubernetes需要创建、挂载、删除存储卷时,就会通过CSI接口调用这个驱动。
CSI的工作流程大致如下:
- 注册:CSI驱动向Kubelet注册自己。
- 创建卷:当PVC需要动态供给时,外部provisioner组件调用CSI驱动的
CreateVolume接口。 - 挂载卷:当Pod调度到某个Node时,Kubelet调用CSI驱动的
NodePublishVolume接口,将卷挂载到Pod中。 - 卸载与删除:Pod删除或PVC删除时,调用对应的接口清理资源。
为什么CSI这么重要?因为它让Kubernetes的存储生态变得极其丰富。现在主流的存储系统(如Ceph、Portworx、Longhorn、AWS EBS、Azure Disk等)都提供了CSI驱动。你几乎可以在Kubernetes上使用任何你想要的存储。
4.5 实战中的选型建议
讲了这么多理论,最后聊聊我个人的选型经验。不同场景下,这四个组件的使用策略完全不同:
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 开发/测试环境 | 使用StorageClass动态供给,回收策略为Delete | 快速创建,用完即删,节省成本 |
| 生产环境(数据库) | 手动创建PV,回收策略为Retain,使用CSI驱动 | 数据安全第一,避免误删。CSI驱动提供更稳定的性能 |
| 生产环境(无状态应用日志) | 使用StorageClass动态供给,回收策略为Delete,但开启快照 | 日志可以丢失,但需要快速扩容。快照用于短期回溯 |
| 混合云/多云 | 使用统一的CSI驱动(如Ceph RBD或Portworx) | 屏蔽底层云厂商差异,实现存储层统一管理 |
重要提醒:不要在生产数据库上使用回收策略为Delete的动态供给。我曾经见过一个同事,因为误删了StatefulSet,导致所有PVC被级联删除,数据库数据全部丢失。血的教训。
最后总结一下:PV是存储资源,PVC是资源请求,StorageClass是动态供给的模板,CSI是标准化的存储接口。这四个组件构成了Kubernetes存储的完整基石。理解了它们,你就能在Kubernetes上放心地跑任何有状态应用了。