> 文章列表 > K8S使用持久化卷存储到NFS(NAS盘)

K8S使用持久化卷存储到NFS(NAS盘)

K8S使用持久化卷存储到NFS(NAS盘)

参考文章:K8S-v1.20中使用PVC持久卷 - 知乎

Persistent Volumes:PV是持久化卷,系统管理员设置的存储,它是群集的一部分,是一种资源,所以它有独立于Pod的生命周期

Persistent Volume Claim:PVC是用户存储的请求。它与Pod相似,Pod消耗节点的CPU和内存资源,PVC则消耗PV资源,可以声明特定的容量大小和访问模式。

部署模式:

NFS(NAS)  ==》PV  ==》PVC

1、概念:

1.1 基础概念

容器本身是非持久化的,首先当容器崩溃,kubelet将重新启动容器,容器将会以镜像的初始状态重新开始在通过一个Pod中一起运行的容器,此时之前写入容器的文件将会丢失,另外容器之间通常需要共享容文件。Kubernetes通过Volum卷解决上述的两个问题。

Docker中存储卷只是磁盘的或另一个容器中的目录,并没有对其生命周期进行管理。Kubernetes的存储卷有自己的生命周期,它的生命周期与使用的它Pod生命周期一致。相比于在Pod中运行的容器来说,存储卷的存在时间会比的其中的任何容器都长,并且在容器重新启动时会保留数据。当然,当Pod停止存在时,存储卷也将不再存在。

在Pod中通过指定下面的字段来使用存储卷:

        spec.volumes:通过此字段提供指定的存储卷

        spec.containers.volumeMounts:通过此字段将存储卷挂接到容器中

1.2 PV的配置

1.2.1 静态PV配置

        管理员创建静态一些PV,它们带有可供集群用户使用的实际存储的细节,它们存在于Kubernetes API中,可用于消费

1.2.2 动态PV配置

        当管理员创建的静态 PV 都不匹配用户的 PersistentVolumeClaim 时,集群可能会尝试动态地为 PVC 创建卷。此配置基于 StorageClasses:PVC 必须请求存储类,并且管理员必须创建并配置该类才能进行动态创建。声明该类为 "" 可以有效地禁用其动态配置。要启用基于存储级别的动态存储配置,集群管理员需要启用 API server 上的 DefaultStorageClass 准入控制器。例如,通过确保 DefaultStorageClass 位于 API server 组件的 --admission-control 标志,使用逗号分隔的有序值列表中,可以完成此操作。有关 API server 命令行标志的更多信息,请检查 kube-apiserver 文档。

1.2.3 PVC与PV的绑定

        一旦用户创建或已经创建了具有特定存储量的 PersistentVolumeClaim 以及某些访问模式。Kubernetes控制器会监视到新的 PVC,并寻找匹配的 PV,并将它们绑定在一起。 PVC 跟 PV 绑定是一对一的映射。        

1.2.4 PVC及PV的使用

        Pod 使用PVC作为卷,集群检查PVC以查找绑定的卷并为集群挂载该卷。对于支持多种访问模式的卷,用户指定在使用声明作为容器中的卷时所需的模式(读写、只读)

2 部署PV及PVC

2.1 所有K8S机器都需要安装NFS程序

yum -y install nfs-utils rpcbind

2.2 仅针对需要暴露文件服务的机器开启NFS服务

        作为最终保存数据的linux服务器需要开启NSF服务,或者使用NAS盘的开启NSF服务即可。

        2.2.1 Linux为例开启NFS服务对外暴露服务目录

# 先创建一个对外共享的目录
mkdir -p /share/
chmod 777 /share/echo "/share 192.168.10.0/24(rw,sync,no_root_squash)" >>/etc/exports# 声明对外暴露
exportfs -rv# 设置开机启动NFS服务并正式启动
systemctl enable rpcbind
systemctl enable nfs
systemctl enable nfs-lock
systemctl enable nfs-idmapsystemctl start rpcbind
systemctl start nfs
systemctl start nfs-lock
systemctl start nfs-idmap
#在需要成为NFS文件服务器上执行如上4行,可以对192.168.10.X网段暴露/share目录的读写

在实际需要暴露NFS的机器上执行如上命令,代表该机器对192.168.10.X网端内的机器对/share目录共享存储。rw支持读写、sync代表同步写入到NFS才算写成功、async代表写入到NFS机器的缓冲区就算成功。no_root_squash登入 NFS 主机使用分享目录的使用者,如果是 root 的话,那么对于这个分享的目录来说,他就具有 root 的权限!这个项目『极不安全』,不建议使用!root_squash:在登入 NFS 主机使用分享之目录的使用者如果是 root 时,那么这个使用者的权限将被压缩成为匿名使用者,通常他的 UID 与 GID 都会变成 nobody 那个系统账号的身份。

        2.2.2   群晖NAS盘为例开启NFS服务对外暴露共享目录

