在 Kubernetes 的容器编排世界里,Pod 是短暂的。它们可以随时被创建、删除或迁移到集群中的其他节点。这对于无状态应用(如一个只提供静态内容的 Web 服务器)来说不是问题,但对于有状态应用(如数据库、消息队列或需要存储用户上传文件的应用),数据持久化是核心需求。数据绝不能随着 Pod 的消失而丢失。

为了解决这一挑战,Kubernetes 引入了三个关键概念:Persistent Volume (PV)Persistent Volume Claim (PVC)StorageClass (SC)。它们共同构建了一套强大的存储管理系统,将存储的生命周期与 Pod 的生命周期解耦,确保您的应用数据能够安全地、长期地保存下来。


1. PV (Persistent Volume):集群的“共享硬盘”

想象一下,PV 是 Kubernetes 集群中真实存在的、可供使用的存储资源。它就像是集群管理员在数据中心里准备好的一个个“共享硬盘”或“存储单元”。这些存储可能来自各种底层技术:网络文件系统 (NFS)、iSCSI、云提供商的块存储服务(如 AWS EBS、Google Persistent Disk、Azure Disk)、或本地存储。

关键特性:

  • 集群级别资源: PV 属于整个 Kubernetes 集群,不依附于任何特定的 Pod 或命名空间。
  • 存储细节抽象: PV 封装了底层存储的所有复杂细节,比如存储类型、容量、访问模式(如只读、单节点读写、多节点读写)以及回收策略。
  • 独立生命周期: 即使使用它的 Pod 被删除或重新调度,PV 中的数据仍然安全地保留。
  • 两种供应方式:
    • 静态供应: 管理员手动创建 PV 对象,使其指向一个预先存在的存储卷。
    • 动态供应: (更常用和推荐)当用户请求存储时,Kubernetes 会根据规则自动创建 PV。

PV YAML 示例(静态供应,指向一个 NFS 共享):

apiVersion: v1
kind: PersistentVolume
metadata:
  name: my-nfs-pv # PV 的唯一名称
spec:
  capacity:
    storage: 10Gi # 这个 PV 提供的存储容量
  accessModes:
    - ReadWriteMany # 访问模式:可以被多个节点同时以读写方式挂载
  persistentVolumeReclaimPolicy: Retain # 回收策略:当 PVC 被删除时,保留底层数据
  nfs: # 存储类型为 NFS
    path: /srv/nfs/volumes/data # NFS 服务器上的共享路径
    server: 192.168.1.100 # NFS 服务器的 IP 地址

2. PVC (Persistent Volume Claim):应用的“存储申请单”

如果 PV 是“共享硬盘”,那么 PVC 就是你的应用程序(Pod)向 Kubernetes 提交的一份“存储申请单”。作为应用开发者,你不需要关心 PV 的具体物理位置或类型,你只在申请单上写明自己需要的存储特性。

关键特性:

  • 命名空间资源: PVC 属于特定的命名空间,方便团队管理。
  • 需求声明: PVC 声明了所需存储的容量(例如 5GB)、访问模式(例如单节点读写)以及要使用的存储类别(通过 StorageClass)。
  • 绑定机制: Kubernetes 收到 PVC 申请后,会尝试找到一个符合要求的 PV 与之绑定。一旦绑定,这个 PV 就被该 PVC 独占了。
  • Pod 中引用: Pod 通过引用 PVC 来使用持久化存储,而不是直接引用 PV。

PVC YAML 示例:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-app-data-pvc # PVC 的名称
  namespace: default   # PVC 所属的命名空间
spec:
  accessModes:
    - ReadWriteOnce # 请求单节点读写访问
  resources:
    requests:
      storage: 5Gi # 请求 5GB 的存储空间
  storageClassName: fast-ssd-storage # 指定要使用的 StorageClass

3. StorageClass (SC):硬盘的“自动化生产线”与“服务标准”

StorageClass 是实现存储动态供应的关键。它由集群管理员定义,用于描述不同**“类别”**的存储服务,以及 Kubernetes 如何自动创建这些存储卷。你可以把它看作是“硬盘生产线标准”或“存储服务模板”。

关键特性:

  • 管理员定义: 由集群管理员创建和配置,通常一次配置,多处复用。
  • 动态供应核心: 当 PVC 引用某个 StorageClass 时,SC 中定义的 provisioner(存储插件)会被调用,自动在底层存储系统上创建实际的存储卷,并生成对应的 PV 对象。
  • 定义存储特性: SC 中可以定义:
    • provisioner: 指定哪个存储插件负责供应存储(例如,GCP 的 pd.csi.storage.gke.io,AWS 的 ebs.csi.aws.com,或本地路径的 rancher.io/local-path)。
    • parameters: 传递给 provisioner 的具体参数,用于细化存储特性,如硬盘类型(SSD/HDD)、IOPS、复制策略等。
    • reclaimPolicy: 当 PV 被释放(PVC 被删除)时,底层存储资源如何处理(Retain 保留数据,Delete 自动删除数据)。
    • volumeBindingMode: 定义 PV 和 PVC 绑定的时机(Immediate 立即绑定,WaitForFirstConsumer 等 Pod 真正需要时再绑定,有助于调度优化)。

