K8S 之 StorageClass

摘要

StorageClass(别名sc) 介绍

  • K8s 的存储资源分为两种供应模式:静态供应模式 和 动态供应模式。

    • 静态模式下,管理员需要预先创建许多PV,等待 PVC 来绑定。
    • 动态模式下,Kubernetes 会通过 StorageClass 自动创建 PV,并完成与 PVC 的绑定。
  • StorageClass 是 Kubernetes 中用来定义 存储后端类型和配置参数 的资源

  • 作用: 自动创建 PV(PersistentVolume)的“模板”或“规则”

  • 使用场景: 配合 PVC 使用时,Kubernetes 可以根据 StorageClass 自动 动态创建 对应的持久卷。

  • ✅ 核心优势:

    • 无需手动创建 PV
    • 支持多种存储后端(NFS、云盘、Ceph、GlusterFS、iSCSI…)

创建 StorageClass

  • 一个 NFS 的 StorageClass 的 yaml 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# nfs-storage.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-csi # 存储类名称
provisioner: nfs.csi.k8s.io # 指定存储插件的 CSI 驱动名称,需要安装 nfs-csi-driver
reclaimPolicy: Delete # Retain:PVC 删除后,PV 不会被自动删除(数据保留),默认是 Delete,生产环境慎用。
allowVolumeExpansion: true # 是否允许 PVC 自动扩容,nfs 支持扩容
mountOptions: # 挂载选项,csi插件不同,选项不同。这里是 nfs的
- hard # 服务器异常时客户端会一直发请求直到挂载成功
- nfsvers=4.1 # nfs版本 nfs4.1
- nolock # 允许多个客户端同时访问
volumeBindingMode: Immediate # 默认值:Immediate: 创建PVC时立即绑定 ,WaitForFirstConsumer:等到 Pod 调度到节点后再分配卷,适合多可用区场景
parameters: # 参数配置
server: 10.211.55.88 # nfs服务器地址
share: /nfs-server/data # nfs-server上的存储目录

csi-driver-nfs 从 v4.x 起,已经支持 Delete reclaimPolicy 和 allowVolumeExpansion 自动扩容。
生产环境慎用 Delete reclaimPolicy,因为此时删除 PVC 后,nfs-server 上的存储目录也会被删除。

  • 每个 StorageClass 配置中有三个必填的参数:provisionerparametersreclaimPolicy 字段, 这些字段会在 StorageClass 需要动态制备 PersistentVolume (PV) 以满足 PersistentVolumeClaim (PVC) 时使用到。

provisioner: 存储制备器

  • 每个 StorageClass 都有一个制备器(Provisioner),用来决定使用哪个卷插件制备 PV。 该字段必须指定。

  • 比如 NFS 存储制备器:provisioner: nfs.csi.k8s.io,是 K8S 官方维护的 NFS CSI 插件。Github

  • 安装 NFS CSI 插件:Install NFS CSI Driver

1
2
3
4
5
6
# 在线安装
curl -skSL https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/deploy/install-driver.sh | bash -s master --

# 验证
kubectl -n kube-system get pod -o wide -l app=csi-nfs-controller
kubectl -n kube-system get pod -o wide -l app=csi-nfs-node
  • 卸载

1
curl -skSL https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/deploy/uninstall-driver.sh | bash -s master --

示例

  • 创建 StorageClass

1
2
3
4
5
6
kubectl apply -f nfs-storage.yaml

## 查看 storageclass
$ k get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
nfs-csi nfs.csi.k8s.io Delete Immediate true 5s
  • 创建 PVC 时指定 storageclass,StorageClass 会自动创建 PV

1
2
3
4
5
6
7
8
9
10
11
12
13
# nfs-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
namespace: default
spec:
storageClassName: nfs-csi # 指定 StorageClass 的名称,如果设置了默认的sc,此处可以删除该配置
accessModes:
- ReadWriteMany # 指定访问模式
resources:
requests:
storage: 800Mi # 申请的容量
  • 创建 PVC

