之前发了持久化存储相关的知识—PV,PVC,StorageClass,有朋友问,如果我需要在使用的基础上扩容应该怎么办呢?本文来给你介绍一下用NFS做后端存储能不能扩容的问题,当然在生产上没有哪个愿意用NFS做后端存储……

    你首先需要准备好NFS存储

    首先我们介绍一下淡出你的pv和pvc模式,也就是纯手动创建的模式。
    (1)、首先我们创建一个PV的YAML文件(pv2.yaml)

    1. apiVersion: v1
    2. kind: PersistentVolume
    3. metadata:
    4. name: my-pv02
    5. labels:
    6. storage: pv
    7. spec:
    8. accessModes:
    9. - ReadWriteOnce
    10. capacity:
    11. storage: 500Mi
    12. persistentVolumeReclaimPolicy: Recycle
    13. nfs:
    14. path: /data/k8s
    15. server: 10.1.10.128

    创建pv

    1. # kubectl apply -f pv2.yaml
    2. persistentvolume/my-pv02 created
    3. # kubectl get pv
    4. NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE 30s
    5. my-pv02 500Mi RWO Recycle Bound default/pvc-test 30s

    (2)、创建PVC(pvc.yaml),我们这里创建一个比较小的pvc,如下

    1. apiVersion: v1
    2. kind: PersistentVolumeClaim
    3. metadata:
    4. name: pvc-test
    5. spec:
    6. accessModes:
    7. - ReadWriteOnce
    8. resources:
    9. requests:
    10. storage: 500Mi

    然后创建pvc

    1. # kubectl apply -f pvc.yaml
    2. # kubectl get pvc
    3. NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
    4. pvc-test Bound my-pv02 500Mi RWO 58s

    我们其实可以看到,我在pvc里定义的是500Mi,它会自动匹配刚好合适的PV。
    这时候如果我们修改my-pv02的YAML文件,如下:

    1. apiVersion: v1
    2. kind: PersistentVolume
    3. metadata:
    4. name: my-pv02
    5. labels:
    6. storage: pv
    7. spec:
    8. accessModes:
    9. - ReadWriteOnce
    10. capacity:
    11. storage: 2Gi
    12. persistentVolumeReclaimPolicy: Recycle
    13. nfs:
    14. path: /data/k8s
    15. server: 10.1.10.128

    然后我们尝试创建这个PV,如下:

    1. # kubectl apply -f pv2.yaml
    2. persistentvolume/my-pv02 configured
    3. # kubectl get pv
    4. NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
    5. my-pv02 2Gi RWO Recycle Bound default/pvc-test 116m
    6. # kubectl get pvc
    7. NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
    8. pvc-test Bound my-pv02 500Mi RWO 116m

    我们看到我们my-pv02的CAPACITY变成了2Gi,我们的pvc还是500Mi,那么我们能不能对PVC进行扩容呢?我尝试的修改了pvc的配置文件,如下:

    1. apiVersion: v1
    2. kind: PersistentVolumeClaim
    3. metadata:
    4. name: pvc-test
    5. spec:
    6. accessModes:
    7. - ReadWriteOnce
    8. resources:
    9. requests:
    10. storage: 1Gi

    然后我们执行这个配置文件

    1. ]# kubectl apply -f pvc.yaml
    2. Error from server (Forbidden): error when applying patch:
    3. {"metadata":{"annotations":{"kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"v1\",\"kind\":\"PersistentVolumeClaim\",\"metadata\":{\"annotations\":{},\"name\":\"pvc-test\",\"namespace\":\"default\"},\"spec\":{\"accessModes\":[\"ReadWriteOnce\"],\"resources\":{\"requests\":{\"storage\":\"1Gi\"}}}}\n"}},"spec":{"resources":{"requests":{"storage":"1Gi"}}}}
    4. to:
    5. Resource: "/v1, Resource=persistentvolumeclaims", GroupVersionKind: "/v1, Kind=PersistentVolumeClaim"
    6. Name: "pvc-test", Namespace: "default"
    7. Object: &{map["apiVersion":"v1" "kind":"PersistentVolumeClaim" "metadata":map["annotations":map["kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"v1\",\"kind\":\"PersistentVolumeClaim\",\"metadata\":{\"annotations\":{},\"name\":\"pvc-test\",\"namespace\":\"default\"},\"spec\":{\"accessModes\":[\"ReadWriteOnce\"],\"resources\":{\"requests\":{\"storage\":\"500Mi\"}}}}\n" "pv.kubernetes.io/bind-completed":"yes" "pv.kubernetes.io/bound-by-controller":"yes"] "creationTimestamp":"2019-12-24T10:46:05Z" "finalizers":["kubernetes.io/pvc-protection"] "name":"pvc-test" "namespace":"default" "resourceVersion":"15059" "selfLink":"/api/v1/namespaces/default/persistentvolumeclaims/pvc-test" "uid":"6a905af6-5e44-4940-ba87-e56f6e99df24"] "spec":map["accessModes":["ReadWriteOnce"] "resources":map["requests":map["storage":"500Mi"]] "volumeMode":"Filesystem" "volumeName":"my-pv02"] "status":map["accessModes":["ReadWriteOnce"] "capacity":map["storage":"500Mi"] "phase":"Bound"]]}
    8. for: "pvc.yaml": persistentvolumeclaims "pvc-test" is forbidden: only dynamically provisioned pvc can be resized and the storageclass that provisions the pvc must support resize

    我们发现报错了,错误信息如下:

    1. persistentvolumeclaims "pvc-test" is forbidden: only dynamically provisioned pvc can be resized and the storageclass that provisions the pvc must support resize

    它的意思就是说只有动态供应的pvc可以调整大小,供应pvc的存储类必须支持调整大小。也就是说我们手动创建PV,PVC模式是不支持调整大小的。

    我们现在来验证一下用storageclass创建的PVC。
    (1)、因为我们使用nfs作为后端存储,所以我们需要先创建一个nfs-client,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
    40. ---
    41. apiVersion: extensions/v1beta1
    42. kind: Deployment
    43. metadata:
    44. name: nfs-client-prosioner
    45. spec:
    46. replicas: 1
    47. strategy:
    48. type: Recreate
    49. template:
    50. metadata:
    51. labels:
    52. app: nfs-client-prosioner
    53. spec:
    54. serviceAccountName: nfs-client-provisioner
    55. containers:
    56. - name: nfs-client-prosioner
    57. image: registry.cn-hangzhou.aliyuncs.com/rookieops/nfs-client-provisioner:v0.1
    58. imagePullPolicy: IfNotPresent
    59. volumeMounts:
    60. - name: nfs-client-root
    61. mountPath: /data/pv
    62. env:
    63. - name: PROVISIONER_NAME
    64. value: rookieops/nfs
    65. - name: NFS_SERVER
    66. value: 10.1.10.128
    67. - name: NFS_PATH
    68. value: /data/k8s
    69. volumes:
    70. - name: nfs-client-root
    71. nfs:
    72. server: 10.1.10.128
    73. path: /data/k8s

    创建YAML文件

    1. # kubectl apply -f !$
    2. kubectl apply -f nfs-client.yaml
    3. serviceaccount/nfs-client-provisioner created
    4. clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-clusterrole created
    5. clusterrolebinding.rbac.authorization.k8s.io/nfs-client-provisioner-clusterrolebinding created
    6. deployment.extensions/nfs-client-prosioner created
    7. # kubectl get pod
    8. NAME READY STATUS RESTARTS AGE
    9. nfs-client-prosioner-56f44c675b-t2ml9 1/1 Running 0 6s

    (1)我们创建一个普通的StorageClass(storageclass.yaml)

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

    创建这个YAML文件

    1. # kubectl apply -f storageclass.yaml
    2. storageclass.storage.k8s.io/nfs-client-storageclass created
    3. # kubectl get storageclasses.storage.k8s.io
    4. NAME PROVISIONER AGE
    5. nfs-client-storageclass rookieops/nfs 34s

    创建一个PVC(storageclass-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

    创建这个YAML文件

    1. # kubectl apply -f storageclass-pvc.yaml
    2. persistentvolumeclaim/test-nfs-pvc2 created
    3. # kubectl get pvc
    4. NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
    5. pvc-test Bound my-pv02 500Mi RWO 26d
    6. test-nfs-pvc2 Bound pvc-9dd9c608-6875-4609-829c-c92920aaa783 1Mi RWX nfs-client-storageclass 21s

    我们看到已经Bound了,我们现在直接修改pvc(storageclass-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: 100Mi

    然后我们执行这个YAML文件

    1. # kubectl apply -f storageclass-pvc.yaml
    2. Error from server (Forbidden): error when applying patch:
    3. {"metadata":{"annotations":{"kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"v1\",\"kind\":\"PersistentVolumeClaim\",\"metadata\":{\"annotations\":{\"volume.beta.kubernetes.io/storage-class\":\"nfs-client-storageclass\"},\"name\":\"test-nfs-pvc2\",\"namespace\":\"default\"},\"spec\":{\"accessModes\":[\"ReadWriteMany\"],\"resources\":{\"requests\":{\"storage\":\"100Mi\"}}}}\n"}},"spec":{"resources":{"requests":{"storage":"100Mi"}}}}
    4. to:
    5. Resource: "/v1, Resource=persistentvolumeclaims", GroupVersionKind: "/v1, Kind=PersistentVolumeClaim"
    6. Name: "test-nfs-pvc2", Namespace: "default"
    7. Object: &{map["apiVersion":"v1" "kind":"PersistentVolumeClaim" "metadata":map["annotations":map["kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"v1\",\"kind\":\"PersistentVolumeClaim\",\"metadata\":{\"annotations\":{\"volume.beta.kubernetes.io/storage-class\":\"nfs-client-storageclass\"},\"name\":\"test-nfs-pvc2\",\"namespace\":\"default\"},\"spec\":{\"accessModes\":[\"ReadWriteMany\"],\"resources\":{\"requests\":{\"storage\":\"1Mi\"}}}}\n" "pv.kubernetes.io/bind-completed":"yes" "pv.kubernetes.io/bound-by-controller":"yes" "volume.beta.kubernetes.io/storage-class":"nfs-client-storageclass" "volume.beta.kubernetes.io/storage-provisioner":"rookieops/nfs"] "creationTimestamp":"2020-01-20T06:04:20Z" "finalizers":["kubernetes.io/pvc-protection"] "name":"test-nfs-pvc2" "namespace":"default" "resourceVersion":"29483" "selfLink":"/api/v1/namespaces/default/persistentvolumeclaims/test-nfs-pvc2" "uid":"9dd9c608-6875-4609-829c-c92920aaa783"] "spec":map["accessModes":["ReadWriteMany"] "resources":map["requests":map["storage":"1Mi"]] "volumeMode":"Filesystem" "volumeName":"pvc-9dd9c608-6875-4609-829c-c92920aaa783"] "status":map["accessModes":["ReadWriteMany"] "capacity":map["storage":"1Mi"] "phase":"Bound"]]}
    8. for: "storageclass-pvc.yaml": persistentvolumeclaims "test-nfs-pvc2" is forbidden: only dynamically provisioned pvc can be resized and the storageclass that provisions the pvc must support resize

    发现报与上面一样的错,这是因为要支持动态扩容需要满足两个条件:

    • 后端底层存储支持卷扩展(后端存储保证足够资源)
    • 需要在StorageClass对象中设置allowVolumeExpansion为true

    我们由于是测试,申请的资源比较少,我们直接对StorageClass对象进行修改(storageclass.yaml),如下:

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

    然后我们重新申明一下

    1. # kubectl apply -f storageclass.yaml
    2. storageclass.storage.k8s.io/nfs-client-storageclass configured

    我们再次创建前面的storageclass-pvc.yaml文件

    1. # kubectl apply -f storageclass-pvc.yaml
    2. persistentvolumeclaim/test-nfs-pvc2 configured

    我们发现这次没有报错,我们查看一下这个PVC

    1. # kubectl get pvc
    2. NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
    3. pvc-test Bound my-pv02 500Mi RWO 26d
    4. test-nfs-pvc2 Bound pvc-9dd9c608-6875-4609-829c-c92920aaa783 1Mi RWX nfs-client-storageclass 14m

    我们一看,发现并没有扩展成功,这是为什么呢?我们describe一下这个pvc,发现如下提示:

    1. # kubectl describe pvc test-nfs-pvc2
    2. ...
    3. Warning ExternalExpanding 7m43s volume_expand
    4. Ignoring the PVC: didn't find a plugin capable of expanding the volume; waiting for an external controller to process this PVC.

    报错信息为:没有找到可扩展的插件。
    我上官方网站一看,原来人家已经说的很清楚了:Although the feature is enabled by default, a cluster admin must opt-in to allow users to resize their volumes. Kubernetes v1.11 ships with volume expansion support for the following in-tree volume plugins: AWS-EBS, GCE-PD, Azure Disk, Azure File, Glusterfs, Cinder, Portworx, and Ceph RBD.

    我们的NFS并不被支持(用NFS做后端存储的小伙伴注意了哈)。那就到此结束了吗?

    NO!(男人不能说不行),既然NFS不行,那我们就换一个文件系统吧,比如下面的Glusterfs。