StorageClass YAML 示例(使用 GCP 的 SSD 持久盘):

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd-storage # StorageClass 的名称
provisioner: pd.csi.storage.gke.io # GCP CSI 驱动,用于动态创建持久盘
parameters:
  type: pd-ssd # 参数:创建 SSD 类型的持久盘
reclaimPolicy: Delete # 当 PVC 删除时,自动删除对应的 GCP 持久盘
volumeBindingMode: WaitForFirstConsumer # 等待第一个 Pod 调度到节点时才创建实际存储

PV、PVC、SC 协同工作:一个 Nginx 网站的例子

让我们用一个具体的例子来理解这三者是如何协同工作的:为 Nginx 网站提供持久化存储。

目标: 部署一个 Nginx Pod,并将它的网页文件存储在一个 1GB 的高速 SSD 盘上,确保数据不丢失。

步骤 1:集群管理员定义 StorageClass (SC)

管理员首先定义一个名为 my-fast-ssd 的 StorageClass,告诉 Kubernetes 如何提供高速 SSD 存储

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: my-fast-ssd # 定义一个名为 'my-fast-ssd' 的存储类别
provisioner: pd.csi.storage.gke.io # 使用 GCP 的 CSI 驱动来创建存储
parameters:
  type: pd-ssd # 指定创建 SSD 类型的磁盘
reclaimPolicy: Delete # 当 PVC 被删除时,对应的 SSD 磁盘也会被删除
volumeBindingMode: Immediate # 立即绑定 PVC 和 PV

管理员执行:kubectl apply -f 1-storageclass.yaml 此时,没有实际的存储卷被创建,只是定义了一个规则。

步骤 2:开发者(你)在 StatefulSet 中声明 PVC 模板

你编写一个 Nginx 的 StatefulSet,并在其 volumeClaimTemplates 中声明你需要的存储。

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: nginx-web # StatefulSet 的名称
spec:
  serviceName: "nginx-service" # 关联的 Headless Service
  replicas: 1
  selector:
    matchLabels:
      app: nginx-web
  template:
    metadata:
      labels:
        app: nginx-web
    spec:
      containers:
      - name: nginx-container
        image: nginx:latest
        ports:
        - containerPort: 80
        volumeMounts:
        - name: website-content # 引用下方 PVC 模板中定义的卷
          mountPath: /usr/share/nginx/html # Nginx 存放网页文件的目录
      # ... 其他可能的卷和配置 ...
  volumeClaimTemplates: # <<--- 关键:在这里定义 PVC 模板
  - metadata:
      name: website-content # 这个名称会成为实际 PVC 名称的一部分,并与 volumeMounts 对应
    spec:
      accessModes: [ "ReadWriteOnce" ] # 请求单节点读写
      storageClassName: "my-fast-ssd" # <<--- 明确指出使用我们定义的 StorageClass
      resources:
        requests:
          storage: 1Gi # <<--- 请求 1GB 的存储空间

执行:kubectl apply -f 2-nginx-statefulset.yaml

魔法发生:

  1. nginx-web StatefulSet 启动时,它会根据 replicas: 1 创建一个 Pod (例如 nginx-web-0)。
  2. 为了给 nginx-web-0 提供存储,StatefulSet 会自动创建一个 PVC,例如名称可能为 website-content-nginx-web-0
  3. 这个自动创建的 PVC 会请求 1GB 存储,并指定 storageClassName: "my-fast-ssd"
  4. Kubernetes 调度器接收到这个 PVC 请求,并找到匹配的 my-fast-ssd StorageClass。
  5. my-fast-ssd StorageClass 定义的 pd.csi.storage.gke.io provisioner 被调用。
  6. 这个 provisioner 会在 GCP 云平台上动态创建一个实际的 1GB SSD 持久盘。
  7. 同时,Kubernetes 会为这个新创建的 GCP 持久盘生成一个对应的 PersistentVolume (PV) 对象(例如 pvc-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)。
  8. 最后,这个新的 PV 会自动与 website-content-nginx-web-0 这个 PVC 绑定。
  9. Nginx Pod (nginx-web-0) 启动后,这个 PV(代表的 GCP SSD 盘)就会被挂载到其 /usr/share/nginx/html 路径下。

验证一下:

kubectl get pvc
# 应该会看到一个类似 'website-content-nginx-web-0' 的 PVC,状态为 'Bound'

kubectl get pv
# 应该会看到一个自动创建的 PV,状态也是 'Bound',且容量为 1Gi

现在,即使 Nginx Pod 重启、被删除或被调度到其他节点,只要 website-content-nginx-web-0 这个 PVC 不被删除,底层的数据就始终存储在那个 1GB 的 SSD 持久盘上,不会丢失。

通过 PV、PVC 和 StorageClass,Kubernetes 实现了存储的解耦和自动化

  • StorageClass (SC): 定义了存储服务的“类型”和“自动化创建规则”。
  • PersistentVolumeClaim (PVC): 应用对存储的“需求声明”,包括容量和所需类型。
  • PersistentVolume (PV): Kubernetes 中实际可用的、持久化的存储资源。
Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