Kubernetes StorageClass的使用

前提条件

问题背景

PV 都是静态的,PVC 需要用的存储,就必须手动去创建一个 PV,如果有很多PVC就必须手动创建很多PVC,非常不方便。这种方式在很大程度上并不能满足我们的需求,比如我们有一个应用需要对存储的并发度要求比较高,而另外一个应用对读写速度又要求比较高,特别是对于 StatefulSet 类型的应用简单的来使用静态的 PV 就很不合适了,这种情况下就需要用到动态 PV,也就是 StorageClass。

StorageClass简介

StorageClass 是 Kubernetes 中用于抽象存储供应的一种机制。它允许管理员定义不同类型的存储供应方式,使得用户(通过 PVC)能够以一种更灵活的方式请求存储资源,而无需关心存储的具体实现细节。简单来说,它就像是存储资源的 “模板”,规定了存储是如何被动态分配的。

主要属性和功能

  • 存储供应器(Provisioner)
    • 这是 StorageClass 的核心组件。存储供应器负责根据 PVC 的请求实际创建 PV。不同的存储供应器对应不同的存储后端或存储技术。例如,在云环境中,有针对云存储服务的供应器;在本地数据中心,可能有针对 Ceph、GlusterFS 等分布式存储系统的供应器。当用户创建一个 PVC 并指定了一个 StorageClass 时,存储供应器会被调用,按照预定的规则创建合适的 PV。
  • 参数(Parameters)
    • StorageClass 可以包含各种参数,这些参数用于定制存储供应的细节。例如,对于某些存储系统,可能需要指定存储卷的类型(如高性能 SSD、大容量 HDD 等)、存储的加密方式、备份策略等参数。以 Ceph 存储系统为例,参数可能包括 Ceph 存储池(pool)的名称、副本数量等信息。这些参数会被传递给存储供应器,以确保创建的 PV 符合用户期望的存储特性。
  • 回收策略(Reclaim Policy)
    • 与 PV 的回收策略类似,StorageClass 也可以定义回收策略。不过,它定义的回收策略会应用于通过该 StorageClass 动态创建的 PV。常见的回收策略有DeleteRetain。如果是Delete策略,当 PVC 被删除后,通过该 StorageClass 创建的 PV 及其数据也会被自动删除;如果是Retain策略,PV 会被保留,数据也不会被自动清除,需要管理员手动处理。
  • 动态分配 PV 的功能
    • 这是 StorageClass 的一个关键优势。在没有 StorageClass 的情况下,如果没有预先创建足够的 PV,当用户创建 PVC 时可能会因为没有合适的 PV 而导致绑定失败。而有了 StorageClass,当 PVC 被创建并且没有匹配的静态 PV 时,Kubernetes 会根据 PVC 的请求和 StorageClass 的配置自动创建一个 PV。例如,一个应用开发团队创建了一个 PVC,请求 10Gi 的存储容量和ReadWriteOnce访问模式,并指定了一个 StorageClass。如果集群中没有合适的静态 PV,系统会根据这个 StorageClass 的供应器和参数自动创建一个新的 PV 来满足 PVC 的需求。

创建StorageClass

要使用 StorageClass,我们就得安装对应的存储供应器(Provisioner),比如我们这里存储后端使用的是 nfs,那么我们就需要使用到一个 nfs-client 的存储供应器,这个程序使用我们已经配置好的 nfs 服务器,来自动创建持久卷,也就是自动帮我们创建 PV。

  • 自动创建的 PV 以${namespace}-${pvcName}-${pvName}这样的命名格式创建在 NFS 服务器上的共享数据目录中

  • 而当这个 PV 被回收后会以archieved-${namespace}-${pvcName}-${pvName}这样的命名格式存在 NFS 服务器上。

当然在部署nfs-client之前,我们需要先成功安装上 nfs 服务器, 可参考:Linux环境搭建NFS服务

第一步:配置 Deployment,将里面的对应的参数替换成自己的 nfs 配置(nfs-client.yaml)

创建yaml文件

vi nfs-client.yaml

内容如下

kind: Deployment
apiVersion: apps/v1
metadata:
  name: nfs-client-provisioner
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: vbouchaud/nfs-client-provisioner:latest
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: fuseim.pri/ifs
            - name: NFS_SERVER
              value: k8s-node02
            - name: NFS_PATH
              value: /data/k8s
      volumes:
        - name: nfs-client-root
          nfs:
            server: k8s-node02
            path: /data/k8s

