在公司内部有十几台 Linux 服务器的时候,最头疼的问题之一就是数据不统一。开发在 A 机器上打包好的产物,交付到 B 机器测试时发现路径不对;运维写脚本要同时处理三台机器上的日志,结果每台的挂载方式都不一样。我当时的解法很简单:把公共数据放到一台存储服务器上,用 NFS 共享出去,再配合 autofs 按需挂载,彻底解决"重启就丢挂载、机器一多就乱挂"的毛病。这篇文章就是把我从零开始搭建这套环境的过程、踩过的坑、以及后来在企业环境里做得更完善的思路整理出来,希望对准备上手 NFS 或正在被静态挂载困扰的人有点帮助。
先说清楚,我这篇不是单纯的命令清单,而是带着决策过程写的。每一步都尽量解释"为什么这样做",因为 NFS 和 autofs 的坑大多不在命令本身,而在参数选择和边界情况。
1. 服务器挂载需求的不同解法,我为什么选NFS+autofs
1.1 这是什么样的使用场景
NFS(Network File System)说白了就是让一台 Linux 机器把目录通过网络共享出来,其他 Linux 机器像访问本地目录一样访问它。它跟 Samba 最大的区别是:Samba 是为 Windows 客户端服务的,协议重、权限模型要靠 SMB 和 POSIX 互相映射;NFS 天生就是 Linux/Unix 之间聊天的语言,权限模型和本地文件系统几乎一致,性能好、配置简单,不需要额外处理文件锁、ACL 映射之类的复杂问题。
网络上关于 NFS 的需求场景其实很典型:
- 应用服务器集群需要共享上传目录或静态资源,比如多台 Tomcat 后面挂同一个图片目录。
- 计算节点需要访问统一的数据集,训练数据放在 NFS 上,各节点直接读取。
- 缓存目录、构建产物目录需要跨机器复用,比如 Jenkins 的 workspace。
- 用户目录集中在服务器上,不同机器登录后家目录一致,方便管理员统管。
如果你只是偶尔在两台机器间传几个文件,rsync 或 scp 就够了,完全没必要上 NFS。NFS 的价值在于"持续、多客户端、像本地目录一样访问"。
1.2 NFS与其他共享方案的取舍
我还真对比过几种常见方案,最后才选了 NFS。
| 方案 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| NFS | 配置简单,Linux 原生支持好,性能高 | 高可用需要额外方案,公网上不安全 | Linux 局域网共享 |
| Samba | Windows/Linux 混合环境友好 | 协议开销大,权限处理复杂 | 混合环境办公共享 |
| iSCSI | 走块设备,数据库等应用可直接用 | 运维成本高,多客户端同时读写要集群文件系统 | 单机需要裸盘场景 |
| WebDAV | 跨平台,可走 HTTPS | 性能差,文件锁支持弱 | 非实时协作、云盘场景 |
| SSHFS | 无需服务端额外配置,通过 SSH 即可 | 性能一般,不适合高并发 | 临时访问 |
对于纯 Linux 环境下的服务器间共享,NFS 是综合成本最低的。它直接挂在 VFS 层,应用感知不到远程文件的存在,很多需要 mmap 或 flock 的程序在 Samba 上跑不起来,在 NFS 上却没问题。这一点做应用部署的人应该深有体会。
1.3 静态mount的痛点与autofs的价值
传统做法是在/etc/fstab里写一条 mount 记录,开机自动挂载:
192.168.10.10:/data/share /mnt/data nfs4 defaults,_netdev 0 0但用一段时间你就发现问题了:
- 服务器一多,fstab 越来越长,哪台机器上挂载了哪些共享完全靠脑子记,排障时根本不知道从哪里查起。
- 如果 NFS 服务器暂时不可用(比如机房重启、交换机割接),客户机的
mount会一直阻塞,fstab 里的记录会导致开机流程卡住,最坏情况系统硬等几分钟才进入登录界面。很多刚接触的人在这里吃过亏。 - 客户端只要开机就挂载,哪怕这个共享一个月才用一次,也在浪费文件句柄和网络连接。
- 服务端导出的共享有几十个,客户端统统挂上,不仅
mount列表难看,一不小心还会碰到未授权的共享目录。
autofs 解决的问题恰恰就是这些:它不是开机一次性把所有共享都挂上,而是当进程真正访问某个挂载点目录时,才触发内核去执行挂载;一段时间没人访问,又自动卸载。这样客户端始终只需要维护一个"访问入口"配置,实际的挂载动作完全按需触发,不会因为 NFS 服务器暂时失联拖垮开机,也不会闲挂着一堆不用的网络盘。
用一句话总结:NFS 解决"怎么把目录共享出去",autofs 解决"客户端怎么优雅地挂上、用完再放掉"。这两者配合才是生产里最常见的组合,而不是光在 fstab 里加一行。
2. NFS服务端配置:从软件安装到导出一份可用的共享
2.1 服务端软件安装与服务管理
服务端我用的是 Debian/Ubuntu 系,RHEL/CentOS 命令我顺便列出来,差别不大。
Debian/Ubuntu 下:
apt update apt install -y nfs-kernel-server systemctl enable --now nfs-serverRHEL/CentOS 下:
yum install -y nfs-utils systemctl enable --now nfs-server装完以后,系统会有这么几个关键进程:
rpcbind:负责 RPC 端口注册,NFSv3 依赖它。nfsd:NFS 服务主进程,实际处理文件读写。rpc.mountd:处理挂载请求和导出权限校验。exportfs:维护 NFS 导出表,exportfs -ra就是重新读取导出配置。
一个小提示:如果你用的防火墙开启了严格策略,一定要把rpcbind和nfs相关端口放通,否则客户端会一直挂在 mount 阶段。这个我在第 4 章展开讲。
2.2 /etc/exports导出的配置细节
NFS 要共享哪个目录、允许哪些客户端访问、权限怎么控制,全部写在服务端的/etc/exports文件里。下面是我常用的一个配置示例:
# 仅允许 192.168.10.0/24 网段的机器读写访问 /data/share 192.168.10.0/24(rw,sync,no_subtree_check,sec=sys) # 只允许特定备份机只读访问 /data/backup 192.168.20.5(ro,sync,no_subtree_check,all_squash,anonuid=1000,anongid=1000) # 禁止root写入,适合普通用户家目录共享 /home 192.168.10.0/24(rw,sync,no_wdelay,no_subtree_check)每一行的格式很简单:导出路径 允许访问的主机(选项1,选项2...)。允许访问的主机可以是单个 IP、网段、主机名、域名通配符,但生产环境建议老老实实用 IP 或网段,不要依赖 DNS,否则 DNS 抖动会让导出权限判断变得不可靠。
exports 选项是我见过很多人忽略的地方,这里把关键的几个讲清楚:
rw/ro:读写还是只读。默认是只读,要明确加rw。sync/async:sync表示服务器端把数据写入磁盘后才响应客户端,安全性高,断电不丢文件;async是服务器先响应客户端、后台慢慢落盘,性能好,但服务器掉电可能丢数据。生产环境非特殊情况不要用async,默认就是sync。no_subtree_check:如果导出的目录本身是某个更大文件系统的子目录,NFS 服务端默认会检查客户端访问的文件是否仍在这个导出目录内,这在文件重命名频繁时会造成问题,而且有性能开销。加上这个选项可以关闭检查,风险很小,推荐加上。root_squash:默认选项,客户端 root 会被映射成匿名用户(nobody),防止客户端 root 在共享目录上为所欲为。no_root_squash:客户端 root 保持 root 身份,千万别随便开,意味着客户端披着 root 进来就能直接改你共享目录里的任何文件,包括那些只有 root 能写的系统文件。只有在特殊应用(比如无盘工作站)才会用。all_squash:所有客户端用户都被映射成匿名用户,适合共享公共目录,配合anonuid/anongid可以指定匿名用户映射成哪个本地 uid/gid。sec=sys:使用传统的 AUTH_SYS 认证,也就是基于 uid/gid 的认证。要注意,这种认证方式的安全性建立在"客户端可信"的基础上,如果客户端被攻破,完全可以伪造 uid。要求极高的环境建议上sec=krb5/krb5p(Kerberos 认证),代价是对客户端和服务端都要部署 Kerberos,我后面会提一句。
2.3 验证导出的三种方式
写完/etc/exports后,执行:
exportfs -rav-r是重新导出,-a是全部导出,-v是显示详细信息。执行后建议立刻验证:
showmount -e localhost这条命令会列出本机所有允许导出的目录和允许的主机列表。如果在客户端执行:
showmount -e 192.168.10.10看到的结果一样,说明服务端导出表没问题。另外还可以用:
exportfs -v查看导出选项是否按预期生效,比如root_squash是否打上了,这些细节肉眼就能看出配置有没有写错。
2.4 共享目录的权限陷阱
服务端导出配置只是第一道门槛,真正决定能不能读写的是目录本身的权限。NFS 默认不带身份认证,客户端请求里的 uid/gid 是什么,服务端就用什么 uid/gid 去访问共享目录。
举一个我踩过的例子:我把/data/share导出了,/etc/exports里写的是rw,权限也给了777,但客户端写入时报Permission denied。排查到最后发现,客户端访问的用户 uid 是 1000,服务端上/data/share属主是 root(uid 0),目录权限是755,普通用户没有写权限。虽然导出的 rw 说的是"NFS 层面允许写",但文件系统层仍然按 POSIX 权限判断,两个权限必须同时通过才行。
所以配置共享目录时,要明确几个问题:
- 谁来写?写操作的用户 uid 是什么?
- 服务端上这些文件到底属于谁?属组是谁?
- 是否需要强制把所有客户端用户都映射成服务端上的同一个用户?
第二个问题尤其重要。如果你希望多台机器的用户都能写同一个目录,最简单的方法是让客户端和服务端的 uid/gid 对齐。比如公司里所有 Linux 用户统一用 OpenLDAP 或 SSSD 集中认证,uid 全局唯一,这时候 NFS 共享目录的权限模型几乎不用额外处理,天然一致。如果没有集中认证,那就老老实实给共享目录用一个大 ID 的属主(比如 uid 1000),并用all_squash,anonuid=1000,anongid=1000把所有人都映射过去,省得各机器 uid 对不上导致文件属主错乱。
3. 客户端挂载方式与autofs的自动挂载配置
3.1 客户端手动挂载的基础用法
先看手动挂载,因为 autofs 本质上也是调用 mount,只是时机由内核触发。客户端需要先装软件:
# Debian/Ubuntu apt install -y nfs-common autofs # RHEL/CentOS yum install -y nfs-utils autofs手动挂载一条命令:
mkdir -p /mnt/data mount -t nfs 192.168.10.10:/data/share /mnt/data这时检查:
df -hT /mnt/data能看到类似输出:
Filesystem Type Size Used Avail Use% Mounted on 192.168.10.10:/data/share nfs4 500G 120G 380G 24% /mnt/data手动挂载最大的问题是不能抵抗客户端或者服务端重启。尤其客户端一重启,挂载点全没,应用如果依赖这个目录,启动就会异常。所以生产环境几乎都要配合 autofs 或 fstab 自动挂载。
3.2 autofs的核心运行机制
autofs 的原理值得花两分钟理解,理解之后配置思路就通了。
系统里有一个专门的 autofs 挂载点,它并不真正挂载任何远程目录,而是注册了内核里的 automount 机制。当进程访问这个挂载点下的某个子目录时,内核发现"这个目录还没挂载,需要请 autofs 来处理",于是触发 autofs 守护进程去查询映射配置,执行真正的 mount。挂载完成后,内核把访问请求正常转发到新挂载的文件系统。
这个机制决定了两件事:
- 客户端不访问时,远程目录根本没有挂载,既不占文件句柄,也不维持网络连接。
- 挂载是延迟的、按需的,重启服务端期间,客户端最多只是某些目录暂时不可用,而不会影响开机的其他挂载。
autofs 的配置核心是/etc/auto.master。它维护的是"挂载入口"和"映射文件"之间的关系。比如:
/mnt/nfs /etc/auto.nfs --timeout=120 --ghost意思是:所有以/mnt/nfs开头的自动挂载点,映射规则放在/etc/auto.nfs文件里;超过 120 秒没有被访问就自动卸载;--ghost表示即使没挂载,目录也先在/mnt/nfs下"虚位以待",ls /mnt/nfs能看到子目录名,而不是一片空白。--ghost对用户体验很重要,不然你真不知道哪些共享是可用的。
3.3 间接映射与直接映射的配置写法
autofs 有两种映射方式:间接映射和直接映射。容易混淆,分开讲。
间接映射:/etc/auto.master里定义一个基础目录(base directory),映射文件里的键名就是基础目录下面的子目录名。访问/mnt/nfs/data时,autofs 在映射文件里找data这个键,找到后把键名替换为共享地址。
/etc/auto.nfs示例:
data -fstype=nfs,rw,vers=4.2,hard,intr 192.168.10.10:/data/share backup -fstype=nfs,ro 192.168.10.10:/data/backup访问时:
ls /mnt/nfs/dataautofs 会看到data键,然后自动执行mount -t nfs -o rw,vers=4.2,hard,intr 192.168.10.10:/data/share /mnt/nfs/data。
直接映射:当挂载点不是一个基础目录下面的固定子目录,而是想直接把网络目录挂载到系统任意路径时,用直接映射。在 auto.master 里写:
/- /etc/auto.direct/-是直接映射的固定表示,意味着映射文件里的每一行都写完整的本地挂载路径:
/mnt/docs -fstype=nfs,rw 192.168.10.10:/data/docs /opt/apps -fstype=nfs,rw,vers=4.1 192.168.10.10:/data/apps直接映射更灵活,能精确控制每个目录挂载的位置,适合那种挂载点分散、路径不好统一规划的场合。
3.4 通配符映射与按用户名挂载
通配符是 autofs 最强大的特性之一,也是很多人看到一个配置文件觉得"这能行?"的地方。
/etc/auto.nfs里写:
* -fstype=nfs,rw 192.168.10.10:/data/home/&*匹配访问的子目录名,&在 autofs 里代表"与键名相同的字符串"。所以当客户端访问/mnt/nfs/user1时,实际挂载的是192.168.10.10:/data/home/user1;访问/mnt/nfs/user2时,挂载的是192.168.10.10:/data/home/user2。
这个特性用来做用户家目录集中管理非常香。我在公司的做法是:
- 服务端
/data/home下按用户名建目录,用户 ssh 登录任意一台机器,家目录都是 NFS 上的同一个。 - autofs 配置成
*通配,用户访问自己家目录时才挂载,不会把所有人的家目录在每台机器上都挂满。 /etc/auto.master里写/home /etc/auto.home --timeout=600,挂载点就是/home/user1,因为服务器上 /home 本身就是自动挂载入口,所以/etc/passwd里用户的家目录就填/home/user1,完全不冲突。
要注意权限:服务端/data/home/user1的属主必须是 uid 1000(用户 user1 的 uid),客户端 uid 也得一致,否则挂上去后文件权限是错乱的。建议配合 LDAP 或 SSSD 统一 uid,这套方案才真正好使。
3.5 正确设置超时参数
--timeout是 autofs 里最容易被低估的参数。它决定目录多久没有访问就被卸载。
我见过有人图方便把 timeout 设成 0,意思是永不过期,结果每台客户端都把远程目录挂得满满的,NFS 服务端句柄数量和网络连接数攀升,真到出问题时查都难查。还有的设太短,比如 60 秒,用户编辑文件时稍微停顿一下,autofs 就把目录卸载了,下次触摸文件又要触发一次挂载,体验很割裂。
一般建议:
- 对于频繁使用的家目录:
--timeout=600(10 分钟)比较合适。 - 对于偶尔访问、只用来同步数据的:
--timeout=120够了。 - 对于必须常驻的共享目录:其实更适合用 fstab 或 systemd mount 而不是 autofs,不过这种场景很少,因为常驻就失去了 autofs 的意义。
要提醒的是,超时卸载是在"该目录空闲"时才发生,如果进程一直打开着目录里的文件,autofs 不会卸载,不用担心文件句柄断掉。
4. 生产环境中的选项调优与常见坑
4.1 挂载选项的语义与选型
挂载参数决定了 NFS 在网络异常时的表现、读写性能、以及内核如何处理缓存。我列一下生产环境常用的:
| 选项 | 含义 | 建议 |
|---|---|---|
hard | NFS 服务器无响应时,一直重试直到恢复 | 默认,推荐 |
soft | NFS 服务器无响应时,重试有限次后返回错误给应用 | 慎用,应用可能读到不完整数据 |
intr | 允许信号中断被阻塞的硬挂载进程 | 老内核推荐加 |
timeo=600 | 每次重试的超时时间(0.1秒单位),默认 600(60秒) | 局域网可减小到 50~200 |
retrans=2 | 重试次数 | 默认按发行版不同,局域网不用动 |
vers=4.2 | 指定 NFS 版本 | 新版内核用 4.2,跨平台兼容选 4.1 |
proto=tcp | 使用 TCP 协议 | 默认,UDP 适合极小传输但不可靠,不推荐 |
noatime | 不更新访问时间戳 | 推荐,减少大量写请求 |
rsize/wsize | 读写的 RPC 包大小,单位字节 | 较老内核默认 32K/64K,新版 1M,一般不需要手动改 |
fg/bg | 挂载失败时前台重试还是转后台重试 | 默认 bg,适合 fstab |
其中hard和soft的选择要特别小心。soft模式下客户端等待一定时间就会放弃连接,应用收到 EIO 错误。如果你跑的是数据库、分布式存储这类对数据一致性要求极高的应用,soft可能导致数据只有一半写入远程就报错了,非常危险。宁可让应用进程阻塞,也不要让它拿到一个"假成功"的写入结果。所以我做生产配置基本都用hard,配合timeo和retrans控制重试节奏。
4.2 NFS版本怎么选
很多老教程还在讲 NFSv3,但新环境推荐直接上 NFSv4。原因很实在:
- NFSv3 需要依赖多个 RPC 服务(mountd、nlockmgr 等),端口都是动态的,防火墙配置非常麻烦。
- NFSv4 把挂载协议、文件锁、状态回收都合并到一个主协议里,统一走 TCP 2049 端口,防火墙省心太多。
- NFSv4.2 引入了服务端复制、空间预留、部分文件读取等特性。比如
cp --reflink可以远程复制不占网络带宽,这对共享目录的备份复制场景很有价值。
如果客户端内核太老,NFSv4.2 挂载不了,可以先退到vers=4.1,而不是直接掉回 v3。NFSv4.1 增加了目录委派(Directory Delegation)等特性,性能和并发比 v3 好不少。只有遇到服务器存储是老系统、内核实在不支持 v4 的情况,才考虑 v3。
另一个容易忽略的问题:NFSv4 的导出根路径。v4 的服务端会把所有导出目录都放在一个伪根(pseudo filesystem)下面,客户端挂载时可以使用完整路径。少数情况下,如果导出目录路径写错,v4 会返回No such file or directory而不是直接拒绝,这时候要检查服务端 exportfs 列表、客户端挂载路径是否完全一致。
4.3 需要提前固定RPC端口
如果你坚持要用 NFSv3,或者有客户端内核太老只能 v3,那么必须在服务端固定 RPC 端口,否则防火墙规则写完第二天可能就不生效了。
NFSv3 的动态端口包括:
rpc.mountd:默认随机,需要固定到比如 20048。rpc.lockd:文件锁服务,固定到 20049。rpc.quotad:配额服务,固定到 20050。rpc.statd/rpc.nsm:状态通知服务,固定到 20051。
Debian/Ubuntu 可以通过修改/etc/default/nfs-kernel-server和/etc/default/nfs-common指定端口,RHEL/CentOS 则改/etc/sysconfig/nfs。每次改完要重启 NFS 相关服务,再用rpcinfo -p验证端口是不是固定下来了。
很多人在这一步翻车:服务端调好防火墙,只放了 111 和 2049,客户端 mount 的时候 mountd 却用了一个随机高端口,连接直接被防火墙掐断,表现就是mount.nfs: Connection timed out。我印象最深的一次,排了半个多小时,最后用rpcinfo -p才看到 mountd 端口又飘了。
4.4 性能相关参数调整
NFS 性能调优没有银弹,但是有几个方向一定值得做:
- 服务端提高 NFS 内核线程数。默认 nfsd 线程数偏保守,RHEL 里可以改
/etc/sysconfig/nfs里的RPCNFSDCOUNT,比如设成 128 或 256。线程数不是越大越好,要根据 CPU 核数调整,一般跟核数持平或者略多就行。我见过有人设 1024,结果内存开销暴涨,性能反而下降。 - 客户端启用
noatime。这是最廉价的性能优化。每次读取文件不更新 atime,能减少大量 NFS 写请求。很多运维不知道 NFS 读文件也可能触发写请求,这就是其中一个来源。 - 检查
rsize/wsize。新版内核默认是 1048576(1MB),不用改。老内核默认 65536,如果存储网络是万兆,可以适当调大到 262144 或 1MB,但不要随意调到超出网络 MTU 能有效承载的值,不是越大越好。 - 避免把 NFS 挂载点放到被频繁
find的路径上。有些备份工具递归遍历目录时,真正的文件还没访问,autofs 就已经把目录挂上了,性能表现可能不佳。可以用--ghost加合理 timeout 缓解。
4.5 生产环境的一个典型坑:开机挂载延迟与后台恢复
如果某些导出的共享在客户端上确实需要常驻(比如 Web 应用依赖的目录),有人会双管齐下:既在 fstab 里写了一条_netdev挂载,又配了 autofs。这样做的坏处是 fstab 那条记录是静态的,如果服务端暂时不可用,autofs 的按需挂载机制完全没机会接管,开机照样可能卡住。
我的建议是二选一:
- 必须常驻:用 fstab 配
nfs defaults,_netdev,bg,hard,intr。_netdev告诉 systemd 先等网络可用再挂载,bg是挂载失败时转到后台重试,避免开机卡死。 - 允许按需:交给 autofs,通过 timeout 控制卸载,服务端重启也不影响客户端开机。
这两种思路没有谁绝对好,取决于业务能不能接受首次访问时的挂载延迟。一般首次访问 autofs 挂载也就几百毫秒到一两秒,用户基本感知不到,所以多数场景我都会推 autofs。
5. 排障思路:从mount卡住到权限异常的完整排查链路
5.1 一套通用的排查顺序
NFS 出问题的时候,很多人的第一反应是去翻业务日志,其实正确的排查顺序是从底层往上层走。
我自己在排 NFS 故障时基本按这个链路来:
- 网络层:
ping服务端 IP,确认通不通。 - RPC 层:在客户端执行
rpcinfo -p 服务端IP,确认 rpcbind 可达,NFS 服务已注册。如果这里超时,大概率是防火墙或 rpcbind 没起来。 - 导出列表:执行
showmount -e 服务端IP,确认客户端 IP 是否在允许列表里。 - 挂载层:执行
mount -t nfs 服务端IP:/共享路径 /本地路径 -v,观察报错信息。 - 内核日志:同时开
journalctl -f或dmesg -T | tail -50看内核有没有反馈。 - 权限层:查看共享目录属主、属组、权限位,以及
/etc/exports里的 squash 选项。
这套顺序的好处是,每次都能把问题定位在具体的层,不会在业务日志里瞎找。
下面用表格把常见的几种现象、可能原因、解决方向列出来:
| 现象 | 可能原因 | 解决方向 |
|---|---|---|
mount.nfs: Connection timed out | 网络不通、防火墙拦截、rpcbind 没起来 | 检查rpcinfo -p、防火墙规则、双方时间 |
showmount -e有输出但 mount 拒绝 | exports 主机限制 | 检查 exports 里写的网段是否覆盖客户端 IP |
Permission denied | 共享目录 POSIX 权限不够、squash 映射导致匿名用户无权限 | 用exportfs -v查看实际生效选项,检查目录属主 |
mount.nfs: access denied by server | 客户端 IP 不在允许列表 | 修改 exports 后exportfs -ra |
No such file or directory | NFSv4 路径写错或导出目录不存在 | 用showmount -e核对导出路径 |
| 挂载后写文件卡死 | 服务端无响应、hard 挂载一直重试 | 检查服务端负载、网络丢包、nfsstat |
| 挂载能成功但性能极差 | rsize/wsize 不合适、nfsd 线程太少 | 调整线程数,确认网络带宽 |
5.2 mount长时间卡住的常见元凶
mount命令挂在那里长时间不返回,是生产环境里最烦人的问题之一。它的本质往往是:TCP 连接建立不了,但 mount 一直在等待超时。
典型流程是:客户端发 RPC 到 rpcbind(端口 111),rpcbind 返回 mountd 的端口号,客户端再去连 mountd。如果中间某一步被防火墙丢包,mount 就不会立刻报错,而是反复重试,直到 mount 选项里的timeo和retrans组合起来超时,或者干脆一直卡住。
排查这种问题,最快的办法是抓包看有没有 SYN 包发出,或者直接对服务端跑一遍:
rpcinfo -p 服务端IP如果 rpcinfo 也卡住,基本可以断定是 111 端口不通。如果 rpcinfo 秒回,那问题集中在 mountd 端口上。
有一次我在 AWS 安全组里只放行了 2049,认为 NFSv4 只需要这一个端口,结果客户端死活挂不上。后来发现那套环境虽然挂载选项写的是 v4,但服务端还带了 v3 的 mountd 流程,mount 前会先去 rpcbind 查询,而安全组没放 111。最后我在服务端把 NFSv3 的 mountd 端口固定下来,安全组加上对应端口才解决。所以:即使你打算只用 NFSv4,也建议把 111 端口放通,否则 rpcbind 查询这一步就会卡住。
5.3 autofs一键调试
autofs 出问题时,很多人只会systemctl restart autofs,这显然不够。我总结了一套调试命令:
systemctl status autofs journalctl -u autofs -f automount -m -vsystemctl status autofs可以看守护进程是否在跑;journal 日志里能看到 autofs 触发挂载和卸载的详细过程;automount -m -v会打印当前 autofs 挂载点的状态和映射信息,用来确认配置文件是否被正确读取。
如果你访问/mnt/nfs/data时没有触发挂载,先检查映射文件路径和 auto.master 里的基础目录是否匹配。间接映射里键名不能以/开头,直接映射里键名必须是完整路径。写错键名格式是 autofs 配置里最常见的低级错误,但提示信息很隐晦——通常只是访问时目录不存在。
另外注意,修改 auto.master 或映射文件后,不需要重启 autofs,执行:
systemctl reload autofs或者直接systemctl restart autofs,都能让新配置生效。如果只是改了映射文件,restart 会自动重新加载。
5.4 权限与映射问题的判断
权限问题在 NFS 里的表现往往很迷惑。比如你用 root 执行touch成功,但换成普通用户就Permission denied;又比如客户端目录能看到文件名,但读不了内容。
判断思路是:
- 在客户端执行
id,确认当前用户在客户端上的 uid。 - 在服务端用同一个 uid 的本地用户去试
ls -l /共享目录。 - 看
/etc/exports里有没有all_squash/anonuid覆盖映射。
如果客户端显示 uid 是 1000,服务端上一个不存在的 uid 1000 去访问只有 uid 0 有权限的目录,自然被拒绝。这时候我通常会让服务端把这些家目录全部改成 1000 属主,或者干脆在 exports 里加all_squash,anonuid=1000,anongid=1000,把所有客户端用户都收敛到一个固定的 uid。
不过要强调,匿名映射只适合公共目录,不适合多用户场景。多用户场景下必须有统一的账号体系(LDAP/AD/SSSD),否则 uid 错乱问题会无穷无尽。
6. 我自己在落地这套方案时总结的几点心得
6.1 别小看root_squash,更别随便开
我见过一个团队为了省事,在一台开发机上把导出目录配置成no_root_squash,理由是"开发环境无所谓"。结果一个同事在客户端上误执行了rm -rf某个共享目录下的缓存文件,因为 root 身份直接穿透到了服务端,服务端上的原始数据也被删了。开发环境的数据也是数据,这句"无所谓"代价不低。
我现在的策略是:默认全部用root_squash,普通共享目录用all_squash映射成某个专有账号。只有极少数无盘启动、需要管理整个根文件系统的场景,才考虑no_root_squash,而且必须通过防火墙限制严格从哪些机器访问。
6.2 关于rsize/wsize,别被玄学调参带偏
网上关于 NFS 调参的教程非常多,一上来就是让你把 rsize/wsize 从 64K 改成 1M。但实际上新版内核已经默认 1M 了,老内核里改一改确实有效果,新环境里你再去改这些参数,不仅收益甚微,还可能因为和服务器端的max_block_size不匹配导致性能下降。我后来基本只在遇到具体瓶颈时才去抓包分析,否则不动这两个参数。
真正值得投入精力的是服务端 nfsd 线程数和网络质量。线程数不够,客户端再多带宽也跑不满;网络上乱丢包,调再大的包只会放大重传成本。
6.3 autofs里的共享目录缓存问题
有一个细节很多人不注意:autofs 挂载的目录如果被某些常驻服务(比如 inotify 文件监控)盯上了,就可能永远不会超时卸载,因为监控进程一直打开着目录。这时候--timeout形同虚设,远程目录会一直挂在客户端上,占用文件句柄。
我的建议是:对这类服务,要么明确把它需要监控的目录从 autofs 改成静态挂载,要么在设计业务时就避免长时间持有文件句柄。另外,如果你发现 autofs 卸载了但进程还能访问目录内文件,那通常不是 autofs 的问题——打开的文件句柄在进程关闭前一直有效,这是 Unix 的标准语义,不是 bug。
6.4 一台服务器被多客户端高强度访问时的软硬件配合
最后说一个经验层面的感受。NFS 的性能上限,往往不取决于 NFS 本身,而取决于存储介质和网络。HDD 的随机 IO 能力再强,多客户端同时读写产生的 IOPS 也会把它压垮;换成 SSD/NVMe 后,同样一套配置性能能翻几倍。网络方面,万兆网卡 + 低延迟交换机对 NFS 这种同步写模型尤其友好。
如果预算允许,尽量给 NFS 服务端配独立网卡和独立存储卷,别跟业务流量抢资源。很多"NFS 很慢"的抱怨,最后查下来都是服务端网卡中断被 CPU 单核打满、或者磁盘 io.bytes 被日志写满导致的。硬件资源隔离比调任何参数都管用。
另外,如果你要把这套方案拉入更严格的容灾体系,建议往 Kerberos(sec=krb5p)、多路径(NFS multipathing)、以及上层高可用(比如把 NFS 挂在共享存储上配合 HA 软件)的方向去扩展。这些内容每一块都够单独写一篇,我在这里就不展开了。先把基础的 NFS + autofs 跑稳,才是性价比最高的事。