动态PV 使用UFS
背景
前面我们描述了通过创建静态 PV 的方式在 UK8S 中使用 UFS,但这种方式存在两个问题:一是每次都需要手动创建 PV 和 PVC,非常不便;二是无法自动在 UFS 创建子目录,需要预先配置。
下面介绍一个名为nfs-subdir-external-provisioner
的开源项目,项目地址为 nfs-subdir-external-provisioner 。此项目可以为我们提供一个基于 UFS 的StorageClass
:在业务需要 UFS 存储资源时,只需要创建 PVC,nfs-client-provisioner
就会自动创建 PV,并在 UFS 下创建一个名为${namespace}-${pvcName}-${pvName}
的子目录。
工作原理
我们将 nfs 相关参数通过环境变量传入到nfs-client-provisioner
,这个 provisioner 通过 Deployment 控制器运行一个 Pod 来管理 nfs 的存储空间。服务启动后,我们再创建一个StorageClass
,其 provisioner 与nfs-client-provisioner
服务内的provisioner-name
一致。nfs-client-provisioner
会 watch 集群内的 PVC 对象,为其提供适配 PV 的服务,并且会在 nfs 根目录下创建对应的目录。 这些官方文档的描述已经比较详细了,不再赘述。
以下说明如何在 UK8S 中使用这个服务来管理 UFS。
操作指南
1、克隆项目
在deploy/
目录下,我们需要关注三个文件,分别是rbac.yaml
, deployment.yaml
, class.yaml
。
# git clone https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner.git
# cd nfs-subdir-external-provisioner/deploy/
# ls
class.yaml kustomization.yaml rbac.yaml test-claim.yaml
deployment.yaml objects test-pod.yaml
2、修改nfs-client-provisioner
服务的 namespace
我们将把nfs-client-provisioner
服务和 RBAC 需要的资源都部署在系统插件所在的kube-system
namespace。
sed -i "s/namespace:.*/namespace: kube-system/g" ./rbac.yaml ./deployment.yaml
3、修改deployment.yaml
nfs-client-provisioner
服务启动时,需要挂载 UFS,官网文档是通过spec.volume.nfs
来声明的,我们这里改为静态声明 PV 的方式。具体原因说明如下:
UFS 文件系统在 mount 到云主机时需要指定额外的mountOption
参数,但spec.volume.nfs
不支持这个参数。而PersistentVolume
的声明中可以支持这个参数,因此我们通过挂载静态 PV 的方式来完成首次挂载。
deployment.yaml
文件改动处较多,建议直接替换即可。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
namespace: kube-system
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
# 使用uhub提供的镜像以获取更快的拉取速度
image: uhub.an-link.com/uk8s/nfs-subdir-external-provisioner:v4.0.2
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: ucloud/ufs # 修改为安联云/ufs
- name: NFS_SERVER
value: 10.9.x.x # 这里需要修改为UFS的Server地址
- name: NFS_PATH
value: / # 这里改成UFS的挂载路径
volumes:
- name: nfs-client-root
persistentVolumeClaim: #这里由 nfs 改为 persistentVolumeClaim
claimName: nfs-client-root
---
# 创建 PVC
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: nfs-client-root
namespace: kube-system
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 200Gi
---
#手动创建 PV 并指定 mountOption
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-client-root
spec:
capacity:
storage: 200Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /
server: 10.9.x.x #这里直接写UFS的Server地址即可。
mountOptions:
- nolock
- nfsvers=4.0
4、修改class.yaml
最后需要修改的是StorageClass
的定义文件 class.yaml
。
主要是新增了mountOption
参数,这个值会传递给nfs-client-provisioner
; 如果不加的话,挂载UFS的时候会失败。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-client
provisioner: ucloud/ufs
parameters:
onDelete: "retain"
mountOptions:
- nolock
- nfsvers=4.0
此外您可以在parameters
中指定 PV 的回收策略: 删除、保留或者归档。上面示例中配置了删除 PV 后保留对应的目录。
参数 | 选项及作用 | 默认选项 |
---|---|---|
onDelete | "delete" : 删除目录"retain" : 保留目录"" (空值): 取决于archiveOnDelete 参数 | 空值 |
archiveOnDelete | "true" : 归档, 目录会被重命名为archived-<volume.Name> "false" : 删除目录 | "true" |
5、执行部署
依次执行
# kubectl create -f rbac.yaml
# kubectl create -f deployment.yaml
# kubectl create -f class.yaml
6、验证
创建test-nfs-sc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-claim
spec:
storageClassName: nfs-client
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Mi
---
kind: Pod
apiVersion: v1
metadata:
name: test-pod
spec:
containers:
- name: test-pod
image: uhub.an-link.com/uk8s/busybox:1.31.1
command:
- "/bin/sh"
args:
- "-c"
- "echo 1 > /mnt/SUCCESS; sleep 10000000"
volumeMounts:
- name: nfs-pvc
mountPath: "/mnt"
restartPolicy: "Never"
volumes:
- name: nfs-pvc
persistentVolumeClaim:
claimName: test-claim
创建测试 pod 并验证挂载的 UFS 可读写:
# kubectl create -f test-nfs-sc.yaml
# kubectl exec test-pod -- /bin/sh -c 'ls /mnt/ && cat /mnt/*'
SUCCESS
1
# kubectl delete -f test-nfs-sc.yaml
升级指南
从旧版external-storage
到新版nfs-subdir-external-provisioner
的升级流程
升级背景
旧版provisioner能支持的最新k8s版本为1.23, 其中1.20及以上需要在apiserver上添加--feature-gates=RemoveSelfLink=false
支持。
1.24及更新的k8s版本必须使用新版provisioner(因为已经不支持该apiserver参数)。
对存量pvc和pod的影响
使用旧版provisioner (即managed-nfs-storage
storage class) 申请的pvc,升级完成后可以继续挂载使用,pod内的挂载不受影响,pod重启后仍然可以挂载。pod和pvc删除后不会清理ufs上对应的文件,如需释放空间应手动删除文件。
升级流程
结合新版provisioner部署文档(见本文前部分)进行升级。
rbac
确认rbac.yaml
中指定的namespace与原nfs-client-provisioner ServiceAccount所在namespace相同,比如同为default或同为kube-system, 修改完成后执行kubectl apply -f rbac.yaml
升级
deployment
- 确认namespace与上面rbac部署的namespace相同
- 删除原来的deployment: kubectl delete deploy nfs-client-provisioner
- apply新的
deployment.yaml
。因为创建静态pvc和pv的部分和原来相同, 这一步apply的结果应如下所示
deployment.apps/nfs-client-provisioner configured
persistentvolumeclaim/nfs-client-root unchanged
persistentvolume/nfs-client-root unchanged
storage class
直接apply -f class.yaml
即可。(旧的storage class managed-nfs-storage
可以不删除)。