一、什么是StorageClass

kubernetes提供了一套可以自动创建pv的机制,即:dynamic provisioning,而这个机制的核心在于StorageClass这个API对象。

StorageClass对象会定义下面两部分内容:

1、pv的属性,比如 存储类型、volume大小等

2、创建这种pv需要用到的存储插件

有了这两个信息之后,kubernetes就能够根据用户提交的pvc找到一个对应的StorageClass,之后kubernetes就会调用该StorageClass声明的存储插件创建出需要的pv,但是其实使用起来比较简单,只需要根据自己的需求编写yaml即可,然后使用kubectl create命令执行。

二、为什么需要StorageClass

在一个大规模的kubernetes集群里,可能有成千上万个pvc,这就意味着运维人员必须实现创建出这么多个pv,此外,随着项目的需要,会有新的pvc不断被提交,那么运维人员就需要不断的添加新的满足要求的pv,否则新的pod就会因为pvc绑定不到pv而导致创建失败,而且通过pvc请求到一定的存储空间也很有可能不足以满足应用对于存储设备的各种需求,而不同的应用程序对于存储性能的要求也不尽相同,比如读写速度、并发性能等,为了解决这一问题kubernetes又为我们引入了一个新的资源对象StorageClass,通过StorageClass的定义,管理员可以将存储资源定义为某种类型的资源,比如快速存储、慢速存储等,用户根据StorageClass的描述就可以非常直观的知道各种存储源的具体特性了,这样就可以根据应用的特性去申请合适的存储资源了

三、StorageClass运行原理及部署流程

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

1、自动创建的pv以kubernetes学习笔记StorageClass nfs_运维{pvcname}-${pvname}这样的命名格式创建在NFS服务器上的共享数据目录中

2、而当这个pv被回收后会以archieved-kubernetes学习笔记StorageClass nfs_运维{pvcname}-${pvname}这样的命名格式存在NFS服务器上

1、原理及部署流程说明

详细的运作流程可以参考下图

kubernetes学习笔记StorageClass nfs_运维_03

搭建StorageClass+NFS,大致有以下几个步骤:

1、创建一个可用的NFS server

2、创建service account,这是用来管控nfs provisioner在kubernetes集群中运行的权限

3、创建StorageClass,负责建立pvc并调用nfsprovisioner进行预定的工作,并让pv与pvc建立管理

4、创建NFS provisioner,有两个功能,一个是在nfs共享目录下创建挂载点(volume),另一个则是建立pv并将pv与nfs的挂载点建立关联

四、创建StorageClass

1、搭建NFS共享服务器,该步骤比较简单不在赘述,大家可以自行百度搭建,当前环境NFS server及共享目录信息

IP:192.168.159.131     Export Path: /nfs

2、使用以下文档配置account及相关权限

rbac.yaml  #唯一需要修改的地方只有namespace,根据实际情况定义即可

apiVersion: v1

kind: ServiceAccount

metadata:

 name: nfs-client-provisioner

 # replace with namespace where provisioner is deployed

 namespace: default        #根据实际环境设定namespace,下面类同

---

kind: ClusterRole

apiVersion: /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: [""]

   resources: ["storageclasses"]

   verbs: ["get", "list", "watch"]

 - apiGroups: [""]

   resources: ["events"]

   verbs: ["create", "update", "patch"]

---

kind: ClusterRoleBinding

apiVersion: /v1

metadata:

 name: run-nfs-client-provisioner

subjects:

 - kind: ServiceAccount

   name: nfs-client-provisioner

   # replace with namespace where provisioner is deployed

   namespace: default

roleRef:

 kind: ClusterRole

 name: nfs-client-provisioner-runner

 apiGroup:

---

kind: Role

apiVersion: /v1

metadata:

 name: leader-locking-nfs-client-provisioner

   # replace with namespace where provisioner is deployed

 namespace: default

rules:

 - apiGroups: [""]

   resources: ["endpoints"]

   verbs: ["get", "list", "watch", "create", "update", "patch"]

---

kind: RoleBinding

apiVersion: /v1

metadata:

 name: leader-locking-nfs-client-provisioner

subjects:

 - kind: ServiceAccount

   name: nfs-client-provisioner

   # replace with namespace where provisioner is deployed

   namespace: default

roleRef:

 kind: Role

 name: leader-locking-nfs-client-provisioner

 apiGroup: ​​​

3、创建NFS资源的StorageClass

apiVersion: /v1

kind: StorageClass