1、开启群晖NFS服务

2、创建共享文件、并设置NFS权限。

2.3 在需要挂载NFS服务的机器上执行挂载目录

假定NFS服务启动在192.168.10.31机器上

showmount -e 192.168.10.31   # 执行该命令可以看到该NFS服务器下对外暴露的目录#### 下面是展示结果
Export list for 192.168.10.31:
/volume1/development *         # 代表允许所有机器访问
/volume1/test 192.168.10.80    # 代表仅允许80机器访问

将远程NFS服务器的某个目录挂载到本机上,例如把远程development目录挂载到本机的/root/nfs上

showmount -e 192.168.10.31 # 可以先查看下远程NFS服务器有哪些目录暴露出来mkdir /root/nfschmod 777 /root/nfsmount -t nfs 192.168.10.31:/volume1/development /root/nfs# 可以执行如下命令查看挂载的结果
df -h可以看下面的结果
192.168.10.31:/volume1/development nfs        14T  690G   14T    5% /root/nfs

1.3.4 取消挂载

umount /root/nfs
或强制取消挂载
umount -l /root/nfs

2.4 执行如下命令,创建PV及PVC

apiVersion: v1
kind: PersistentVolume
metadata:name: pv-nfsnamespace: zo-devlabels:type: nfs
spec:capacity:storage: 100GiaccessModes:- ReadWriteManypersistentVolumeReclaimPolicy: Retain   #Recycle会清除数据,自动回收;Retain需要手动清理回收;Delete:云存储专用的回收空间使用命令storageClassName: nfsnfs:path: "/volume1/development"server: 192.168.10.31  #nfs服务器地址readOnly: false
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: pvc-nfsnamespace: zo-dev
spec:accessModes:- ReadWriteManyresources:requests:storage: 20GistorageClassName: nfs

3、在部署时使用PVC

 volumeMounts:- mountPath: /etc/nginx/nginx.confname: nginx-php74subPath: nginx.conf- mountPath: "/var/www/html" ##挂载容器中的目录到pvc nfs中的目录name: storage    ##增加storagevolumes:- name: nginx-php74configMap:name: nginx-php-configmapitems:- key: nginx_php_confpath: nginx.conf- name: storage   ##与前面对应persistentVolumeClaim:  ##pvc声明claimName: pvc-nfs   ##创建好的pvc lab name

4、PV及PVC更高阶的用法

我们可以相信到一个大型的系统中会存在非常的PV及PVC的要求,挂载不同的目录,不同的容量大小,不同的NFS服务器、高速的SSD、低速的。因此如何管理好PV、PVC,最好是能依据PVC的声明要求,能做到自动分配PV岂不是很爽?

4.1 什么是StorageClass

参考这篇文章:k8s-1.22.3版本部署持久化存储之StorageClass+NFS_k8s 部署storageclass_归海听雪的博客-CSDN博客

StatefulSet是为了解决有状态服务的问题(对应Deployments和ReplicaSets是为无状态服务而设计),其应用场景包括:

稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现
稳定的网络标志,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现
有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现
有序收缩,有序删除(即从N-1到0)
从上面的应用场景可以发现,StatefulSet由以下几个部分组成:

用于定义网络标志(DNS domain)的Headless Service
用于创建PersistentVolumes的volumeClaimTemplates
定义具体应用的StatefulSet
StatefulSet中每个Pod的DNS格式为statefulSetName-{0..N-1}.serviceName.namespace.svc.cluster.local,其中

serviceName为Headless Service的名字
0..N-1为Pod所在的序号,从0开始到N-1
statefulSetName为StatefulSet的名字
namespace为服务所在的namespace,Headless Servic和StatefulSet必须在相同的namespace
.cluster.local为Cluster Domain

在一个大规模的Kubernetes集群里,可能有成千上万个PVC,这就意味着运维人员必须实现创建出这个多个PV,此外,随着项目的需要,会有新的PVC不断被提交,那么运维人员就需要不断的添加新的,满足要求的PV,否则新的Pod就会因为PVC绑定不到PV而导致创建失败.而且通过 PVC 请求到一定的存储空间也很有可能不足以满足应用对于存储设备的各种需求,而且不同的应用程序对于存储性能的要求可能也不尽相同,比如读写速度、并发性能等,为了解决这一问题,Kubernetes 又为我们引入了一个新的资源对象:StorageClass,通过 StorageClass 的定义,管理员可以将存储资源定义为某种类型的资源,比如快速存储、慢速存储等,用户根据 StorageClass 的描述就可以非常直观的知道各种存储资源的具体特性了,这样就可以根据应用的特性去申请合适的存储资源了。

