Kubernetes 使用NFS动态卷

Kubernetes uses NFS dynamic volumes

Posted by alovn on December 7, 2020

K8S 通过引入 PV 和 PVC 的概念,可以让集群的管理人员和开发人员各司其职。不过也增加了额外 PV 和 PVC 创建、绑定、删除、回收等资源管理的工作,也显现出了存储方面的流程复杂和繁琐。

K8S 又引入了 StorageClass 动态卷的概念,可以使得存储能够灵活分配,可以按需请求自动创建存储卷。如果没有动态卷,集群管理员必须通过 Kubernetes 集群创建 PersistentVolume 对象来表示这些卷。 动态供应功能避免了集群管理员预先配置存储的需要。

K8S 动态卷供应的实现基于 storage.k8s.io API 组中的 StorageClass API 对象。集群管理员可以根据需要定义多个 StorageClass 对象,每个对象指定一个卷插件(又名 provisioner), 卷插件向卷供应商提供在创建卷时需要的数据卷信息及相关参数。

StorageClass 作为对存储资源的抽象定义, 对用户设置的NFS申请屏蔽后端存储的细节, 一方面减少了用户对于存储资源细节的关注, 另一方面减轻了集群管理员手工管理 pv 的工作, 由系统自动完成pv的创建和绑定。StorageClass 是一种资源对象, 不提供pv的创建。

NFS 共享存储简单易部署,成本低,不知道怎么安装部署的可以查看这里:Linux 下使用 NFS 文件共享。这里以NFS共享存储为例, 使用nfs-client-provisioner 组件连接nfs服务器以及pv的创建。

声明部署

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//deployment.yaml
kind: Deployment
apiVersion: apps/v1
metadata:
  name: nfs-client-provisioner
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nfs-client-provisioner
  strategy:
    type: Recreate
  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:latest
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME # 可以改为你想用的名字
              value: nfs-storage
            - name: NFS_SERVER # NFS服务地址
              value: s1001.lab.org
            - name: NFS_PATH # NFS服务目录
              value: /data/nfs-data/k8s/dynamic/
      volumes:
        - name: nfs-client-root
          nfs:
            server: s1001.lab.org
            path: /data/nfs-data/k8s/dynamic/

注意需将以上NFS服务地址和路径参数改成自己的。

StorageClass

StorageClass,此处修改provisioner名称,需要和部署文件中的PROVISIONER_NAME一致:

1
2
3
4
5
6
7
8
//class.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: managed-nfs-storage
provisioner: nfs-storage # 这里名称需和上面 PROVISIONER_NAME 一致
parameters:
  archiveOnDelete: "false"

storageclass 支持以下几个参数:

  • onDelete:delete 删除目录。retain:归档。
  • archiveOnDelete:false 删除目录。如果设置了onDelete参数,archiveOnDelete就会被忽略。
  • pathPattern:用于设置创建目录的路径, 默认格式是
1
${.PVC.namespace}/${.PVC.annotations.nfs.io/storage-path}

当PVC删除时,默认会对创建的目录进行规定并重命名保存:

1
archived-+volume.Name

注意 storageclass 一旦创建, parameters 参数不允许再次更改,只能重新创建。

执行安装

依次执行:

1
2
3
kubectl apply -f https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner/raw/master/deploy/rbac.yaml
kubectl apply -f deployment.yaml
kubectl apply -f class.yaml

测试验证

现在就可以使用动态卷了, 创建一个用于测试的 Pod, 通过 PersistentVolumeClaim 申请使用nfs共享卷,执行后如果你在挂载的目录中看到一个SUCCESS文件就说明成功了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//test.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: test-claim
  annotations:
    volume.beta.kubernetes.io/storage-class: "managed-nfs-storage"
spec:
  storageClassName: managed-nfs-storage
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Mi
---
kind: Pod
apiVersion: v1
metadata:
  name: test-pod
spec:
  containers:
  - name: test-pod
    image: busybox:1.28.4
    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-claim

验证一下,pv、pvc 已经自动创建好了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//查看pv
$ kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                          STORAGECLASS          REASON   AGE
pvc-c95e877a-6418-417f-bfac-cb9853a2bfc7   1Mi        RWX            Delete           Bound    default/test-claim             managed-nfs-storage            32m

$ kubectl get pvc
NAME                   STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE
test-claim             Bound    pvc-c95e877a-6418-417f-bfac-cb9853a2bfc7   1Mi        RWX            managed-nfs-storage   34m

$ ls /data/nfs-data/k8s/dynamic
default-test-claim-pvc-c95e877a-6418-417f-bfac-cb9853a2bfc7

$ ls -al /data/nfs-data/k8s/dynamic/default-test-claim-pvc-c95e877a-6418-417f-bfac-cb9853a2bfc7/
SUCCESS

如果执行删除 kubectl delete -f deployment.yaml 删除pvc后, 创建的目录和文件就会自动删除,因为我们上面设置到了 StorageClass 的参数 archiveOnDelete 为 false,也就不会自动归档。

参考文档

https://kubernetes.io/zh/docs/concepts/storage/dynamic-provisioning/

https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner