深色模式
StorageClass 与动态供给
摘要:本文面向集群管理员,深入 StorageClass 对象与动态供给机制。讲清
volumeBindingMode的Immediate与WaitForFirstConsumer差异(后者是避免跨可用区死锁的关键)、allowVolumeExpansion扩容前提、默认 class 的设定,以及厂商参数差异与容量成本权衡。适用 Kubernetes v1.28+。
适用版本与前提
- Kubernetes:v1.28+(CSI 卷扩容需 v1.24+;详见正文)
- 工具:
kubectl、helm - 前提:已理解 PV/PVC 绑定
背景与问题
没有 StorageClass 时,管理员必须手工预创建每个 PV——规模一大就无法运维。StorageClass 把"供给逻辑"抽象成可声明的模板:用户在 PVC 里指一个 class 名字,集群自动调用对应 provisioner 创建 PV。它同时承担三件事:
- 选供给器:
provisioner决定后端由谁创建盘; - 定策略:回收策略、绑定时机、是否可扩容;
- 传参数:后端特有参数(盘类型、加密、性能档)。
StorageClass 对象结构
yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: low-latency
annotations:
storageclass.kubernetes.io/is-default-class: "false"
provisioner: csi-driver.example-vendor.example
reclaimPolicy: Retain # 默认 Delete
allowVolumeExpansion: true
mountOptions:
- discard # 触发 UNMAP/TRIM
volumeBindingMode: WaitForFirstConsumer
parameters:
guaranteedReadWriteLatency: "true" # 厂商特定1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
核心字段解析
provisioner(必填)
标识用哪个卷插件供给 PV。可以是 in-tree(前缀 kubernetes.io/,已逐步废弃)或外部 provisioner(如 ebs.csi.aws.com)。NFS 没有 in-tree provisioner,必须用外部 CSI(见 CSI 选型篇)。
reclaimPolicy
动态创建的 PV 继承此值,默认 Delete。强烈建议有状态服务用 Retain(见 PV/PVC 篇 的事故说明)。
allowVolumeExpansion
设为 true 后,用户可通过编辑 PVC 的 spec.resources.requests.storage 扩容。支持扩容的卷类型(部分):
| 卷类型 | 所需 K8s 版本 |
|---|---|
| CSI | v1.24 |
| Azure File | v1.11 |
| Portworx | v1.11 |
| RBD | v1.11 |
版本相关
CSI 卷的扩容能力自 v1.24 起稳定;早期版本需启用 ExpandCSIVolumes 特性门。扩容是否真的能在线生效,还取决于具体 CSI 驱动与文件系统(ext4/xfs 支持在线扩,某些块设备需 Pod 重启)。行动前先查驱动文档。
volumeBindingMode(拓扑关键)
Immediate(默认):PVC 一创建就绑定并动态供给,不管 Pod 将来调度到哪。WaitForFirstConsumer:延迟到"第一个使用该 PVC 的 Pod 被调度"后再供给,使 PV 落在 Pod 所在拓扑(可用区/节点)内。
生产危险
拓扑受限的后端(云盘、本地盘)若用 Immediate,盘可能在 Pod 调度节点之外的可用区被创建,导致 Pod 因"卷与节点不在同一 AZ"无法启动(Multi-Attach / NodeAffinity conflict)。所有云盘、本地盘 StorageClass 都应设 WaitForFirstConsumer。这是存储死锁最常见的根因。
注意
启用 WaitForFirstConsumer 后,PVC 在 Pod 创建前会一直 Pending——这是预期行为,不是故障。另外,此时 Pod 不要用 spec.nodeName 指定节点(会绕过调度器导致 PVC 永久 pending),改用 nodeSelector: kubernetes.io/hostname。
allowedTopologies
当 WaitForFirstConsumer 仍不足以约束时,可显式限制供给拓扑(替代旧 zone/zones 参数):
yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: standard
provisioner: example.com/example
parameters:
type: pd-standard
volumeBindingMode: WaitForFirstConsumer
allowedTopologies:
- matchLabelExpressions:
- key: topology.kubernetes.io/zone
values:
- us-central-1a
- us-central-1b1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
动态供给原理
动态供给依赖:
- API server 启用
DefaultStorageClassadmission 插件; - StorageClass 的 provisioner 对应的 external-provisioner 正在运行(通常是 CSI 驱动的一部分)。
默认 StorageClass
集群中最多一个 class 带注解 storageclass.kubernetes.io/is-default-class: "true"。PVC 不指定 class 时落默认;指定 storageClassName: "" 则禁用动态供给(仅匹配静态 PV)。
bash
kubectl get storageclass # 带 (default) 标记
kubectl patch storageclass <name> \
-p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'1
2
3
2
3
厂商参数差异
不同 provisioner 的 parameters 完全不同(厂商特定):
- AWS EBS CSI(
ebs.csi.aws.com):type(gp3/io2)、iopsPerGB、encrypted、csi.storage.k8s.io/fstype; - 阿里云盘 CSI(
disk.csi.alibabacloud.com):type(cloud_ssd/cloud_essd)、performanceLevel; - NFS CSI(
csi-driver-nfs):server、share; - Rook-Ceph RBD:
imageFormat、csi.storage.k8s.io/fstype。
厂商特定
厂商参数随驱动版本变化,本文不穷举。落地前务必核对目标驱动官方文档的 parameters 列表,错误参数会导致 provision 失败。
生产实践
- 按"性能/成本/备份"建立多档 class:如
gold(ESSD+Retain)、standard(SSD+Delete)、cheap(HDD); - 有状态服务统一挂
Retain+ 默认不开扩容(避免意外扩大账单),按需显式开启; - 给所有拓扑受限后端设
WaitForFirstConsumer; - 用
ResourceQuota限制 namespace 的 PVC 总量与storage总请求,防容量失控。
验证
bash
kubectl get storageclass
kubectl describe storageclass <name> # 查看 Provisioner/BindingMode/ReclaimPolicy
# 创建测试 PVC 观察是否自动生成 PV
kubectl apply -f pvc-test.yaml
kubectl get pv # 应出现新 PV 且 Bound1
2
3
4
5
2
3
4
5
回滚与清理
生产危险
删除 StorageClass 不会删除已用其创建的 PV/PVC(对象保留),但会断掉未来动态供给。删除前确认无 pending PVC 依赖该 class。
bash
kubectl delete storageclass <name> # 仅删 class 定义,不动已有 PV
# 若误删且需恢复,重新 apply 相同 yaml 即可(不影响已有 PV)1
2
2
容量与成本权衡
Immediate模式下预创建盘会提前占用配额与成本;WaitForFirstConsumer按需创建更省,但首次调度略慢。- 扩容
allowVolumeExpansion方便,但云盘扩容后通常不能缩容,规划初始容量需留余量。 - 多可用区部署时,跨 AZ 流量与副本盘会带来额外成本,需在 RPO/RTO 与账单间权衡。
常见坑
- 忘了设默认 class,导致用户 PVC 因
storageClassName缺失而 pending。 - 把
WaitForFirstConsumer的 PVC pending 误判为故障,反复重建。 - 动态 PV 默认
Delete,删 PVC 连带删盘——生产数据库务必改Retain。
参考资料
- Kubernetes 官方文档 - Storage Classes,访问日期:2026-10-08。
- Kubernetes 官方文档 - Persistent Volumes(动态供给/扩容),访问日期:2026-10-08。
- Kubernetes 官方文档 - Volumes (CSI),访问日期:2026-10-08。