在 k8s 中当我们需要持久化存储一些数据的使用,会使用到的就是 PV 和 PVC,但 PV 和 PVC 都是需要手动创建的话会很麻烦,特别是当有 StatefulSet 应用存在的时候,如果你需要手动对每个 pod 都创建一个 PVC 和 PV 就非常麻烦,于是 StorageClass 就是来解决这个问题的。

准备

首先你需要一个 nfs 或其他存储,这里我以 nfs 为例进行部署

我们先来梳理一下思路和几个需要的东西:

  • nfs 是我们最终的存储
  • nfs-client 是用来动态创建 pv 和 pvc 的,我们称为 provisioner
  • StorageClass 关联到对应的 provisioner 就可以使用
  • statefulset(或别的资源)需要配置 storageClassName 进行使用

部署

创建 ServiceAccount

创建对应需要使用的 ServiceAccount,因为需要操作 pv 和 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
40
41
42
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
namespace: my-system

---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
namespace: my-system
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["list", "watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]

---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: my-system
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io

部署 nfs-client

创建一个 deployment 用于部署 nfs-client 来分配想对应所需资源,注意其中 NFS_SERVERNFS_PATH 需要修改为你自己的地址

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
40
41
42
43
44
45
kind: Deployment
apiVersion: apps/v1
metadata:
name: nfs-client-provisioner
namespace: my-system
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
#image: quay.io/external_storage/nfs-client-provisioner:latest,使用这个会报错 unexpected error getting claim reference: selfLink was empty, can't make reference,故使用下面的镜像
image: registry.cn-beijing.aliyuncs.com/mydlq/nfs-subdir-external-provisioner:v4.0.0
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
# 这个名称你可以自定义,需要记住,下面还要用
value: linkinstars.com/nfs
- name: NFS_SERVER
value: 172.16.1.1
- name: NFS_PATH
value: /kubernetes/test
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 200m
memory: 512Mi
volumes:
- name: nfs-client-root
nfs:
server: 172.16.1.1
path: /kubernetes/test

创建StorageClass

最后创建我们需要的 StorageClass 就可以了

1
2
3
4
5
6
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: my-nfs-storage
namespace: my-system
provisioner: linkinstars.com/nfs # 这里的名字需要和上面配置的 PROVISIONER_NAME 名称一致

测试一下

我们可以创建一个 pvc 来实际测试一下,如果创建之后没有出现错误,并且正常创建了 pv 的话那么证明你的部署就成功了

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-pvc
namespace: my-system
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Mi
storageClassName: my-nfs-storage

statefulset 使用

StorageClass 经常会使用在 statefulset 的情况下,因为通常它需要配置 volumeClaimTemplates 动态根据模板创建对应需要使用的 pvc

1
2
3
4
5
6
7
8
9
10
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 32Mi
storageClassName: my-nfs-storage

配置也非常简单,只需要配置 storageClassName 就可以了

这里我使用 statefulset 部署 consul

image-20211125171315343

image-20211125171403990

可以看到这些 pv 和 pvc 都是自动创建的,非常方便,这样 consul 的 data-dir 就可以做到持久化了

总结

StorageClass 真的是一个非常方便的设计,不需要运维每次吭叽吭叽去创建 pv 真的很麻烦

参考链接:https://kubernetes.io/zh/docs/concepts/storage/storage-classes/#nfs