上面介绍的PV和PVC模式是需要运维人员先创建好PV,然后开发人员定义好PVC进行一对一的Bond,但是如果PVC请求成千上万,那么就需要创建成千上万的PV,对于运维人员来说维护成本很高,Kubernetes提供一种自动创建PV的机制,叫StorageClass,它的作用就是创建PV的模板。

具体来说,StorageClass会定义一下两部分:

  1. PV的属性 ,比如存储的大小、类型等;
  2. 创建这种PV需要使用到的存储插件,比如Ceph等;

有了这两部分信息,Kubernetes就能够根据用户提交的PVC,找到对应的StorageClass,然后Kubernetes就会调用 StorageClass声明的存储插件,创建出需要的PV。

这里我们以NFS为例,要使用NFS,我们就需要一个nfs-client的自动装载程序,我们称之为Provisioner,这个程序会使用我们已经配置好的NFS服务器自动创建持久卷,也就是自动帮我们创建PV。
说明:

  • 自动创建的PV会以${namespace}-${pvcName}-${pvName}的目录格式放到NFS服务器上;
  • 如果这个PV被回收,则会以archieved-${namespace}-${pvcName}-${pvName}这样的格式存放到NFS服务器上;

详细可以参考:https://github.com/kubernetes-incubator/external-storage/tree/master/nfs-client
在部署之前,首先得确保有可用得NFS服务器,这里默认已经有可用得NFS服务器了。
1、创建ServiceAccount,为nfs-client授权。
nfs-client-sa.yaml

  1. ---
  2. apiVersion: v1
  3. kind: ServiceAccount
  4. metadata:
  5. name: nfs-client-provisioner
  6. ---
  7. apiVersion: rbac.authorization.k8s.io/v1
  8. kind: ClusterRole
  9. metadata:
  10. name: nfs-client-provisioner-clusterrole
  11. rules:
  12. - apiGroups: [""]
  13. resources: ["persistentvolumes"]
  14. verbs: ["get", "list", "watch", "create", "delete"]
  15. - apiGroups: [""]
  16. resources: ["persistentvolumeclaims"]
  17. verbs: ["get", "list", "watch", "update"]
  18. - apiGroups: ["storage.k8s.io"]
  19. resources: ["storageclasses"]
  20. verbs: ["get", "list", "watch"]
  21. - apiGroups: [""]
  22. resources: ["events"]
  23. verbs: ["list", "watch", "create", "update", "patch"]
  24. - apiGroups: [""]
  25. resources: ["endpoints"]
  26. verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
  27. ---
  28. apiVersion: rbac.authorization.k8s.io/v1
  29. kind: ClusterRoleBinding
  30. metadata:
  31. name: nfs-client-provisioner-clusterrolebinding
  32. subjects:
  33. - kind: ServiceAccount
  34. name: nfs-client-provisioner
  35. namespace: default
  36. roleRef:
  37. kind: ClusterRole
  38. name: nfs-client-provisioner-clusterrole
  39. apiGroup: rbac.authorization.k8s.io

通过上面得配置,设置nfs-client对PV,PVC,StorageClass等得规则。接下来我们创建这个YAML文件:

  1. [root@master storageclass]# kubectl apply -f nfs-client-sa.yaml
  2. serviceaccount/nfs-client-provisioner created
  3. clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-clusterrole created
  4. clusterrolebinding.rbac.authorization.k8s.io/nfs-client-provisioner-clusterrolebinding created

2、创建nfs-client
使用Deployment来创建nfs-client,配置如下:
nfs-client.yaml

  1. ---
  2. apiVersion: apps/v1
  3. kind: Deployment
  4. metadata:
  5. name: nfs-client-prosioner
  6. spec:
  7. replicas: 1
  8. strategy:
  9. type: Recreate
  10. selector:
  11. matchLabels:
  12. app: nfs-client-prosioner
  13. template:
  14. metadata:
  15. labels:
  16. app: nfs-client-prosioner
  17. spec:
  18. serviceAccountName: nfs-client-provisioner
  19. containers:
  20. - name: nfs-client-prosioner
  21. image: registry.cn-hangzhou.aliyuncs.com/rookieops/nfs-client-provisioner:v0.1
  22. imagePullPolicy: IfNotPresent
  23. volumeMounts:
  24. - name: nfs-client-root
  25. mountPath: /data/pv
  26. env:
  27. - name: PROVISIONER_NAME
  28. value: rookieops/nfs
  29. - name: NFS_SERVER
  30. value: 192.168.0.177
  31. - name: NFS_PATH
  32. value: /data/k8s
  33. volumes:
  34. - name: nfs-client-root
  35. nfs:
  36. server: 192.168.0.177
  37. path: /data/k8s

然后创建这个YAML文件。

  1. [root@master storageclass]# kubectl apply -f nfs-client.yaml
  2. deployment.extensions/nfs-client-prosioner created

查看其状态:

  1. [root@master storageclass]# kubectl get pod
  2. NAME READY STATUS RESTARTS AGE
  3. nfs-client-prosioner-66c9bb7f88-q2qm4 1/1 Running 0 52m

3、上面得创建完成后就可以创建StorageClass了。
nfs-client-storageclass.yaml

  1. apiVersion: storage.k8s.io/v1
  2. kind: StorageClass
  3. metadata:
  4. name: nfs-client-storageclass
  5. provisioner: rookieops/nfs