提示:如果vbouchaud/nfs-client-provisioner:latest镜像下不下来,可以使用registry.cn-hangzhou.aliyuncs.com/my-common-images/nfs-client-provisioner:latest代替

第二步:将环境变量 NFS_SERVER 和 NFS_PATH 替换,当然也包括下面的 nfs 配置,这里使用了一个名为 nfs-client-provisioner 的serviceAccount,所以我们也需要创建一个 sa,然后绑定上对应的权限:(nfs-client-sa.yaml)  

创建nfs-client-sa.yaml

[root@k8s-master01 pvpvctest]# vi nfs-client-sa.yaml

内容如下

apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner

---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-client-provisioner-runner
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: default
roleRef:
  kind: ClusterRole
  name: nfs-client-provisioner-runner
  apiGroup: rbac.authorization.k8s.io

新建的一个名为 nfs-client-provisioner 的ServiceAccount,然后绑定了一个名为 nfs-client-provisioner-runner 的ClusterRole,而该ClusterRole声明了一些权限,其中就包括对persistentvolumes的增、删、改、查等权限,所以可以利用该ServiceAccount来自动创建 PV。

第三步:创建一个StorageClass对象:(nfs-client-class.yaml)

创建nfs-client-class.yaml

vi nfs-client-class.yaml

内容如下

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: course-nfs-storage
provisioner: fuseim.pri/ifs # or choose another name, must match deployment's env PROVISIONER_NAME'

声明了一个名为 course-nfs-storage 的StorageClass对象,注意下面的provisioner对应的值一定要和上面的Deployment下面的 PROVISIONER_NAME 这个环境变量的值一样。

前面通过YAML文件声明这些资源对象后,接下来创建这些资源对象:

[root@k8s-master01 pvpvctest]# kubectl create -f nfs-client.yaml
deployment.apps/nfs-client-provisioner created
​
[root@k8s-master01 pvpvctest]# kubectl create -f nfs-client-sa.yaml
serviceaccount/nfs-client-provisioner created
clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-runner created
clusterrolebinding.rbac.authorization.k8s.io/run-nfs-client-provisioner created
​
[root@k8s-master01 pvpvctest]# kubectl create -f nfs-client-class.yaml
storageclass.storage.k8s.io/course-nfs-storage created
​

创建完成后查看下资源状态:

[root@k8s-master01 pvpvctest]# kubectl get pods
NAME                                     READY   STATUS    RESTARTS   AGE
nfs-client-provisioner-8557778f8-7vfw4   1/1     Running   0          63s
​
$ kubectl get storageclass
NAME                 PROVISIONER      AGE
course-nfs-storage   fuseim.pri/ifs   11s
​
[root@k8s-master01 pvpvctest]# kubectl get storageclass
NAME                 PROVISIONER      RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
course-nfs-storage   fuseim.pri/ifs   Delete          Immediate           false                  11m
​

新建PVC

上面把StorageClass资源对象创建成功了,接下来通过一个示例测试下动态 PV,首先创建一个 PVC 对象:(test-pvc.yaml)

创建

[root@k8s-master01 pvpvctest]# vi test-pvc.yaml

内容如下

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: test-pvc
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 1Mi

这里声明了一个 PVC 对象,采用 ReadWriteMany 的访问模式,请求 1Mi 的空间,但是可以看到上面的 PVC 文件我们没有标识出任何和 StorageClass 相关联的信息,那么如果现在直接创建这个 PVC 对象能够自动绑定上合适的 PV 对象吗?显然是不能的(前提是没有合适的 PV),这里有两种方法可以来利用上面创建的 StorageClass 对象来自动创建一个合适的 PV:

  • 第一种方法:在这个 PVC 对象中添加一个声明 StorageClass 对象的标识,这里可以利用一个 annotations 属性来标识,如下:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: test-pvc
  annotations:
    volume.beta.kubernetes.io/storage-class: "course-nfs-storage"
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 1Mi
  • 第二种方法:可以设置这个 course-nfs-storage 的 StorageClass 为 Kubernetes 的默认存储后端,可以用 kubectl patch 命令来更新:

$ kubectl patch storageclass course-nfs-storage -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

