网站从一台服务器扩到三台,问题立刻来了:用户在 A 机器上传了头像,下次请求被负载均衡分到 B 机器,图片 404。数据库可以统一,文件却散在三台机器的本地磁盘上。解决办法是把文件从"每台机器各存一份"变成"所有机器访问同一份"——也就是搭建共享文件存储。Linux 环境下最主流的两套方案是 NFS 和 Samba:前者是 Linux/Unix 世界的原生方案,性能高、权限语义原生;后者实现 SMB/CIFS 协议,是 Windows 环境下的标准,也常被需要跨平台访问的场景选用。本文从零搭建两套服务,讲清配置项的真实含义和常见故障的排查方法。
一、先选方案:NFS 还是 Samba
| 对比项 | NFS | Samba (SMB/CIFS) |
|---|---|---|
| 主要场景 | Linux/Unix 服务器之间共享 | Linux 与 Windows 混用,或需 Windows 客户端访问 |
| 协议与端口 | NFSv4 主要用 2049/TCP;NFSv3 还需 rpcbind 111 及动态端口 | 445/TCP(现代);老版本还需 137-139 |
| 权限模型 | 基于 UID/GID,两端用户 ID 必须一致 | 基于用户名与密码认证,映射到系统用户 |
| 认证方式 | 基于 IP/网段授权(NFSv4 可加 Kerberos) | 用户名密码、域认证 |
| 性能 | 通常更高,内核态实现,开销小 | 用户态进程,开销略大,小文件性能差距更明显 |
| 配置复杂度 | 简单,但防火墙与版本问题容易踩坑 | 稍复杂,权限映射与协议版本要调 |
| 典型用途 | Web 集群共享上传目录、日志集中、K8s PV | 办公文件共享、跨平台访问、Windows 客户端挂载 |
结论优先:纯 Linux 集群选 NFS;有 Windows 客户端或要给用户提供"网上邻居"式访问选 Samba。两者可以同时部署在同一台机器上,共享同一个目录也没问题,但要注意两者的权限模型不同,混用时容易产生权限冲突。
二、NFS 服务端部署
2.1 安装与目录准备
# RHEL/CentOS/Rocky yum install -y nfs-utils rpcbind systemctl enable --now rpcbind nfs-server # Debian/Ubuntu apt-get install -y nfs-kernel-server systemctl enable --now nfs-server # 准备共享目录(放到独立的数据盘上,别用系统盘) mkdir -p /data/share chown -R nfsnobody:nfsnobody /data/share # 或统一为业务用户 chmod 775 /data/share lsblk -f /dev/vdb1 # 确认数据盘已挂载到 /data
2.2 /etc/exports 配置与字段含义
/etc/exports 一行定义一个导出,格式为:共享目录 客户端(选项1,选项2) 客户端2(选项)。
# 示例配置 vim /etc/exports /data/share 192.168.10.0/24(rw,sync,no_root_squash,no_subtree_check) /data/pub 192.168.10.0/24(ro,sync,root_squash,no_subtree_check) /data/backup 192.168.10.20(rw,sync,all_squash,anonuid=1001,anongid=1001) # 使配置生效(无需重启服务) exportfs -rv # 查看当前已导出的目录及生效选项 exportfs -v exportfs -s # 服务端自检:查看自己导出了什么 showmount -e localhost
| 选项 | 含义 | 建议 |
|---|---|---|
rw / ro | 读写 / 只读 | 按需,最小化授权 |
sync | 数据真正落盘后才向客户端返回成功,安全但慢 | 生产建议 sync,保证崩溃时不丢数据 |
async | 未落盘即返回,性能高但崩溃可能丢数据 | 仅用于可重建的数据,谨慎使用 |
root_squash | 把客户端 root(uid 0)映射为匿名用户,防止客户端 root 拥有服务端 root 权限 | 默认值,安全选项,不要随便去掉 |
no_root_squash | 保留客户端 root 权限 | 危险,仅在受信任的内网且确有必要(如无盘工作站、某些虚拟化场景)时使用 |
all_squash | 把所有客户端用户都映射为匿名用户 | 配合 anonuid/anongid 统一属主,适合公共共享 |
anonuid / anongid | 指定被 squash 后映射到的 UID/GID | 配合 all_squash 使用,保证多客户端写入属主一致 |
subtree_check | 校验访问的文件确实位于导出的子目录内 | 文件被 rename 时可能出错,建议关闭 |
no_subtree_check | 不校验子树,性能更好、行为更稳定 | 推荐 |
insecure | 允许客户端使用大于 1024 的源端口 | 某些客户端(如部分 NAS、macOS)需要 |
fsid=0 | 在 NFSv4 中把该导出标记为根(用于伪文件系统) | NFSv4 多导出时需要合理规划 |
客户端地址可以写成网段(192.168.10.0/24)、单 IP、主机名或通配符域名(*.example.com)。不要写成 *,那等于对全网开放。
2.3 防火墙与端口
# NFSv4 只需放行 2049/TCP firewall-cmd --permanent --add-service=nfs firewall-cmd --permanent --add-service=rpc-bind firewall-cmd --permanent --add-service=mountd firewall-cmd --reload # 若仍使用 NFSv3,需要固定动态端口后才能精确放行 # 编辑 /etc/modprobe.d/lockd.conf:options lockd nlm_tcpport=32803 nlm_udpport=32769 # 编辑 /etc/sysconfig/nfs:RQUOTAD_PORT=875 MOUNTD_PORT=892 STATD_PORT=662 firewall-cmd --permanent --add-port=32803/tcp --add-port=32769/udp --add-port=892/tcp firewall-cmd --reload # 确认端口监听 ss -lntp | grep -E '2049|111|892'
三、NFS 客户端挂载
# 安装客户端工具 yum install -y nfs-utils # 查看服务端导出了哪些目录 showmount -e 192.168.10.10 # 手动挂载(指定 NFSv4) mkdir -p /mnt/share mount -t nfs -o nfsvers=4,hard,timeo=600,retrans=2,noatime 192.168.10.10:/data/share /mnt/share # 验证 df -h /mnt/share mount | grep nfs touch /mnt/share/.test && rm /mnt/share/.test # 写入 /etc/fstab 实现开机自动挂载 blkid 对该场景不适用(NFS 无 UUID),直接写远端路径: echo '192.168.10.10:/data/share /mnt/share nfs4 defaults,_netdev,nofail,hard,timeo=600,retrans=2 0 0' >> /etc/fstab mount -a
关键挂载选项:
hard:服务端不可达时无限重试,保证数据完整性。生产必须用 hard;用soft会在超时后返回错误,可能导致应用写入半截数据甚至文件损坏。timeo/retrans:单次超时时间(十分之一秒为单位,600 即 60 秒)与重传次数。_netdev:告诉系统这是网络设备,等网络就绪后再挂载,避免开机时因网络未起导致启动失败。nofail:挂载失败也不阻断系统启动。nfsvers=4:显式指定版本,避免协议协商带来的不确定性。
四、Samba 服务端部署
4.1 安装与配置文件
# RHEL/CentOS/Rocky yum install -y samba samba-client systemctl enable --now smb nmb # Debian/Ubuntu apt-get install -y samba samba-common-bin smbclient systemctl enable --now smbd nmbd # 备份并编辑主配置 cp /etc/samba/smb.conf /etc/samba/smb.conf.bak vim /etc/samba/smb.conf
下面是 /etc/samba/smb.conf 的共享段示例,注释说明每个字段:
[global]
workgroup = WORKGROUP
server string = File Server
security = user # 用户级认证(AD 环境用 security = ads)
passdb backend = tdbsam
# 协议版本:禁用古老的 SMB1,最低用 SMB2,推荐 3.0 及以上
server min protocol = SMB2
client min protocol = SMB2
log file = /var/log/samba/log.%m
max log size = 1024
[share]
comment = Shared Data
path = /data/samba/share
browseable = yes
read only = no # 等价于 writable = yes
guest ok = no # 禁止匿名访问
valid users = smbuser @smbgroup # 只允许指定用户/组(组前加 @)
write list = smbuser
create mask = 0664 # 新建文件的权限掩码
directory mask = 0775 # 新建目录的权限掩码
force user = smbuser # 强制文件属主,避免多用户写入混乱
force group = smbgroup
# 回收站(可选,防止误删)
vfs objects = recycle
recycle:repository = .recycle
recycle:keeptree = yes
4.2 创建用户与生效配置
# 创建系统用户(Samba 用户必须是已存在的系统用户) useradd -M -s /sbin/nologin smbuser # 添加 Samba 密码(与系统密码独立,单独维护) smbpasswd -a smbuser smbpasswd -e smbuser # 启用该用户 pdbedit -L # 列出所有 Samba 用户 # 准备目录与权限 mkdir -p /data/samba/share chown -R smbuser:smbgroup /data/samba/share chmod 775 /data/samba/share # 校验配置文件语法(务必执行) testparm # 重载配置(不中断现有连接) systemctl reload smb # 或 smbcontrol all reload-config # 防火墙 firewall-cmd --permanent --add-service=samba firewall-cmd --reload # SELinux(RHEL 系常见坑) setsebool -P samba_enable_home_dirs on semanage fcontext -a -t samba_share_t '/data/samba(/.*)?' restorecon -Rv /data/samba
4.3 客户端访问与挂载
# 列出远端共享(不挂载,先探测)
smbclient -L 192.168.10.10 -U smbuser
smbclient //192.168.10.10/share -U smbuser # 进入交互式操作
# Linux 客户端挂载 CIFS
yum install -y cifs-utils
mkdir -p /mnt/smb
mount -t cifs //192.168.10.10/share /mnt/smb \
-o username=smbuser,vers=3.0,iocharset=utf8,file_mode=0664,dir_mode=0775
# 更安全的做法:用凭证文件,避免密码出现在命令行与 history
cat > /etc/samba/cred-share <> /etc/fstab
mount -a
df -h /mnt/smb
vers=3.0 显式指定 SMB 协议版本很重要:不指定时客户端可能协商到已被废弃、且存在严重安全风险的 SMB1。Windows 客户端同理,现代系统已默认禁用 SMB1,不要为了兼容老设备去开它。
五、部署与验收步骤清单
- 规划共享目录位置:放在独立数据盘上,用
lsblk/df -h确认挂载点,避开系统盘。 - 确定协议:纯 Linux 用 NFS,有 Windows 用 Samba。
- 服务端安装软件包并启动服务,用
systemctl is-active确认状态。 - 配置
/etc/exports或/etc/samba/smb.conf,权限遵循最小化原则(能只读就只读,能限定 IP 就不放开网段)。 exportfs -rv或testparm校验配置,这一步能挡掉大部分语法错误。- 放行防火墙端口(NFS 2049,Samba 445;必要时 rpcbind)。
- RHEL 系处理 SELinux 上下文,否则访问会被静默拒绝。
- 客户端用
showmount -e或smbclient -L探测连通性,先探测再挂载。 - 手动挂载并做读写测试:创建文件、写入内容、删除文件。
- 统一 UID/GID(NFS 场景):确认所有客户端上业务用户的 UID 一致,否则权限会错乱。
- 写入
/etc/fstab,加_netdev与nofail,然后用mount -a验证。 - 模拟服务端重启与网络中断,验证客户端的
hard重试行为与业务表现。
小结与避坑建议
NFS 和 Samba 都是成熟的方案,出问题绝大多数不在软件本身,而在权限模型、防火墙、协议版本这三处。
避坑清单:
- NFS 权限错乱:NFS 认 UID 不认用户名。服务端文件的属主是 uid 1001,客户端上 uid 1001 是另一个用户,就会看到"文件属于陌生人"。解决方法是统一所有机器上的业务用户 UID/GID,或用
all_squash + anonuid/anongid统一映射。 - 滥用
no_root_squash:客户端 root 直接拥有服务端 root 权限,等于把服务端的 root 交出去。仅在充分信任且确有必要时用。 - 用
soft挂载 NFS:网络抖动时应用会收到 I/O 错误,可能造成数据损坏。生产一律hard。 - fstab 忘加
_netdev:开机时网络服务还没起来就去挂载网络文件系统,导致启动卡死。加上_netdev和nofail双保险。 - SELinux 静默拒绝:Samba 配置全对但就是访问不了,
/var/log/audit/audit.log里全是 AVC denied。用semanage fcontext设置正确的上下文类型。 - NFSv3 动态端口导致防火墙时通时不通:重启后 mountd 端口变了。要么固定端口,要么统一升级到 NFSv4 只放行 2049。
- SMB1 兼容问题:老客户端连不上时,正确做法是升级客户端,而不是在服务端开启 SMB1。
- 单点故障:一台 NFS 服务器挂了,所有依赖它的 Web 节点一起挂。生产环境要考虑服务端高可用(双机 + 漂移 IP,或改用分布式文件系统),并做定期备份。
- 直接用 root 挂载 CIFS 且密码写在命令行:密码会留在
history和ps输出里。用credentials文件并设置 600 权限。
最后,共享文件存储解决了"多台机器看到同一份数据"的问题,但没有解决"这份数据本身会不会丢"。所以共享目录之上仍然要有备份,且在搭建完成后做一次真实的故障演练——停掉服务端,观察业务是优雅降级还是整体崩溃。
本文为存储与硬盘管理系列,配置路径与参数可能因发行版版本不同而略有差异。
企业QQ咨询




