NFS服务器的搭建以及在Kubernetes中的使用
本文简要介绍了NFS服务器的搭建以及在Kubernetes中的使用Storage Class进行NFS挂载的操作步骤。
NFS
NFS 是 Network File System 的缩写,是 Sun 公司于1984年开发的一种分布式文件系统协议。它的核心功能就是可以通过网络,让不同的客户端可以彼此访问共同的文件系统,来实现文件的共享。像许多其他的协议,NFS 建立在开放的网络计算的远程过程调用(RPC)之上。NFS 服务器可以让客户端将网络远程的 NFS 服务器分享的目录,直接挂载到本地端的机器当中。本地端的机器通过直接读写挂载的目录,就可以同步到 NFS 服务器之上 [1]。
NFS客户端与服务端通讯过程
NFS的数据传输基于RPC协议,RPC为Remote Procedure Call(远程过程调用)的简写。该协议允许运行于一台计算机的程序调用另一个地址空间(通常为一个开放网络的一台计算机)的子程序,而程序员就像调用本地程序一样,无需额外地为这个交互作用编程(无需关注细节)。RPC是一种服务器-客户端(Client/Server)模式,经典实现是一个通过发送请求、接受回应进行信息交互的系统。
RPC可以统一管理NFS随机的服务端口,并且统一对外的端口为111。NFS启动之前需要先启动RPC,这样NFS才能够到RPC去注册端口信息。客户端的RPC可以通过向服务端的RPC请求获取服务端的NFS端口信息。当获取到了NFS端口信息后,就会以实际端口进行数据的传输。
客户端与服务端的通讯过程如下[2]:
- 首先服务器端启动RPC服务,并开启111端口
- 服务器端启动NFS服务,并向RPC注册端口信息
- 客户端启动RPC(portmap)服务,向服务端的RPC(portmap)服务请求服务端的NFS端口
- 服务端的RPC(portmap)服务反馈NFS端口信息给客户端
- 客户端通过获取的NFS端口来建立和服务端的NFS连接并进行数据的传输
NFS安装与配置
组件安装
NFS安装需要两个基础组件,分别是RPC主程序rpcbind
与NFS主程序nfs-utils
。
1 | # 安装组件 |
服务端配置
- 新建文件夹:
mkdir /public; chmod 777 /public
- 修改
/etc/exports
并添加/public 192.168.37.0/24(rw,sync)
(其中192.168.37.0/24
需要改为用户自己机器集群的IP段) - 重载NFS服务:
systemctl reload nfs
对于配置文件的详解请看这里。简单来讲,最前面的/public
为共享的目录,后面的IP为允许访问客户端的IP网段,其余参数配置在后面的括号中指定,用逗号隔开。rw
表示可读写,ro
表示只读;sync
表示数据写入内存与硬盘,可以保证不丢失,async
则优先保存到内存,再保存到硬盘,可以保证高效率,但可能会丢失数据[3]。
客户端挂载配置
- 使用
showmount -e 192.168.37.128
查看NFS服务器的共享信息 - 在客户端创建目录
mkdir /mnt/public
- 修改
/etc/fstab
并添加192.168.37.128:/public /mnt/public nfs defaults 0 0
- 使用
mount -a
使配置生效 - 在服务器上使用
touch /public/test
,可以看到客户端的/mnt/public
中也出现了此文件,挂载成功
Kubernetes Storage Class
本小节假设读者已经对Kubernetes有基础的了解。在熟悉Storage Class的概念之前,建议先熟悉Volume与Persistent Volume的概念。
持久卷(PersistentVolume,PV)是集群中的一块存储,可以由管理员事先供应,或者使用存储类(Storage Class)来动态供应。PV 持久卷和普通的 Volume 一样,也是使用 Volume 插件来实现的,只是它们拥有独立于任何使用 PV 的 Pod 的生命周期。Storage Class为管理员提供了描述存储类的方法,可以实现动态创建 PV。每个 StorageClass 都包含 provisioner
、parameters
和 reclaimPolicy
字段,这些字段会在 StorageClass 需要动态分配 PersistentVolume 时会使用到。
StorageClass 对象的命名很重要,用户使用这个命名来请求生成一个特定的类。 当创建 StorageClass 对象时,管理员设置 StorageClass 对象的命名和其他参数,一旦创建了对象就不能再对其更新。每个 StorageClass 都有一个制备器(Provisioner),用来决定使用哪个卷插件制备 PV。该字段必须指定。
自动创建的PV会以${namespace}-${pvcName}-${pvName}
这样的名字出现在NFS服务器的共享目录中,如果它被回收,则会改为archived-${namespace}-${pvcName}-${pvName}
这样的名字[4]。
通过Helm安装
安装Helm
安装Helm的方法十分简单,只需遵循官网的安装步骤即可。
Helm Chart 简介
Helm Chart可以理解为 Helm 包,它包含在 Kubernetes 集群内部运行应用程序,工具或服务所需的所有资源定义。你可以把它看作是 Homebrew formula,Apt dpkg,或 Yum RPM 在 Kubernetes 中的等价物。Repository(仓库) 是用来存放和共享 Charts 的地方。Release 是运行在 Kubernetes 集群中的 Chart 的实例,一个 Chart 通常可以在同一个集群中安装多次。每一次安装都会创建一个新的 Release[5]。
Helm Chart里通常会有以下部分[6]:
Chart.yaml
:Chart的元数据信息values.yaml
:定义Chart默认的配置信息charts/
:手动管理的Chart依赖信息templates/
:模板YAML文件,定义所有需要用到的Kubernetes对象
关于更详细的解释,可以参考这篇文章。
通过Helm直接安装nfs-subdir-external-provisioner
此部分参考GitHub项目。
首先新建一个namespace
:kubectl create namespace nfs-provisioner
,随后执行:
1 | helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/ |
安装完毕后通过kubectl get all -n nfs-provisioner
检查是否正常。
通过第三方镜像安装nfs-subdir-external-provisioner
在网络不好的情况下,在添加Chart Repository后,需要下载并解压修改Chart的配置,使用替换的镜像源。
1 | helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/ |
解压完毕,修改文件夹里的values.yaml
(下面只列举开头部分):
1 | replicaCount: 1 |
随后新建一个namespace
:kubectl create namespace nfs-provisioner
,然后运行helm install nfs-subdir-external-provisioner . -n nfs-provisioner
安装nfs-subdir-external-provisioner
。
安装完毕后通过kubectl get all -n nfs-provisioner
检查是否正常。
测试NFS Storage Class
创建test-pvc.yaml
1 | kind: PersistentVolumeClaim |
检查创建的PV
使用kubectl apply -f test-pvc.yaml
创建PVC,随后使用kubectl get pvc
查看PVC的信息,可以看到状态为Bound。kubectl get pv
也可以看到对应的PV状态也为Bound。
在NFS服务器上使用ls /public
查看对应生成的文件夹,名称应该符合${namespace}-${pvcName}-${pvName}
这样的规则。再使用kubectl delete -f test-pvc.yaml
删除创建的PVC与PV,可以看到对应的文件夹已经改为archived-${namespace}-${pvcName}-${pvName}
的名称。
更改默认的Storage Class
默认的Storage Class会在kubectl get sc
的结果中以default
标记。标记默认的方法:
1 | kubectl patch sc <your-class-name> -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}' |
取消标记:
1 | kubectl patch sc <your-class-name> -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}' |