K8s使用新版NFS Provisioner配置Subdir

时间:2022-05-12 06:33:31

K8s使用新版NFS Provisioner配置Subdir

背景

NFS在k8s中作为volume存储已经没有什么新奇的了,这个是最简单也是最容易上手的一种文件存储。最近有一个需求需要在k8s中使用NFS存储,于是记录如下,并且还存在一些骚操作和过程中遇到的坑点,同时记录如下。

访问nfs provisioner的GitHub仓库会发现他提示你该仓库已经被个人归档并且状态已经是只读了。

老的NFS仓库地址:https://github.com/kubernetes-retired/external-storage/tree/master/nfs-client

下面Deprecated表示仓库已经移动到了另外一个GitHub地址了,也就是说他这个仓库已经不在更新了,持续更新的仓库是Moved to 后面指定的仓库了。

新仓库地址:https://github.com/lorenzofaresin/nfs-subdir-external-provisioner

发现更新的仓库中相比老仓库多了一个功能:添加了一个参数pathPattern,实际上也就是通过设置这个参数可以配置PV的子目录。

K8s使用新版NFS Provisioner配置Subdir

nfs-client-provisioner部署

带着好奇心我们来部署一下新的NFS,以下yaml配置文件可以在项目中的deploy目录中找到。我这里的配置根据我的环境稍微做了更改,比如NFS的服务的IP地址。你们根据实际情况修改成自己的nfs服务器地址和path路径。

「本次实践在k8s 1.19.0上」

class.yaml

  1. $ cat class.yaml 
  2. apiVersion: storage.k8s.io/v1 
  3. kind: StorageClass 
  4. metadata: 
  5.   name: managed-nfs-storage 
  6. provisioner: k8s-sigs.io/nfs-subdir-external-provisioner # or choose another name, must match deployment's env PROVISIONER_NAME' 
  7. parameters: 
  8.   archiveOnDelete: "false" 

deployment.yaml

  1. apiVersion: apps/v1 
  2. kind: Deployment 
  3. metadata: 
  4.   name: nfs-client-provisioner 
  5.   labels: 
  6.     app: nfs-client-provisioner 
  7.   # replace with namespace where provisioner is deployed 
  8.   namespace: kube-system 
  9. spec: 
  10.   replicas: 1 
  11.   strategy: 
  12.     type: Recreate 
  13.   selector: 
  14.     matchLabels: 
  15.       app: nfs-client-provisioner 
  16.   template: 
  17.     metadata: 
  18.       labels: 
  19.         app: nfs-client-provisioner 
  20.     spec: 
  21.       serviceAccountName: nfs-client-provisioner 
  22.       containers: 
  23.         - name: nfs-client-provisioner 
  24.           image: k8s.gcr.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2 
  25.           volumeMounts: 
  26.             - name: nfs-client-root 
  27.               mountPath: /persistentvolumes 
  28.           env: 
  29.             - name: PROVISIONER_NAME 
  30.               value: k8s-sigs.io/nfs-subdir-external-provisioner 
  31.             - name: NFS_SERVER 
  32.               value: 172.16.33.4 
  33.             - name: NFS_PATH 
  34.               value: / 
  35.       volumes: 
  36.         - name: nfs-client-root 
  37.           nfs: 
  38.             server: 172.16.33.4 
  39.             path: / 

