← 返回首页

在多机 AI 训练里搭跨主机共享:NFS / SSHFS / Samba 的取舍与避坑

2026-05-26 · LinuxNFSSambaSSHFSAI 训练

环境: Ubuntu 26.04(服务端)+ Ubuntu 24.04(客户端 cygnus),千兆局域网。共享目录 /lyra-share,用途是 Flux LoRA 训练的数据集 / latent 缓存 / checkpoint。两台机器 zj 的 UID 不一致(服务端 1000,客户端 1002)。

两台机器一起跑训练,数据集要放共享盘。三种主流方案 NFS / SSHFS / Samba 各自都有"看着能跑、上去就疼"的地方,这篇按踩坑顺序把每种的取舍写清楚。最终我用的是 Samba(SMB3),理由放最后讲。

性能基准:对训练影响多大

千兆同机房环境下三种方案的实测对比:

维度NFSSamba(SMB3)SSHFS
顺序吞吐~110 MB/s(满速)~110 MB/s(multichannel 可破千兆)40-80 MB/s(AES + 单 TCP)
元数据(stat / open)快,~ms中等偏快慢,每次 RTT,几十 ms
多连接并发nconnect=4 / 8原生 multichannel单连接
小文件随机读明显劣势
用户级认证❌(信客户端传过来的 UID)✅(账号密码原生支持)✅(按 SSH 账号)

对 Flux LoRA 训练这种场景:

  1. 小数据集(50-500 张):三种都行,第一个 epoch 慢一点,之后 OS page cache 吃掉差距。
  2. 缓存的 latents(.npz / .safetensors):每 epoch 顺序读,SSHFS 比 NFS / Samba 慢 30-50%,batch 大、GPU 快(4090 / H100)时容易成瓶颈。
  3. 大数据集(数千张)+ 不缓存 latents:SSHFS 的元数据延迟会拖死 dataloader,只能 NFS / Samba。
  4. checkpoint 写:LoRA 几十~几百 MB,偶发写,影响可忽略。

最稳的实战做法其实是训练前 rsync 把数据集拉到本地 SSD,共享盘只用来分发原始数据和回传 checkpoint。下面假设你不能这么干、必须直接挂着用。

NFS 的核心痛点:它不认人,只认 UID

nfs-kernel-server 装好、/etc/exports 写好就能跑,但你立刻会撞上两个客户端机器 UID 不一致的问题:服务端的 zj 是 1000,客户端是 1002,客户端写文件,到服务端看属主就是 “1002”——一个根本不存在的用户。

四种处理方式,优劣分明:

方案 A:all_squash + anonuid(最简单)

让客户端所有用户都映射成服务端的 zj:

/lyra-share 192.168.2.147(rw,sync,no_subtree_check,all_squash,anonuid=1000,anongid=1000)

任何 UID 写入,到服务端都变成 zj:zj。缺点:丢失客户端的原始 UID 区分,如果客户端有多个用户访问会全部挤成一个身份。

方案 B:强制对齐 UID(最干净)

任选一边改:

# 客户端 2.147 上把 zj 改成 1000(前提:1000 没被占用)
sudo usermod -u 1000 zj
sudo groupmod -g 1000 zj
sudo find / -xdev -uid 1002 -exec chown -h 1000 {} \;
sudo find / -xdev -gid 1002 -exec chgrp -h 1000 {} \;

之后 NFS 直接用即可。问题是 find / 会扫整盘,有 systemd unit / cron 持有旧 UID 的话还要单独处理。

方案 C:NFSv4 idmapping

理论上按用户名映射,但默认 sec=sys 下 Linux 内核仍按数字 UID 传递,只有启用 Kerberos(sec=krb5)才真生效。家用场景上 Kerberos 性价比太低,不推荐。

方案 D:换 Samba——直接绕开 NFS 的 UID 模型,见下文。

怎么挡掉另一台机器的其它用户

NFS 服务端按 IP 授权,只列 192.168.2.147 时,2.134 mount 会被拒绝。这一层默认就够。但你没法挡住 2.147 这台机器上的其它本地用户访问已挂载的 /lyra-share——NFS 不知道客户端是谁在 read,只看 UID。

