macOS系统 NFS v4 服务

我在 Colima mountType 9p 实践中遇到的I/O性能问题让我非常困扰,即使做了 cpuType 的微调,但依然让我无法忍受。和gemini讨论之后,我决定放弃 Colima 内置的 sshfs9p 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 v4 服务端支持
nfs.server.v4 = 1

# 核心排毒:强制让 macOS 的 NFSv4 彻底脱离 rpcbind 依赖,只监听标准的 2049 端口
nfs.server.mount.require_resvport = 0

更为优化的性能配置:

开启NFS v4的优化配置
# =========================================================================
# 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

输出 /Users/admin 目录
# -mapall=501:20 强行透明映射权限为 Mac 当前用户,解决权限夹生
# -async 开启极致的内存异步刷盘
/Users/admin -mapall=501:20 -async -network 192.168.5.0 -mask 255.255.255.0
  • 重启nfsd

重启nfsd
# 强行停止、清洗并重启 Mac 内置的 NFS 守护进程
sudo nfsd stop
sudo killall -9 nfsd 2>/dev/null || true
sudo nfsd start
  • 检查nfsd状态

检查nfsd状态
sudo nfsd status

输出显示

检查nfsd状态,可以看到nfsd运行中
nfsd service is enabled
nfsd is running (pid 98571, 8 threads)

检查 rpcinfo -p 输出可以看到 2049 端口已经处于正常的监听状态

检查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的 nfsdrpcbind 并关闭掉 v2/v3 的不再需要的服务

gemini提供了解释: NFSv3 和 NFSv4 在文件系统导出上的底层设计有本质区别

  • NFSv3:传统的离散导出,给出 /Users/admin ,就会直接把这个绝对路径暴露出去

  • NFSv4: 伪根文件系统(Pseudo-root file system)约束 NFSv4 要求服务端必须构建一个单一的、虚拟的全局目录树。所有的导出路径,都必须是这个 伪根(/) (Pseudo-root)目录下的子目录

所以不需要修订 /etc/nfs.conf ,而是需要修订 /etc/exports 增加一个 伪根

  • 修改 /etc/exports :

添加"伪根"(pseudo-root)的/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

重启nfsd
# 强行停止、清洗并重启 Mac 内置的 NFS 守护进程
sudo nfsd stop
sudo killall -9 nfsd 2>/dev/null || true
sudo nfsd start