macOS系统 NFS v4 服务
我在 Colima mountType 9p 实践中遇到的I/O性能问题让我非常困扰,即使做了 cpuType 的微调,但依然让我无法忍受。和gemini讨论之后,我决定放弃 Colima 内置的 sshfs 和 9p mountType ,改为采用 Colima使用NFS共享存储 来实现共享存储,以期能够提升容器的I/O性能。
macOS 已经完全内置了极为成熟的 NFS(包括 v2, v3, v4)服务端和客户端软件堆栈,所以无需通过 Homebrew 安装任何额外软件就可以完成配置。
警告
虽然我反复尝试,但是我还是没有解决NFS v4输出,所以我最终啊还是回退到 macOS系统 NFS v3 服务
NFS说明
NFS v3 的行为:传统的 NFS v3 需要依赖 rpcbind(Portmapper)服务。它会随机动态分配一堆端口(如 2049, 111, 锁管理器端口等)。
NFS v4 的进化:NFS v4 规范高度简化了网络结构,只需要固定监听
2049端口,完全脱离了对rpcbind的依赖。macOS 内核内置的 nfsd 在提供 NFS v4 服务时,默认依然固执地去寻找 rpcbind。如果你在 Colima 中强制指定 nfsvers=4.1 挂载,而 Mac 端没有做特殊配置,内核会因为端口握手死锁,导致挂载直接卡死或报 Connection refused。
配置
配置
/etc/nfs.conf开启NFS v4支持,并设置只监听2049端口
# 强制开启 NFS v4 服务端支持
nfs.server.v4 = 1
# 核心排毒:强制让 macOS 的 NFSv4 彻底脱离 rpcbind 依赖,只监听标准的 2049 端口
nfs.server.mount.require_resvport = 0
更为优化的性能配置:
# =========================================================================
# macOS 内核 NFSv4 高性能基础设施调优
# =========================================================================
# 1. 强开 NFSv4 支持,并锁定为仅使用 v4 协议,彻底关闭 v2/v3 的旧端口(111, 888, 965)
nfs.server.v4 = 1
nfs.server.v4.only = 1
# 2. 核心安全放行:允许来自非特权端口(>1024)的连接请求
# 配合虚拟机端的挂载参数,双重确保不被 Mac 内核的安全洁癖 Reset
nfs.server.mount.require_resvport = 0
# 3. 狂暴多线程优化:将内核 nfsd 守护线程池从默认的 4-8 个强行扩容至 124 个
# 彻底解决多并发编译(如 make -j、go build)或大规模小文件扫描时因线程排队导致的 I/O 便秘
nfs.server.async = 1
nfs.server.threads = 124
# 4. 拓宽内核 TCP 读写泵血管道:将发送与接收队列缓冲区提升至 4MB (4194304 字节)
# 完美匹配客户端 rsize/wsize=1M 级别的超大吞吐量,杜绝高频 I/O 时的内核丢包和重传
nfs.server.tcp.send_quota = 4194304
nfs.server.tcp.recv_quota = 4194304
配置
/etc/exports
# -mapall=501:20 强行透明映射权限为 Mac 当前用户,解决权限夹生
# -async 开启极致的内存异步刷盘
/Users/admin -mapall=501:20 -async -network 192.168.5.0 -mask 255.255.255.0
重启nfsd
# 强行停止、清洗并重启 Mac 内置的 NFS 守护进程
sudo nfsd stop
sudo killall -9 nfsd 2>/dev/null || true
sudo nfsd start
检查nfsd状态
sudo nfsd status
输出显示
nfsd service is enabled
nfsd is running (pid 98571, 8 threads)
检查 rpcinfo -p 输出可以看到 2049 端口已经处于正常的监听状态
program vers proto port
100000 2 udp 111 rpcbind
100000 3 udp 111 rpcbind
100000 4 udp 111 rpcbind
100000 2 tcp 111 rpcbind
100000 3 tcp 111 rpcbind
100000 4 tcp 111 rpcbind
100024 1 udp 766 status
100021 0 udp 842 nlockmgr
100024 1 tcp 1021 status
100021 1 udp 842 nlockmgr
100021 3 udp 842 nlockmgr
100021 4 udp 842 nlockmgr
100021 0 tcp 1017 nlockmgr
100021 1 tcp 1017 nlockmgr
100021 3 tcp 1017 nlockmgr
100021 4 tcp 1017 nlockmgr
100003 2 udp 2049 nfs
100003 3 udp 2049 nfs
100003 2 tcp 2049 nfs
100003 3 tcp 2049 nfs
100005 1 udp 940 mountd
100005 3 udp 940 mountd
100005 1 tcp 993 mountd
100005 3 tcp 993 mountd
100011 1 udp 969 rquotad
100011 2 udp 969 rquotad
100011 1 tcp 983 rquotad
100011 2 tcp 983 rquotad
奇怪,为何 nfsd 显示的版本居然还是 v2 和 v3 ,并没有看到 v4 并且,并没有如预期的那样只启动NFS v4的 nfsd 和 rpcbind 并关闭掉 v2/v3 的不再需要的服务
gemini提供了解释: NFSv3 和 NFSv4 在文件系统导出上的底层设计有本质区别
NFSv3:传统的离散导出,给出
/Users/admin,就会直接把这个绝对路径暴露出去NFSv4: 伪根文件系统(Pseudo-root file system)约束 NFSv4 要求服务端必须构建一个单一的、虚拟的全局目录树。所有的导出路径,都必须是这个
伪根(/)(Pseudo-root)目录下的子目录
所以不需要修订 /etc/nfs.conf ,而是需要修订 /etc/exports 增加一个 伪根
修改
/etc/exports:
# 使用 -V4 参数宣告整个全局伪根(Pseudo-root)。这里我们直接将 /Users 设为 v4 的虚拟根
-V4: /Users
# -mapall=501:20 强行透明映射权限为 Mac 当前用户,解决权限夹生
# -async 开启极致的内存异步刷盘
# 导出真实的物理子目录。注意,它必须在上面定义的伪根路径范围内
/Users/admin -mapall=501:20 -async -network 192.168.106.0 -mask 255.255.255.0
重启nfsd
# 强行停止、清洗并重启 Mac 内置的 NFS 守护进程
sudo nfsd stop
sudo killall -9 nfsd 2>/dev/null || true
sudo nfsd start