K8s 系列 | 第 9 天:StorageClass 与动态存储供给实战


title: “K8s 系列 | 第 9 天:StorageClass 与动态存储供给实战”
tags: [Kubernetes, K8s系列, DevOps, Storage, StorageClass, PVC, CSI]
date: 2026-06-25


第 9/30 天

引言

在前一篇文章中,我们深入探讨了 Volume 和 PersistentVolume(PV)的基本概念——它们是 Kubernetes 容器存储的基石。但在生产环境中,手动管理 PV 是一件极其繁琐的任务:每次 Pod 需要存储时,运维人员都必须预先准备好存储设备、创建 PV、再手动绑定到 PVC。这种做法在大规模集群中完全不可持续。

StorageClass 的诞生就是为了解决这个问题。 它引入了一种”动态存储供给”(Dynamic Provisioning)机制——当用户创建 PVC 时,Kubernetes 不再需要等待管理员手动创建 PV,而是根据 StorageClass 中定义的配置自动生成 PV,实现存储的”按需分配”。

本文将深入讲解 StorageClass 的核心原理、常见配置场景,并通过实战演示如何在不同环境中配置和使用动态存储供给。


核心概念

什么是 StorageClass?

StorageClass 是 Kubernetes 中用于定义存储”类别”的 API 资源。它充当了一个存储模板的角色,告诉 Kubernetes:

  1. 使用哪个存储后端(例如:本地存储、NFS、Ceph RBD、AWS EBS、GCE PD 等)
  2. 使用什么参数(例如:磁盘类型、IOPS、回收策略等)
  3. 使用哪个 Provisioner(负责实际创建存储卷的插件)

动态供给 vs 静态供给

对比维度 静态供给(无 StorageClass) 动态供给(有 StorageClass)
操作流程 管理员提前创建 PV → 用户创建 PVC → 绑定 用户创建 PVC(指定 StorageClass)→ 自动创建 PV
运维成本 高——需要管理员持续预分配存储 低——按需自动分配
资源利用率 低——预分配可能造成浪费 高——真正按需分配
适用场景 测试集群、存储后端有限的环境 生产集群、云环境

StorageClass 的核心字段

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp3
  iopsPerGB: "10"
  fsType: ext4
reclaimPolicy: Retain      # 或 Delete(默认)
allowVolumeExpansion: true  # 是否允许在线扩容
volumeBindingMode: Immediate  # 或 WaitForFirstConsumer
mountOptions:
  - discard
  • provisioner:指定存储插件(CSI 驱动或内置驱动)
  • parameters:传递给 provisioner 的配置参数,不同 provisioner 各不相同
  • reclaimPolicy:当 PVC 被删除时,PV 的处理方式——Delete(自动删除底层存储)或 Retain(保留数据)
  • allowVolumeExpansion:是否允许后续对 PVC 进行扩容
  • volumeBindingModeImmediate 表示立即创建 PV(即使 Pod 还未调度),WaitForFirstConsumer 表示等到 Pod 被调度到某节点后再创建 PV(确保 PV 与节点在同一可用区)

动态供给的工作流程

用户创建 PVC (指定 StorageClass)
        ↓
Kubernetes 检测到 PVC 未绑定
        ↓
查找匹配的 StorageClass
        ↓
调用对应的 Provisioner 插件
        ↓
Provisioner 在存储后端创建真实存储卷
        ↓
Kubernetes 创建 PV 对象并绑定到 PVC
        ↓
Pod 使用 PVC 挂载存储卷

实战步骤

环境准备

首先检查集群中已有的 StorageClass:

# 查看集群中所有 StorageClass
kubectl get storageclass

# 查看默认的 StorageClass(带 (default) 标注)
kubectl get sc

# 查看 StorageClass 的详细配置
kubectl describe storageclass <name>

在一个典型的 kubeadm 部署的集群中,如果安装了相关的 CSI 驱动,你可能会看到类似这样的输出:

NAME                 PROVISIONER                RECLAIMPOLICY   VOLUMEBINDINGMODE
local-storage        rancher.io/local-path      Delete          WaitForFirstConsumer
standard (default)   k8s.io/minikube-hostpath   Delete          Immediate

场景一:使用本地路径 StorageClass(开发环境)

对于本地开发集群(如 minikube、kind、kubeadm 单节点),推荐使用 rancher.io/local-path provisioner 来提供本地存储:

# 安装 local-path-provisioner
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml

# 验证安装
kubectl -n local-path-storage get pod

然后创建一个使用该 StorageClass 的 PVC:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: local-pvc-demo
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: local-path        # 指定 StorageClass
  resources:
    requests:
      storage: 5Gi

