Kubernetes系列《三》——存储
一、k8s中PV、PVC、StorageClass的简单理解
1、PV概念
PersistentVolume(PV)就可以理解为是一个网络存储,就是一个实实在在的存储数据的地方,只不过是以网络的方式发生数据到存储的地方,比如NFS, iSCSI和云提供商指定的存储系统。
若严格来说,PV是k8s里面的一个概念,它本身不是存储,只不过是创建pv的资源清单文件中指定了网络存储的地址,同时也指定了一些存储的参数,例如一些大小,性能等指标。
apiVersion: v1 kind: PersistentVolume metadata: name: nfs-pv namespace: qsm-pv-test spec: capacity: storage: 100Mi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Recycle storageClassName: slow # StorageClass nfs: # FIXME: use the right IP server: 10.130.44.20 path: "/test/mysql-nfs01"
2、PVC概念
PersistentVolumeClaim(PVC)可以理解为Pod对所需网络存储卷需满足一定要求的一个申明。它展示了Pod想需要存储的性能指标,也就是说PVC是服务于Pod,为其寻找一个合适的PV给Pod使用。绑定之后,PVC和PV是一一对应关系。
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: qsm-nfs-pvc namespace: qsm-pv-test spec: accessModes: - ReadWriteMany storageClassName: slow # StorageClass resources: requests: storage: 90Mi
3、Pod使用PVC
使用Deployment字段清单的方式
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: qsm-nginx namespace: qsm-pv-test spec: replicas: 1 template: metadata: labels: name: qsm-nginx spec: containers: - name: qsm-nginx image: nginx imagePullPolicy: IfNotPresent ports: - containerPort: 80 volumeMounts: - name: qsm-pvc-volume mountPath: "/usr/share/nginx/html" volumes: - name: qsm-pvc-volume persistentVolumeClaim: claimName: qsm-nfs-pvc
这个时候,若有符合的PV,那就可以为Pod服务了。
若没有合适的PV,或者PV数量不够,而PVC随着业务的发展,越来越多,那么就要有更多的PV马上要创建,否则Pod就一直创建不成功,这很不友好。
所以就有了StorageClass,k8s根据它可以自动创建PV。
4、StorageClass
原理:
管理员可以部署PV配置器(provisioner),然后定义对应的StorageClass,这样开发者在创建PVC的时候就可以选择需要创建存储的类型,PVC会把StorageClass传递给PV provisioner,由provisioner自动创建PV。如CCE就提供csi-disk、csi-nas、csi-obs等StorageClass,在声明PVC加上StorageClassName,就可以自动创建PV,并自动创建底层的存储资源。定义了StorageClass后,就可以减少创建并维护PV的工作,PV变成了自动创建,作为使用者,只需要在声明PVC时指定StorageClassName即可,这就大大减少工作量。
上面介绍的PV和PVC模式是需要运维人员先创建好PV,然后开发人员定义好PVC进行一对一的Bond,但是如果PVC请求成千上万,那么就需要创建成千上万的PV,对于运维人员来说维护成本很高,Kubernetes提供一种自动创建PV的机制,叫StorageClass,它的作用就是创建PV的模板。
具体来说,StorageClass会定义一下两部分:
- PV的属性 ,比如存储的大小、类型等;
- 创建这种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
--- apiVersion: v1 kind: ServiceAccount metadata: name: nfs-client-provisioner --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: nfs-client-provisioner-clusterrole 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"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: nfs-client-provisioner-clusterrolebinding subjects: - kind: ServiceAccount name: nfs-client-provisioner namespace: default roleRef: kind: ClusterRole name: nfs-client-provisioner-clusterrole apiGroup: rbac.authorization.k8s.io 通过上面得配置,设置nfs-client对PV,PVC,StorageClass等得规则。接下来我们创建这个YAML文件: [root@master storageclass]# kubectl apply -f nfs-client-sa.yaml serviceaccount/nfs-client-provisioner created clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-clusterrole created clusterrolebinding.rbac.authorization.k8s.io/nfs-client-provisioner-clusterrolebinding
2、创建nfs-client
使用Deployment来创建nfs-client,配置如下:
nfs-client.yaml
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: nfs-client-prosioner spec: replicas: 1 strategy: type: Recreate template: metadata: labels: app: nfs-client-prosioner spec: serviceAccountName: nfs-client-provisioner containers: - name: nfs-client-prosioner image: registry.cn-hangzhou.aliyuncs.com/rookieops/nfs-client-provisioner:v0.1 imagePullPolicy: IfNotPresent volumeMounts: - name: nfs-client-root mountPath: /data/pv env: - name: PROVISIONER_NAME value: rookieops/nfs - name: NFS_SERVER value: 122.51.79.172 - name: NFS_PATH value: /data/k8s/prometheus volumes: - name: nfs-client-root nfs: server: 122.51.79.172 path: /data/k8s/prometheus
然后创建这个YAML文件。
[root@master storageclass]# kubectl apply -f nfs-client.yaml
deployment.extensions/nfs-client-prosioner created
查看其状态:
[root@master storageclass]# kubectl get pod NAME READY STATUS RESTARTS AGE nfs-client-prosioner-66c9bb7f88-q2qm4 1/1 Running 0 52m
3、上面得创建完成后就可以创建StorageClass了。
nfs-client-storageclass.yaml
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: nfs-client-storageclass provisioner: rookieops/nfs
注意provisioner必须和上面得Deployment的YAML文件中PROVISIONER_NAME的值保持一致。
创建这个YAML文件:
[root@master storageclass]# kubectl apply -f nfs-client-storageclass.yaml storageclass.storage.k8s.io/nfs-client-storageclass created [root@master storageclass]# kubectl get storageclass NAME PROVISIONER AGE nfs-client-storageclass fuseim.pri/ifs 15s
4、创建PVC
test-nfs-pvc.yaml
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: test-nfs-pvc2 annotations: volume.beta.kubernetes.io/storage-class: "nfs-client-storageclass" spec: accessModes: - ReadWriteMany resources: requests: storage: 1Mi
annotations的作用是在PVC里声明一个StorageClass对象的标识。
创建这个YAML文件,观察其状态:
[root@master storageclass]# kubectl apply -f test-pvc.yaml persistentvolumeclaim/test-nfs-pvc created [root@master storageclass]# kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE 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
apiVersion: v1 kind: Pod metadata: name: test-storageclass-pod spec: containers: - name: busybox image: busybox:latest imagePullPolicy: IfNotPresent command: - "/bin/sh" - "-c" args: - "sleep 3600" volumeMounts: - name: nfs-pvc mountPath: /mnt restartPolicy: Never volumes: - name: nfs-pvc persistentVolumeClaim: claimName: test-nfs-pvc2
然后查看NFS服务器上是否生成对应的目录:
[root@master k8s]# ll total 0 drwxrwxrwx 2 root root 6 Oct 29 17:21 default-test-nfs-pvc2-pvc-91671ba7-8da8-4611-8bd5-3673f63d15cb
我们可以看到生成了对应的目录,格式和我们上面说的一致。现在进Pod向该目录下写一个文件,然后查看NFS服务器上是否存在该文件:
[root@master storageclass]# kubectl exec -it test-storageclass-pod -- /bin/sh / # cd /mnt/ /mnt # echo "hello,I am NFS Server!" > test /mnt # ls test [root@master default-test-nfs-pvc2-pvc-91671ba7-8da8-4611-8bd5-3673f63d15cb]# ls test [root@master default-test-nfs-pvc2-pvc-91671ba7-8da8-4611-8bd5-3673f63d15cb]# cat test hello,I am NFS Server!
我们发现NFS服务器上存在,说明我们验证成功。
另外我们可以看到我们这里是手动创建的一个 PVC 对象,在实际工作中,使用 StorageClass 更多的是 StatefulSet 类型的服务,StatefulSet 类型的服务我们也可以通过一个 volumeClaimTemplates 属性来直接使用 StorageClass,如下:(test-statefulset-nfs.yaml)
apiVersion: apps/v1beta1 kind: StatefulSet metadata: name: nfs-web spec: serviceName: "nginx" replicas: 2 template: metadata: labels: app: nfs-web spec: terminationGracePeriodSeconds: 10 containers: - name: nginx image: nginx:1.7.9 imagePullPolicy: IfNotPresent ports: - containerPort: 80 name: web volumeMounts: - name: www mountPath: /usr/share/nginx/html volumeClaimTemplates: - metadata: name: www annotations: volume.beta.kubernetes.io/storage-class: nfs-client-storageclass spec: accessModes: [ "ReadWriteOnce" ] resources: requests: storage: 1Gi
可以看到volumeClaimTemplates就是我们上面的PVC模板。然后我们创建这个文件:
[root@master storageclass]# kubectl apply -f test-statefulset.yaml statefulset.apps/nfs-web created [root@master storageclass]# kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE www-nfs-web-0 Bound pvc-5df69dbe-8b54-45dd-acd0-769c0c9ee1b8 1Gi RWO nfs-client-storageclass 7s www-nfs-web-1 Bound pvc-0e83fef9-ec16-4e02-8482-7e04b7c81c92 1Gi RWO nfs-client-storageclass 3s
可以看到会自动生成两个PVC。
在NFS服务器上也可以看到正常创建了目录:
[root@master k8s]# ll total 0 drwxrwxrwx 2 root root 18 Oct 29 17:29 default-test-nfs-pvc2-pvc-91671ba7-8da8-4611-8bd5-3673f63d15cb drwxrwxrwx 2 root root 6 Oct 29 17:34 default-www-nfs-web-0-pvc-5df69dbe-8b54-45dd-acd0-769c0c9ee1b8 drwxrwxrwx 2 root root 6 Oct 29 17:34 default-www-nfs-web-1-pvc-0e83fef9-ec16-4e02-8482-7e04b7c81c92
总结
pv理解为一个网络存储卷,pvc是一个满足pod所需性能的一个申明,StorageClass是一个找不到合适pv之后可以自动创建的pv的一个机制。
附录
---------------------------------------------------
作者:杨兮臣
本博客所有文章仅用于学习、研究和交流目的,欢迎非商业性质转载。
博主的文章没有高度、深度和广度,只是凑字数。由于博主的水平不高,不足和错误之处在所难免,希望大家能够批评指出。
博主是利用闲暇时间,把自己毕生所学整理一下,感谢行业的技术大咖