环境: Ubuntu 26.04(服务端)+ Ubuntu 24.04(客户端 cygnus),千兆局域网。共享目录
/lyra-share,用途是 Flux LoRA 训练的数据集 / latent 缓存 / checkpoint。两台机器 zj 的 UID 不一致(服务端 1000,客户端 1002)。
两台机器一起跑训练,数据集要放共享盘。三种主流方案 NFS / SSHFS / Samba 各自都有"看着能跑、上去就疼"的地方,这篇按踩坑顺序把每种的取舍写清楚。最终我用的是 Samba(SMB3),理由放最后讲。
性能基准:对训练影响多大
千兆同机房环境下三种方案的实测对比:
| 维度 | NFS | Samba(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 训练这种场景:
- 小数据集(50-500 张):三种都行,第一个 epoch 慢一点,之后 OS page cache 吃掉差距。
- 缓存的 latents(
.npz/.safetensors):每 epoch 顺序读,SSHFS 比 NFS / Samba 慢 30-50%,batch 大、GPU 快(4090 / H100)时容易成瓶颈。 - 大数据集(数千张)+ 不缓存 latents:SSHFS 的元数据延迟会拖死 dataloader,只能 NFS / Samba。
- 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/exports 用 root_squash(默认):
/lyra-share 192.168.2.147(rw,sync,no_subtree_check,root_squash)
效果:
- 客户端 zj(UID 1002)→ ACL 放行 ✅
- 客户端其它用户(其它 UID)→ 既不是 owner、不在 ACL → 拒绝 ✅
- 服务端本地 zj(UID 1000)→ owner ✅
- 客户端 root → root_squash 变 nobody → 挡住 ✅
但客户端 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
几个关键选项的含义:
cache=strict:开客户端缓存,训练顺序读速度更稳。mfsymlinks:支持符号链接,sd-scripts / diffusers 偶尔会用。vers=3.1.1:现代 SMB3,支持加密和 multichannel。uid=1002,gid=1002:文件本地呈现为 zj,直接绕过 UID 不一致问题。_netdev,nofail,x-systemd.automount:等网络就绪、失败不阻塞启动、首次访问时再真正挂载。
生效:
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(见上一节)。
小结
- 同一管理域、追求极致性能:NFS + ACL,可以接受 UID 模型的限制。
- 多用户客户机、要用户级鉴权:Samba,挂载时
uid=直接抹平 UID 不一致。 - 数据集小、对安全性更敏感:SSHFS 加
Ciphers=aes128-gcm@openssh.com,kernel_cache,compression=no也能凑合,但训练 IO 重的话别用。 - 任何方案都挡不住本机 root——这条永远成立。