NFS,全称Network File System(网络文件系统),位于TCP/IP的传输层之上,实现linux/unix平台之间的文件共享。

一、NFS部署

学以致用,但要先会用,而且毕竟本是偏向于运维的知识范畴,还是从怎么部署NFS讲起吧!

NFS是CS架构的协议,需要启动一个NFS Server,配置共享目录和权限,NFS Client就可以挂载共享目录到本地目录上,直接打开本地目录进行读写。

在不同操作系统上安装NFS
1.Ubuntu/Linux
  1. 安装NFS软件包
sudo apt-get install nfs-kernel-server  # 安装 NFS服务器端
sudo apt-get install nfs-common         # 安装 NFS客户端
  1. 配置 NFS 共享目录
sudo vim /etc/exports

配置示例:
/nfsroot *(rw,sync,no_root_squash) # * 表示允许任何网段 IP 的系统访问该 NFS 目录
创建’nfsroot’目录并赋予权限

sudo mkdir /nfsroot
sudo chmod -R 777 /nfsroot
sudo chown ipual:ipual /nfsroot/ -R   # ipual 为当前用户,-R 表示递归更改该目录下所有文件
  1. 启动NFS服务
sudo /etc/init.d/nfs-kernel-server start
# 或者
sudo /etc/init.d/nfs-kernel-server restart
  1. 挂载NFS服务器
    在NFS Client端执行
sudo mount -t nfs 10.11.97.234:/nfsroot /mnt -o nolock

10.11.97.234 为主机 ip,/nfsroot 为主机共享目录,/mnt 为设备挂载目录。
另外,也可以先本地挂载验证下,IP使用127.0.0.1。

如需卸载:

umount /mnt

注意点:

  1. NFS Client和Server直接的网络要通畅
  2. 更改了/etc/exports之后需要重启
2. Win10
  1. 安装haneWIN NFS Server
  2. 配置共享目录(Windows的操作就是都比较可视化)
  3. dd 从NFS目录复制数据到本地 nfs server用到的文件和目录_NFS

  4. 安装NFS Client
  5. dd 从NFS目录复制数据到本地 nfs server用到的文件和目录_客户端_02

  6. 使用cmd,挂载
mount 10.11.97.234:/g/nfsroot x:

注意点:

  1. Windows作为Client,只能挂载到一个 未使用 的盘符上,而不是具体目录
  2. 挂载的共享目录路径,需要是Exports(输出)界面下的路径,如/nfsroot,而不能是Windows盘符的路径
3.Mac OS
  1. Mac默认安装了 NFS Server,检查nfsd的启动情况
sudo nfsd status

如果没有开启,执行以下指令启动

sudo nfsd enable
sudo nfsd start
  1. 配置共享目录
sudo vi /etc/exports

配置方式基本和linux差不多
/Users/mac/nfs-share -alldirs -maproot=root:wheel -network=192.168.0.0 -mask=255.255.0.0
3. 启动服务

# 检查配置状态
sudo nfsd checkexports
# 重启服务
sudo nfsd restart
# 查看挂载状态
showmount -e
  1. 挂载
sudo mount -t nfs -o nolock,nfsvers=3,vers=3 192.168.31.103:/Users/mac/nfs-share /Users/mac/demo

以上给了我们平时经常会用的OS的配置方式,主流其实还是以CenterOS为代表的Linux,因为NFS一般是为了实现文件服务器集群的。

/etc/exports的配置格式

共享目录路径 允许的IP或主机名1(选项1,选项2,...)  允许的IP或主机名2(选项1,选项2,...)

dd 从NFS目录复制数据到本地 nfs server用到的文件和目录_rpc_03

mount格式

mount.nfs <服务器 IP 或主机名>:<共享路径> <挂载点> [-o <选项>]

dd 从NFS目录复制数据到本地 nfs server用到的文件和目录_rpc_04

【查看NFS状态】
ps -aux|grep -v grep|grep nfs
rpcinfo -p

【查看udp和tcp的端口占用】
netstat -tunlp

Android
  1. Android要在kernel层开启NFS,可以在kernel目录下执行 make menuconfig ,找到对应选项勾选,重新编译kernel。
  2. 重新烧录resources.img,kernel.img,即可执行 busybox mount -t nfs -o nolock,rw 10.11.97.234:/home/szc/nfsroot /mnt/sdcard/nfs

BusyBox 是一个集成了三百多个最常用Linux命令和工具的软件

内核的配置文件也可开启:
路径:kernel-5.10/arch/arm64/configs/rockchip_defconfig
配置:
# NFS支持
CONFIG_NFS_FS=y
CONFIG_NFS_V3_ACL=y
CONFIG_NFS_V4=y
CONFIG_NFS_SWAP=y

二、协议剖析

先来看一张NFS的CS架构图:

dd 从NFS目录复制数据到本地 nfs server用到的文件和目录_dd 从NFS目录复制数据到本地_05

由此可见,NFS的基础是RPC,那么什么是RPC呢?作为一个客户端出身的嵌入式开发,自然不是很了解,但对于后台服务器开发,这却是构建分布式的基石,包括如今非常流行的微服务。

RPC(Remote Procedure Call)远程过程调用协议,那过程是什么?过程就是业务处理、计算任务。说简单点,就像调用一个本地方法,调用远程计算机上的方法,对调用方来说屏蔽了网络传输相关的实现。

RPC执行过程是什么样的?

所以RPC的实现主要涉及动态代理和序列化,这也是开发者首先接触到的两块内容。

dd 从NFS目录复制数据到本地 nfs server用到的文件和目录_dd 从NFS目录复制数据到本地_06

首先讲述一下远程方法是怎么调用的,其实就是协议的设计:

  1. 假如RPC Server有一个com.hikvision.Test.java,定义了String hello(String hello) { xxx } 方法,我们需要怎么调用他?
    答:包名+类名+方法名+方法签名(避免重载)+方法参数,即可明确一个方法。
  2. 那么动态代理的作用是什么?
    答:解耦,运行时生成代理类,实现“偷梁换柱”,屏蔽远程调用的细节
  3. 网络传输需要序列化,xml、json都是序列化,选择序列化框架也尤其重要,需要同时考虑性能和兼容性等,如Hessian、Protobuf。
NFS通信过程

了解完RPC是什么,再回过头看看NFS服务器和客户端的通信过程:

  1. 首先,客户端的RPC程序连接到服务器上的111端口(UDP)来查询NFS各项服务所使用的端口
  2. 分别连接rpc.nfsd和rpc.mountd端口,获取挂载点的信息
  3. 挂载文件系统,客户端即可操作共享文件系统了

搬运一张Wireshark的抓包图,辅助理解:

dd 从NFS目录复制数据到本地 nfs server用到的文件和目录_共享目录_07

三、NFS服务的权限和用户映射

NFS服务虽然不具备用户身份验证的功能,但提供了一种身份映射的机制来对用户身份进行管理。

nfsnobody:NFS服务在系统中自动创建的程序用户账号,不能用于登录系统,专门用作NFS服务的匿名用户账号。

压缩配置在/etc/exports,有如下选项:

root_squash

默认值,当NFS客户端以root用户身份访问时,映射为NFS服务器的nfsnobody用户。

no_root_squash

当NFS客户端以root身份访问时,映射为NFS服务器的root用户,也就是要为超级用户保留权限。这个选项会留下严重的安全隐患,一般不建议采用。

all_squash

无论NFS客户端以哪种用户身份访问,均映射为NFS服务器的nfsnobody用户。

具体流程如下图所示:

这边的压缩,其实有点映射的意思。
所以,在实际应用中,一般NFS服务器当存储服务,为了方便各个用户
能像操作本地磁盘一样存取,一般采用将用户压缩成指定用户的方式,并且在NFS服务器上将共享目录的权限配置为该用户和所属组。

四、NFS相关服务

名称

用途

rpc.nfsd

必要,NFS的主要服务进程,大部分工作由它来完成,主要负责登录和判别登录者ID

rpc.mountd

必要,主要共享目录的文件权限判断,读取/etc/exports

rpc.lockd

非必要,管理文件的锁定,解决多个客户端同时写入某个文件的问题,必须同时在客户端与服务端都开启才有效,常与rpc.statd同时使用

rpc.statd

非必要,用于检查文件的一致性,检测文件是否被损毁,必须同时在客户端与服务端都开启才有效

rpcbind: rpcbind的功能与DNS服务器非常相似,DNS根据域名查找IP,rpcbind是查找功能对应的port,所以,早期的rpcbind叫portmap。基本原理如下所示:

dd 从NFS目录复制数据到本地 nfs server用到的文件和目录_rpc_08


通过IP和port就可以建立起具体功能的连接了,回想一下,http、redis等等都是这么建立连接的。

至此,NFS的CS之间怎么建立起通信就分析完了,通过NFS模糊了设备间的边界,到了文件系统这一步,VFS和fuse,以及内部DMA及VMA管理,需要另开篇幅分析linux操作系统,此处不再赘述。

另外来看看NFS相关的代码结构,加深一下印象。

kernel内核的nfs:

dd 从NFS目录复制数据到本地 nfs server用到的文件和目录_NFS_09


在fs目录,说明定位是文件系统,同目录还有熟悉的fuse、quota,下回分解。libnfs的源码,也可以看nfsutils的源码,都是nfs的应用

dd 从NFS目录复制数据到本地 nfs server用到的文件和目录_dd 从NFS目录复制数据到本地_10