上面这两种方法都是可以的,当然为了不影响系统的默认行为,这里还是采用第一种方法,直接创建即可:

[root@k8s-master01 pvpvctest]# kubectl create -f test-pvc.yaml
Warning: metadata.annotations[volume.beta.kubernetes.io/storage-class]: deprecated since v1.8; use "storageClassName" attribute instead
persistentvolumeclaim/test-pvc created
​
[root@k8s-master01 pvpvctest]# kubectl get pvc
NAME       STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS         VOLUMEATTRIBUTESCLASS   AGE
...
test-pvc   Bound    pvc-d0806802-e3c1-481f-b205-7535732981b5   1Mi        RWX            course-nfs-storage   <unset>                 19s
​

可以看到一个名为 test-pvc 的 PVC 对象创建成功了,状态已经是 Bound 了,也产生了一个对应的 VOLUME 对象,最重要的一栏是 STORAGECLASS,也有值了,就是刚刚创建的 StorageClass 对象 course-nfs-storage。

然后查看下 PV 对象呢:

[root@k8s-master01 pvpvctest]# kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM              STORAGECLASS         VOLUMEATTRIBUTESCLASS   REASON   AGE
...
pvc-d0806802-e3c1-481f-b205-7535732981b5   1Mi        RWX            Delete           Bound    default/test-pvc   course-nfs-storage   <unset>                          58s
​

可以看到是不是自动生成了一个关联的 PV 对象,访问模式是 RWX,回收策略是 Delete,这个 PV 对象并不是手动创建的,这是通过上面的 StorageClass 对象自动创建的。这就是 StorageClass 的创建方法。

测试

接下来用一个示例来测试下上面用 StorageClass 方式声明的 PVC 对象吧:(test-pod.yaml)

创建yaml文件

[root@k8s-master01 pvpvctest]# vi test-pod.yaml

内容如下

kind: Pod
apiVersion: v1
metadata:
  name: test-pod
spec:
  containers:
  - name: test-pod
    image: busybox
    imagePullPolicy: IfNotPresent
    command:
    - "/bin/sh"
    args:
    - "-c"
    - "touch /mnt/SUCCESS && exit 0 || exit 1"
    volumeMounts:
    - name: nfs-pvc
      mountPath: "/mnt"
  restartPolicy: "Never"
  volumes:
  - name: nfs-pvc
    persistentVolumeClaim:
      claimName: test-pvc

上面这个 Pod 非常简单,就是用一个 busybox 容器,在 /mnt 目录下面新建一个 SUCCESS 的文件,然后把 /mnt 目录挂载到上面我们新建的 test-pvc 这个资源对象上面了,要验证很简单,只需要去查看下nfs 服务器上面的共享数据目录下面是否有 SUCCESS 这个文件即可:

[root@k8s-master01 pvpvctest]# kubectl create -f test-pod.yaml
pod/test-pod created

然后可以在 nfs 服务器的共享数据目录下面查看下数据:

[root@k8s-node02 ~]# ls /data/k8s/
default-test-pvc-pvc-d0806802-e3c1-481f-b205-7535732981b5  nginxpvc-test  test.txt
​

看到下面有名字很长的文件夹,这个文件夹的命名方式是不是和我们上面的规则:${namespace}-${pvcName}-${pvName}是一样的,再看下这个文件夹下面是否有其他文件:

[root@k8s-node02 ~]# ls /data/k8s/default-test-pvc-pvc-d0806802-e3c1-481f-b205-7535732981b5/
SUCCESS

看到下面有一个 SUCCESS 的文件,就证明上面的验证是成功的。

另外可以看到我们这里是手动创建的一个 PVC 对象,在实际工作中,使用 StorageClass 更多的是 StatefulSet 类型的服务,StatefulSet 类型的服务也可以通过一个 volumeClaimTemplates 属性来直接使用 StorageClass,如下:(test-statefulset-nfs.yaml)

创建test-statefulset-nfs.yaml

[root@k8s-master01 pvpvctest]# vi test-statefulset-nfs.yaml

内容如下

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: nfs-web
spec:
  serviceName: "nginx"
  replicas: 3
  selector:
    matchLabels:
      app: nfs-web
  template:
    metadata:
      labels:
        app: nfs-web
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: nginx:1.16.1
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
      annotations:
        volume.beta.kubernetes.io/storage-class: course-nfs-storage
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

