- 为什么需要共享存储
- NFS 工作原理
- 服务端部署(192.168.2.100)
- 客户端部署(192.168.2.101)
- 排错实录:mount.nfs: No route to host
- 客户端永久挂载
- 服务端核心配置详解
- 用户压缩机制与实战案例
- 生产环境安全优化
为什么需要共享存储
当网站从单台服务器扩展到多台 Web 节点时,一个现实问题就出现了:用户上传了一张图片,请求落在了 Web-A 上,文件存到了 Web-A 的本地磁盘。下一次请求被负载均衡分到了 Web-B,Web-B 上根本没有这张图片,用户看到的就是一个裂图。
共享存储的目标很简单:让多台服务器像访问本地目录一样,共同读写同一份文件。
NFS(Network File System)就是 Linux 下最经典的共享存储方案。它通过网络把服务端的某个目录「挂载」到客户端的本地路径,对上层应用完全透明,就像操作本地磁盘一样。
NFS 工作原理
NFS 依赖 RPC(Remote Procedure Call) 机制工作。NFS 服务启动时会随机分配端口,然后向 RPC 服务(rpcbind)注册自己的端口信息。客户端要访问 NFS 时,先问 rpcbind 拿到端口号,再建立连接传输数据。
NFS 相关文件一览
| 文件路径 | 角色 | 说明 |
|---|---|---|
/etc/exports | 服务端 | NFS 主配置文件,定义共享目录和权限 |
/var/lib/nfs/etab | 服务端 | 当前运行时生效的完整配置 |
/etc/fstab | 客户端 | 永久挂载配置 |
/etc/rc.local | 客户端 | 开机自动执行的命令 |
/proc/mounts | 通用 | 系统当前实际挂载情况 |
NFS 的 systemctl 命令背后实际调用的是 exportfs 命令。启动时执行 exportfs -r 加载配置,停止时执行 exportfs -au 卸载所有共享。
服务端部署(192.168.2.100)
部署总览
安装 rpcbind 和 nfs-utils
yum install -y rpcbind nfs-utils
rpcbind 提供 RPC 端口映射服务,nfs-utils 是 NFS 核心工具集合。
创建共享目录并设置权限
# 创建共享目录 mkdir -p /data/ # 设置目录所有者为 nfsnobody chown -R nfsnobody.nfsnobody /data/
编辑配置文件 /etc/exports
/data/ 192.168.2.0/24(rw)
配置含义:允许 192.168.2.0/24 整个网段以读写权限访问服务端的 /data/ 目录。
按顺序启动服务
# 先启动 rpcbind systemctl enable rpcbind systemctl start rpcbind rpcinfo -p # 再启动 NFS systemctl enable nfs systemctl start nfs rpcinfo -p
服务端本地测试
# 本地挂载测试 mount -t nfs 192.168.2.100:/data/ /mnt/
/etc/exports 后,建议使用 systemctl reload nfs 重新加载配置。reload 是优雅重启,不会断开已有客户端连接。而 restart 会导致客户端出现短暂的夯住(卡顿无响应)。
客户端部署(192.168.2.101)
客户端只需要安装 nfs-utils,不需要启动 NFS 服务。
# 安装工具包 yum install -y nfs-utils # 挂载 NFS 共享目录到本地 /mnt mount -t nfs 192.168.2.100:/data /mnt
挂载成功后,可以通过以下命令确认:
[root@web ~]# grep /mnt /proc/mounts 192.168.2.100:/data /mnt nfs4 rw,relatime,vers=4.1,rsize=262144, wsize=262144,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys, clientaddr=192.168.2.101,local_lock=none,addr=192.168.2.100 0 0
此时在客户端 /mnt 下读写文件,数据实际存储在服务端 192.168.2.100 的 /data/ 磁盘上。
排错实录:mount.nfs: No route to host
在客户端执行挂载命令时,直接报错:
[root@web ~]# mount -t nfs 192.168.2.100:/data /mnt
mount.nfs: No route to host以下是完整的排查过程:
第一步:ping 测网络连通性
[root@web ~]# ping 192.168.2.100 64 bytes from 192.168.2.100: icmp_seq=1 ttl=64 time=0.556 ms 64 bytes from 192.168.2.100: icmp_seq=2 ttl=64 time=0.573 ms 64 bytes from 192.168.2.100: icmp_seq=3 ttl=64 time=0.501 ms ✓ 能 ping 通,网络层没问题
第二步:nc 测端口连通性
[root@web ~]# nc -zv 192.168.2.100 2049 Ncat: Version 7.50 ( https://nmap.org/ncat ) Ncat: No route to host. ✗ 能 ping 通但端口不通 → 高度怀疑防火墙拦截
第三步:关闭客户端防火墙
[root@web ~]# systemctl stop firewalld [root@web ~]# systemctl disable firewalld Removed symlink ...firewalld.service # 关闭后再测 → 仍然不通 [root@web ~]# nc -zv 192.168.2.100 2049 Ncat: No route to host.
客户端防火墙关了还是不通,说明问题在服务端。
第四步:关闭服务端防火墙
[root@nfs ~]# systemctl stop firewalld [root@nfs ~]# systemctl disable firewalld Removed symlink ...firewalld.service
第五步:验证通过
# 端口通了 [root@web ~]# nc -zv 192.168.2.100 2049 Ncat: Connected to 192.168.2.100:2049. # 挂载成功 [root@web ~]# mount -t nfs 192.168.2.100:/data /mnt [root@web ~]# ✓
firewall-cmd --add-service=nfs --permanent 放行 NFS 服务,或针对 111、2049 端口配置白名单。
排错工具速查
| 命令 | 用途 |
|---|---|
rpcinfo -p IP | 检查目标主机的 RPC 注册信息,确认 NFS 是否已注册 |
showmount -e IP | 查看 NFS 服务端共享了哪些目录 |
cat /proc/mounts | 查看当前系统的真实挂载情况 |
nc -zv IP 2049 | 测试 NFS 端口连通性 |
客户端永久挂载
直接执行 mount 是临时挂载,服务器重启后就会丢失。要实现开机自动挂载,有两种方式:
方式一:写入 /etc/rc.local
# 先确保 rc.local 有执行权限 chmod +x /etc/rc.d/rc.local # 追加挂载命令 echo "mount -t nfs 192.168.2.100:/data /mnt" >> /etc/rc.local
方式二:写入 /etc/fstab(推荐)
# 创建挂载点 [root@web ~]# mkdir /upload/ # 编辑 fstab [root@web ~]# vi /etc/fstab # 在文件末尾添加以下内容,保存退出(:wq)
fstab 格式说明:
| 设备 | 挂载点 | 文件系统 | 挂载参数 | 是否备份 | 是否检查 |
|---|---|---|---|---|---|
192.168.2.100:/data/ |
/upload/ |
nfs |
defaults |
0 |
0 |
df -h 也会夯住。此时可通过 /proc/mounts 排查失败挂载。所以请记住:配了永久挂载,未来开机要优先启动 NFS 服务端。
服务端核心配置详解
配置文件格式
/etc/exports 每行定义一条共享规则,格式为:
# 共享目录 网段(选项1,选项2,...) /data/ 192.168.2.0/24(rw,sync)
核心选项
| 选项 | 说明 |
|---|---|
rw | 读写权限,客户端可以读取和写入 |
ro | 只读权限(read only),客户端只能读取 |
sync | 同步模式。用户上传数据后,NFS 立即写入磁盘,数据安全但性能稍低 |
async | 异步模式。数据先缓存在内存中,过一段时间再写入磁盘。高并发性能好,但突然断电可能丢数据 |
用户压缩机制与实战案例
NFS 客户端挂载共享目录后,客户端用户在目录中创建文件,这个文件在服务端上属于谁?这就是用户压缩(Squash)要解决的问题。
简单说:客户端用户 → 经过 NFS 映射 → 变成服务端上的某个用户。默认情况下,客户端 root 用户会被「压缩」为服务端的 nfsnobody 用户。
用户压缩选项
| 选项 | 说明 |
|---|---|
root_squash | 客户端 root 到服务端变成 nfsnobody(默认开启) |
no_all_squash | 客户端非 root 用户保持原始身份不压缩(默认开启) |
all_squash | 所有用户都压缩为匿名用户 |
anonuid | 指定匿名用户的 UID(默认 65534 即 nfsnobody) |
anongid | 指定匿名用户的 GID(默认 65534 即 nfsnobody) |
客户端普通用户 → 服务端保持原用户(前提是服务端存在相同 UID 的用户)
实战案例:自定义匿名用户 whqzn
需求:新建 /nfsdata 共享目录,所有客户端用户创建的文件都归属 whqzn 用户(UID=1999, GID=1999),客户端挂载到 /upload-video/。
所有主机统一创建用户(UID/GID 必须一致)
groupadd -g 1999 whqzn useradd -u 1999 -g whqzn -s /sbin/nologin -M whqzn
服务端配置 /etc/exports
/data/ 192.168.2.0/24(rw) /nfsdata/ 192.168.2.0/24(rw,all_squash,anonuid=1999,anongid=1999)
关键配置解读:all_squash 把所有用户都压缩,anonuid=1999,anongid=1999 指定压缩后的身份为 whqzn。
客户端挂载并验证
# 挂载 mount -t nfs 192.168.2.100:/nfsdata /upload-video/ # 创建测试文件 touch /upload-video/test.txt # 查看文件属主 → 应该是 whqzn ll /upload-video/ -rw-r--r-- 1 whqzn whqzn 0 Apr 26 10:00 test.txt
生产环境安全优化
客户端安全挂载选项
生产环境中,NFS 共享目录通常只用来存储上传文件(图片、视频等),不应该允许执行程序。通过挂载选项加固安全:
mount -o noexec,nosuid,nodev -t nfs 192.168.2.100:/data /video/
| 选项 | 作用 |
|---|---|
noexec | 目录中的可执行文件无法运行,防止上传恶意脚本被执行 |
nosuid | 忽略文件的 SUID 权限位,防止通过共享目录提权 |
nodev | 忽略设备文件,防止利用特殊设备文件攻击系统 |
NFS 的局限与替代方案
NFS 最大的问题是单点故障:服务端宕机,所有客户端的共享目录都不可用。在生产环境中,根据业务规模可以考虑以下替代方案:
- 公有云对象存储:如阿里云 OSS、腾讯云 COS,通过 SDK 在代码中直接调用,天然高可用
- 分布式存储:GlusterFS(GFS)、MinIO 等,数据分布在多个节点,没有单点问题
- 硬件优化:如果坚持用 NFS,建议使用物理服务器 + SSD 磁盘,提升 IO 性能
全文核心总结
- 服务端配置文件
/etc/exports的格式和选项 - 客户端永久挂载的两种方式(rc.local / fstab),以及永久挂载后要优先启动服务端
- 用户压缩机制:all_squash + anonuid/anongid 控制文件归属