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的工作流程大致如下:

  1. 注册:CSI驱动向Kubelet注册自己。
  2. 创建卷:当PVC需要动态供给时,外部provisioner组件调用CSI驱动的CreateVolume接口。
  3. 挂载卷:当Pod调度到某个Node时,Kubelet调用CSI驱动的NodePublishVolume接口,将卷挂载到Pod中。
  4. 卸载与删除: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上放心地跑任何有状态应用了。