1. 为什么要在 Manjaro 上折腾 NFS 服务
手里这台 Manjaro 用了快两年,滚动更新一路跟下来,桌面体验确实舒服,但真正让我离不开它的原因,是它作为一台“家庭数据中转站”的稳定性。家里三台设备——一台主力台式、一台旧笔记本改的影音机、还有一台常年挂着的迷你主机——之间要频繁倒腾文件。最初我用的是移动硬盘来回插拔,后来换成局域网共享,试过 SMB,也试过 NFS,最后长期留下来的方案是 NFS。
原因很直接:在纯 Linux 环境里,NFS 的性能和权限模型是最贴合原生文件系统语义的。SMB 虽然跨平台兼容性好,但在 Linux 与 Linux 之间传输大量小文件时,元数据操作的开销明显更高,而且权限映射那一层经常让人抓狂。NFS 不一样,它直接把服务端的 UID/GID 带到客户端,权限判断几乎和本地文件一样自然。对于我这种“三台机器同一个用户名、同一套目录结构”的场景,NFS 几乎是零心智负担。
这篇内容适合谁看?如果你手上有一台跑 Manjaro 的机器,想把它变成局域网里的文件服务器,让其他 Linux 设备像访问本地目录一样访问它,那这篇就是写给你的。我会从零开始,把服务端配置、客户端挂载、权限调优、开机自启、故障排查整条链路讲透,包括我踩过的那些坑。整个过程不需要你懂什么高深的内核知识,只要会敲命令、会改配置文件就行。
需要提前说明的是,NFS 的设计前提是“可信局域网”。它本身没有强认证机制,早期版本甚至靠 IP 加主机名做信任。所以这套方案只适合你完全掌控的家庭网络或实验室内网,绝对不要把它暴露到公网。这一点我在后面还会反复强调。
2. 整体方案设计与核心思路拆解
2.1 为什么选 NFS 而不是 SMB 或其它方案
先把选型这件事说清楚,因为很多人一上来就问“为什么不用 SMB”。我做过对比测试,在同样的千兆内网、同样的机械硬盘条件下,传输一个包含两万个小文件的源码目录,NFSv4 比 SMB 快了大约三成。差距主要来自协议开销:SMB 每个操作都要走一遍复杂的协商和签名流程,而 NFS 的 RPC 调用轻量得多。
另一个关键点是权限。SMB 默认走的是“用户映射”那一套,服务端和客户端的用户需要额外配置对应关系,稍不注意就变成 nobody 或者权限拒绝。NFS 直接把数字 UID/GID 传过去,只要两端用户名对应的 UID 一致,权限就是通的。我在三台机器上都用同一个 UID(1000),所以从来没为权限头疼过。
至于为什么不选更“现代”的方案,比如各种分布式文件系统或者同步工具:那些东西要么太重,要么是“同步”而非“共享”。我要的是“一份数据、多处访问、实时一致”,NFS 正好卡在这个需求点上。
2.2 Manjaro 上跑 NFS 的版本选择
NFS 有几个版本,v3 和 v4 是主流。我的建议是直接用NFSv4,理由有三条。
第一,v4 只需要开放一个端口(默认 2049),而 v3 需要 rpcbind 加上一堆动态端口,防火墙配置起来很烦。第二,v4 原生支持 ACL 和更完善的锁机制。第三,v4 有“伪文件系统”的概念,可以把多个导出目录挂在一个虚拟根下,客户端看起来更整洁。
不过 v4 也有个坑:它的身份映射依赖idmapd,如果配置不对,客户端看到的文件属主会变成 nobody。这个我在后面排查章节会详细讲。
2.3 目录规划与导出策略
在动手之前,先想清楚你要共享什么。我的做法是单独划一个目录树,而不是直接把/home整个导出去。原因很简单:导出范围越大,权限失控的风险越高。我建的是/srv/nfs,下面按用途分子目录:
/srv/nfs/media:影音文件,只读为主/srv/nfs/share:公共交换区,读写/srv/nfs/backup:备份落地区,只有特定机器可写
这种“一个根、多个子目录”的结构,配合 NFSv4 的伪根,客户端挂载时只需要挂一个地址,就能看到所有子目录,非常清爽。
导出策略上,我遵循“最小权限”原则:能只读就不给读写,能限定 IP 就不放开网段。具体配置在下一章展开。
3. 服务端配置的完整实操
3.1 安装必要的软件包
Manjaro 基于 Arch,包管理用 pacman。NFS 服务端需要两个包:nfs-utils提供核心服务,rpcbind在 v4 下其实不是必须的,但某些工具链还会依赖它,装上无妨。
sudo pacman -Syu sudo pacman -S nfs-utils rpcbind这里有个细节:pacman -Syu先做全系统更新,是因为 Manjaro 是滚动发行版,如果只装包不更新,可能出现依赖版本不匹配。我遇到过好几次因为内核和 nfs-utils 版本错位导致服务起不来的情况,养成先更新再装包的习惯能省很多事。
装完之后,用systemctl检查服务状态。注意,NFSv4 主要靠nfs-server.service,它会自动拉起需要的依赖。
sudo systemctl enable --now nfs-server.serviceenable --now这个组合很实用,既设置开机自启,又立刻启动。启动后可以用systemctl status nfs-server确认,看到 active (exited) 是正常的,因为 NFS 服务本身是个“触发式”的服务,它拉起内核模块后就退出了。
3.2 编写 exports 配置文件
核心配置文件是/etc/exports。这个文件的语法是“导出目录 + 客户端 + 选项”,每行一条规则。我的配置长这样:
/srv/nfs 192.168.1.0/24(rw,sync,fsid=0,no_subtree_check) /srv/nfs/media 192.168.1.0/24(ro,sync,no_subtree_check) /srv/nfs/share 192.168.1.0/24(rw,sync,no_subtree_check) /srv/nfs/backup 192.168.1.50(rw,sync,no_subtree_check)逐条解释这些选项,因为这是最容易出错的地方。
fsid=0只加在根导出上,它把/srv/nfs标记为 NFSv4 的伪根。客户端挂载时写server:/就能看到这个根,而不是写完整路径。这是 v4 的特性,v3 不需要。
rw和ro不用多说,读写和只读。sync表示数据先落盘再返回确认,安全性高但性能略低;对应的async性能好但断电可能丢数据。家庭场景我建议用sync,因为丢数据的代价比那点性能损失大得多。
no_subtree_check是个性能优化项。默认情况下 NFS 会检查请求的文件是否在导出的子树内,这个检查在某些场景下会引发问题且拖慢速度。关掉它,前提是你信任客户端不会乱来。
192.168.1.0/24是网段写法,表示整个子网可访问。如果你只想给单台机器,就写完整 IP,像 backup 那行一样。
注意:
/etc/exports里绝对不能有行尾多余空格,也不能用 Tab 和空格混排,否则解析会出错。我因为这个排查了半小时。
3.3 让配置生效并验证
改完 exports 后,不需要重启服务,用exportfs命令重新加载即可:
sudo exportfs -ra-r是重新导出,-a是全部。执行后用exportfs -v查看当前生效的导出列表,确认选项和你写的一致。
sudo exportfs -v输出里会列出每个目录、客户端和选项。如果某个目录没出现,说明那行配置有问题,回去检查语法。
接下来验证服务端本地能否访问。装完 nfs-utils 后会有showmount工具:
showmount -e localhost正常的话会列出所有导出目录。如果报“clnt_create: RPC: Program not registered”,说明 nfs-server 没起来,回去看服务状态。
3.4 防火墙放行
Manjaro 默认可能没开防火墙,但如果你开了ufw或firewalld,需要放行 NFSv4 的端口。v4 主要用 2049,但mountd和rpcbind在某些配置下还会用到其它端口。最省事的做法是直接放行整个内网网段:
sudo ufw allow from 192.168.1.0/24 to any port nfs如果你用的是 firewalld,对应命令是:
sudo firewall-cmd --permanent --add-service=nfs sudo firewall-cmd --reload我个人的习惯是家庭内网直接关掉防火墙,因为路由器那层已经做了 NAT 隔离。但如果你有访客网络或者不信任的设备在同一网段,还是老老实实配防火墙。
4. 客户端挂载与权限调优
4.1 安装客户端工具并测试连通性
客户端这边,Manjaro 同样需要nfs-utils:
sudo pacman -S nfs-utils装完后先别急着挂载,用showmount探一下服务端:
showmount -e 192.168.1.10把 IP 换成你服务端的实际地址。如果能看到导出列表,说明网络和服务端都没问题。如果卡住或者超时,先 ping 一下确认网络通,再检查服务端防火墙。
4.2 手动挂载验证
第一次挂载建议手动来,方便看报错。先建挂载点:
sudo mkdir -p /mnt/nfs然后挂载:
sudo mount -t nfs4 192.168.1.10:/ /mnt/nfs注意这里写的是:/,因为服务端配了fsid=0伪根。如果你用的是 v3,要写完整路径:/srv/nfs。
挂载后用df -h确认,再ls /mnt/nfs看看能不能列出子目录。如果列出来但权限不对,比如全是 nobody,那就是 idmap 的问题,下一节讲。
4.3 权限映射与 UID 一致性
NFSv4 默认启用 idmap,服务端和客户端通过idmapd把用户名和 UID 做映射。如果两端用户名相同但 UID 不同,就会出现权限错乱。最稳妥的做法是保证两端 UID 一致。
在服务端和客户端分别执行:
id看输出里的 uid 是不是都是 1000。如果不是,要么改一端的用户 UID,要么在 idmapd 配置里做映射。改 UID 风险较大,因为会影响本地文件属主,所以我更推荐在新建用户时就规划好。
如果确实需要映射,编辑/etc/idmapd.conf,把Domain设成一致的值,比如localdomain,然后重启nfs-idmapd.service。不过说实话,家庭环境里统一 UID 是最省心的,映射那套我试过,坑不少。
4.4 写入 fstab 实现开机自动挂载
手动挂载没问题后,就可以写进/etc/fstab了。我推荐用_netdev选项,它告诉系统这是网络设备,等网络就绪后再挂载,避免开机时因为网络没起来而挂载失败。
192.168.1.10:/ /mnt/nfs nfs4 _netdev,noauto,x-systemd.automount,x-systemd.idle-timeout=60 0 0这里我用了x-systemd.automount,它的好处是“按需挂载”:只有真正访问/mnt/nfs时才建立连接,闲置 60 秒后自动断开。对于不常访问的共享,这个方案能省资源,也能避免网络波动导致的挂载僵死。
如果你希望开机就挂上,把noauto去掉,改成:
192.168.1.10:/ /mnt/nfs nfs4 _netdev,defaults 0 0改完 fstab 后,用sudo mount -a测试,没报错就说明配置正确。
4.5 性能调优参数
默认挂载参数在千兆网下够用,但如果你追求更高吞吐,可以加几个选项。我实测有效的组合是:
192.168.1.10:/ /mnt/nfs nfs4 _netdev,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 0 0rsize和wsize是读写块大小,默认可能只有 64K 或 128K,调到 1M 能显著提升大文件传输速度。hard表示遇到服务端无响应时持续重试而不是报错退出,适合稳定性优先的场景。timeo是超时时间(单位是 0.1 秒),retrans是重传次数。
不过要注意,rsize/wsize不是越大越好,超过网络 MTU 和交换机缓冲能力反而会降速。我建议从 256K 开始试,逐步往上调,用dd或scp测速找到最佳值。
5. 常见问题与排查技巧实录
5.1 挂载报错 “access denied by server”
这是最常见的错误,原因通常有三个。第一,客户端 IP 不在 exports 允许的范围内。检查服务端/etc/exports里的网段是否包含客户端实际 IP。第二,exports 改了但没执行exportfs -ra。第三,服务端防火墙拦了请求。
排查顺序:先在服务端showmount -e localhost确认导出正常,再从客户端showmount -e 服务端IP确认能连上,最后看防火墙日志。
5.2 文件属主显示为 nobody
前面提过,这是 idmap 问题。先确认两端id输出的 UID 是否一致。如果一致还出问题,检查/etc/idmapd.conf里的 Domain 是否两端相同。改完后重启nfs-idmapd和nfs-server。
还有一个隐蔽原因:服务端导出目录的父目录权限。NFS 在访问子目录时会检查路径上每一级的执行权限。如果/srv对其他人没有x权限,客户端就会看到 nobody。用namei -l /srv/nfs/share可以逐级检查权限。
5.3 写入速度慢或卡顿
先排除网络问题,用iperf3测一下两端带宽。如果带宽正常但 NFS 慢,多半是sync选项导致的。可以临时改成async测试,如果速度上来了,说明是同步写入的开销。但如前所述,我不建议长期用async。
另一个原因是rsize/wsize太小。按上一节的方法调大试试。还有可能是服务端磁盘本身慢,用hdparm -t测一下裸盘速度做对比。
5.4 服务端重启后客户端挂载僵死
这是 NFS 的经典问题。服务端一重启,客户端的挂载点就变成“僵尸”,任何访问都会卡住,连df都能卡死终端。解决办法是挂载时加soft选项,让超时后返回错误而不是无限等待。但soft有丢数据的风险,所以更好的方案是用hard配合x-systemd.automount,让 systemd 在服务端恢复后自动重连。
如果已经卡死了,用umount -f -l /mnt/nfs强制懒卸载,然后重新挂载。
5.5 常见问题速查表
| 现象 | 可能原因 | 排查命令 | 解决方向 |
|---|---|---|---|
| access denied | IP 不在允许范围 | showmount -e | 改 exports 并exportfs -ra |
| 属主 nobody | UID 不一致或 idmap 配置错 | id、cat /etc/idmapd.conf | 统一 UID 或改 Domain |
| 写入慢 | sync 或块大小小 | iperf3、dd | 调 rsize/wsize,评估 async |
| 挂载僵死 | 服务端重启 | mount、dmesg | 用 automount 或 soft 选项 |
| 服务起不来 | 包版本不匹配 | systemctl status nfs-server | 先pacman -Syu再重装 |
5.6 几个我踩过的坑
第一个坑是 exports 里的空格。有次我从网页复制配置,行尾带了个不可见空格,结果那条规则死活不生效,exportfs -v里就是不显示。后来用cat -A才看出来。
第二个坑是 SELinux。Manjaro 默认不开 SELinux,但如果你从别的发行版迁移过来,可能带着相关配置。NFS 导出目录需要正确的 SELinux 上下文,否则会被拦。用getenforce确认状态,如果是 Enforcing,就得用semanage加规则。
第三个坑是 IPv6。有次客户端解析服务端主机名时优先走了 IPv6,但服务端只监听了 IPv4,导致连接超时。解决办法是在 exports 里同时写 IPv4 和 IPv6 网段,或者在客户端 fstab 里直接用 IPv4 地址。
6. 进阶玩法与长期维护建议
6.1 用 autofs 做更灵活的自动挂载
systemd 的 automount 已经够用,但如果你有多个共享目录、想按需挂载不同位置,autofs更专业。它通过一个映射文件定义“访问哪个路径就挂哪个服务端”,支持通配符和超时卸载。配置稍微复杂点,但管理大量挂载点时优势明显。
6.2 定期检查导出状态
我写了个简单的脚本,每周跑一次,检查所有导出目录是否可访问、磁盘空间是否充足。核心就是exportfs -v加df -h,输出到日志。这样能在问题变大之前发现苗头。
6.3 备份策略
NFS 共享的数据,尤其是 backup 目录,本身也需要备份。我的做法是在服务端用rsync定期把/srv/nfs同步到另一块物理盘。注意不要用 NFS 挂载来做备份源,因为网络抖动会导致备份不完整,直接在服务端本地操作最可靠。
6.4 版本升级注意事项
Manjaro 滚动更新时,偶尔会遇到 nfs-utils 大版本升级导致配置格式变化。升级前先看 Arch 的新闻页,升级后立刻exportfs -v确认导出正常。如果服务起不来,journalctl -u nfs-server看日志,多半是某个选项被废弃了。
我个人在实际操作中的体会是,NFS 这套东西“配一次、稳三年”,前期把 UID、目录权限、exports 选项这三件事做对,后面基本不用管。真正会出问题的,往往是网络环境变化或者系统大版本升级。所以与其频繁折腾配置,不如把排查命令练熟,出问题时能快速定位。最后再分享一个小技巧:把常用的showmount、exportfs -v、systemctl status nfs-server做成一个 alias 或者小脚本,排查时一条命令全跑一遍,比一条条敲快得多。