metadata:

 name: managed-nfs-storage

 annotations:

   "/is-default-class": "true"  # 配置是否将managed-nfs-storage作为默认StorageClass

provisioner: qgg-nfs-storage # or choose another name, must match deployment's env PROVISIONER_NAME'

parameters:

 archiveOnDelete: "false"

4、创建NFS provisioner

apiVersion: apps/v1

kind: Deployment

metadata:

 name: nfs-client-provisioner

 labels:

   app: nfs-client-provisioner

 # replace with namespace where provisioner is deployed

spec:

 replicas: 1

 selector:

   matchLabels:

     app: nfs-client-provisioner

 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:v1

         imagePullPolicy: IfNotPresent

         volumeMounts:

           - name: nfs-client-root

             mountPath: /persistentvolumes

         env:

           - name: PROVISIONER_NAME

             value: qgg-nfs-storage

           - name: NFS_SERVER

             value: 192.168.159.131

           - name: NFS_PATH

             value: /nfs

     volumes:

       - name: nfs-client-root

         nfs:

           server: 192.168.159.131

           path: /nfs

五、创建测试pod,检查是否部署成功

1、pod+pvc

创建pvc

apiVersion: v1

kind: PersistentVolumeClaim

metadata:

 name: test-claim

 annotations:

   /storage-class: "managed-nfs-storage"   #与nfs-StorageClass.yaml metadata.name保持一致

spec:

 accessModes:

   - ReadWriteMany

 resources:

   requests:

     storage: 1Mi

确保pvc状态为bound

[root@k8s-master1 storageclass]# kubectl get pvc  

NAME         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE

test-claim   Bound    pvc-db84b416-de99-4cfb-be94-b51d61b8de1a   1Mi        RWX            managed-nfs-storage   4h32m

[root@k8s-master1 storageclass]# kubectl get pv

NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                STORAGECLASS          REASON   AGE

pvc-db84b416-de99-4cfb-be94-b51d61b8de1a   1Mi        RWX            Delete           Bound       default/test-claim   managed-nfs-storage            4h34m

创建测试pod查看是否可正常挂载

apiVersion: v1

kind: Pod

metadata:

 name: test-pod

spec:

 containers:

 - name: test-pod

   image: busybox:1.24

   command:

     - "/bin/sh"

   args:

     - "-c"

     - "touch /mnt/SUCCESS && exit 0 || exit 1"   #创建一个SUCCESS文件后退出

   volumeMounts:

     - name: nfs-pvc

       mountPath: "/mnt"

 restartPolicy: "Never"

 volumes:

   - name: nfs-pvc

     persistentVolumeClaim:

       claimName: test-claim  #与PVC名称保持一致

检查结果

去NFS服务器上查看/nfs目录查看success是否被创建成功

[root@k8s-master2 ~]# ll /nfs/default-test-claim-pvc-db84b416-de99-4cfb-be94-b51d61b8de1a/

总用量 0

-rw-r--r-- 1 root root 0 9月  15 15:58 SUCCESS

2、Statefulset + VolumeClaimTemplate 自动创建pv

创建无头服务及statefulset

---

apiVersion: v1

kind: Service

metadata:

 name: nginx-headless

 labels:

   app: nginx

spec:

 ports:

 - port: 80

   name: web

 clusterIP: None   #注意此处的值,None表示无头服务

 selector:

   app: nginx

---

apiVersion: apps/v1

kind: StatefulSet

metadata:

 name: web

spec:

 serviceName: "nginx"

 replicas: 2  #两个副本

 selector:

   matchLabels:

     app: nginx

 template:

   metadata:

     labels:

       app: nginx

   spec:

     containers:

     - name: nginx

       image: ikubernetes/myapp:v1

       ports:

       - containerPort: 80

         name: web

       volumeMounts:

       - name: www

         mountPath: /usr/share/nginx/html

 volumeClaimTemplates:

 - metadata:

     name: www

     annotations:

       /storage-class: "managed-nfs-storage"   #managed-nfs-storage为我们创建的storage-class名称

   spec:

     accessModes: [ "ReadWriteOnce" ]

     resources:

       requests:

         storage: 1Gi

六、关于StorageClass回收策略对数据的影响

1、第一种配置

archiveOnDelete: "false"  
reclaimPolicy: Delete #默认没有配置,默认值为Delete

测试结果

1.pod删除重建后数据依然存在,旧pod名称及数据依然保留给新pod使用
删除重建后数据依然存在,旧pod名称及数据依然保留给新pod使用
3.删除PVC后,PV被删除且NFS Server对应数据被删除

