快速创建NFS类型的StorageClass,实现动态供给
K8s 中又引入了 Persistent Volumes 概念,它可以将存储和计算分离,通过不同的组件来管理存储资源和计算资源,然后解耦 pod 和 Volume 之间生命周期的关联。这样,当把 pod 删除之后,它使用的PV仍然存在,还可以被新建的 pod 复用。
K8S中的持久化存储又分为静态供给(Static Volume Provisioning)和动态供给(Dynamic Volume Provisioning)两种方式
动态供给是什么意思呢?就是说现在集群管理员不预分配 PV,他写了一个模板文件,这个模板文件是用来表示创建某一类型存储(块存储,文件存储等)所需的一些参数,这些参数是用户不关心的,给存储本身实现有关的参数。用户只需要提交自身的存储需求,也就是PVC文件,并在 PVC 中指定使用的存储模板(StorageClass)。
K8s 集群中的管控组件,会结合 PVC 和 StorageClass 的信息动态,生成用户所需要的存储(PV),将 PVC 和 PV 进行绑定后,pod 就可以使用 PV 了。通过 StorageClass 配置生成存储所需要的存储模板,再结合用户的需求动态创建 PV 对象,做到按需分配,在没有增加用户使用难度的同时也解放了集群管理员的运维工作。
阿里云原生公开课:应用存储和持久化数据卷:核心知识
(图片来源:阿里云原生公开课:应用存储和持久化数据卷:核心知识)
环境:
Kubernetes 1.21.1
NFS Provisioner 是一个自动配置卷程序,它使用现有的和已配置的 NFS 服务器来支持通过持久卷声明动态配置 Kubernetes 持久卷。
NFS subdir external provisioner is an automatic provisioner that use your existing and already configured NFS server to support dynamic provisioning of Kubernetes Persistent Volumes via Persistent Volume Claims. Persistent volumes are provisioned as n a m e s p a c e − {namespace}- namespace−{pvcName}-${pvName}.
注意:k8s 1.21版本中创建pvc时nfs-provisioner会报错
E0903 08:00:24.858523 1 controller.go:1004] provision “default/test-claim” class “managed-nfs-storage”: unexpected error getting claim reference: selfLink was empty, can’t make reference
解决方法:
修改 /etc/kubernetes/manifests/kube-apiserver.yaml文件
增加 - --feature-gates=RemoveSelfLink=false
spec:
containers:
- command:
- kube-apiserver
- --feature-gates=RemoveSelfLink=false # 增加这行
- --advertise-address=172.24.0.5
- --allow-privileged=true
- --authorization-mode=Node,RBAC
- --client-ca-file=/etc/kubernetes/pki/ca.crt
... 省略剩余部分 ...
由于是kubeadm部署的集群,修改kube-apiserver.yaml文件后,会自动重载
以下开始部署nfs-provisioner
注意:本次测试都部署在default namespace下,如果需要部署到其他的ns,需要修改yaml文件中namespace。
(1)创建ServiceAccount、ClusterRole、ClusterRoleBinding等,为nfs-client-provisioner授权
# rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
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: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
(2)部署nfs-client-provisioner
nfs-client-provisioner 是一个 Kubernetes 的简易 NFS 的外部 provisioner,本身不提供 NFS,需要现有的 NFS 服务器提供存储。
注意:地址和目录要改成实际的NFS服务对应配置
# nfs-provisioner.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default #与RBAC文件中的namespace保持一致
spec:
replicas: 1
selector:
matchLabels:
app: nfs-client-provisioner
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
image: quay.io/external_storage/nfs-client-provisioner:latest
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: gxf-nfs-storage #provisioner名称,请确保该名称与 nfs-StorageClass.yaml文件中的provisioner名称保持一致
- name: NFS_SERVER
value: 10.24.X.X #NFS Server IP地址
- name: NFS_PATH
value: /home/nfs/1 #NFS挂载卷
volumes:
- name: nfs-client-root
nfs:
server: 10.24.X.X #NFS Server IP地址
path: /home/nfs/1 #NFS 挂载卷
# 部署
kubectl apply -f rbac.yaml
kubectl apply -f nfs-provisioner.yaml
kubectl get pod
NAME READY STATUS RESTARTS AGE
nfs-client-provisioner-888d748c6-7c8hh 1/1 Running 0 4m24s
StorageClass是对存储资源的抽象定义,对用户设置的PVC申请屏蔽后端存储的细节,减轻管理员手工管理PV的工作,由系统自动完成PV的创建和绑定,实现动态资源供给。
StorageClass的定义主要包括名称、后端存储的提供者(provisioner)和后端存储对应的参数。对于后端NFS存储来说,配置相对简单,只需要指定provisioner即可。
# nfs-StorageClass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: managed-nfs-storage
provisioner: gxf-nfs-storage #这里的名称要和provisioner配置文件中的环境变量PROVISIONER_NAME保持一致
reclaimPolicy: Retain # 默认为delete
parameters:
archiveOnDelete: "true" # false表示pv被删除时,在nfs下面对应的文件夹也会被删除,true正相反
# 部署
kubectl apply -f nfs-StorageClass.yaml
kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
managed-nfs-storage gxf-nfs-storage Retain Immediate false 6s
部署一个有2个副本的deployment,挂载共享目录
创建pvc,“storageClassName"为上面创建的"managed-nfs-storage”,即指定动态创建PV的模板文件的名字。
# test-pvclaim.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-claim
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 100Mi
storageClassName: managed-nfs-storage
使用动态供给方式,pv被自动创建,pvc与pv自动绑定
kubectl apply -f test-pvclaim.yaml
kubectl get pvc,pv
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/test-claim Bound pvc-d743747b-3b68-4e35-b18f-560d42cc79ec 100Mi RWX managed-nfs-storage 49s
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/pvc-d743747b-3b68-4e35-b18f-560d42cc79ec 100Mi RWX Retain Bound default/test-claim managed-nfs-storage 49s
在nfs目录下生成了文件夹(与pv对应,名称为{namespace}.{pvc name}.{pv name})
deployment中两个pod的将共享存储挂载到/mnt目录
# test-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-deploy
labels:
app: test-deploy
namespace: default #与RBAC文件中的namespace保持一致
spec:
replicas: 2
selector:
matchLabels:
app: test-deploy
strategy:
type: Recreate
selector:
matchLabels:
app: test-deploy
template:
metadata:
labels:
app: test-deploy
spec:
containers:
- name: test-pod
image: busybox:1.24
command:
- "/bin/sh"
args:
- "-c"
# - "touch /mnt/SUCCESS3 && exit 0 || exit 1" #创建一个SUCCESS文件后退出
- touch /mnt/SUCCESS5; sleep 50000
volumeMounts:
- name: nfs-pvc
mountPath: "/mnt"
# subPath: test-pod-3 # 子路径 (这路基代表存储卷下面的test-pod子目录)
volumes:
- name: nfs-pvc
persistentVolumeClaim:
claimName: test-claim #与PVC名称保持一致
kubectl apply -f test-deploy.yaml
kubectl get pod
NAME READY STATUS RESTARTS AGE
test-deploy-5d649d6566-q9kmr 1/1 Running 0 34m
test-deploy-5d649d6566-rm7bv 1/1 Running 0 34m
进入其中一个pod,在/mnt目录中创建一个文件,即将这个文件持久化到nfs共享目录中,再到另一个pod也能看到。
# 进入test-deploy-5d649d6566-q9kmr, 新建文件test.c
kubectl exec -it test-deploy-5d649d6566-q9kmr -- /bin/sh
/ # cd /mnt
cat > test.c << EOF
> 2021
> EOF
# 进入test-deploy-5d649d6566-rm7bv, 查看共享目录
kubectl exec -it test-deploy-5d649d6566-rm7bv -- /bin/sh
/ # cd /mnt
/mnt # ls
SUCCESS5 test.c
/mnt # cat test.c
2021
# test-sts-1.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: test-sts
labels:
k8s-app: test-sts
spec:
serviceName: test-sts-svc
replicas: 3
selector:
matchLabels:
k8s-app: test-sts
template:
metadata:
labels:
k8s-app: test-sts
spec:
containers:
- image: busybox:1.24
name: test-pod
command:
- "/bin/sh"
args:
- "-c"
# - "touch /mnt/SUCCESS3 && exit 0 || exit 1" #创建一个SUCCESS文件后退出
- touch /mnt/SUCCESS5; sleep 50000
imagePullPolicy: IfNotPresent
volumeMounts:
- name: nfs-pvc
mountPath: "/mnt"
volumeClaimTemplates:
- metadata:
name: nfs-pvc
spec:
accessModes: ["ReadWriteMany"]
storageClassName: managed-nfs-storage
resources:
requests:
storage: 20Mi
kubectl apply -f test-sts-1.yaml
kubectl get sts
NAME READY AGE
test-sts 3/3 4m46s
这里直接用volumeClaimTemplates,指定storageClass和需要的存储容量,无需事先创建pvc,k8s直接创建出对应的pvc和pv。由于是有状态应用,所以创建3个pvc和对应的pv。
参考:
k8s学习笔记之StorageClass+NFS
Ubuntu 下搭建 NFS 服务