网络文件系统(NFS, Network File System) 是一种分布式文件系统,目的是让客户端主机访问服务器端文件,并且过程时与访问本地存储是一样。
NFSv4 介绍
NFSv4 (Network File System version 4) 是网络文件系统协议的一个重大版本更新。与之前的版本(NFSv2 和 NFSv3)相比,它不仅是功能的增强,更是一次架构上的重构。
NFSv4 由 IETF(互联网工程任务组)标准化(RFC 3530, RFC 5661 等),旨在解决旧版本在互联网环境下的性能、安全性和跨平台兼容性问题。
核心特点与改进
单一端口与防火墙友好 (Firewall Friendly)
- 旧版本痛点: NFSv3 依赖
portmapper (rpcbind),使用随机端口传输数据,导致配置防火墙极其困难。
- NFSv4 改进: 仅使用 TCP 2049 端口。所有的操作(包括挂载、锁定、数据传输)都在这一个端口上通过 TCP 连接完成。这使得在防火墙后配置 NFS 变得非常简单。
有状态协议 (Stateful)
- 旧版本痛点: NFSv3 是
无状态的。服务器不知道客户端打开了哪些文件。为了实现文件锁,它不得不依赖辅助协议(NLM, NSM),这增加了复杂性。
- NFSv4 改进: NFSv4 是有状态的。协议本身集成了文件锁定(Locking)和挂载(Mounting)功能。服务器维护客户端的状态(通过
租约机制),这大大提高了数据的一致性和文件锁的可靠性。
复合操作 (Compound Operations)
- 性能提升: NFSv4 允许客户端将多个请求(例如:查找文件 LOOKUP、打开文件 OPEN、读取属性 GETATTR)打包成一个数据包发送给服务器。
- 优势: 减少了网络往返次数(RTT),显著降低了在高延迟网络(如广域网 WAN)下的延迟。
安全性增强 (Security)
- 强制安全: NFSv4 强制要求支持 RPCSEC_GSS 框架。
- Kerberos 集成: 原生支持 Kerberos 身份验证,不再仅依赖弱不禁风的 IP 地址或 UID/GID 验证。
- ACL 支持: 支持类似 Windows NTFS 的丰富访问控制列表 (NFS4 ACLs),比传统的 POSIX 读/写/执行权限更细粒度。
伪文件系统 (Pseudo File System)
- 统一命名空间: 服务器导出一个
根目录(fsid=0),所有共享的目录都作为这个根目录下的子目录呈现。客户端只需要挂载这个根,就可以浏览所有导出的共享,而不需要像 v3 那样单独挂载每一个共享目录。
客户端缓存委托 (Delegation)
- 机制: 服务器可以将文件的管理权暂时
委托给客户端。客户端可以在本地进行读写操作,而无需频繁与服务器通信。
- 回收: 当其他客户端请求访问该文件时,服务器会
召回委托。这极大地提高了单一客户端频繁操作文件时的性能。
NFSv4 的子版本演进
NFSv4 并不是一成不变的,它有几个重要的次级版本:
- NFSv4.0 (RFC 3530): 基础版本,确立了上述的有状态、单一端口等特性。
- NFSv4.1 (RFC 5661):
- pNFS (Parallel NFS): 这是最大的革新。允许客户端直接并行地从多个存储设备(Data Servers)读取数据,而元数据(Metadata)仍由管理服务器处理。解决了单服务器的 I/O 瓶颈。
- 会话层 (Session trunking): 提供了
仅执行一次的语义,增强了网络断连后的恢复能力。
- NFSv4.2 (RFC 7862):
- 服务器端复制 (Server-Side Copy): 允许在服务器内部复制文件,无需数据先流向客户端再流回服务器。
- 稀疏文件 (Sparse Files) 与 空间预留: 支持更现代的存储特性。
- 安全性增强: 支持 SELinux 标签传输等。
NFSv3 vs NFSv4 对比总结
| 特性 |
NFSv3 |
NFSv4 |
| 协议状态 |
无状态 (Stateless) |
有状态 (Stateful) |
| 传输协议 |
UDP 或 TCP |
TCP (强制) |
| 端口使用 |
随机端口 (依赖 rpcbind) |
固定端口 (TCP 2049) |
| 文件锁 |
依赖外部协议 (NLM) |
协议集成 |
| 性能机制 |
单次 RPC 请求 |
复合 RPC (Compound) |
| 用户验证 |
信任 UID/GID (较弱) |
支持 Kerberos (强) |
| 挂载方式 |
单独挂载每个 Export |
挂载伪根 (Pseudo-fs) |
| 缓存机制 |
较弱 |
Delegation (委托) |
| 并行访问 |
不支持 |
支持 pNFS (v4.1+) |
NFS 服务部署
NFS 服务端和客户端服务均依赖于 rpcbind,rpcbind 是一个将 RPC(Remote Procedure Call,远程过程调用) 程序编号转换为通用地址的服务,RPC 调用依赖 rpcbind 服务。
服务:
- rpc.nfsd NFS 主进程,通过
systemctl start nfs-utils.service 启动,监听 TCP/UDP 2049 端口
- rpc.mountd 监听在 TCP/UDP 20048 端口
- rpc.lockd(可选,防止多个客户端同时写入一个文件)
- rpc.statd(可选,负责数据的状态及一致性检查)
- rpcbind 依赖进程,通过
systemctl start rpcbind.service 启动,监听在 TCP/UDP 20048 端口
配置文件:
- /etc/nfs.conf
- /etc/nfsmount.conf
准备
本次部署直接关闭防火墙:
systemctl stop firewalld
systemctl disable firewalld
服务端安装
# CentOS
yum install -y nfs-utils
# Ubuntu
apt install nfs-kernel-server
注意:安装 nfs-utils 时,会自动安装 rpcbind
systemctl enable rpcbind.service
systemctl enable nfs-server.service
systemctl start rpcbind.service
systemctl start nfs-server.service
/etc/exports 文件中一行表示一个共享目录。格式如下:
共享目录路径 IP或主机名1(选项1,选项2,...) IP或主机名2(选项1,选项3,...) ...
选项说明:
| 选项 |
功能 |
ro |
以只读方式共享(默认) |
rw |
以读写方式共享 |
sync |
客户端写入的数据立即写入到磁盘中(默认) |
async |
客户端写入的数据先写入到内存中,再写入磁盘中 |
root_squash |
把 root 用户映射为匿名用户(默认) |
no_root_squash |
允许使用 root 用户 |
all_squash |
把所有用户映射为匿名用户 |
anonuid=<UID> |
指定匿名用户的 UID |
anongid=<GID> |
指定匿名用户的 GID |
secure |
允许客户端使用小于 1024 的端口连接 |
insecure |
允许客户端使用大于 1024 的端口连接 |
subtree_check |
共享子目录时,强制检查父目录权限(默认) |
no_subtree_check |
不检查父目录权限 |
wdelay |
多个用户要写入 NFS 目录,则归组写入(默认) |
no_wdelay |
多个用户要写入 NFS 目录,则立即写入,当使用 async 时,无需此设置 |
示例:
$ cat /etc/exports
/home *(rw,no_subtree_check,fsid=10,no_root_squash)
/opt 10.0.0.1(rw,all_squash) 10.0.0.2(ro,no_root_squash)
注意:
- 配置后需要重启
systemctl restart nfs-server 或执行 exportfs -rv 或执行 exportfs -a。
$ showmount -e <nfs-server>
$ showmount -e 172.20.100
Export list for 172.20.100:
/home *
/opt 10.0.0.2,10.0.0.1
k8s 部署
使用
客户端
yum install nfs-utils
mount.nfs 172.20.0.100:/home /mnt/
echo "172.20.0.100:/home /home nfs nfsvers=3,nodev,nosuid 0 0" >> /etc/fstab
mount -a
配置基于 RDMA 的 NFS 服务
# 加载rdma内核模块
# 服务端
modprobe xprtrdma
# 客户端
modprobe svcrdma
# 服务端监听 RDMA 传输端口
$ echo 'rdma 20049' | tee /proc/fs/nfsd/portlist
$ cat /proc/fs/nfsd/portlist
rdma 20049
rdma 20049
udp 2049
tcp 2049
udp 2049
tcp 2049
# 挂载
$ mount -t nfs <nfs-server-ip>:/ /mnt/ -o vers=4.1,_netdev,rdma,port=20049,hard,intr,noatime,nodiratime,async,nolock,noacl,sec=sys,noresvport
k8s 使用
- nfs-subdir-external-provisioner 是 k8s 中,支持在一个远程 NFS 服务器上的动态子目录卷供应器
- PV 以目录名为
${namespace}-${pvcName}-${pvName} 的格式在 NFS 服务器上创建
- 回收的格式为
archieved-${namespace}-${pvcName}-${pvName}
F&Q
Unable to mount network drive in Debian; error is “mount(2) system call failed: No route to host.”
sudo apt-get install keyutils cifs-utils