2、第二种配置

archiveOnDelete: "false"  
reclaimPolicy: Retain

测试结果

1.pod删除重建后数据依然存在,旧pod名称及数据依然保留给新pod使用
删除重建后数据依然存在,旧pod名称及数据依然保留给新pod使用
3.删除PVC后,PV不会别删除,且状态由Bound变为Released,NFS Server对应数据被保留
4.重建sc后,新建PVC会绑定新的pv,旧数据可以通过拷贝到新的PV中

3、第三种配置

archiveOnDelete: "tre"  
reclaimPolicy: Retain

测试结果

1.pod删除重建后数据依然存在,旧pod名称及数据依然保留给新pod使用
删除重建后数据依然存在,旧pod名称及数据依然保留给新pod使用
3.删除PVC后,PV不会别删除,且状态由Bound变为Released,NFS Server对应数据被保留
4.重建sc后,新建PVC会绑定新的pv,旧数据可以通过拷贝到新的PV中

4、第四种配置

archiveOnDelete: "tre"  
reclaimPolicy: Delete

测试结果

1.pod删除重建后数据依然存在,旧pod名称及数据依然保留给新pod使用
删除重建后数据依然存在,旧pod名称及数据依然保留给新pod使用
3.删除PVC后,PV不会别删除,且状态由Bound变为Released,NFS Server对应数据被保留
4.重建sc后,新建PVC会绑定新的pv,旧数据可以通过拷贝到新的PV中

总结:除第一种配置外,其他三种配置在pv/pvc被删除后数据依然保留

七、常见问题

1、如何设置默认的StorageClass

我们可以用 kubectl patch 命令来更新

[root@k8s-master-155-221 classStorage]# kubectl get sc  #查看当前sc 
NAME PROVISIONER AGE
managed-nfs-storage qgg-nfs-storage 20h
[root@k8s-master-155-221 classStorage]# kubectl patch storageclass managed-nfs-storage -p '{"metadata": {"annotations":{"/is-default-class":"true"}}}' #设置managed-nfs-storage为默认后端存储
storageclass./managed-nfs-storage patched
[root@k8s-master-155-221 classStorage]# kubectl get sc #再次查看,注意是否有default标识
NAME PROVISIONER AGE
managed-nfs-storage (default) qgg-nfs-storage 20h
[root@k8s-master-155-221 deploy]# kubectl patch storageclass managed-nfs-storage -p '{"metadata": {"annotations":{"/is-default-class":"false"}}}' #取消默认存储后端
storageclass./managed-nfs-storage patched
[root@k8s-master-155-221 deploy]# kubectl get sc
NAME PROVISIONER AGE
managed-nfs-storage qgg-nfs-storage 20h

YAML文件

apiVersion: /v1
kind: StorageClass
metadata:
name: managed-nfs-storage
annotations:
"/is-default-class": "true" #添加此注释
provisioner: qgg-nfs-storage #or choose another name, must match deployment's env PROVISIONER_NAME'
parameters:
archiveOnDelete: "false"

2、如何使用默认的StorageClass

如果集群中有一个默认的StorageClass能够满足我们的需求,那么剩下所有需要做的就是创建persistentvolumeclaim(pvc),剩下的都有默认的动态配置搞定,包括无需去指定StorageClassName

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mypvc
namespace: testns
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi

3、修改默认回收策略(默认是delete)

apiVersion: /v1
kind: StorageClass
metadata:
name: managed-nfs-storage
annotations:
provisioner: qgg-nfs-storage #or choose another name, must match deployment's env PROVISIONER_NAME'
parameters:
archiveOnDelete: "ture" #暂时不清楚该值对回收策略产生什么影响
reclaimPolicy: Retain #只有NFS 和hostPth支持两种回收策略

4、能否删除/关闭默认的StorageClass

不能删除默认的StorageClass,因为它是作为集群的add-on安装的,如果它被删除会被重新安装。

当然可以停掉默认的StorageClass行为,通过删除annotation:​​/is-default-class,或者设置为false。​

如果没有StorageClass对象标记默认的annotation,那么persistentvolume对象(在不指定StorageClass情况下)将不自动触发动态配置。相反,它们将回到绑定可用的pv状态

5、当删除pvc会发生什么

如果一个卷是动态配置的卷,则默认的回收策略为delete,这意味着,在默认情况下,当pvc删除时基础的pv和对应的存储也会被删除,如果需要保留存储在卷上的数据,则必须在pv被设置之后将回收策略从delete更改为retain