服务器如何搭建共享文件存储?NFS与Samba部署实战

网站从一台服务器扩到三台,问题立刻来了:用户在 A 机器上传了头像,下次请求被负载均衡分到 B 机器,图片 404。数据库可以统一,文件却散在三台机器的本地磁盘上。解决办法是把文件从"每台机器各存一份"变成"所有机器访问同一份"——也就是搭建共享文件存储。Linux 环境下最主流的两套方案是 NFS 和 Samba:前者是 Linux/Unix 世界的原生方案,性能高、权限语义原生;后者实现 SMB/CIFS 协议,是 Windows 环境下的标准,也常被需要跨平台访问的场景选用。本文从零搭建两套服务,讲清配置项的真实含义和常见故障的排查方法。

一、先选方案:NFS 还是 Samba

对比项NFSSamba (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,不要为了兼容老设备去开它。

五、部署与验收步骤清单

  1. 规划共享目录位置:放在独立数据盘上,用 lsblk/df -h 确认挂载点,避开系统盘。
  2. 确定协议:纯 Linux 用 NFS,有 Windows 用 Samba。
  3. 服务端安装软件包并启动服务,用 systemctl is-active 确认状态。
  4. 配置 /etc/exports 或 /etc/samba/smb.conf,权限遵循最小化原则(能只读就只读,能限定 IP 就不放开网段)。
  5. exportfs -rv 或 testparm 校验配置,这一步能挡掉大部分语法错误。
  6. 放行防火墙端口(NFS 2049,Samba 445;必要时 rpcbind)。
  7. RHEL 系处理 SELinux 上下文,否则访问会被静默拒绝。
  8. 客户端用 showmount -e 或 smbclient -L 探测连通性,先探测再挂载。
  9. 手动挂载并做读写测试:创建文件、写入内容、删除文件。
  10. 统一 UID/GID(NFS 场景):确认所有客户端上业务用户的 UID 一致,否则权限会错乱。
  11. 写入 /etc/fstab,加 _netdev 与 nofail,然后用 mount -a 验证。
  12. 模拟服务端重启与网络中断,验证客户端的 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 权限。

最后,共享文件存储解决了"多台机器看到同一份数据"的问题,但没有解决"这份数据本身会不会丢"。所以共享目录之上仍然要有备份,且在搭建完成后做一次真实的故障演练——停掉服务端,观察业务是优雅降级还是整体崩溃。


本文为存储与硬盘管理系列,配置路径与参数可能因发行版版本不同而略有差异。