注意provisioner必须和上面得Deployment的YAML文件中PROVISIONER_NAME的值保持一致。
创建这个YAML文件:

  1. [root@master storageclass]# kubectl apply -f nfs-client-storageclass.yaml
  2. storageclass.storage.k8s.io/nfs-client-storageclass created
  3. [root@master storageclass]# kubectl get storageclass
  4. NAME PROVISIONER AGE
  5. nfs-client-storageclass fuseim.pri/ifs 15s

4、创建PVC
test-nfs-pvc.yaml

  1. apiVersion: v1
  2. kind: PersistentVolumeClaim
  3. metadata:
  4. name: test-nfs-pvc2
  5. annotations:
  6. volume.beta.kubernetes.io/storage-class: "nfs-client-storageclass"
  7. spec:
  8. accessModes:
  9. - ReadWriteMany
  10. resources:
  11. requests:
  12. storage: 1Mi

annotations的作用是在PVC里声明一个StorageClass对象的标识。

创建这个YAML文件,观察其状态:

  1. [root@master storageclass]# kubectl apply -f test-pvc.yaml
  2. persistentvolumeclaim/test-nfs-pvc created
  3. [root@master storageclass]# kubectl get pvc
  4. NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
  5. test-nfs-pvc Bound pvc-e5b8765b-1d7b-4529-860f-bbe34e0b4109 1Mi RWX nfs-client-storageclass 2m16s

我们看到该PVC自动申请到空间,其STORAGECLASS就是我们创建的nfs-client-storageclass。

5、创建一个Pod,进行测试
test-pod.yaml

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: test-storageclass-pod
  5. spec:
  6. containers:
  7. - name: busybox
  8. image: busybox:latest
  9. imagePullPolicy: IfNotPresent
  10. command:
  11. - "/bin/sh"
  12. - "-c"
  13. args:
  14. - "sleep 3600"
  15. volumeMounts:
  16. - name: nfs-pvc
  17. mountPath: /mnt
  18. restartPolicy: Never
  19. volumes:
  20. - name: nfs-pvc
  21. persistentVolumeClaim:
  22. claimName: test-nfs-pvc2

然后查看NFS服务器上是否生成对应的目录:

  1. [root@master k8s]# ll
  2. total 0
  3. drwxrwxrwx 2 root root 6 Oct 29 17:21 default-test-nfs-pvc2-pvc-91671ba7-8da8-4611-8bd5-3673f63d15cb

我们可以看到生成了对应的目录,格式和我们上面说的一致。现在进Pod向该目录下写一个文件,然后查看NFS服务器上是否存在该文件:

  1. [root@master storageclass]# kubectl exec -it test-storageclass-pod -- /bin/sh
  2. / # cd /mnt/
  3. /mnt # echo "hello,I am NFS Server!" > test
  4. /mnt # ls
  5. test
  6. [root@master default-test-nfs-pvc2-pvc-91671ba7-8da8-4611-8bd5-3673f63d15cb]# ls
  7. test
  8. [root@master default-test-nfs-pvc2-pvc-91671ba7-8da8-4611-8bd5-3673f63d15cb]# cat test
  9. hello,I am NFS Server!

我们发现NFS服务器上存在,说明我们验证成功。

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

  1. apiVersion: apps/v1beta1
  2. kind: StatefulSet
  3. metadata:
  4. name: nfs-web
  5. spec:
  6. serviceName: "nginx"
  7. replicas: 2
  8. template:
  9. metadata:
  10. labels:
  11. app: nfs-web
  12. spec:
  13. terminationGracePeriodSeconds: 10
  14. containers:
  15. - name: nginx
  16. image: nginx:1.7.9
  17. imagePullPolicy: IfNotPresent
  18. ports:
  19. - containerPort: 80
  20. name: web
  21. volumeMounts:
  22. - name: www
  23. mountPath: /usr/share/nginx/html
  24. volumeClaimTemplates:
  25. - metadata:
  26. name: www
  27. annotations:
  28. volume.beta.kubernetes.io/storage-class: nfs-client-storageclass
  29. spec:
  30. accessModes: [ "ReadWriteOnce" ]
  31. resources:
  32. requests:
  33. storage: 1Gi

可以看到volumeClaimTemplates就是我们上面的PVC模板。然后我们创建这个文件:

  1. [root@master storageclass]# kubectl apply -f test-statefulset.yaml
  2. statefulset.apps/nfs-web created
  3. [root@master storageclass]# kubectl get pvc
  4. NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
  5. www-nfs-web-0 Bound pvc-5df69dbe-8b54-45dd-acd0-769c0c9ee1b8 1Gi RWO nfs-client-storageclass 7s
  6. www-nfs-web-1 Bound pvc-0e83fef9-ec16-4e02-8482-7e04b7c81c92 1Gi RWO nfs-client-storageclass 3s

可以看到会自动生成两个PVC。

在NFS服务器上也可以看到正常创建了目录:

  1. [root@master k8s]# ll
  2. total 0
  3. drwxrwxrwx 2 root root 18 Oct 29 17:29 default-test-nfs-pvc2-pvc-91671ba7-8da8-4611-8bd5-3673f63d15cb
  4. drwxrwxrwx 2 root root 6 Oct 29 17:34 default-www-nfs-web-0-pvc-5df69dbe-8b54-45dd-acd0-769c0c9ee1b8
  5. drwxrwxrwx 2 root root 6 Oct 29 17:34 default-www-nfs-web-1-pvc-0e83fef9-ec16-4e02-8482-7e04b7c81c92

补充

(1)标记默认 StorageClass 非默认

  1. kubectl patch storageclass standard -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'

(2)标记一个 StorageClass 为默认

  1. kubectl patch storageclass <your-class-name> -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'