【Linux从入门到精通】第37篇:NFS网络文件系统——无状态的数据共享

目录

一、引言:当多台服务器需要同一份文件

二、NFS服务端配置

[2.1 安装NFS服务](#2.1 安装NFS服务)

[2.2 创建共享目录](#2.2 创建共享目录)

[2.3 配置exports文件](#2.3 配置exports文件)

[2.4 应用配置并启动](#2.4 应用配置并启动)

三、客户端挂载

[3.1 安装客户端工具](#3.1 安装客户端工具)

[3.2 查看服务端共享的目录](#3.2 查看服务端共享的目录)

[3.3 挂载到本地](#3.3 挂载到本地)

[3.4 开机自动挂载](#3.4 开机自动挂载)

[四、hard vs soft:NFS卡死的根源](#四、hard vs soft:NFS卡死的根源)

[4.1 hard模式(默认)](#4.1 hard模式(默认))

[4.2 soft模式](#4.2 soft模式)

[4.3 深入理解:为什么hard会导致D状态?](#4.3 深入理解:为什么hard会导致D状态?)

[4.4 如何选择?生产环境的折中方案](#4.4 如何选择?生产环境的折中方案)

五、NFS故障排查

[5.1 诊断命令](#5.1 诊断命令)

[5.2 常见问题](#5.2 常见问题)

六、现代替代方案简介

七、本篇小结

动手练习

八、下篇预告


一、引言:当多台服务器需要同一份文件

假设你的Web应用部署在3台服务器上,用户上传的头像需要所有服务器都能访问。你会怎么解决?

  • 方案一:每台服务器存一份?存储浪费,更难搞的是"数据一致性"------用户换头像后,三台机器上哪份是最新的?

  • 方案二:FTP/SCP手动同步?太慢,而且文件一多就乱套

  • 方案三 :用一台服务器专门存文件,其他服务器像访问本地目录一样访问它

方案三就是NFS的设计初衷。NFS是1984年由Sun公司开发的网络文件系统协议,至今已有40年历史。它的核心理念是:把远程服务器上的目录,挂载到本地目录树上,让用户和应用程序感觉不到它在远程

二、NFS服务端配置

2.1 安装NFS服务

bash

复制代码
# Ubuntu/Debian
sudo apt update && sudo apt install nfs-kernel-server -y

# CentOS/RHEL
sudo dnf install nfs-utils -y

2.2 创建共享目录

bash

复制代码
sudo mkdir -p /srv/nfs/shared
sudo chown nobody:nogroup /srv/nfs/shared
sudo chmod 755 /srv/nfs/shared

为什么用nobody用户? NFS默认会对客户端的root用户做"权限压缩"(root squash),将client端的root映射为nobody,防止客户端以root身份任意修改服务端文件。这是NFS的一项安全设计。服务端目录的属主设为nobody:nogroup,配合这个机制使用。

2.3 配置exports文件

NFS通过/etc/exports文件控制哪些目录被共享、共享给谁、以什么权限共享:

bash

复制代码
sudo vim /etc/exports

text

复制代码
/srv/nfs/shared 192.168.1.0/24(rw,sync,no_subtree_check)

字段逐一解读

这条共享规则由三部分组成:共享目录 + 允许访问的客户端网段 + (选项)

组件 内容 含义
共享目录 /srv/nfs/shared 要共享出去的本地目录
客户端 192.168.1.0/24 允许访问的IP网段,也可以用*允许所有(不推荐),或写具体IP
选项 rw 读写权限(ro为只读)
选项 sync 同步写入:客户端写入操作必须等数据真正写入磁盘后才返回确认。数据安全性高,性能略低
选项 no_subtree_check 不检查子目录权限,提升性能

async vs sync的关键区别

选项 行为 优缺点
sync 数据必须写入磁盘后才回复客户端"写入完成" 数据安全性高,性能略低(推荐
async 数据写入内存后立即回复,延迟写入磁盘 性能好,但服务器突然宕机可能丢失数据

安全建议 :生产环境如果不确定所有客户端都是可信的,建议加root_squash(通常默认开启)让客户端的root用户操作被映射为nobody,限制其对共享目录的权限。

2.4 应用配置并启动

bash

复制代码
# 重新加载exports配置(不中断现有NFS连接)
sudo exportfs -ra

# 查看当前已共享的目录
sudo exportfs -v

# 确保NFS服务开机自启
sudo systemctl enable --now nfs-kernel-server   # Ubuntu/Debian
sudo systemctl enable --now nfs-server          # CentOS/RHEL
  • exportfs -r:重新读取/etc/exports并更新共享列表

  • -a:导出所有在exports中定义的目录

  • -v:显示详细输出

三、客户端挂载

3.1 安装客户端工具

bash

复制代码
# Ubuntu/Debian
sudo apt install nfs-common -y

# CentOS/RHEL
sudo dnf install nfs-utils -y

3.2 查看服务端共享的目录

在挂载之前,先看看服务器提供了哪些共享:

bash

复制代码
# 列出NFS服务器上的所有共享目录
showmount -e 192.168.1.100

输出示例:

text

复制代码
Export list for 192.168.1.100:
/srv/nfs/shared 192.168.1.0/24

3.3 挂载到本地

bash

复制代码
# 创建本地挂载点
sudo mkdir -p /mnt/nfs

# 挂载
sudo mount -t nfs 192.168.1.100:/srv/nfs/shared /mnt/nfs

验证:

bash

复制代码
df -h | grep nfs
cd /mnt/nfs
echo "Hello NFS" > test.txt
# 如果能在服务端 /srv/nfs/shared 目录下看到这个文件,说明挂载成功

3.4 开机自动挂载

编辑/etc/fstab,添加一行:

text

复制代码
192.168.1.100:/srv/nfs/shared  /mnt/nfs  nfs  defaults  0  0

关于fstab的配置,我们在第13篇讲过。这里的defaults相当于rw,suid,dev,exec,auto,nouser,async。你可能会注意到async------这是客户端挂载的默认选项,它控制的是客户端缓存 行为,与第2节服务端的sync选项(控制服务端写入行为)是不同的概念。

安全建议:如果NFS用于敏感数据,建议在fstab中显式指定安全选项,如:

text

复制代码
192.168.1.100:/srv/nfs/shared  /mnt/nfs  nfs  rw,hard,intr,noexec  0  0

四、hard vs soft:NFS卡死的根源

这是NFS最核心的两个挂载选项,直接决定了NFS出问题时客户端的行为。

4.1 hard模式(默认)

当NFS服务器无响应时,hard模式下的行为是:

  • 客户端持续不断重试,直到NFS服务器恢复正常

  • 在此期间,访问NFS挂载点的进程会进入不可中断睡眠状态(D状态) ,无法被kill -9杀死

  • 这就是第11篇进程管理中提到的D状态------你在服务器上执行df -h,如果恰好挂载了一个无响应的NFS目录,df命令就会挂住不动,Ctrl+C也无效

适用场景:数据完整性要求高,短暂的"卡住等待"可以接受。

4.2 soft模式

soft模式下,当NFS服务器无响应时:

  • 客户端重试指定的次数后,放弃并向应用程序返回错误

  • 进程不会永久挂起,可以被正常终止

  • 代价是数据可能丢失------如果写入操作在服务端宕机时返回了错误,应用需要自行处理重试

适用场景:可用性优先,宁可让请求失败也不能让进程挂死。

4.3 深入理解:为什么hard会导致D状态?

NFS的hard模式利用了内核的RPC(远程过程调用)机制。当I/O请求发出后,内核RPC层会不断重试,如果服务器持续不响应,调用进程就卡在内核的RPC等待队列中,形成不可中断睡眠(D状态)。

这种设计在理论上保证了数据可靠性(服务恢复后操作继续),但在实践中,如果NFS服务器长时间宕机,所有依赖NFS的进程都会像"排队等一个永远不回来的人"。

4.4 如何选择?生产环境的折中方案

bash

复制代码
# hard模式 + intr + timeo(推荐:既保证数据可靠,又允许Ctrl+C中断)
sudo mount -t nfs -o hard,intr,timeo=30 192.168.1.100:/srv/nfs /mnt/nfs
参数 含义 典型值
hard 服务器不响应时重试(保证数据可靠性) 默认
intr 允许用信号(Ctrl+C)中断hard模式的挂死操作 推荐添加
timeo=N 重试超时(单位:0.1秒) timeo=30即3秒
retrans=N 重试次数,超过后放弃 retrans=3
soft 超时后直接返回错误 非关键数据可用
retry=N 后台挂载重试时间(分钟) retry=1(系统启动时如果NFS不可用,后台尝试1分钟后放弃)

选择策略

  • 核心业务数据(如数据库文件、用户上传)→ hard,intr

  • 日志归档、临时缓存等可恢复数据 → soft

  • 系统启动时如果NFS依赖可能导致启动卡死 → fstab中加retry=1,让系统在NFS不可用时不至于无限等待

五、NFS故障排查

5.1 诊断命令

bash

复制代码
# 查看NFS挂载状态
nfsstat -m

# 查看RPC服务状态
rpcinfo -p 192.168.1.100

# 查看NFS服务器的共享列表
showmount -e 192.168.1.100

# 实时日志
sudo journalctl -u nfs-server -f

5.2 常见问题

问题一:客户端mount卡住不动

通常是防火墙阻止了NFS或RPC端口。NFS除了NFS主端口(2049)外,还依赖mountd、statd、lockd等RPC服务(端口不确定)。解决方案:

bash

复制代码
# 使用NFS v3时,需要放行RPC端口
sudo firewall-cmd --add-service=nfs --permanent
sudo firewall-cmd --add-service=rpc-bind --permanent
sudo firewall-cmd --add-service=mountd --permanent
sudo firewall-cmd --reload

NFS v4的简化优势 :如果客户端和服务端都支持NFSv4,可以在挂载时指定-o nfsvers=4,NFSv4只需要TCP 2049一个端口,极大简化防火墙配置。

问题二:Permission denied

客户端用户的UID与服务端文件属主的UID不匹配。NFS默认使用UID来判断权限,如果客户端和服务端上同名用户的UID不一致(如客户端zhangsan是UID 1001,服务端是UID 1002),就会出现权限拒绝:

bash

复制代码
# 检查客户端当前用户的UID
id -u

# 检查服务端共享目录的属主UID
ls -ln /srv/nfs/shared

解决方案:统一两边的UID(创建用户时指定useradd -u 统一UID),或使用NFSv4的Kerberos认证。

问题三:服务端重启后客户端挂载点卡死

如果服务端意外重启,客户端以hard模式挂载的NFS目录会持续重试,直到服务端恢复。检查客户端是否受影响:

bash

复制代码
df -h | grep nfs   # 如果输出卡住 = NFS挂死
lsof | grep /mnt/nfs   # 查看哪些进程在使用NFS挂载点

六、现代替代方案简介

NFS虽然经典,但在现代云原生环境中,有更适合的替代方案:

方案 特点 适用场景
GlusterFS 分布式文件系统,无中心节点,横向扩展 自建分布式存储
CephFS 高性能、可大规模扩展的分布式文件系统 私有云存储平台
MinIO 兼容S3 API的对象存储,单机部署简单 对象存储(图片、视频等非结构化数据)
rsync + inotify 半实时同步(1-3秒延迟),配置简单 少量服务器间的轻量级同步(下一篇详解)

如果你的需求是"多台Web服务器共享上传的图片",MinIO的S3接口通常比NFS更合适;如果有大量小文件需要强一致性共享,NFS依然是成熟、简单的选择。

七、本篇小结

NFS工作流程

text

复制代码
服务端:sudo apt install nfs-kernel-server
       vim /etc/exports → /srv/nfs/shared 192.168.1.0/24(rw,sync)
       exportfs -ra

客户端:showmount -e 服务器IP(查看共享)
       mount -t nfs 服务器IP:/共享目录 /本地挂载点

hard vs soft选择

选项 服务器故障时 数据安全性 适用场景
hard(默认) 进程进入D状态,持续重试 核心业务数据
hard,intr 可用Ctrl+C中断挂死 推荐折中方案
soft 超时后返回错误 低(丢失风险) 临时文件、缓存

动手练习

在两台虚拟机之间搭建NFS:

bash

复制代码
# === 服务端(NFS服务器) ===
# IP: 192.168.1.100
sudo apt install nfs-kernel-server -y
sudo mkdir -p /srv/nfs/shared
sudo chown nobody:nogroup /srv/nfs/shared
echo "/srv/nfs/shared 192.168.1.0/24(rw,sync,no_subtree_check)" | sudo tee -a /etc/exports
sudo exportfs -ra
sudo exportfs -v   # 验证共享列表

# === 客户端 ===
# IP: 192.168.1.101
sudo apt install nfs-common -y
showmount -e 192.168.1.100   # 先查看共享
sudo mkdir -p /mnt/nfs
sudo mount -t nfs 192.168.1.100:/srv/nfs/shared /mnt/nfs
echo "Hello from client" | sudo tee /mnt/nfs/test.txt   # 写入测试
# 切回服务端验证
cat /srv/nfs/shared/test.txt   # 应该看到客户端的写入

八、下篇预告

NFS解决了"多台服务器共享同一份数据",但它的同步是实时的------文件一写入,所有客户端都能立刻看到。如果不需要那么高的实时性,而是需要"定期把文件同步到备份服务器",rsync是更好的选择。

下一篇我们将学习rsync与inotify------rsync负责增量同步(只传变化的部分),inotify负责监控文件变化并自动触发同步。两者组合,实现"文件一变,秒级同步到备份机"的效果。


延伸思考 :NFS协议本身是无状态的。这意味着服务器端不记录"哪些客户端打开了哪些文件"------如果你在客户端A上打开了一个文件,服务器宕机重启后,客户端A需要重新建立连接,而文件锁可能丢失。这与Samba(SMB协议)的状态化设计形成对比。理解"无状态"和"有状态"的取舍,是做好分布式系统选型的基础。

相关推荐
两个人的幸福15 小时前
Windows 桌面应用自研 PHP 队列(下):完整代码与六大工程化优化
php
zzzzzz31019 小时前
9K Star 炸裂开源!这个 C 语言写的代码知识图谱,把 Linux 内核索引压缩到了 3 分钟
linux·服务器·sql
XIAOHEZIcode19 小时前
Linux系统鼠标偏移常见原因以及修复方案
linux·运维·游戏
A小辣椒3 天前
TShark:Wireshark CLI 功能
linux
A小辣椒3 天前
TShark:基础知识
linux
BingoGo3 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
JaguarJack3 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
AlfredZhao3 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao3 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户3074596982074 天前
PHP 扩展——从入门到理解
php