
1. 为什么还要折腾 NFS 文件共享在对象存储、分布式文件系统满天飞的今天很多人第一反应是都什么年代了还搞 NFS我一开始也这么想直到去年帮一个做视频剪辑的小团队处理素材流转的问题才发现 NFS 在局域网场景里依然是那个“最不折腾”的答案。他们的场景很典型三台剪辑工作站、一台渲染节点、一台群晖做冷备素材总量大概 40TB每天要频繁读写 4K 素材。试过用某网盘同步结果带宽跑满、版本冲突、权限乱套试过 SMBWindows 和 Linux 混用的时候权限映射能把人逼疯。最后回到 NFS两台机器一挂载读写直接跑满万兆内网权限用 UID/GID 对齐世界清净了。NFS 全称 Network File System直译就是网络文件系统核心能力一句话概括让一台 Linux 机器上的目录像本地磁盘一样出现在另一台 Linux 机器上。它解决的是“多台机器共享同一份数据”的问题适合的场景包括开发团队共享代码仓库、渲染农场共享素材、数据库备份集中存储、容器集群共享持久化卷、家庭实验室里多台设备共享影音库。适合谁来参考只要你会基本的 Linux 命令、能看懂/etc/fstab这篇内容就能直接抄作业。哪怕你之前没碰过 NFS跟着走一遍也能搭起来。我写这篇的出发点很简单网上关于 NFS 的教程要么太老还在讲 NFSv3 的 portmap要么太碎只给命令不讲为什么要么踩坑点完全不提比如 root squash 导致写不进去、防火墙只开 2049 结果挂载卡死。所以我把从零搭建到调优排错的完整链路整理出来重点讲清楚每个参数背后的逻辑以及我实际踩过的坑。2. 搭建前的整体设计与选型思路2.1 版本怎么选NFSv3 还是 NFSv4这是第一个要做的决定很多人直接默认用系统自带的结果后面遇到权限和防火墙问题一脸懵。我先把结论摆出来新环境一律上 NFSv4除非你有老设备必须兼容 NFSv3。NFSv3 是 1995 年的产物它依赖 rpcbind端口 111做端口映射服务端会随机开一堆端口防火墙配置极其痛苦。NFSv4 从 2003 年开始把端口固定为 2049不再需要 rpcbind还内置了 ACL、状态化锁、更强的安全机制。更关键的是NFSv4 引入了“伪文件系统”的概念服务端可以把多个导出目录组织成一棵树客户端只需要挂载一个入口。对比项NFSv3NFSv4端口2049 随机端口 rpcbind 111固定 2049防火墙需要开一堆端口只开 2049锁机制依赖 NLM 独立服务内置ACL需额外配置原生支持挂载入口每个导出单独挂单一入口树推荐场景老设备兼容新环境首选我实测下来NFSv4 在万兆内网的大文件顺序读写能跑到 1.1GB/s 左右基本吃满带宽和 NFSv3 差距不大但运维复杂度低了一个量级。所以后面的实操我全部以 NFSv4 为主线。2.2 服务端与客户端的角色划分NFS 是典型的 C/S 架构但和 Web 服务不一样它的“服务端”只是把本地目录暴露出去“客户端”挂载后看到的就是一个普通目录。这里有个容易混淆的点NFS 本身不负责数据同步它只是把文件操作通过网络转发到服务端的磁盘上。也就是说客户端写文件数据最终落在服务端的硬盘上客户端本地不存副本。这跟 rsync 那种同步工具是两码事。角色划分上我建议选一台磁盘容量大、IO 性能好的机器做服务端客户端可以是任意数量的 Linux 机器。服务端不需要多强的 CPU但磁盘和网络要跟得上。我那个剪辑团队的场景里服务端是一台带 8 块 SAS 盘的塔式服务器做了 RAID6客户端通过万兆交换机接入。2.3 网络与权限的前置规划动手之前有三件事必须先想清楚否则后面一定返工。第一是网段规划。NFS 导出最好限定在特定网段比如192.168.10.0/24不要图省事写*允许所有 IP。我见过有人把导出设成*然后机器直接暴露在公网虽然 NFS 有安全机制但这种做法本身就不该出现。第二是UID/GID 对齐。NFS 默认用数字 UID 和 GID 来标识用户服务端和客户端如果同一个用户在两边 UID 不一样就会出现“文件明明是我的却写不进去”的诡异现象。最稳妥的做法是提前规划好统一用户比如都建一个 UID 为 1001 的shareuser。第三是目录结构设计。NFSv4 的伪文件系统可以把多个目录组织成一棵树比如服务端有/data/video、/data/code、/data/backup可以统一导出成/export下的子目录客户端挂载/export就能看到全部。这样管理起来清爽很多。3. 服务端环境准备与核心配置3.1 系统环境与依赖安装我用的服务端是常见的 Linux 发行版内核 5.x 以上NFS 相关模块都是内核自带的不需要额外编译。先确认一下内核模块# 检查 NFS 服务端模块是否加载 lsmod | grep nfs # 如果没有输出手动加载 modprobe nfs modprobe nfsd然后安装用户态工具。不同发行版包名略有差异# Debian/Ubuntu 系 apt update apt install -y nfs-kernel-server # RHEL/CentOS/Rocky 系 dnf install -y nfs-utils装完之后服务端核心配置文件是/etc/exports但 NFSv4 还需要一个/etc/nfs.conf部分发行版是/etc/nfsmount.conf来调优。我建议先别急着改配置把服务状态确认一遍systemctl status nfs-server # 或者 systemctl status nfs-kernel-server注意NFSv4 不需要 rpcbind但很多发行版装 nfs-utils 时会顺带装上。如果你只用 v4可以把 rpcbind 停掉减少一个攻击面。不过停之前确认没有其他服务依赖它。3.2 目录规划与权限初始化假设我们规划三个共享目录统一放在/srv/nfs下mkdir -p /srv/nfs/video mkdir -p /srv/nfs/code mkdir -p /srv/nfs/backup接下来是权限。这里我要重点讲一个概念root squash。NFS 默认会把客户端的 root 用户映射成服务端的nobody用户这是为了防止客户端 root 随意改服务端文件。这个机制是好的但如果你没意识到就会出现“我用 root 挂载了却写不进去”的情况。我的做法是创建一个专用用户组把共享目录的属组设成它然后让需要写入的用户加入这个组。# 创建共享组GID 固定为 2000 groupadd -g 2000 sharegroup # 创建共享用户UID 固定为 1001 useradd -u 1001 -g sharegroup -M -s /sbin/nologin shareuser # 设置目录属主和权限 chown -R shareuser:sharegroup /srv/nfs chmod -R 2775 /srv/nfs那个2775里的2是SGID 位作用是让目录下新建的文件自动继承父目录的属组。这个细节很关键否则用户 A 建的文件属组是 A 的默认组用户 B 就写不了。我踩过这个坑当时团队里两个人互相改不了对方的文件查了半天才发现是 SGID 没设。3.3 /etc/exports 配置详解/etc/exports是 NFS 的核心配置文件每一行定义一个导出。格式是导出目录 客户端(选项1,选项2,...)我的配置长这样/srv/nfs/video 192.168.10.0/24(rw,sync,no_subtree_check,root_squash) /srv/nfs/code 192.168.10.0/24(rw,sync,no_subtree_check,root_squash) /srv/nfs/backup 192.168.10.0/24(ro,sync,no_subtree_check,root_squash)逐个解释这些选项因为每一个都影响实际行为rw / ro读写或只读。backup 目录我设成只读防止客户端误删备份。sync同步写入数据落盘后才返回成功。性能略低但安全。对应的async是先返回再落盘断电可能丢数据。生产环境我强烈建议sync。no_subtree_check关闭子树检查。子树检查是 NFS 为了安全做的一个校验但会带来性能开销而且在文件被重命名时容易出问题。现代内核默认建议关闭。root_squash把客户端 root 映射成匿名用户。这是默认行为显式写出来是为了提醒自己。如果你确实需要客户端 root 有完整权限比如某些备份场景可以用no_root_squash但要想清楚安全风险。还有一个常用选项all_squash把所有用户都映射成匿名适合公共只读共享。以及anonuid/anongid指定匿名用户的 UID/GID。改完配置后用exportfs命令让配置生效不用重启服务# 重新加载所有导出 exportfs -ra # 查看当前导出列表 exportfs -vexportfs -v的输出会列出每个导出目录和实际生效的选项这个命令我建议每次改完配置都跑一遍确认没有语法错误。3.4 防火墙与 SELinux 处理防火墙这块NFSv4 只需要开 2049# firewalld firewall-cmd --permanent --add-servicenfs firewall-cmd --reload # 或者直接开端口 firewall-cmd --permanent --add-port2049/tcp firewall-cmd --reload如果你还在用 NFSv3那还得开 rpcbind 的 111 和 mountd 的随机端口非常麻烦这也是我推荐 v4 的原因之一。SELinux 是另一个坑。在 RHEL 系上SELinux 默认会阻止 NFS 导出非标准目录。如果你把共享目录放在/srv/nfs需要打上正确的上下文# 安装 SELinux 管理工具 dnf install -y policycoreutils-python-utils # 允许 NFS 导出 setsebool -P nfs_export_all_rw on # 给目录打上下文 semanage fcontext -a -t nfs_t /srv/nfs(/.*)? restorecon -Rv /srv/nfs如果不想折腾 SELinux可以临时设成 permissive 模式排查问题但生产环境不建议直接关掉。我一般先用ausearch -m avc -ts recent看有没有拒绝日志有的话针对性放行。4. 客户端挂载与实操验证4.1 客户端依赖与手动挂载客户端这边同样先装工具# Debian/Ubuntu apt install -y nfs-common # RHEL 系 dnf install -y nfs-utils然后创建挂载点并手动挂载mkdir -p /mnt/nfs/video mount -t nfs4 192.168.10.100:/srv/nfs/video /mnt/nfs/video注意这里服务端地址后面的路径写法。NFSv4 的路径是相对于伪文件系统根目录的如果你在 exports 里写的是/srv/nfs/video客户端就写这个完整路径。挂载成功后用df -h应该能看到192.168.10.100:/srv/nfs/video 500G 20G 480G 4% /mnt/nfs/video如果挂载卡住不动大概率是防火墙没开或者服务端没启动。可以用showmount -e 192.168.10.100先探测一下服务端导出了哪些目录这个命令能快速定位是网络问题还是配置问题。4.2 写入测试与权限验证挂载成功只是第一步能不能写才是关键。我一般做三层测试# 测试1普通用户写入 su - shareuser touch /mnt/nfs/video/test1.txt echo hello nfs /mnt/nfs/video/test1.txt # 测试2root 用户写入验证 root_squash 行为 sudo touch /mnt/nfs/video/test2.txt # 测试3大文件读写性能 dd if/dev/zero of/mnt/nfs/video/testfile bs1M count4096测试1 应该成功。测试2 如果失败说明 root_squash 生效了这是预期行为如果成功说明你可能设了no_root_squash要确认是否符合预期。测试3 用来测吞吐4GB 文件在万兆网下大概 4 秒左右完成。实操心得测试写入时一定要用实际业务用户去测不要只用 root。我见过太多案例是 root 能写、普通用户写不了根因就是 UID 没对齐或者目录权限不对。4.3 开机自动挂载配置手动挂载重启就没了要持久化得写/etc/fstab。NFS 的 fstab 条目格式192.168.10.100:/srv/nfs/video /mnt/nfs/video nfs4 defaults,_netdev,soft,timeo100,retrans3 0 0这里几个参数值得说明_netdev告诉系统等网络就绪后再挂载否则开机时网络还没起来会挂载失败。soft软挂载服务端无响应时返回错误而不是一直卡住。对应的hard是无限重试。我一般用soft配合timeo和retrans避免客户端进程被卡死。timeo100超时时间单位是 0.1 秒所以是 10 秒。retrans3重试 3 次。不过soft也有风险如果服务端短暂抖动客户端可能直接报 IO 错误。对于数据库这类不能容忍数据不一致的场景应该用hard。这个取舍要看业务。改完 fstab 后用mount -a测试没有报错就说明配置正确。然后systemctl daemon-reload让 systemd 重新读取。5. 性能调优与常见问题排查5.1 读写性能调优参数NFS 的性能受多个因素影响我按重要性排序讲。rsize 和 wsize是最关键的两个参数分别控制单次读写的数据块大小。NFSv4 默认会协商一个值但有时候协商结果不理想。可以在挂载时显式指定mount -t nfs4 -o rsize1048576,wsize1048576 192.168.10.100:/srv/nfs/video /mnt/nfs/video1048576就是 1MB这是很多场景下的较优值。但要注意不是越大越好超过网络 MTU 太多反而会因为分片降低效率。我实测在万兆网下 1MB 比默认的 64KB 吞吐高不少。nconnect是 NFSv4.1 之后引入的多连接特性可以显著提升单客户端的吞吐mount -t nfs4 -o nconnect8 192.168.10.100:/srv/nfs/video /mnt/nfs/videonconnect8表示建立 8 条 TCP 连接并行传输。这个参数对多线程读写场景提升明显我测下来能提升 30% 到 50%。但要注意服务端也要支持且连接数不是越多越好8 到 16 是比较常见的范围。服务端线程数也要调。/etc/nfs.conf里的[nfsd]段可以设置[nfsd] threads16默认值通常偏小高并发场景下会成为瓶颈。一般设成 CPU 核心数的 2 到 4 倍。5.2 常见问题速查表我把这些年遇到的 NFS 问题整理成一张表方便快速定位现象可能原因排查方法解决挂载卡住无响应防火墙拦截 2049telnet 服务端 2049开放端口Permission deniedUID/GID 不对齐两端id 用户名对比统一 UID/GIDroot 写不进去root_squash 生效检查 exports 选项用普通用户或调 anonuid写入很慢rsize/wsize 太小mount查看实际值显式指定大块文件属组不对缺 SGID 位ls -ld 目录chmod gs服务端改了配置不生效没重新导出exportfs -vexportfs -ra开机挂载失败网络未就绪dmesg看日志加_netdev大文件传输中断超时太短检查 timeo/retrans调大或改 hard5.3 几个我踩过的坑第一个坑是NFSv4 的 idmapd。NFSv4 用userdomain格式来映射用户如果服务端和客户端的/etc/idmapd.conf里 domain 不一致就会出现用户映射失败所有文件都变成nobody。解决办法是把两端的 domain 设成一样比如都设成localdomain然后重启nfs-idmapd服务。第二个坑是文件锁。有些应用比如 SQLite依赖文件锁NFS 的锁机制和本地锁不一样。如果应用报 “database is locked”可能是 NFS 锁没配好。NFSv4 内置锁支持但需要确保nfslock服务在跑。实在不行可以在挂载时加nolock但这样多个客户端同时写同一文件会出问题。第三个坑是导出目录的父目录权限。NFS 在访问导出目录时会检查从根到该目录的每一级权限。如果/srv的权限是700且属主不是共享用户即使/srv/nfs/video权限全开客户端也访问不了。这个坑很隐蔽因为本地访问/srv/nfs/video是正常的只有通过网络才暴露。解决办法是确保路径上每一级目录都有x权限给相应用户。第四个坑是SELinux 的布尔值。在 RHEL 系上即使目录上下文对了nfs_export_all_rw没开也会导致只读。这个布尔值默认可能是关的需要显式打开。6. 安全加固与进阶玩法6.1 访问控制与安全加固NFS 的安全模型基于 IP 和 UID本身不算强。在可信内网里够用但如果网络边界模糊就得加固。最基础的是限定客户端网段前面已经讲了。进阶一点可以用NFSv4 的 Kerberos 认证也就是seckrb5系列选项。配置起来比较复杂需要 KDC 和 keytab适合对安全要求高的场景。我一般在内网用secsys默认的 UID 认证就够了但会配合网络层的隔离。另一个加固点是只读导出。像备份目录、影音库这种只需要读的场景一律用ro。这样即使客户端被入侵也改不了服务端数据。还有noexec挂载选项防止客户端执行共享目录里的二进制文件。这个在共享代码目录时特别有用避免有人上传恶意脚本被执行。6.2 多客户端并发与锁管理多个客户端同时读写同一批文件时锁管理就很重要。NFSv4 的状态化锁比 v3 好很多但仍有注意事项。如果应用需要严格的文件锁建议在挂载时不要加nolock并确保服务端的nfsdcldNFSv4 客户端跟踪守护进程在运行。这个服务负责在服务端重启后恢复锁状态。对于并发写入同一文件的场景NFS 本身不提供原子性保证需要应用层自己加锁。我一般建议不同客户端写不同文件或者用数据库这类自带并发控制的应用。6.3 结合 autofs 实现按需挂载如果客户端很多每个都写 fstab 很麻烦而且有些目录可能很少访问。这时候可以用autofs实现按需挂载访问目录时才挂载一段时间不用自动卸载。配置方式是在/etc/auto.master里加一行/mnt/nfs /etc/auto.nfs --timeout300然后/etc/auto.nfs里定义具体挂载video -fstypenfs4,rsize1048576,wsize1048576 192.168.10.100:/srv/nfs/video code -fstypenfs4,rsize1048576,wsize1048576 192.168.10.100:/srv/nfs/code这样访问/mnt/nfs/video时才会挂载5 分钟不用自动卸载。对于客户端数量多、访问不频繁的场景这个方案能省不少资源。6.4 监控与日常维护要点NFS 跑起来之后日常维护主要是看几个指标。服务端用nfsstat -s看统计信息重点关注retrans重传次数和timeout超时次数。这两个值持续增长说明网络或服务端有瓶颈。客户端用nfsstat -c看客户端统计nfsiostat可以看每个挂载点的 IO 情况类似iostat但针对 NFS。日志方面服务端的/var/log/messages或journalctl -u nfs-server会有导出和访问记录。客户端的dmesg里会有挂载和超时信息。我一般会写一个简单的巡检脚本每天跑一次检查挂载点是否正常、磁盘空间是否充足、有没有异常重传。这个脚本不复杂但能提前发现很多问题。7. 一些实际场景的取舍经验搭 NFS 这件事配置本身不难难的是根据场景做取舍。我分享几个实际遇到的情况。有个做机器学习训练的团队他们的数据集体积大、读取频繁但很少写入。我给他们的方案是服务端用ro导出客户端挂载加nconnect16和rsize1048576训练任务启动时预读数据。这样既保证了数据安全又跑满了带宽。另一个做 CI/CD 的场景多个构建节点需要共享缓存目录写入非常频繁。这种场景下sync会成为瓶颈我改用了async同时服务端上了 UPS 防止断电。这是一个典型的用可靠性换性能的取舍前提是能接受极端情况下丢少量缓存数据。还有一个家庭实验室的场景几台设备共享影音库访问频率低但要求稳定。我用了 autofs 按需挂载配合soft挂载选项避免某台设备关机导致其他设备卡死。这些经验归结起来就一句话没有万能配置只有适合场景的配置。先把业务特点想清楚再决定用哪些参数比盲目抄配置靠谱得多。最后再分享一个小技巧如果你不确定某个挂载参数的效果可以先用mount -o remount动态调整不用卸载重挂。比如mount -o remount,rsize1048576 /mnt/nfs/video改完立刻用dd测一下对比前后差异。这个方法我调试性能时经常用比反复改 fstab 重启高效多了。