最近又有一批测试机要搭共享存储,我把之前踩过的NFS服务安装配置过程完整整理了一遍。NFS服务安装配置这事,看着不难,可真上手跑生产环境,各种细节能让人怀疑人生。很多人以为 yum install nfs-utils 然后 /etc/exports 写一行就完事,实际上后面还有权限、防火墙、挂载参数、性能调优,任何一个环节出了问题,客户端就会冒出 server not responding, timed out 这种让人挠头的报错。这篇手册适合系统运维、开发环境搭建者,以及所有打算从零搭一套稳定 NFS 服务的人。
我会按实际配置的顺序,把服务端安装、客户端挂载、权限安全、性能调优、故障排查完整走一遍。环境以 RHEL 系为主,Ubuntu 和银河麒麟 V10 大概率也适用。先交代一下本文假设的网络环境:一台 NFS 服务器 192.168.1.100,共享目录 /data/share;若干客户端在 192.168.1.0/24 网段。下面的命令我会带注释,方便你直接抄。
1. 为什么到今天还要自己搭NFS服务:场景、原理与版本选择
1.1 哪些场景确实需要NFS
大多数静态网站、动静分离的应用,如果只有一台服务器,根本用不上 NFS。可一旦你有多台 Web 后端需要共享上传目录,或者一堆计算节点要共用同一份数据集,NFS 的好处就体现出来了:不用每次把文件拷来拷去,所有机器看到的是同一份数据。这种场景下,NFS 仍然是最省事的方案之一,配置比 Ceph 轻量,也不需要额外引入分布式文件系统的运维成本。
但要说清楚,NFS 并不适合所有场景。高并发小文件随机读写、数据库数据文件这类对延迟和一致性极端敏感的业务,用 NFS 会很难受。NFS 更偏向“共享之上的文件共享”:比如上传目录、静态资源、代码发布目录、日志汇聚目录。我自己通常建议,先把业务分清楚,再决定要不要上 NFS,而不是一上来就搭。
1.2 NFS v3 vs NFS v4:先确定协议版本
绝大多数现代 Linux 默认同时支持 NFSv3 和 NFSv4,但配置思路有差异。NFSv3 依赖 rpcbind、portmap 等一系列动态端口,防火墙规则很难写;NFSv4 把 mountd、lockd 等全部并入 2049 端口,天然更适合跨防火墙部署。所以我现在的建议很直接:新环境全部用 NFSv4,默认端口 2049,防火墙只需要放行一个端口,省心太多。
NFSv4 还引入了伪文件系统(pseudo-filesystem)的概念,客户端挂载的是整个导出根,而不一定直接对应服务端目录。只要你在服务端导出了 /data/share,客户端挂载 服务器:/ 也可以看到一个完整视图,但更常用的是直接挂载 服务器:/data/share。如果你手里还有老设备只支持 v3,那只能在 /etc/nfs.conf 里关闭 v4 或者保留 v3,但我不建议在生产环境混用,排错会非常痛苦。
1.3 服务端与客户端需要的软件包
先把这个概念理清楚:NFS 分服务端和客户端,角色不一样,软件包也不一样。服务端要装 nfs-utils 和 rpcbind(虽然 v4 不完全依赖 rpcbind,但大部分发行版还是要求装上);客户端大多数发行版自带 mount 的 NFS 支持,但你也需要 nfs-utils 才能执行 showmount、mount.nfs 这类命令。
在 RHEL/CentOS 上,服务端和客户端都建议直接安装 nfs-utils。Ubuntu 服务端是 nfs-kernel-server,客户端是 nfs-common。银河麒麟 V10 这类 RHEL 系系统,直接走 yum install -y nfs-utils rpcbind 就行。下表可以快速对照:
| 操作系统 | 服务端软件包 | 客户端软件包 |
|---|---|---|
| RHEL/CentOS/Rocky | nfs-utils, rpcbind | nfs-utils |
| Ubuntu/Debian | nfs-kernel-server | nfs-common |
| 银河麒麟 V10 | nfs-utils, rpcbind | nfs-utils |
注意:现在很多发行版已经默认会拉取相关依赖,你不用自己手动一个个装依赖包,但离线安装时依赖关系需要特别留意。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 服务端安装:命令简单,决胜在细节
2.1 在线安装与服务启动
在线环境安装没什么好说的,三行命令的事。
bash复制# RHEL / CentOS / Rocky / 银河麒麟 V10
yum install -y nfs-utils rpcbind
# Ubuntu / Debian
apt update && apt install -y nfs-kernel-server
安装完之后,重点来了:启动顺序不要错。先启动 rpcbind,再启动 nfs-server,最后用 exportfs -arv 让配置生效。
bash复制systemctl enable --now rpcbind
systemctl enable --now nfs-server
systemctl status nfs-server
有些老教程会让你用 service nfs start 或 service nfslock start,那是 sysvinit 时代的习惯,现在没必要再手动起 nfslock、rquotad 这些辅助服务,systemd 会自动拉起你需要的组件。这一点坑了不少从老文档照抄的人。
2.2 离线环境安装:银河麒麟V10等场景
离线安装是高频需求,尤其是内网服务器。银河麒麟 V10 的离线 NFS 安装包,可以优先在操作系统安装光盘的 Packages 目录里找,或者在一台同样版本且已经装好 nfs-utils 的机器上用 yumdownloader 打包:
bash复制# 在有网的相同版本机器上执行
yumdownloader --resolve --destdir=/tmp/nfs-rpms nfs-utils rpcbind
然后把整个目录拷进内网机器,执行:
bash复制cd /tmp/nfs-rpms
yum localinstall -y *.rpm
# 或者
rpm -Uvh *.rpm
离线安装最容易踩的坑是依赖缺失。nfs-utils 依赖 libtirpc、libnfsidmap、keyutils、rpcbind 等,不同版本依赖还不一样。如果你解压出来的 RPM 在执行时提示缺少某个 .so,不要硬装,按提示找到对应包再装。这个过程没有捷径,唯一能建议的是尽量和源机器保持完全一致的系统小版本,可以少掉很多麻烦。
2.3 共享目录的属主与权限规划
在写 /etc/exports 之前,先想好共享目录给谁用。NFS 在没接 LDAP/AD 的情况下,是靠 UID/GID 识别用户的。客户端用户 UID 1000 看到的文件,在服务端也对应 UID 1000,名字不同没关系,数字对不上就会显示成 nobody。
所以我的习惯是先建一个专用系统账号和目录:
bash复制groupadd -g 5000 nfsshare
useradd -u 5000 -g 5000 -s /sbin/nologin nfsshare
mkdir -p /data/share
chown -R 5000:5000 /data/share
chmod 755 /data/share
把共享目录的属主固定成特定 UID/GID,后续客户端不管以什么用户访问,只要对方也有相同的 UID,权限就一目了然。如果只是临时传文件,你也可以把目录改成 chown nobody:nobody 或 chmod 777,但那样安全性和可控性都比较差,不太建议。
2.4 /etc/exports 的配置语法与参数
下面是完整的一份 /etc/exports 示例:
bash复制/data/share 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)
/data/share 10.0.0.0/8(ro,sync,no_root_squash)
解释几个关键参数:
rw/ro:读写/只读。sync:服务端在请求落盘后才响应客户端,推荐默认开启;async性能稍好但掉电可能丢数据。no_subtree_check:关闭子树检查。如果你的导出目录不是整个文件系统而是一个子目录,建议加上,否则可能出现奇怪的文件访问问题。no_root_squash:客户端 root 不映射,建议只在可信内网用;默认 root_squash 更安全。
写完配置后,不要重启服务,用 exportfs -arv 重新加载:
bash复制exportfs -arv
exportfs -v # 查看实际生效的导出项
如果一个目录导出后想移除,用 exportfs -u /data/share,不要直接改动 exports 文件然后重启,那样会把当前所有连接断掉。这是我当年吃过大亏的地方。
3. 客户端挂载:mount 参数决定后面省不省心
3.1 最简单的挂载命令
在客户端执行:
bash复制yum install -y nfs-utils # 客户端只需要 nfs-utils
mkdir -p /mnt/nfs
mount -t nfs 192.168.1.100:/data/share /mnt/nfs
挂载成功以后,用 df -h /mnt/nfs 和 mount | grep nfs 验证。如果服务端只启用了 NFSv4,-t nfs 也能自动协商到 v4,不用写成 nfs4。有些文档让你加 -o vers=4.2,这是为了固定版本,不是必须。
3.2 开机自动挂载:fstab 里最容易踩的坑
把挂载项写进 /etc/fstab 后,重启机器有时会卡住,或者挂载失败。原因多半是没有加 _netdev。这个参数的意思是“等网络就绪后再挂载”,没有它,systemd 可能在网卡还没起来时就去挂载 NFS,结果只能失败。
推荐写法:
bash复制192.168.1.100:/data/share /mnt/nfs nfs defaults,_netdev,noatime,nofail 0 0
_netdev:告诉系统这是网络设备,网络就绪后再挂。nofail:即使挂载失败也不阻塞开机流程。noatime:减少读写元数据时间,NFS 场景收益明显。
改完后执行 systemctl daemon-reload,然后 mount -a 测试 fstab 是否正确。不要在没确认能挂载的情况下直接重启生产机器,别问我怎么知道的。
3.3 并发访问多、目录多的时候,试试 autofs
如果你的客户端要访问很多 NFS 共享目录,但平时不一定都在用,一直挂载会占用连接和资源。此时可以用 autofs 做按需挂载:进入目录时自动挂载,一段时间不用自动卸载。
bash复制yum install -y autofs
systemctl enable --now autofs
编辑 /etc/auto.master,加入:
bash复制/nfsauto /etc/auto.nfs --timeout=300
编辑 /etc/auto.nfs:
bash复制share -fstype=nfs,rw,noatime 192.168.1.100:/data/share
这样你访问 /nfsauto/share 的时候才会真正挂载。autofs 尤其适合负载均衡、数据分析这类“目录多、空闲多”的场景,能显著减少挂载点数量。
3.4 卸载不掉怎么办:transport endpoint not connected
NFS 服务端重启或网络断开后,客户端挂载目录会进入假死状态,显示 Transport endpoint is not connected。这时候直接 umount 常常报 target is busy。我的处理步骤:
bash复制# 1. 找出占用进程
lsof +D /mnt/nfs 或者 fuser -mv /mnt/nfs
# 2. 先正常卸载,不行再强制
umount /mnt/nfs
umount -l /mnt/nfs # lazy 卸载,断开挂载但保留目录结构
umount -f /mnt/nfs # 强制卸载
强制卸载是有损操作,正在写入的文件可能损坏。如果业务不能中断,更稳妥的办法是保留挂载点,等 NFS 服务恢复后自动重连。这取决于你 mount 时用的是 hard 还是 soft,下面第5节会展开说。
4. 权限、安全与共享策略:别把NFS裸奔在办公网
4.1 root_squash / no_root_squash 的选择
NFS 设计之初,客户端 root 不能直接以 root 身份操作服务端文件,默认会被 squash 成 nfsnobody,这就是 root_squash。这样做是为了防止客户端 root 用户随意删除服务端数据。但有些场景,比如客户端容器里以 root 写数据,你会希望 root 权限直接映射,这时才考虑加 no_root_squash。
我的建议是:除非你能完全控制客户端,否则永远别开 no_root_squash。一旦客户端被入侵,等于直接拿到服务端共享目录的 root 权限,风险非常大。反过来,如果你需要多个不同 UID 的客户端用户映射到服务端同一个账号,可以用 all_squash 加 anonuid=5000,anongid=5000 强制映射:
bash复制/data/share 192.168.1.0/24(rw,sync,all_squash,anonuid=5000,anongid=5000)
这种方式适合共享上传目录,所有客户端写进来的文件都归 UID 5000,后续由另一个服务统一读取,权限不会乱。
4.2 基于 IP 网段的访问控制
/etc/exports 可以按单个 IP、网段、域名做限制,但我强烈建议只用 IP 和网段,域名解析不可控,内网环境尤其容易出故障。
bash复制/data/share 192.168.1.0/24(rw,sync)
/data/share 172.16.0.0/16(ro,sync)
注意 exports 文件里,多个规则之间是“取并集”的,不是“覆盖”关系。如果你先给整个网段 ro,再给某个 IP rw,那这个 IP 最终是具备 rw 能力的。官方文档管这个叫“最宽松匹配”。所以写规则时一定要把更具体的放后面,避免出现你以为限制了、实际上没限制的情况。
4.3 防火墙放行与固定端口
如果你开了 firewalld,只放行 2049 是不够的,除非你只用 NFSv4,并且关闭了 v3。为了稳妥,我建议把所有 NFS 相关端口固定下来,然后在防火墙里只放行这些固定端口。在 /etc/sysconfig/nfs(老版本)或 /etc/nfs.conf(新版本)中设置:
bash复制RQUOTAD_PORT=20048
LOCKD_TCPPORT=32769
LOCKD_UDPPORT=32769
MOUNTD_PORT=20048
STATD_PORT=662
然后执行:
bash复制systemctl restart nfs-server rpcbind
firewall-cmd --permanent --add-service=nfs
firewall-cmd --permanent --add-service=rpc-bind
firewall-cmd --permanent --add-service=mountd
firewall-cmd --reload
如果你用的是 nftables 或者纯 iptables,把 TCP/UDP 2049、111、20048、662、32769 放行即可。固定端口这件事最容易漏,因为你不固定的话,每次重启 NFS 服务 mountd 端口都可能变,防火墙规则形同虚设。
4.4 再进一步:NFSv4 的 Kerberos 认证
内网环境如果安全要求高,NFSv4 支持 sec=krb5 / krb5i / krb5p 认证和加密。启用前需要先部署 Kerberos KDC,配置服务端和客户端的 keytab,属于比较重的方案。日常运维里,中小型内网很少会走到这一步;如果真需要,建议先把前几节的权限和防火墙做扎实,Kerberos 可以作为后续增强,不建议一开始就上,否则排错难度陡增。
5. 性能调优与可靠性:别等 timeout 了才想起调参数
5.1 先看懂 "server not responding, timed out" 在说什么
客户端日志里如果出现:
text复制nfs: server 172.16.140.200 not responding, timed out
这表示客户端在超过固定时间后没有收到服务端的 NFS 应答。它会反复重试,但不会主动告诉你根因。可能是网络丢包、服务端负载过高、防火墙静默丢包、NFS 服务卡死,甚至可能是客户端自身资源耗尽。
看到一个 timeout,我的第一反应不是去调超时参数,而是去定位链路上哪一段慢了。盲目把 timeout 调大只会让问题隐藏得更深,线上故障反而更难发现。正确的做法按第6节的链路逐层排查。
5.2 服务端调优:进程数、内存和存储
NFS 服务端本质是内核态的 NFS 服务,但一些参数可以调高吞吐。新版 RHEL 系的 /etc/nfs.conf 里有:
ini复制[nfsd]
threads=16
host=192.168.1.100
threads 表示内核 NFSD 线程数,默认通常 8,对高并发场景可以适当调到 16 或 32。但线程数不是越大越好,每个线程都会消耗内存,建议按 CPU 核心数的 1 到 2 倍起步。你还可以在运行时通过 /proc/fs/nfsd/threads 直接调:echo 16 > /proc/fs/nfsd/threads,但重启后失效。
服务端的存储介质也很关键。如果共享目录在机械盘上,不管怎么调 NFS 参数,高并发读写的延迟都下不来。能上 SSD 就上 SSD,至少先保证日志类小文件写入的体感。内存方面,NFS 的读写要走 page cache,服务端可用内存充足时,重复读取共享文件会明显变快。
5.3 客户端挂载参数调优:rsize、wsize、hard还是soft
客户端挂载参数对体验影响很大。常用的完整挂载参数可以这样写:
bash复制mount -t nfs -o rw,rsize=1048576,wsize=1048576,noatime,hard,nconnect=8 192.168.1.100:/data/share /mnt/nfs
rsize/wsize:单次读写请求的最大字节数。传统值是 32768,现代内核和网卡大多支持 1048576。不是越大越好,建议做一次简单测试:用 dd 读写一个 1GB 文件,对比不同 rsize/wsize 下的吞吐和 CPU 占用。hard:NFS 请求永远重试,适合数据库文件、需要数据一致的场景。但如果服务端长时间不恢复,客户端相关进程会卡住,只能重启客户端或强制卸载。soft:超时后返回 I/O 错误,不会一直卡进程,但可能造成数据丢失。适合可容忍失败的临时目录,不建议用于重要数据。nconnect:NFSv4.1+ 的客户端并发连接参数,可以让单个挂载点建立多条 TCP 连接,对高并发场景有帮助。需要服务端和客户端都支持,老内核慎用。
5.4 网络侧的检查清单
不要忽略交换机、MTU、网卡 offload 对 NFS 性能的影响。NFS 默认包大小如果超过路径 MTU,且 MTU 不一致,会导致分片或丢包。建议先统一两端 MTU,再测一下:
bash复制ping -s 8972 -M do 192.168.1.100
如果 jumbo frame 不通,就老老实实保持默认 1500 MTU。另外,网卡多队列和中断绑定对高吞吐也很重要,但这属于 NFS 外围问题,等你遇到性能瓶颈再排查也不迟。
6. 故障排查的完整链路:从客户端到服务端逐层定位
6.1 第一步:先看网络通不通
排查任何 NFS 故障,不要一上来就看 NFS 日志。先确认基础网络:
bash复制ping -c 4 192.168.1.100
nc -vz 192.168.1.100 2049
nc 测 TCP 端口比 ping 更靠谱,因为 ping 通只能说明 ICMP 通,防火墙可能拦截了 2049。如果端口不通,多半是防火墙或 NFS 服务没起来,先去服务端 systemctl status nfs-server。
6.2 第二步:检查 rpcbind 和 NFS 服务状态
在服务端执行:
bash复制systemctl status rpcbind nfs-server
rpcinfo -p 192.168.1.100
正常输出里能看到 nfs、mountd、portmapper 等条目。如果 rpcinfo -p 访问超时,说明 rpcbind 可能被防火墙挡住了。如果服务端本机 rpcinfo -p 正常,但客户端执行超时,重点查防火墙。
另外可以看:
bash复制exportfs -v
showmount -e 192.168.1.100
showmount -e 来自 nfs-utils,能列出服务端当前可导出的目录。如果客户端看不到任何导出项,要么是 exports 配置问题,要么是 rpcbind/mountd 响应异常。
6.3 第三步:客户端挂载失败时看日志
客户端执行挂载后失败,第一时间去翻日志:
bash复制# RHEL/CentOS 系
dmesg -T | grep -i nfs
journalctl -xe | grep -i nfs
# 服务端
journalctl -u nfs-server -u rpcbind
tail -100 /var/log/messages
日志里常见的报错有 Permission denied、No such file or directory、Protocol not supported。如果是 Permission denied,基本是 exports 规则里没放行客户端 IP,或者 root_squash 权限不足。如果是 Protocol not supported,可能是服务端关闭了 v4,客户端强制 v4 导致,反过来也可能。
6.4 第四步:抓包确认 NFS 请求有没有得到回应
如果日志不够明确,直接在客户端和服务端抓包:
bash复制tcpdump -i eth0 host 192.168.1.100 and port 2049 -s 0 -w /tmp/nfs.pcap
抓一段时间后 Ctrl+C,用 Wireshark 打开过滤 nfs。重点看是否存在 NFS 请求后服务端没有任何响应的记录。如果服务端一直不响应,可能服务端 NFSD 线程全部卡在 I/O 等待上,用 top 看服务端 %wa,用 ps -eLf | grep nfsd 查 NFSD 线程状态。
6.5 案例复盘:一次共享目录间歇性卡死的排查
最后分享一个我实际遇到的案例。客户端每隔几分钟就会在日志里出现 server 172.16.140.200 not responding, timed out,但 ping 服务器一直正常。我一开始怀疑是 NFS 服务参数问题,调了 timeo、retrans,情况没有改善。
后来一步步排查发现,客户端到服务端中间经过了一台配置了流量整形的交换机,偶尔丢弃大包。我们用 ping -s 8972 -M do 测 jumbo frame 时发现丢包率在 5% 左右,而默认 1500 MTU 的 ping 完全正常。NFS 挂载参数里 rsize/wsize 设得比较大,实际网络包接近 MTU 上限,所以问题只在数据量大的时候出现。最终把客户端和服务端 MTU 固定为 1500,并把 rsize/wsize 调回 65536,故障消失。
排查 NFS 问题最忌讳的就是一上来重启服务。我见过很多同事在服务端执行 systemctl restart nfs-server,结果客户端所有挂载点瞬间全部 not responding,把一次小问题放大成事故。如果只是配置变更,exportfs -arv 足够;真的需要重启,也得提前通知客户端,并在业务低峰期做。这个习惯,比任何调优参数都管用。