可以靠服务端文件权限 + ACL精确放行客户端 zj 的 UID:

# 1) 目录归 zj,750 挡掉 others
sudo chown zj:zj /lyra-share
sudo chmod 750 /lyra-share

# 2) ACL 仅放行客户端 zj 的 UID(服务端无需此用户存在)
sudo setfacl -m u:1002:rwx /lyra-share
sudo setfacl -dm u:1002:rwx /lyra-share   # 默认 ACL,新建文件继承

/etc/exportsroot_squash(默认):

/lyra-share 192.168.2.147(rw,sync,no_subtree_check,root_squash)

效果:

但客户端 root 仍能 su - anyuser 伪造任何 UID(包括 1002),NFS 服务端无法分辨。要真挡客户端 root,只能上 Kerberos 或换 SSHFS。

一个反直觉的事实:本机 root 总能读你的挂载

这件事单独说一下,我一开始也搞错了:

挂载方式本机 root 直接 cat 能读?
Samba / CIFS(系统挂载)✅ root 绕过 DAC,uid=1002,mode=0700 对 root 无效
NFS(系统挂载)✅ 同上
SSHFS(默认无 allow_other)❌ FUSE 层会挡,root 也得 EACCES
SSHFS + allow_other✅ 一旦开了就没保护

唯一表面上能挡 root 的就是 SSHFS 默认模式——FUSE 只对挂载用户可见。但本机 root 想绕开方法多得是:umount 你的挂载用自己的凭据重挂、ptrace 你的进程偷会话、cat ~zj/.smbcred~zj/.ssh/id_*nsenter 进你的命名空间、加载内核模块读 /dev/mem、改 PAM 配置等下次登录截胡密码……

真要防本机 root,只能在挂载点上再叠 gocryptfs / fscrypt 这层用户态加密,密钥只在你的会话里解锁。即便这样,root 仍可 ptrace 你的进程拿密钥——只能防"懒 root",防不住"硬 root"。

结论很简单:别把不可信节点的 root 当作可控因素。如果 2.147 的 root 是别人管的,只能换机器或者放虚拟机/容器里跑。

Samba 才是这个场景的最佳解

回头看需求:性能要够 + 用户级认证 + UID 不一致也得能用。三个全打中的只有 Samba。SMB3 协议自己鉴权,客户端要挂载必须有账号密码(或 credentials 文件),挂载选项里 uid=1002,gid=1002 直接把所有文件呈现成本地 zj,UID 问题自然消解。

服务端最小配置(/etc/samba/smb.conf 末尾追加):

[lyra-share]
   path = /lyra-share
   valid users = zj
   writable = yes
   create mask = 0664
   directory mask = 0775

然后:

sudo smbpasswd -a zj
sudo systemctl restart smbd

客户端持久化挂载,凭据走独立文件,不裸露在 fstab 里:

sudo apt install -y cifs-utils
sudo tee /etc/samba/lyra.cred >/dev/null <<EOF
username=zj
password=YOUR_PASSWORD
EOF
sudo chmod 600 /etc/samba/lyra.cred

/etc/fstab 加一行:

//192.168.2.177/lyra-share  /home/zj/lyra-share  cifs  credentials=/etc/samba/lyra.cred,uid=1002,gid=1002,vers=3.1.1,iocharset=utf8,cache=strict,mfsymlinks,_netdev,nofail,x-systemd.automount  0  0

几个关键选项的含义:

生效:

sudo systemctl daemon-reload
sudo mount -a

等价 net use 的写法,留个备查

很多人是 Windows 那边过来的,这里给一条 Linux 等价命令对照:

REM Windows
net use m: \\192.168.2.177\lyra-share PASSWORD /user:zj /persistent:yes

对应 Linux 一次性挂载:

sudo mkdir -p /mnt/lyra-share
sudo mount -t cifs //192.168.2.177/lyra-share /mnt/lyra-share \
  -o username=zj,password=PASSWORD,uid=$(id -u),gid=$(id -g),vers=3.1.1

“持久化"等价于把这条挂载写进 /etc/fstab(见上一节)。

小结