创建后验证动态供给是否生效:

kubectl get pvc
kubectl get pv

你会看到 PVC 的状态立即变为 Bound,并且系统自动创建了一个 PV——这就是动态供给的魔力。

场景二:使用 NFS CSI 驱动(生产环境)

生产环境中,NFS 是最广泛使用的共享存储方案之一。我们通过 NFS CSI 驱动配置 StorageClass:

# 1. 部署 NFS CSI 驱动
kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/deploy/example/nfs-provisioner.yaml

# 2. 创建 NFS StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-csi
provisioner: nfs.csi.k8s.io
parameters:
  server: 192.168.1.100              # NFS 服务器地址
  share: /exported/path              # NFS 共享路径
  mountPermissions: "0777"
reclaimPolicy: Delete
volumeBindingMode: Immediate
allowVolumeExpansion: true

创建后测试使用:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc
spec:
  accessModes:
    - ReadWriteMany          # NFS 支持多节点读写
  storageClassName: nfs-csi
  resources:
    requests:
      storage: 10Gi
---
apiVersion: v1
kind: Pod
metadata:
  name: nfs-test-pod
spec:
  containers:
    - name: app
      image: nginx
      volumeMounts:
        - name: storage
          mountPath: /data
  volumes:
    - name: storage
      persistentVolumeClaim:
        claimName: nfs-pvc

场景三:配置默认 StorageClass

设置一个默认的 StorageClass,当 PVC 不指定 storageClassName 时自动使用:

# 查看当前的默认 StorageClass
kubectl get storageclass -o jsonpath='{.items[?(@.metadata.annotations.storageclass.kubernetes.io/is-default-class=="true")].metadata.name}'

# 将某个 StorageClass 设为默认
kubectl patch storageclass nfs-csi -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

# 移除默认标注(从其他 StorageClass 上)
kubectl patch storageclass standard -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'

⚠️ 注意:集群中只能有一个默认 StorageClass。如果存在多个,PVC 不指定 storageClassName 时会报错。

场景四:WaitForFirstConsumer 延迟绑定

对于本地存储或拓扑敏感的存储(如 AWS EBS 只能在特定可用区创建),使用 WaitForFirstConsumer 模式可以延迟 PV 创建,直到 Pod 被调度:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-ssd
provisioner: rancher.io/local-path
volumeBindingMode: WaitForFirstConsumer   # 延迟绑定
reclaimPolicy: Delete

这种模式下,创建 PVC 时它不会立即绑定,而是保持 Pending 状态。直到引用了该 PVC 的 Pod 被调度到某节点后,Kubernetes 才在该节点上创建 PV 并绑定。这样就保证了存储卷一定位于 Pod 所在节点。

# PVC 创建后是 Pending 状态
kubectl get pvc   # STATUS: Pending

# 创建引用了 PVC 的 Pod 后
kubectl apply -f pod.yaml
kubectl get pvc   # STATUS: Bound(Pod 调度后自动绑定)

常见问题

Q1:PVC 一直处于 Pending 状态

可能原因:
1. 没有匹配的 StorageClass — PVC 指定的 storageClassName 不存在
2. Provisioner 未部署 — CSI 驱动 Pod 未正常运行
3. 存储后端不可达 — NFS 服务器不通或权限不足
4. 资源不足 — 存储池空间耗尽

排查方法:

kubectl describe pvc <pvc-name>
kubectl describe storageclass <sc-name>
kubectl -n <csi-namespace> logs -l app=<provisioner-app>

Q2:动态供给创建的磁盘大小不对

某些 CSI 驱动的参数可能以字节或不同单位解释。务必查看对应驱动文档中的 parameters 含义:

# 查看 provisioner 支持的参数
kubectl describe storageclass <name> | grep Parameters

Q3:如何限制 StorageClass 的使用范围

通过 RBAC 或 ResourceQuota 来控制哪些 Namespace 可以使用哪些 StorageClass:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: storage-quota
  namespace: dev-team
spec:
  hard:
    persistentvolumeclaims: "10"                   # PVC 数量限制
    requests.storage: "100Gi"                      # 总存储量限制
    <storage-class-name>.storageclass.storage.k8s.io/requests.storage: "50Gi"  # 针对特定 StorageClass

Q4:动态供给的 PV 可以扩容吗?

可以,但需要满足两个条件:
1. StorageClass 的 allowVolumeExpansion: true
2. 底层存储后端支持在线扩容

# 扩容 PVC(不需要删除重建)
kubectl edit pvc <pvc-name>
# 将 spec.resources.requests.storage 从 10Gi 改为 20Gi

