排查 flock 在 NFSv3 上报 No locks available 而在 NFSv4 正常的问题
最近在一套 NFS 挂载环境里,我们遇到了一个很容易误判的问题:客户端对挂载目录里的文件执行 flock 时,直接报 No locks available;但把挂载卸掉重挂之后,flock 又突然恢复正常。
这个现象第一眼很容易让人怀疑是“客户端偶发异常”或者“文件已经被别人锁住了”,但继续往下排查会发现,真正的根因既不是 flock 命令本身,也不是 local_lock=none 这个挂载参数,而是 NFSv3 锁依赖的服务端 rpc-statd / status 没有正常提供。更容易让人误判的是,后面那次“突然恢复”其实并不是原问题自己好了,而是客户端在未显式指定版本时自动挂成了 NFSv4,走了另一套锁模型。本文把这次排查过程整理下来。
背景
客户端把服务端 10.0.1.44:/zgq-storage/zgq-fastone 挂到本地 /fs,最初看到的挂载信息类似下面这样:
10.0.1.44:/zgq-storage/zgq-fastone on /fs type nfs (
rw,relatime,vers=3,...,local_lock=none,addr=10.0.1.44)
业务侧需要依赖 flock 做文件级互斥,所以当下面这条最基本的命令都失败时,问题就必须先查清楚:
sudo flock /fs/123 ls
故障现象
在 NFSv3 挂载下,flock 的报错非常直接:
flock: /fs/123: No locks available
这类报错有几个很容易让人误判的地方:
- 从字面看,容易被理解成“当前文件已经没有可用锁了”。
- 挂载参数里有
local_lock=none,容易进一步被理解成“是不是客户端禁用了锁”。 - 后面如果执行一次不带
vers=3的重新挂载,又可能发现flock突然恢复正常,看起来像是“卸载重挂就好了”。
但这几个直觉解释,其实都不是最终根因。
排查过程
1. 先确认客户端锁服务是否正常
因为当前挂载是 NFSv3,所以第一步先检查客户端自己的 rpcbind、rpc-statd 和 nlockmgr:
systemctl status rpcbind rpc-statd
rpcinfo -p localhost | egrep 'status|nlockmgr'
客户端输出显示:
rpcbind正常运行rpc-statd正常运行- 本地已经注册了
status - 本地已经注册了
nlockmgr
也就是说,客户端本机这条锁调用链基本是完整的,问题更像是在服务端,或者在客户端到服务端之间的 RPC 锁链路上。
2. 检查服务端是否真的提供了 NFSv3 锁相关 RPC 程序
下一步从客户端检查服务端暴露的 RPC 程序:
rpcinfo -p 10.0.1.44 | egrep 'nfs|mountd|status|nlockmgr'
结果里可以看到:
nfs在mountd在nlockmgr在status不在
为了避免只看列表误判,又直接做了一次点对点检查:
rpcinfo -T tcp 10.0.1.44 status
返回结果是:
rpcinfo: RPC: Program not registered
这一步非常关键。它说明服务端虽然提供了 nlockmgr,但并没有注册 status,也就是 rpc-statd / NSM 这条链路缺失。
3. 回到服务端确认 rpc-statd 状态
登录到服务端后继续检查:
systemctl status rpcbind rpc-statd nfs-server
rpcinfo -p localhost | egrep 'nfs|mountd|status|nlockmgr'
服务端的实际状态是:
rpcbind正常nfs-server正常rpc-statd是 inactive (dead)rpcinfo -p localhost里只有nlockmgr,没有status
到这里,问题基本已经定位了:服务端的 NFSv3 锁服务链路不完整。
4. 为什么重新挂载后 flock 又好了
这个问题最容易让人绕进去。
当时卸载后执行的是:
sudo umount -lf /fs
sudo mount 10.0.1.44:/zgq-storage/zgq-fastone /fs
sudo mount | grep ' /fs '
结果发现新的挂载信息已经变成:
10.0.1.44:/zgq-storage/zgq-fastone on /fs type nfs4 (
rw,relatime,vers=4.2,...,local_lock=none,addr=10.0.1.44)
这说明后面“恢复正常”的那次,根本不是还在测 NFSv3,而是自动协商成了 NFSv4.2。
而 NFSv4 的文件锁是协议内建的,不再依赖 NFSv3 + nlockmgr + rpc-statd/status 这套旁路机制,所以 flock 自然就恢复正常了。
换句话说:
不是“卸载重挂修好了 NFSv3 锁”,而是“重新挂载后换成了 NFSv4,走了另一套锁模型”。
根因分析
1. local_lock=none 不是“禁用锁”
这次排查里,local_lock=none 很容易误导人。
它并不等价于“这个挂载点不能加锁”,而更接近:
- 不做本地伪锁
- 让锁请求走远端协议/服务端
所以在 NFSv4 挂载下,即使同样看到 local_lock=none,flock 仍然完全可以正常工作。
2. NFSv3 的锁并不只依赖 nlockmgr
如果只看服务端的 RPC 列表,看到 nlockmgr 已经在,很多人会下意识以为“那锁服务应该没问题”。
但 NFSv3 上的文件锁链路并不是只要 nlockmgr 就够了,它通常还依赖:
rpcbindnlockmgr/lockdrpc-statd/status(NSM)
这也是为什么服务端明明能正常导出 NFS、客户端也能正常读写文件,但 flock 还是会报:
No locks available
也就是说,这里坏的不是 NFS 数据读写本身,而是 NFSv3 的锁服务链路。
3. NFSv4 正常,并不代表 NFSv3 也没问题
这次故障的另一个迷惑点是:当客户端重新挂成 NFSv4.2 后,flock 立刻正常。
如果没有顺手检查 mount 输出,很容易得出错误结论:
看起来像是客户端偶发异常,重挂一下就好了。
但真实情况是:
NFSv3:锁依赖额外的 RPC 服务链路NFSv4:锁在协议内建
所以“v4 正常”只能说明 当前挂载协议下锁功能可用,并不能反推 “v3 那条链路没有问题”。
解决方案
如果必须继续使用 NFSv3,修复方式就是把服务端缺失的 rpc-statd 补起来。
在服务端执行:
systemctl enable --now rpc-statd
systemctl restart nfs-server
然后验证:
rpcinfo -T tcp localhost status
rpcinfo -p localhost | egrep 'status|nlockmgr'
修复后可以看到:
program 100024 version 1 ready and waiting
并且 rpcinfo -p localhost 里也出现了 status。
接着回到客户端再确认一次:
rpcinfo -T tcp 10.0.1.44 status
如果返回:
program 100024 version 1 ready and waiting
说明客户端已经能访问到服务端的 status 服务,NFSv3 锁链路里最关键的缺口已经补齐。
验证方式
1. 强制按 NFSv3 重新挂载再测
为了避免再次“误挂成 NFSv4”,建议显式指定版本重新测试:
sudo umount -lf /fs
sudo mount -t nfs -o vers=3,proto=tcp 10.0.1.44:/zgq-storage/zgq-fastone /fs
mount | grep ' /fs '
确认输出里明确包含:
type nfsvers=3
然后再执行:
sudo flock /fs/222 ls
2. 做一次跨节点互斥验证
比起只在单机上试一条 flock,更稳妥的方式是做一次双客户端互斥测试。
在机器 A 上:
flock /fs/lock.test sleep 300
在机器 B 上:
flock -w 3 /fs/lock.test true && echo got || echo blocked
如果机器 B 在等待超时后拿不到锁,说明跨节点锁互斥是生效的。
3. 注意防火墙与动态端口
这次修复后,status 和 nlockmgr 使用的端口是动态分配的,例如:
status:tcp 56683/udp 48715nlockmgr:tcp 33893/udp 50682
所以如果服务端重启后仍然异常,还要继续检查:
rpcbind(111)nfs(2049)mountdstatusnlockmgr
这些端口在客户端与服务端之间是否可达。实际生产里,最好把相关 RPC 端口固定下来,避免动态漂移带来新的问题。
小结
这次故障最终可以归纳成三点:
flock: No locks available在NFSv3场景下,首先要怀疑的是 锁服务链路不完整,而不只是“文件被锁住了”。local_lock=none并不等于“禁用文件锁”;真正的区别在于当前挂载到底是NFSv3还是NFSv4。- 如果业务确实依赖
flock,优先使用NFSv4往往更省心;如果必须使用NFSv3,那就必须同时保证rpc-statd/status、nlockmgr以及相关 RPC 端口都正常。
参考资料
man 5 nfsman 8 rpc.statdman 8 rpcinfoman 1 flock