NFS 挂载卡在超时,十有八九是防火墙只放行了
nfs、把 111 和 113 两种报错混为一谈,连 root 写不进去都当成故障。本文先讲清 NFS「先向 RPC 要端口」的工作链路,再把/etc/exports的客户端匹配写法与导出选项逐条列清,接着走一遍服务端导出、客户端挂载、fstab持久化,最后给出 RPC 报错的读法与errno代码对照表。文中命令名与选项均标注英文原意,完整清单见文末《附:命令英文全称速查表》。
本文概要:
| 项目 | 内容 |
|---|---|
| 文章主题 | NFS 网络文件共享:RPC 工作链路 → 服务端导出配置 → 客户端挂载验证 → fstab 持久化与排错 |
| 所属类别 | Linux 系统管理 → 网络存储 → NFS |
| 知识关键字 | nfs-utils·/etc/exports·/etc/exports.d·exportfs·showmount·mount -t nfs·rpcinfo·rw·ro·root_squash·no_root_squash·/etc/fstab·_netdev·nofail·RPC·errno 111/113·NFSv3·NFSv4 |
概念图给出两条并行的读写链路:本地访问不出网络,远程访问必须先由 RPC 拿到服务端口。概要表里的关键字按主线落在:RPC 与工作流程在第一章,导出写法与选项在第二章,客户端挂载在第三章,持久化与排错在第四、五章。
本文的机器与地址约定:服务端
zhb-s2(10.1.8.102),客户端zhb-m2(10.1.8.12),共享网段10.1.8.0/24。
一、NFS 服务介绍
1.1 NFS 是什么
NFS(NetworkFileSystem,网络文件系统)诞生于 20 世纪 80 年代。它由太阳微系统公司(Sun Microsystems)开发,最初用来解决 UNIX 系统之间的文件共享。客户端可以像访问本地文件一样透明地访问远程目录,跨设备共享数据不必再靠人工拷贝。
版本迭代上,NFSv3 是应用最广的版本,支持更大的文件尺寸与更多功能;NFSv4引入了 Kerberos 认证、状态化连接与更完善的文件锁机制,安全性明显更强。
1.2 两个角色
| 角色 | 职责 |
|---|---|
| NFS 服务器 | 存储文件资源,由nfsd守护进程响应请求,用/etc/exports控制哪些目录共享给谁、给什么权限 |
| NFS 客户端 | 把服务端共享目录挂载到本地挂载点,之后按普通文件读写,数据实际落在服务端磁盘 |
1.3 工作链路:先向 RPC 要端口
NFSv3 服务端的端口并不固定。启动时它会把端口注册到 RPC 的端口映射服务(RemoteProcedureCall,远程过程调用)。客户端要先查出端口,才能发出挂载请求:
- 注册阶段:服务端启动 NFS 服务,把服务端口注册给 RPC。
- 建立连接:客户端先向 RPC 查询 NFS 服务端口,再向该端口发起挂载请求。
- 挂载阶段:服务端校验客户端身份与权限,通过后允许挂载到指定挂载点。
- 文件访问:挂载成功后,本地读写被转成远程过程调用,服务端执行后把结果返回。
- 权限控制:服务端按本地文件系统的 UID / GID 与导出选项决定是否放行。
⚠️注意:正因为端口要向 RPC 注册,服务端防火墙必须同时放行
nfs、rpc-bind、mountd三项;只放行nfs会出现「客户端探测不到共享、挂载卡住」。
1.4 优缺点与典型场景
| 维度 | 说明 |
|---|---|
| 优点 | 透明(用法与本地目录一致)、跨平台(UNIX / Linux / macOS)、配置简单、便于集中管理 |
| 缺点 | NFSv3 及更早版本缺少强认证与加密,高安全场景要配合防火墙或改用 NFSv4 的 Kerberos;性能强依赖网络状况;多客户端并发写同一文件时锁机制复杂 |
典型场景:企业局域网内共享文档与安装包;Web、数据库集群的统一共享存储;NAS、路由器等嵌入式设备的文件对外访问。
二、部署 NFS 服务端
2.1 六步总览
| 步骤 | 做什么 | 用哪条命令 |
|---|---|---|
| 1 | 安装软件包 | yum install -y nfs-utils |
| 2 | 准备共享目录与属主 | mkdir -p、chown |
| 3 | 写导出配置 | vim /etc/exports |
| 4 | 启用并启动服务 | systemctl enable nfs-server --now |
| 5 | 放行防火墙 | firewall-cmd --permanent --add-service=... |
| 6 | 重新导出并自查 | exportfs -r、exportfs -v |
# 1. 安装软件包[root@zhb-s2 ~]# yum install -y nfs-utils# 2. 准备用户与共享目录,属主交给 apache(UID 48)[root@zhb-s2 ~]# useradd -u 1000 zhb[root@zhb-s2 ~]# echo redhat | passwd --stdin zhb[root@zhb-s2 ~]# mkdir -p /shares/webapp[root@zhb-s2 ~]# chown 48 /shares/webapp# 3. 写导出配置(写法见 2.2、2.3)[root@zhb-s2 ~]# vim /etc/exports# 4. 启用并启动服务[root@zhb-s2 ~]# systemctl enable nfs-server --now# 5. 配置防火墙(nfs / rpc-bind / mountd 三项都要放)[root@zhb-s2 ~]# firewall-cmd --permanent --add-service=nfs --add-service=rpc-bind --add-service=mountd[root@zhb-s2 ~]# firewall-cmd --reload⚠️注意:NFS 不要使用 SMB 共享的目录。如果这样做,则可能会导致文件损坏或其他文件完整性与一致性问题。
2.2 配置文件与目录
导出规则写在/etc/exports;另外/etc/exports.d/下任何以.exports结尾、且不以点(.)开头的文件同样生效,语法完全一致。
提示:相比直接编辑
/etc/exports,在/etc/exports.d/里创建或删除独立文件更省事,也能减少一次改动误伤其他已有导出的风险。
每一行声明一个导出点:第一个字段是服务端目录,其余部分列出可以访问它的客户端以及授予的权限。
2.3 客户端匹配写法速查表
| 写法 | 作用 | 说明(英文原意) |
|---|---|---|
/shares/webapp zhb-m2.zhb.cloud | 单个可解析主机名 | 只放行这一台 |
/shares/webapp *.zhb.cloud | 域名通配 | *匹配zhb.cloud域下所有主机 |
/shares/webapp 10.1.8.12 | 单个 IPv4 地址 | 只放行这一台 |
/shares/webapp 10.1.8.0/24 | IPv4 网段 | 等价写法10.1.8.0/255.255.255.0 |
/shares/webapp fde2:6494:1e09:2::20 | 单个 IPv6 地址 | — |
/shares/webapp fde2:6494:1e09:2::/64 | IPv6 网段 | — |
/shares/webapp 10.1.8.0/24 zhb-m2.zhb.cloud | 多客户端 | 空格分隔的列表 |
2.4 导出选项速查表
目录默认以只读方式共享;要改权限,在客户端定义后面紧跟括号写选项,客户端定义与左括号之间不能有空格。
| 写法 | 作用 | 说明(英文原意) |
|---|---|---|
(rw) | 允许读 / 写 | read-write;不写该选项(或写ro)就只有读权限 |
(ro) | 只读 | read-only |
(no_root_squash) | 保留客户端 root 的真实身份 | 默认是root_squash:客户端 root 访问时被映射成服务端的nobody,创建的文件属主变成nobody,读取也按nobody判权限 |
(sync) | 数据落盘后才返回 | synchronous,默认值,安全优先 |
(async) | 先返回、后落盘 | 性能更好,异常掉电有丢数据风险 |
(no_subtree_check) | 关闭子树检查 | subtreecheck,减少导出子目录时的额外校验 |
本实验的记录:
[root@zhb-s2 ~]# vim /etc/exports/shares/webapp10.1.8.0/24(rw)⚠️注意:
/etc/exports改完不会自动生效,必须让导出表重新加载,否则客户端看到的仍是旧规则。
2.5 让改动生效
[root@zhb-s2 ~]# exportfs -r # 重新导出:与 /etc/exports 同步,并清掉已删除的条目# 或者[root@zhb-s2 ~]# systemctl reload nfs-server# 或者[root@zhb-s2 ~]# exportfs -av2.6 exportfs 用法速查表
| 写法 | 作用 | 说明(英文原意) |
|---|---|---|
exportfs | 列出当前导出的目录 | 不显示导出选项 |
exportfs -v | 列出导出及选项 | verbose,含默认选项 |
exportfs -r | 重新导出全部目录 | re-export,把/var/lib/nfs/etab与/etc/exports同步 |
exportfs -a | 导出/etc/exports中的全部条目 | all |
exportfs -u <目录> | 取消导出一个目录 | unexport |
exportfs -s | 以/etc/exports的格式打印导出表 | show |
[root@zhb-s2 ~]# exportfs/shares/webapp10.1.8.0/24[root@zhb-s2 ~]# exportfs -v/shares/webapp10.1.8.0/24(sync,...,rw,secure,no_root_squash,...)三、客户端访问与验证
3.1 showmount 用法速查表
| 写法 | 作用 | 说明(英文原意) |
|---|---|---|
showmount -e <服务端> | 查看服务端导出的共享清单 | exports |
showmount -a <服务端> | 列出已挂载的客户端 | all |
showmount -d <服务端> | 列出被客户端挂载的目录 | directories |
3.2 挂载并验证读写
# 客户端装包,并准备 Web 服务[root@zhb-m2 ~]# yum install -y nfs-utils httpd[root@zhb-m2 ~]# id apacheuid=48(apache)gid=48(apache)组=48(apache)[root@zhb-m2 ~]# systemctl enable httpd --now# 查看服务端提供的共享[root@zhb-m2 ~]# showmount -e zhb-s2Export listforzhb-s2: /shares/webapp10.1.8.0/24# 挂载到站点目录[root@zhb-m2 ~]# mkdir -p /var/www/html[root@zhb-m2 ~]# mount -t nfs zhb-s2:/shares/webapp /var/www/html# root 不能创建:默认 root_squash 把 root 压成了 nobody[root@zhb-m2 ~]# touch /var/www/html/root-f1touch: cannottouch‘/var/www/html/root-f1’: Permission denied# apache 用户能创建:UID 48 在服务端目录上就是属主[root@zhb-m2 ~]# su -l -s /bin/bash apache-bash-4.2$echoapache website>/var/www/html/web1.html# 回到服务端访问客户端站点,确认共享目录里的内容真的落盘可读[root@zhb-s2 ~]# curl http://10.1.8.12/Welcome to zhb website.[root@zhb-s2 ~]# curl http://10.1.8.12/web1.htmlapache website3.3 mount -t nfs 常用选项速查表
| 写法 | 作用 | 说明(英文原意) |
|---|---|---|
mount -t nfs zhb-s2:/shares/webapp /mnt | 基础挂载 | -t=type;nfs可省略,由内核自动识别 |
mount -o ro ... | 只读挂载 | read-only |
mount -o hard ... | 服务端无响应时一直重试 | 默认行为,适合有稳定网络的场景 |
mount -o soft ... | 服务端无响应时超时报错返回 | 避免进程被挂起,但可能读到不完整数据 |
mount -o vers=4 ... | 指定 NFS 版本 | version,如vers=3/vers=4.2 |
四、持久化挂载
在/etc/fstab末尾追加一行:
zhb-s2:/shares/webapp /var/www/html nfs defaults,_netdev,nofail 0 0| 选项 | 作用 | 说明 |
|---|---|---|
defaults | 基础默认集 | 即rw,suid,dev,exec,auto,nouser,async七个选项的简写 |
_netdev | 声明「该挂载依赖网络」 | 让 systemd 按网络挂载处理:排进remote-fs-pre.target→remote-fs.target区间,并拉起network-online.target、排在其后——网络没就绪前不会先去挂 |
nofail | 允许挂载失败 | 该条目只被 target「想要(Wants=)」而非「必需(Requires=)」,挂不上也不阻塞启动、不进 emergency 模式 |
⚠️这两个选项管的是两件不同的事,别混为一谈:_netdev管「什么时候挂」(等网络就绪再挂),nofail管「挂不上会怎样」(不挡启动)。少了nofail,服务端一旦不可达,启动阶段就会卡在这一行上等它超时,严重时掉进 emergency shell。
💡可选加强:等待设备/挂载的默认超时约 90 秒,觉得太久可再加
x-systemd.device-timeout=30(单位可写s、min),把等待压到 30 秒——写法为defaults,_netdev,nofail,x-systemd.device-timeout=30。该选项只能写在/etc/fstab里,写进 systemd 单元文件的Options=会被忽略。
⚠️注意:NFS 条目本身就会被 systemd 按文件系统类型识别为网络挂载、排进
remote-fs.target体系,所以_netdev在 NFS 上更多是「显式声明」,写不写都能工作;但在iSCSI 这类 fstype 只是ext4/XFS、看不出依赖网络的挂载上,_netdev是必需的。另外nofail只保证「不挡启动」,不等于挂上了——依赖这个挂载点的服务(如 httpd)照样会起不来,所以 fstab 改完先跑mount -a确认不报错。
五、排错速查
5.1 读懂一条 RPC 报错
showmount与mount -t nfs的报错几乎都出在 RPC 层,格式固定,按段拆开读就能定位:
clnt_create: RPC: Port mapper failure - Unable to receive: errno 113 (No route to host)| 段 | 报错片段 | 含义 | 说明 |
|---|---|---|---|
| ① | clnt_create | 客户端创建 RPC 连接失败 | 卡在「连得上吗」这一步,与导出规则、权限无关 |
| ② | Port mapper failure | 连不上 RPC 端口映射服务 | 即 111 端口上的rpcbind;这一段失败说明连 111 端口都不通 |
| ③ | Unable to receive | 请求已发出,但没有收到回包 | 典型是防火墙丢包,而不是端口没人监听 |
| ④ | errno 113 (...) | 具体原因码 | 见 5.2 对照表 |
5.2 常见 errno 代码对照表
RPC / NFS 报错末尾括号里的errno N是标准系统错误号,含义固定:
| errno | 宏名 | 含义(英文原意) | 触发场景 |
|---|---|---|---|
| 101 | ENETUNREACH | NETworkUNREACHable,网络不可达 | 本地路由表没有到目标网段的条目 |
| 110 | ETIMEDOUT | TIMEDOUT,连接超时 | 请求发出去无人应答:中间链路不通或被静默丢包 |
| 111 | ECONNREFUSED | CONNectionREFUSED,连接被拒绝 | 对端回了 RST:端口没有程序监听,或被主动拒绝 |
| 112 | EHOSTDOWN | HOSTDOWN,主机已关闭 | 对端主机宕机 |
| 113 | EHOSTUNREACH | HOSTUNREACHable,无路由到主机 | 收到 ICMP 主机不可达,或包被防火墙静默丢弃 |
| 116 | ESTALE | STale file hANDLE,文件句柄失效 | 服务端导出被重建或重启,客户端缓存的句柄过期 |
| 4 | EINTR | INTerrupted system call,系统调用被中断 | 读写被信号打断,重试即可 |
| 5 | EIO | Input/Output error,输入输出错误 | 连接中断后仍在读写,或后端存储异常 |
| 13 | EACCES | ACCess denied,权限不足 | 导出选项或 UID 映射导致写不进去 |
| 30 | EROFS | Read-OnlyFileSystem,只读文件系统 | 以只读方式导出或挂载时执行了写入 |
| 121 | EREMOTEIO | REMOTEI/Oerror,远端 I/O 错误 | NFS 服务端读写失败后传回的错误 |
提示:本机可查完整清单——
man 3 errno看名字与描述,装了moreutils的机器用errno -l一次列出全部编号。
5.3 「连接被拒绝」与「无路由到主机」的区别
同样是连不上,111 与 113 的病因完全不同,这张表能省掉大半排查时间:
| 报错 | 网络层发生了什么 | 通常意味着 |
|---|---|---|
errno 111 (Connection refused) | 目标主机回了 RST——包已经送达主机,只是该端口没有程序监听 | rpcbind没启动,或防火墙用的是 REJECT 策略 |
errno 113 (No route to host) | 收到 ICMP 主机不可达,或包被静默丢弃,客户端等到超时也没回包 | 防火墙用的是 DROP 策略、路由不通、主机不在线 |
判据:能ping通服务端却报 113,几乎可以断定是服务端防火墙把 RPC 相关端口挡了。先用rpcinfo -p <服务端>验证 111 端口通不通,再检查firewall-cmd --list-services里有没有rpc-bind、nfs、mountd三项。
# 客户端:探测服务端注册了哪些 RPC 服务(能列出 portmapper / mountd / nfs 才算通)[root@zhb-m2 ~]# rpcinfo -p zhb-s2# 服务端:确认三项服务都已放行[root@zhb-s2 ~]# firewall-cmd --list-services5.4 报错原文与处理速查表
| 报错原文 | 成因 | 处理 |
|---|---|---|
clnt_create: RPC: Port mapper failure ... errno 113 | 服务端防火墙未放行 RPC / NFS 端口,包被丢弃 | 放行rpc-bind、nfs、mountd后firewall-cmd --reload |
clnt_create: ... errno 111 (Connection refused) | 服务端rpcbind没启动,或端口被主动拒绝 | systemctl enable --now rpcbind nfs-server |
RPC: Program not registered | rpcbind在跑,但mountd/nfsd没注册(NFS 服务没起) | 服务端rpcinfo -p是否列出mountd与nfs;systemctl restart nfs-server |
mount.nfs: Connection timed out | 请求无响应,中间链路或目标防火墙丢包 | 逐跳排查防火墙;rpcinfo -p、telnet <服务端> 2049 |
mount.nfs: access denied by server while mounting | 客户端 IP 不在/etc/exports匹配范围内,或改完没重载 | 核对匹配写法;执行exportfs -r |
mount.nfs: reason given by server: No such file or directory | 请求的目录路径写错,或该目录根本没被导出 | 用showmount -e <服务端>核对导出路径拼写 |
mount.nfs: requested NFS version or transport protocol is not supported | 客户端用了服务端不支持的版本或协议 | 加-o vers=3或-o vers=4.2指定;服务端rpcinfo -p看注册的版本 |
mount.nfs: Network is unreachable | 本地路由表没有目标网段条目 | 检查网卡与路由ip route |
挂载成功但写不进去(Permission denied) | 导出选项没给(rw),或客户端操作用户的 UID 与服务端目录属主不一致 | 改(rw)后exportfs -r;核对两端 UID |
| root 写不进去、换普通用户就能写 | 默认root_squash在起作用 | 属正常现象;确需保留 root 身份再加(no_root_squash) |
Stale file handle(errno 116) | 服务端导出被重建或重启,客户端句柄失效 | 卸载后重新挂载;必要时umount -l再mount |
读写报Input/output error(errno 5) | 连接中断后仍在读写,或服务端存储故障 | 查dmesg与网络状态;卸载重挂 |
umount报device is busy | 有进程正占用挂载点 | lsof +D <挂载点>找出占用进程,先离开挂载点再卸载 |
| 开机卡住不动、服务端不可达时掉进 emergency | fstab 条目默认是Requires=,启动会等它挂完 | 挂载选项补_netdev,nofail;仍嫌等得久再加x-systemd.device-timeout=30 |
重启后挂载点空着、df里看不到该目录 | 挂载失败但被nofail放过了,不会报错中断启动 | systemctl status var-www-html.mount看失败原因;确认网络与nfs-server已就绪 |
附:命令英文全称速查表
| 命令 / 缩写 | 英文全称 / 原意 | 作用 |
|---|---|---|
NFS | NetworkFileSystem | 网络文件系统 |
RPC | remoteprocedurecall | 远程过程调用,NFS 依赖它做端口映射 |
rpcbind/portmap | RPCbind/portmap | 把 RPC 服务号映射到监听端口的服务 |
rpcinfo | RPCinformation | 查询某主机注册了哪些 RPC 服务及其端口 |
exportfs | exportfilesystem | 维护服务端导出表 |
showmount | showmount | 查询服务端共享与客户端挂载 |
mountd | mountdaemon | 处理客户端挂载请求的守护进程 |
nfsd | NFSdaemon | 内核态的 NFS 服务线程 |
nobody | nobody | 匿名用户,root_squash之后的映射身份 |
UID/GID | userIDentifier /groupIDentifier | 用户标识号 / 组标识号 |
📚 参考资料(官方文档 · 中英双语)
官方手册(Ubuntu manpages)
- exports(5):NFS 服务端导出表格式与全部选项
- exportfs(8):导出表维护命令
- nfs(5):fstab 里 NFS 挂载选项与传输方式
- systemd.mount(5):fstab 里
nofail、_netdev等 systemd 专用挂载选项
中文版
- mount 命令中文说明(菜鸟教程)
其余手册在服务器上直接查:
man5exports# 导出表语法与全部选项man8exportfs# 导出表的维护动作man5nfs# fstab 里的挂载选项man5systemd.mount# fstab 里的 nofail / _netdev / x-systemd.* 选项man8showmount# 查看服务端共享man8rpc.mountd# 挂载请求的守护进程总结:NFS 的配置就三件事——注册(服务端把端口交给 RPC,客户端才找得到)、匹配(
/etc/exports决定放行哪些客户端)、映射(导出选项与 UID 决定文件落到服务端后归谁)。改完配置先exportfs -r再验证,遇到被拒先看客户端 IP 有没有落在匹配范围内。觉得有用可以收藏,access denied或写权限的问题欢迎在评论区聊聊排查过程。