扩容后需要 Pod 内的文件系统识别新大小(某些系统自动识别,某些需要手动 resize2fs)。


总结

StorageClass 是 Kubernetes 存储体系中最核心的抽象之一。它将存储的”提供”和”使用”彻底解耦,让:

  • 运维人员只需配置好 StorageClass 模板,无需手动管理 PV
  • 开发人员只需在 PVC 中指定存储大小和 StorageClass,存储自动就绪
  • 集群能根据不同负载自动选择最合适的存储后端

关键要点回顾:

概念 作用
StorageClass 存储模板,定义 provisioner 和参数
Dynamic Provisioning 按需自动创建 PV,无需人工干预
reclaimPolicy Delete(自动清理)/ Retain(保留数据)
volumeBindingMode Immediate(立即绑定)/ WaitForFirstConsumer(延迟到 Pod 调度后)
allowVolumeExpansion 允许 PVC 在线扩容

在生产环境中,建议至少配置 2-3 个不同类型的 StorageClass:一个高性能 SSD 类(数据库)、一个普通 HDD 类(日志归档)、一个共享文件系统类(NFS/CephFS),以满足不同工作负载的需求。


下期预告

第 10 天:StatefulSet:有状态应用的部署与管理

在学习了存储和网络的基础之后,我们将进入有状态应用的世界。StatefulSet 是 Kubernetes 为数据库、消息队列等有状态工作负载量身打造的控制器——它如何保证 Pod 的唯一标识?如何与 PV 协同工作?下一篇文章将为你揭晓。


📚 系列目录(持续更新中)**

天数 标题 状态
第 1 天 Kubernetes 是什么?核心概念与架构全景解析 ✅ 已发布
第 2 天 手把手搭建你的第一个 K8s 集群(kubeadm 实战) ✅ 已发布
第 3 天 Pod 详解:K8s 最小的调度单元与生命周期管理 ✅ 已发布
第 4 天 Deployment 与 ReplicaSet:声明式应用管理 ✅ 已发布
第 5 天 Service 与网络基础:ClusterIP、NodePort、LoadBalancer 详解 ✅ 已发布
第 6 天 Namespace 与资源配额:多租户隔离基础 ✅ 已发布
第 7 天 ConfigMap 与 Secret:配置管理最佳实践 ✅ 已发布
第 8 天 Volume 与 PersistentVolume:存储抽象层的核心机制 ✅ 已发布
第 9 天 StorageClass 与动态存储供给实战 📝 本文
第 10 天 StatefulSet:有状态应用的部署与管理 🔜 即将发布
第 11 天 Ingress 与 Ingress Controller:外部流量接入全攻略 🔜 即将发布
第 12 天 NetworkPolicy:K8s 网络安全策略与微隔离 🔜 即将发布
第 13 天 Headless Service 与服务发现机制深度解析 🔜 即将发布
第 14 天 CSI 存储插件与生产存储选型指南 🔜 即将发布
第 15 天 污点与容忍度:掌控 Pod 调度 🔜 即将发布
第 16 天 Node Affinity 与 Pod Affinity:精细化调度策略实战 🔜 即将发布
第 17 天 HPA 水平自动扩缩:基于 CPU/内存/自定义指标的弹性伸缩 🔜 即将发布
第 18 天 VPA 与 Cluster Autoscaler:资源与集群层的自动扩缩 🔜 即将发布
第 19 天 Job 与 CronJob:批处理与定时任务实战 🔜 即将发布
第 20 天 PriorityClass 与抢占式调度机制 🔜 即将发布
第 21 天 资源配额与 LimitRange:多租户资源管控 🔜 即将发布
第 22 天 监控体系搭建:Prometheus + Grafana + Kube-state-metrics 🔜 即将发布
第 23 天 日志收集实战:EFK/ELK 栈在 K8s 中部署 🔜 即将发布
第 24 天 RBAC 权限控制:ServiceAccount、Role、ClusterRole 深度解析 🔜 即将发布
第 25 天 Helm Charts:包管理器使用与 Chart 开发实战 🔜 即将发布
第 26 天 集群备份与恢复:etcd 快照 + Velero 方案 🔜 即将发布
第 27 天 滚动更新与回滚策略:实现零停机发布 🔜 即将发布
第 28 天 集群升级最佳实践:Control Plane 与 Node 升级步骤 🔜 即将发布
第 29 天 多集群管理:Federation / Cluster API / 多集群服务网格 🔜 即将发布
第 30 天 K8s 生产环境踩坑实录:性能调优、故障排查与最佳实践 🔜 即将发布
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容