实际上 volumeClaimTemplates 下面就是一个 PVC 对象的模板,就类似于我们这里 StatefulSet 下面的 template,实际上就是一个 Pod 的模板,不单独创建成 PVC 对象,而用这种模板就可以动态的去创建了对象了,这种方式在 StatefulSet 类型的服务下面使用得非常多。

直接创建上面的对象:

[root@k8s-master01 pvpvctest]# kubectl create -f test-statefulset-nfs.yaml
statefulset.apps/nfs-web created
​
[root@k8s-master01 pvpvctest]# kubectl get pods
NAME                                     READY   STATUS      RESTARTS   AGE
nfs-client-provisioner-8557778f8-7vfw4   1/1     Running     0          14m
nfs-web-0                                1/1     Running     0          33s
nfs-web-1                                1/1     Running     0          31s
nfs-web-2                                1/1     Running     0          27s
test-pod                                 0/1     Completed   0          4m38s
​

创建完成后可以看到上面的3个 名为的nfs-web开头的Pod 已经运行成功,然后查看下 PVC 对象:

[root@k8s-master01 pvpvctest]# kubectl get pvc
NAME            STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS         VOLUMEATTRIBUTESCLASS   AGE
pvc-nfs         Bound    pv1                                        1Gi        RWO                                 <unset>                 106m
test-pvc        Bound    pvc-d0806802-e3c1-481f-b205-7535732981b5   1Mi        RWX            course-nfs-storage   <unset>                 8m20s
www-nfs-web-0   Bound    pvc-e670792b-4e2f-4ea2-8ab9-88838c444184   1Gi        RWO            course-nfs-storage   <unset>                 93s
www-nfs-web-1   Bound    pvc-673a974d-fc48-4c64-a055-8a8334dcc952   1Gi        RWO            course-nfs-storage   <unset>                 91s
www-nfs-web-2   Bound    pvc-a904ff97-5b9d-45fc-84c2-784f54065a4d   1Gi        RWO            course-nfs-storage   <unset>                 87s
​

可以看到是不是也生成了3个www-nfs-web开头的 PVC 对象,名称由模板名称 name 加上 Pod 的名称组合而成,这3个CLAIM名为default/www-nfs-web开头的 PVC 对象也都是 绑定状态了,很显然我们查看 PV 也可以看到对应的3个 PV 对象:

[root@k8s-master01 pvpvctest]# kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                   STORAGECLASS         VOLUMEATTRIBUTESCLASS   REASON   AGE
pv1                                        1Gi        RWO            Recycle          Bound    default/pvc-nfs                              <unset>                          122m
pvc-673a974d-fc48-4c64-a055-8a8334dcc952   1Gi        RWO            Delete           Bound    default/www-nfs-web-1   course-nfs-storage   <unset>                          2m18s
pvc-a904ff97-5b9d-45fc-84c2-784f54065a4d   1Gi        RWO            Delete           Bound    default/www-nfs-web-2   course-nfs-storage   <unset>                          2m14s
pvc-d0806802-e3c1-481f-b205-7535732981b5   1Mi        RWX            Delete           Bound    default/test-pvc        course-nfs-storage   <unset>                          9m7s
pvc-e670792b-4e2f-4ea2-8ab9-88838c444184   1Gi        RWO            Delete           Bound    default/www-nfs-web-0   course-nfs-storage   <unset>                          2m20s
​

查看 nfs 服务器上面的共享数据目录:

[root@k8s-node02 ~]# ls /data/k8s/
default-test-pvc-pvc-d0806802-e3c1-481f-b205-7535732981b5       default-www-nfs-web-2-pvc-a904ff97-5b9d-45fc-84c2-784f54065a4d
default-www-nfs-web-0-pvc-e670792b-4e2f-4ea2-8ab9-88838c444184  nginxpvc-test
default-www-nfs-web-1-pvc-673a974d-fc48-4c64-a055-8a8334dcc952  test.txt
​

有对应的3个default-www-nfs-web相关的数据目录,这就是 StorageClass 的使用方法,对于 StorageClass 多用于 StatefulSet 类型的服务。

查看其中一个文件为空

[root@k8s-node02 ~]# ls /data/k8s/default-www-nfs-web-0-pvc-e670792b-4e2f-4ea2-8ab9-88838c444184/
[root@k8s-node02 ~]# 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值