1
2
3
4
5
6
7
8
9
10
11
# 创建 PVC
$ k apply -f nfs-pvc.yaml
persistentvolumeclaim/nfs-pvc created

# 查看 PV和PVC,可以看到 PV 自动创建了
$ k get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
persistentvolume/pvc-c1d33fec-5e16-4156-8405-1af4fc171907 800Mi RWX Delete Bound default/nfs-pvc nfs-csi <unset> 2m4s

NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/nfs-pvc Bound pvc-c1d33fec-5e16-4156-8405-1af4fc171907 800Mi RWX nfs-csi <unset> 2m4s
  • 自动扩容,修改 PVC 容量到 1Gi

1
2
3
4
5
6
7
8
9
10
11
kubectl edit pvc nfs-pvc
# 修改如下
# "storage":"1Gi"

# 再次查看 PV 和 PVC,可以看到 自动扩容成功
$ k get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
persistentvolume/pvc-c1d33fec-5e16-4156-8405-1af4fc171907 1Gi RWX Delete Bound default/nfs-pvc nfs-csi <unset> 4m50s

NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/nfs-pvc Bound pvc-c1d33fec-5e16-4156-8405-1af4fc171907 1Gi RWX nfs-csi <unset> 4m50s
  • 删除PVC,自动删除PV

1
2
3
4
5
kubectl delete pvc nfs-pvc
# 再次查看 PV 和 PVC,可以看到 PV 已经被自动删除, reclaimPolicy: Delete
$ k get pv,pvc
No resources found
# 此时 nfs-server 的目录下的文件也会被删除,生产环境慎用

设置默认的 StorageClass

  • 上面我们创建 PVC 的时候需要指定 StorageClass,如果我们设置一个默认的 StorageClass,那么在PVC里就可以省略掉 StorageClass 的设置。

  • K8S 中只能设置一个默认的 StorageClass,如果有多个,那么就会报错。

  • 设置默认的 StorageClass

1
2
3
4
5
6
7
8
# 为 sc 添加 `默认` 注解,去掉该注解 或者 设置为 false 就取消默认了。
$ kubectl patch storageclass nfs-csi -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
storageclass.storage.k8s.io/nfs-csi patched

# 查看sc,此时看到名称后面多了一个 (default) ,表示这是默认的 StorageClass
$ k get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
nfs-csi (default) nfs.csi.k8s.io Delete Immediate true 14m

StatefulSet: 自动创建 PVC

  • 上面的方式还是要求我们必须创建 PVC,有什么方法可以不用创建 PVC ,而是在创建控制器的时候就一起把 PVC 创建好呢?

  • 目前只有 StatefulSet控制器 才支持自动创建 PVC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# redis-statefulset.yaml
apiVersion: apps/v1 # 指定使用的 API 版本,这里是 apps/v1,适用于 StatefulSet 资源
kind: StatefulSet # Kubernetes 资源类型,这里是部署(StatefulSet)
metadata:
name: redis-sts # 资源名称,必须唯一(在同一命名空间下)
namespace: sts-ns
spec: # 配置项
revisionHistoryLimit: 10 # 保留的历史版本数,默认值为 10,Deployment 和 StatefulSet 都有这个配置项。回滚时有用。
selector: # 选择器,指定要管理的 Pod
matchLabels: # 标签选择器
app: redis # 选择器,指定 StatefulSet 管理哪些 Pod(标签必须与 template 中匹配)
updateStrategy: # 更新策略,这里要注意这个更新策略与Deployment的属性名字不一样
type: RollingUpdate # 1.RollingUpdate:这是默认的更新策略。使用 RollingUpdate 更新策略时,在更新 StatefulSet 模板后, 老的 StatefulSet Pod 将被终止,并且将以受控方式自动创建新的 StatefulSet Pod。 更新期间,最多只能有 StatefulSet 的一个 Pod 运行于每个节点上。
# 2.OnDelete:使用 OnDelete 更新策略时,在更新 StatefulSet 模板后,只有当你手动删除老的 StatefulSet Pod 之后,新的 StatefulSet Pod 才会被自动创建。
rollingUpdate: # 滚动升级的配置
partition: 0 # 用于控制从第几个 Pod 开始滚动升级
serviceName: redis-svc # 服务名称,sts对象使用无头服务,这个是必填项,需要事先创建好
replicas: 2 # 副本数,默认是 1
template: # 模板,定义 Pod 的内容,具体可以参考 Pod 的配置
metadata:
labels:
app: redis # Pod 的标签,必须与 selector 中的 matchLabels 一致
spec:
containers:
- image: redis:6.2 # 容器使用的镜像,这里是官方的 redis 镜像
name: redis # 容器的名称
volumeMounts: # 挂载数据卷
- name: redis-data
mountPath: /data # Redis 持久化数据存储路径,根据实际情况修改
volumeClaimTemplates: # 配置 PVC 模板
- metadata:
name: redis-data # PVC 名称
spec:
accessModes:
- ReadWriteMany # 访问模式:多节点读写
resources:
requests:
storage: 500Mi # 请求的存储容量,根据实际需求调整
storageClassName: nfs-csi # 存储类名称,如果使用默认存储类,则不需要指定
  • 创建 StatefulSet,并查看 PV,PVC