要使用 StorageClass,我们就得安装对应的自动配置程序,比如我们这里存储后端使用的是 nfs,那么我们就需要使用到一个 nfs-client 的自动配置程序,我们也叫它 Provisioner,这个程序使用我们已经配置好的 nfs 服务器,来自动创建持久卷,也就是自动帮我们创建 PV

搭建StorageClass+NFS,大致有以下几个步骤:

1.创建一个可用的NFS Serve
2.创建Service Account.这是用来管控NFS provisioner在k8s集群中运行的权限
3.创建StorageClass.负责建立PVC并调用NFS provisioner进行预定的工作,并让PV与PVC建立管理
4.创建NFS provisioner.有两个功能,一个是在NFS共享目录下创建挂载点(volume),另一个则是建了PV并将PV与NFS的挂载点建立关联 。

4.2 构建能自动分配PV的storageClass

执行如下部署后,然后创建PVC执行会自动产生PV卷,相当于按照PVC的声明要求,从storageClass中分配一个存储卷给到PVC。

# 如下创建storageClass,用于自动创建存储卷,原因是大规模集群中,很多的应用在不断的通过PVC申请PV,运维人员需要不断的依据PVC创建PV,另外PVC要求快速存储、慢速存储类型不一致
####################################### nfs-rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:name: nfs-client-provisioner# replace with namespace where provisioner is deployednamespace: zo-dev        #根据实际环境设定namespace,下面类同
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:namespace: zo-devname: 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-provisionernamespace: zo-dev
subjects:- kind: ServiceAccountname: nfs-client-provisioner# replace with namespace where provisioner is deployednamespace: zo-dev
roleRef:kind: ClusterRolename: nfs-client-provisioner-runnerapiGroup: 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 deployednamespace: zo-dev
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-provisionernamespace: zo-dev
subjects:- kind: ServiceAccountname: nfs-client-provisioner# replace with namespace where provisioner is deployednamespace: zo-dev
roleRef:kind: Rolename: leader-locking-nfs-client-provisionerapiGroup: rbac.authorization.k8s.io
---
########################################## nfs-provisioner.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: nfs-client-provisionerlabels:app: nfs-client-provisioner# replace with namespace where provisioner is deployednamespace: zo-dev  #与RBAC文件中的namespace保持一致
spec:replicas: 1selector:matchLabels:app: nfs-client-provisionerstrategy:type: Recreatetemplate:metadata:labels:app: nfs-client-provisionerspec:serviceAccountName: nfs-client-provisionercontainers:- name: nfs-client-provisioner#image: quay.io/external_storage/nfs-client-provisioner:latest#这里特别注意,在k8s-1.20以后版本中使用上面提供的包,并不好用,这里我折腾了好久,才解决,后来在官方的github上,别人提的问题中建议使用下面这个包才解决的,我这里是下载后,传到我自已的仓库里#easzlab/nfs-subdir-external-provisioner:v4.0.1image: easzlab/nfs-subdir-external-provisioner:v4.0.2volumeMounts:- name: nfs-client-rootmountPath: /root/zo-nfs   # 宿主机该目录挂载到NFS上env:- name: PROVISIONER_NAMEvalue: zo-nfs-storage  #provisioner名称,请确保该名称与 nfs-StorageClass.yaml文件中的provisioner名称保持一致- name: NFS_SERVERvalue: 192.168.10.31   #NFS Server IP地址- name: NFS_PATHvalue: "/volume1/development"    #NFS挂载卷volumes:- name: nfs-client-rootnfs:server: 192.168.10.31  #NFS Server IP地址path: "/volume1/development"     #NFS 挂载卷
---
###################################### nfs-storageClass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:name: managed-nfs-storage  #无须指定namespace,所有空间都能使用
provisioner: zo-nfs-storage #这里的名称要和nfs-provisioner.yaml配置文件中的环境变量ENV中的PROVISIONER_NAME保持一致
parameters:#  archiveOnDelete: "false"archiveOnDelete: "true"
reclaimPolicy: Retain  ## 文件需要手动删除

然后加入创建一个PVC的时候,此时会自动产生一个PV,例如

kind: PersistentVolumeClaim
apiVersion: v1
metadata:name: test-claimnamespace: zo-devannotations:#与nfs-storageClass.yaml metadata.name保持一致volume.beta.kubernetes.io/storage-class: "managed-nfs-storage"
spec:storageClassName: "managed-nfs-storage"accessModes:- ReadWriteMany#- ReadWriteOnceresources:requests:storage: 10Gi

此时PVC test-claim被自动创建,同时PV也会自动创建并被绑定。