rbac.yaml

  1. apiVersion: v1 
  2. kind: ServiceAccount 
  3. metadata: 
  4.   name: nfs-client-provisioner 
  5.   # replace with namespace where provisioner is deployed 
  6.   namespace: kube-system 
  7. --- 
  8. kind: ClusterRole 
  9. apiVersion: rbac.authorization.k8s.io/v1 
  10. metadata: 
  11.   name: nfs-client-provisioner-runner 
  12. rules: 
  13.   - apiGroups: [""
  14.     resources: ["nodes"
  15.     verbs: ["get""list""watch"
  16.   - apiGroups: [""
  17.     resources: ["persistentvolumes"
  18.     verbs: ["get""list""watch""create""delete"
  19.   - apiGroups: [""
  20.     resources: ["persistentvolumeclaims"
  21.     verbs: ["get""list""watch""update"
  22.   - apiGroups: ["storage.k8s.io"
  23.     resources: ["storageclasses"
  24.     verbs: ["get""list""watch"
  25.   - apiGroups: [""
  26.     resources: ["events"
  27.     verbs: ["create""update""patch"
  28. --- 
  29. kind: ClusterRoleBinding 
  30. apiVersion: rbac.authorization.k8s.io/v1 
  31. metadata: 
  32.   name: run-nfs-client-provisioner 
  33. subjects: 
  34.   - kind: ServiceAccount 
  35.     name: nfs-client-provisioner 
  36.     # replace with namespace where provisioner is deployed 
  37.     namespace: kube-system 
  38. roleRef: 
  39.   kind: ClusterRole 
  40.   name: nfs-client-provisioner-runner 
  41.   apiGroup: rbac.authorization.k8s.io 
  42. --- 
  43. kind: Role 
  44. apiVersion: rbac.authorization.k8s.io/v1 
  45. metadata: 
  46.   name: leader-locking-nfs-client-provisioner 
  47.   # replace with namespace where provisioner is deployed 
  48.   namespace: kube-system 
  49. rules: 
  50.   - apiGroups: [""
  51.     resources: ["endpoints"
  52.     verbs: ["get""list""watch""create""update""patch"
  53. --- 
  54. kind: RoleBinding 
  55. apiVersion: rbac.authorization.k8s.io/v1 
  56. metadata: 
  57.   name: leader-locking-nfs-client-provisioner 
  58.   # replace with namespace where provisioner is deployed 
  59.   namespace: kube-system 
  60. subjects: 
  61.   - kind: ServiceAccount 
  62.     name: nfs-client-provisioner 
  63.     # replace with namespace where provisioner is deployed 
  64.     namespace: kube-system 
  65. roleRef: 
  66.   kind: Role 
  67.   name: leader-locking-nfs-client-provisioner 
  68.   apiGroup: rbac.authorization.k8s.io 

注意:

  • 镜像无法拉取的话可以从国外机器拉取镜像然后再导入
  • rbac基本无需更改,配置子目录的时候需要更改class.yaml文件,后面会说

创建所有资源文件

  1. kubectl apply -f class.yaml -f deployment.yaml -f rbac.yaml 

通过一个简单的例子来创建pvc

  1. $ cat test-pvc-2.yaml 
  2. kind: PersistentVolumeClaim 
  3. apiVersion: v1 
  4. metadata: 
  5.   name: test-pvc-2 
  6.   namespace: nacos 
  7. spec: 
  8.   storageClassName: "managed-nfs-storage" 
  9.   accessModes: 
  10.     - ReadWriteMany 
  11.   resources: 
  12.     requests: 
  13.       storage: 10Gi 
  14.  
  15. $ cat test-nacos-pod-2.yaml 
  16. apiVersion: apps/v1 
  17. kind: StatefulSet 
  18. metadata: 
  19.   name: nacos-c1-sit-tmp-1 
  20.   labels: 
  21.     appEnv: sit 
  22.     appName: nacos-c1-sit-tmp-1 
  23.   namespace: nacos 
  24. spec: 
  25.   serviceName: nacos-c1-sit-tmp-1 
  26.   replicas: 3 
  27.   selector: 
  28.     matchLabels: 
  29.       appEnv: sit 
  30.       appName: nacos-c1-sit-tmp-1 
  31.   template: 
  32.     metadata: 
  33.       labels: 
  34.         appEnv: sit 
  35.         appName: nacos-c1-sit-tmp-1 
  36.     spec: 
  37.       dnsPolicy: ClusterFirst 
  38.       containers: 
  39.       - name: nacos 
  40.         image: www.ayunw.cn/library/nacos/nacos-server:1.4.1 
  41.         ports: 
  42.         - containerPort: 8848 
  43.         env: 
  44.         - name: NACOS_REPLICAS 
  45.           value: "1" 
  46.         - name: MYSQL_SERVICE_HOST 
  47.           value: mysql.ayunw.cn 
  48.         - name: MYSQL_SERVICE_DB_NAME 
  49.           value: nacos_c1_sit 
  50.         - name: MYSQL_SERVICE_PORT 
  51.           value: "3306" 
  52.         - name: MYSQL_SERVICE_USER 
  53.           value: nacos 
  54.         - name: MYSQL_SERVICE_PASSWORD 
  55.           value: xxxxxxxxx 
  56.         - name: MODE 
  57.           value: cluster 
  58.         - name: NACOS_SERVER_PORT 
  59.           value: "8848" 
  60.         - name: PREFER_HOST_MODE 
  61.           value: hostname 
  62.         - name: SPRING_DATASOURCE_PLATFORM 
  63.           value: mysql 
  64.         - name: TOMCAT_ACCESSLOG_ENABLED 
  65.           value: "true" 
  66.         - name: NACOS_AUTH_ENABLE 
  67.           value: "true" 
  68.         - name: NACOS_SERVERS 
  69.           value: nacos-c1-sit-0.nacos-c1-sit-tmp-1.nacos.svc.cluster.local:8848 nacos-c1-sit-1.nacos-c1-sit-tmp-1.nacos.svc.cluster.local:8848 nacos-c1-sit-2.nacos-c1-sit-tmp-1.nacos.svc.cluster.local:8848 
  70.         imagePullPolicy: IfNotPresent 
  71.         resources: 
  72.           limits: 
  73.             cpu: 500m 
  74.             memory: 5Gi 
  75.           requests: 
  76.             cpu: 100m 
  77.             memory: 512Mi 
  78.         volumeMounts: 
  79.         - name: data 
  80.           mountPath: /home/nacos/plugins/peer-finder 
  81.           subPath: peer-finder 
  82.         - name: data 
  83.           mountPath: /home/nacos/data 
  84.           subPath: data 
  85.   volumeClaimTemplates: 
  86.     - metadata: 
  87.         name: data 
  88.       spec: 
  89.         storageClassName: "managed-nfs-storage" 
  90.         accessModes: 
  91.           - "ReadWriteMany" 
  92.         resources: 
  93.           requests: 
  94.             storage: 10Gi 

查看pvc以及nfs存储中的数据

  1. # ll 
  2. total 12 
  3. drwxr-xr-x 4 root root 4096 Aug  3 13:30 nacos-data-nacos-c1-sit-tmp-1-0-pvc-90d74547-0c71-4799-9b1c-58d80da51973 
  4. drwxr-xr-x 4 root root 4096 Aug  3 13:30 nacos-data-nacos-c1-sit-tmp-1-1-pvc-18b3e220-d7e5-4129-89c4-159d9d9f243b 
  5. drwxr-xr-x 4 root root 4096 Aug  3 13:31 nacos-data-nacos-c1-sit-tmp-1-2-pvc-26737f88-35cd-42dc-87b6-3b3c78d823da 
  6.  
  7. # ll nacos-data-nacos-c1-sit-tmp-1-0-pvc-90d74547-0c71-4799-9b1c-58d80da51973 
  8. total 8 
  9. drwxr-xr-x 2 root root 4096 Aug  3 13:30 data 
  10. drwxr-xr-x 2 root root 4096 Aug  3 13:30 peer-finder 

可以发现手动创建了一个PVC,并且创建一个nacos的deployment使用这个PVC后已经自动创建出相应的PV并且与之绑定,且挂载了数据。

配置子目录

删除之前创建的class.yaml,添加pathPattern参数,然后重新生成sc

  1. $ kubectl delete -f class.yaml 
  2.  
  3. $ vi class.yaml 
  4. apiVersion: storage.k8s.io/v1 
  5. kind: StorageClass 
  6. metadata: 
  7.   name: managed-nfs-storage 
  8. provisioner: k8s-sigs.io/nfs-subdir-external-provisioner # or choose another name, must match deployment's env PROVISIONER_NAME' 
  9. parameters: 
  10.   archiveOnDelete: "false" 
  11.   # 添加以下参数 
  12.   pathPattern: "${.PVC.namespace}/${.PVC.annotations.nfs.io/storage-path}" 

创建pvc来测试生成的PV目录是否生成了子目录

  1. $ cat test-pvc.yaml 
  2. kind: PersistentVolumeClaim 
  3. apiVersion: v1 
  4. metadata: 
  5.   name: test-pvc-2 
  6.   namespace: nacos 
  7.   annotations: 
  8.     nfs.io/storage-path: "test-path-two" # not required, depending on whether this annotation was shown in the storage class description 
  9. spec: 
  10.   storageClassName: "managed-nfs-storage" 
  11.   accessModes: 
  12.     - ReadWriteMany 
  13.   resources: 
  14.     requests: 
  15.       storage: 100Mi 

创建资源

  1. kubectl apply -f class.yaml -f test-pvc.yaml 

查看结果

  1. # pwd 
  2. /data/nfs# ll nacos/ 
  3. total 4 
  4. drwxr-xr-x 2 root root 4096 Aug  3 10:21 nacos-pvc-c1-pro 
  5.  
  6. # tree -L 2 . 
  7. └── nacos 
  8.     └── nacos-pvc-c1-pro 
  9. 2 directories, 0 files 

在mount了nfs的机器上查看生成的目录,发现子目录的确已经生成,并且子目录的层级是以"命名空间/注解名称"为规则的。刚好符合了上面StorageClass中定义的pathPattern规则。

provisioner高可用

生产环境中应该尽可能的避免单点故障,因此此处考虑provisioner的高可用架构 更新后的provisioner配置如下:

  1. $ cat nfs-deployment.yaml 
  2. apiVersion: apps/v1 
  3. kind: Deployment 
  4. metadata: 
  5.   name: nfs-client-provisioner 
  6.   labels: 
  7.     app: nfs-client-provisioner 
  8.   # replace with namespace where provisioner is deployed 
  9.   namespace: kube-system 
  10. spec: 
  11.   # 因为要实现高可用,所以配置3个pod副本 
  12.   replicas: 3 
  13.   strategy: 
  14.     type: Recreate 
  15.   selector: 
  16.     matchLabels: 
  17.       app: nfs-client-provisioner 
  18.   template: 
  19.     metadata: 
  20.       labels: 
  21.         app: nfs-client-provisioner 
  22.     spec: 
  23.       serviceAccountName: nfs-client-provisioner 
  24.       imagePullSecrets: 
  25.         - name: registry-auth-paas 
  26.       containers: 
  27.         - name: nfs-client-provisioner 
  28.           image: www.ayunw.cn/nfs-subdir-external-provisioner:v4.0.2-31-gcb203b4 
  29.           imagePullPolicy: IfNotPresent 
  30.           volumeMounts: 
  31.             - name: nfs-client-root 
  32.               mountPath: /persistentvolumes 
  33.           env: 
  34.             - name: PROVISIONER_NAME 
  35.               value: k8s-sigs.io/nfs-subdir-external-provisioner 
  36.               # 设置高可用允许选举 
  37.             - name: ENABLE_LEADER_ELECTION 
  38.               value: "True" 
  39.             - name: NFS_SERVER 
  40.               value: 172.16.33.4 
  41.             - name: NFS_PATH 
  42.               value: / 
  43.       volumes: 
  44.         - name: nfs-client-root 
  45.           nfs: 
  46.             server: 172.16.33.4 
  47.             path: / 

重建资源

  1. kubectl delete -f nfs-class.yaml -f nfs-deployment.yaml 
  2. kubectl apply -f nfs-class.yaml -f nfs-deployment.yaml 

查看provisioner高可用是否生效

  1. # kubectl get po -n kube-system | grep nfs 
  2. nfs-client-provisioner-666df4d979-fdl8l    1/1     Running   0          20s 
  3. nfs-client-provisioner-666df4d979-n54ps    1/1     Running   0          20s 
  4. nfs-client-provisioner-666df4d979-s4cql    1/1     Running   0          20s 
  5. # kubectl logs -f --tail=20 nfs-client-provisioner-666df4d979-fdl8l -n kube-system 
  6. I0803 06:04:41.406441       1 leaderelection.go:242] attempting to acquire leader lease  kube-system/nfs-provisioner-baiducfs... 
  7. ^C 
  8. # kubectl logs -f --tail=20  -n kube-system nfs-client-provisioner-666df4d979-n54ps 
  9. I0803 06:04:41.961617       1 leaderelection.go:242] attempting to acquire leader lease  kube-system/nfs-provisioner-baiducfs... 
  10. ^C 
  11. [root@qing-core-kube-master-srv1 nfs-storage]# kubectl logs -f --tail=20  -n kube-system nfs-client-provisioner-666df4d979-s4cql 
  12. I0803 06:04:39.574258       1 leaderelection.go:242] attempting to acquire leader lease  kube-system/nfs-provisioner-baiducfs... 
  13. I0803 06:04:39.593388       1 leaderelection.go:252] successfully acquired lease kube-system/nfs-provisioner-baiducfs 
  14. I0803 06:04:39.593519       1 event.go:278] Event(v1.ObjectReference{Kind:"Endpoints", Namespace:"kube-system"Name:"nfs-provisioner-baiducfs", UID:"3d5cdef6-57da-445e-bcd4-b82d0181fee4", APIVersion:"v1", ResourceVersion:"1471379708", FieldPath:""}): type: 'Normal' reason: 'LeaderElection' nfs-client-provisioner-666df4d979-s4cql_590ac6eb-ccfd-4653-9de5-57015f820b84 became leader 
  15. I0803 06:04:39.593559       1 controller.go:820] Starting provisioner controller nfs-provisioner-baiducfs_nfs-client-provisioner-666df4d979-s4cql_590ac6eb-ccfd-4653-9de5-57015f820b84! 
  16. I0803 06:04:39.694505       1 controller.go:869] Started provisioner controller nfs-provisioner-baiducfs_nfs-client-provisioner-666df4d979-s4cql_590ac6eb-ccfd-4653-9de5-57015f820b84! 

通过successfully acquired lease kube-system/nfs-provisioner-baiducfs可以看到第三个pod成功被选举为leader节点了,高可用生效。

报错

在操作过程中遇到describe pod发现报错如下:

  1. Mounting arguments: --description=Kubernetes transient mount for /data/kubernetes/kubelet/pods/2ca70aa9-433c-4d10-8f87-154ec9569504/volumes/kubernetes.io~nfs/nfs-client-root --scope -- mount -t nfs 172.16.41.7:/data/nfs_storage /data/kubernetes/kubelet/pods/2ca70aa9-433c-4d10-8f87-154ec9569504/volumes/kubernetes.io~nfs/nfs-client-root 
  2. Output: Running scope as unit: run-rdcc7cfa6560845969628fc551606e69d.scope 
  3. mount: /data/kubernetes/kubelet/pods/2ca70aa9-433c-4d10-8f87-154ec9569504/volumes/kubernetes.io~nfs/nfs-client-root: bad optionfor several filesystems (e.g. nfs, cifs) you might need a /sbin/mount.<type> helper program. 
  4.   Warning  FailedMount  10s  kubelet, node1.ayunw.cn  MountVolume.SetUp failed for volume "nfs-client-root" : mount failed: exit status 32 
  5. Mounting command: systemd-run 

解决方式:经排查原因是pod被调度到的节点上没有安装nfs客户端,只需要安装一下nfs客户端nfs-utils即可。

原文链接:https://mp.weixin.qq.com/s/3Oo_bSg5umu2vdH8XgsGQA