1
2
3
4
5
6
7
8
9
10
11
12
# 创建 StatefulSet
$ kubectl apply -f redis-statefulset.yaml

# 查看 PV,PVC
$ k get pv,pvc -n sts-ns
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
persistentvolume/pvc-748fc0d3-a142-4d08-b5c4-7320daea5618 500Mi RWX Delete Bound sts-ns/redis-data-redis-sts-1 nfs-csi <unset> 19s
persistentvolume/pvc-807018bb-ec66-4a5c-87f1-9c11df6f4784 500Mi RWX Delete Bound sts-ns/redis-data-redis-sts-0 nfs-csi <unset> 22s

NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/redis-data-redis-sts-0 Bound pvc-807018bb-ec66-4a5c-87f1-9c11df6f4784 500Mi RWX nfs-csi <unset> 22s
persistentvolumeclaim/redis-data-redis-sts-1 Bound pvc-748fc0d3-a142-4d08-b5c4-7320daea5618 500Mi RWX nfs-csi <unset> 20s
  • StatefulSet 的 volumeClaimTemplates 每个 Pod 单独创建 PVC,Pod 和 PVC 一一对应,即有几个副本就创建几个 PVC。PVC 名称是:<volumeClaimTemplates.metadata.name>-<statefulset-name>-<pod-ordinal>

  • 删除 StatefulSet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
kubectl delete -f redis-statefulset.yaml

# 查看 PV,PVC,发现删除 StatefulSet 后,PV 和 PVC 是不会被自动删除的,需要手动删除 PVC
$ k get pv,pvc -n sts-ns
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
persistentvolume/pvc-748fc0d3-a142-4d08-b5c4-7320daea5618 500Mi RWX Delete Bound sts-ns/redis-data-redis-sts-1 nfs-csi <unset> 6m30s
persistentvolume/pvc-807018bb-ec66-4a5c-87f1-9c11df6f4784 500Mi RWX Delete Bound sts-ns/redis-data-redis-sts-0 nfs-csi <unset> 6m33s

NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/redis-data-redis-sts-0 Bound pvc-807018bb-ec66-4a5c-87f1-9c11df6f4784 500Mi RWX nfs-csi <unset> 6m33s
persistentvolumeclaim/redis-data-redis-sts-1 Bound pvc-748fc0d3-a142-4d08-b5c4-7320daea5618 500Mi RWX nfs-csi <unset> 6m31s

# 手动删除 PVC,因为sc配置的回收策略为 Delete,所以 PV 也会被删除
k delete pvc -n sts-ns redis-data-redis-sts-0
k delete pvc -n sts-ns redis-data-redis-sts-1
  • 实际上,无论是删除 statefulset 还是缩容 statefulset,PVC 都不会被自动删除。