1. 先聊点实际的:RH134为什么绕不开NFS
如果你正在啃RH134教材,走到第九章"访问网络附加存储"这里,可能会觉得有点突然——前面还在折腾进程管理、SELinux、磁盘分区,怎么突然跳到网络存储了?但其实你只要在公司里待过几天,就会发现NFS(Network File System,网络文件系统)是Linux环境里最常用的共享存储方案之一。刚进运维这行时,我最常干的活就是把应用服务器的日志目录挂载到一台集中的存储服务器上,或者让好几台web后端共享同一份静态资源。这类需求十有八九都是用NFS解决。
RH134把这章放在这里,并不是教材编排随意,而是因为你要理解NFS的配置、安全选项、自动化挂载,需要的前置知识(用户权限、文件权限、systemd、防火墙、SELinux)刚好在前几章全部打过了,所以这章本身就是一次综合实战。换句话说,弄懂NFS的过程,等于把前面学的那些零散知识点串了一遍。
这篇笔记我按自己的学习路径来写:先讲清楚NFS的工作机制和版本差异,再分别从服务端、客户端两个角度搭建共享,然后重点说说autofs自动挂载,最后把最近踩过的坑和排查思路整理出来。文中的配置命令都是基于RHEL 9系和Rocky Linux 9实测过的,CentOS 7、8有些小差异我会顺手标注一下。
在实际干活的过程中,我强烈建议你边看边搭一个环境出来,哪怕只是两台虚拟机(一台当NFS服务端,一台当客户端)。因为NFS的很多选项只看文档是体会不出来的,比如root_squash和no_root_squash的区别,没有实操过,你根本不会意识到一个选项可能让整个集群的权限管理失控。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 先把NFS的原理吃透,配置才有底气
2.1 NFS到底解决什么问题
NFS的运行思路其实很朴素:通过网络让一台Linux主机把自己的目录"借"给其他主机用。客户端把一个远程目录mount到自己本地目录树上之后,对这个目录的操作(读写、建文件、删文件)就好像在操作本地磁盘一样,而后端真正落盘的位置是那台NFS服务端。
有个很形象的类比:NFS就像一个公共的共享网盘,只不过它不通过网页界面,而是直接嵌进你的文件系统里。你打开"我的电脑"就能看到这个盘符,cd进去、ls、vim、rm,一切照旧,只有当你df -h的时候才会发现它其实在网络对面。
这个设计模式在架构上带来一个巨大的好处:多个客户端访问的是同一份数据,天然达成了"数据一致性"(至少从用户视角看是同一个文件),而不用像数据库那样做复杂的同步。所以NFS非常契合以下场景:
- 应用集群共享上传目录、静态资源目录
- 集中备份时让各节点把数据写到挂载点
- 无状态应用的水平扩展,多实例共享同一份存储
2.2 NFS协议的核心机制——RPC
NFS本身不是直接跑在TCP/UDP之上的,它依赖RPC(Remote Procedure Call,远程过程调用)机制。用大白话说,RPC就是让你可以像调用本地函数一样调用远程服务器的函数。NFS的每次文件操作(open、read、write、getattr)在底层都是一个RPC调用,客户端把请求发给服务端的rpcbind(端口111),rpcbind负责告诉客户端"你要找的NFS服务在哪个端口",然后客户端再向那个端口发起真正的调用。
NFSv3时代,因为各个辅助服务(mountd、nlockmgr等)使用的端口是动态分配的,所以必须依赖rpcbind来做端口映射。这带来一个让人头疼的问题:防火墙很难配,因为你不知道nfsd接下来会用哪个端口。到了NFSv4,协议本身已经默认跑在TCP 2049端口上,很多辅助服务也被整合了,防火墙配置就简单多了。不过RHEL 9上如果还是用NFSv3,老问题依旧存在。
2.3 版本差异和选型建议
NFS的版本迭代经过了很多年,但在实际生产环境中你大概率只会遇到两个版本:
- NFSv3(默认老协议):兼容性好,历史包袱轻,支持锁和squash选项,但它没有内置的用户认证,主要靠IP和文件权限来控制访问。
- NFSv4/NFSv4.1(现代默认):在RHEL 8/9里是默认协议,走2049端口,引入伪文件系统(pseudo filesystem)的概念,提供更强的安全性和操作原子性,还支持服务端授权(nfsv4的krb5p)等特性。
实操选择上,我的建议是:新环境一律用NFSv4,除非你这套存储需要兼容大量古老客户端,才退回v3。RHEL 9的mount.nfs会自动协商协议版本,你可以通过mount命令的输出确认最终用了哪个版本。
2.4 关键词:为什么要关心"squash"
理解NFS时有个必须弄清的概念是squash(压制)。NFS服务端在共享目录时,会收到来自客户端的RPC请求,里面带了客户端的用户ID(UID)和组ID(GID)。问题是,客户端机器上的UID 1000和服务器上的UID 1000,真的应该是同一个用户吗?
大多数情况下答案是否定的。为了安全,NFS默认开启了root_squash,也就是当客户端以root(UID 0)访问时,服务端会把它的身份"压制"成nobody用户(UID 65534),这就防止了客户端的root在共享目录上为所欲为。而如果你关闭root_squash(no_root_squash),客户端的root就相当于服务端的root,能对共享目录做任何操作,一般只有特殊场景(比如无盘工作站)才这么干。
这个点不光是RH134考试的重点,也是实际排障的高频现场:很多新手搭好NFS后,客户端root可以写文件,换普通用户就Permission denied,一脸懵地跑来找我排查。其实十有八九是文件属主和权限本身的问题,跟"NFS不起作用"没关系。
3. 服务端配置:搭一个标准的NFS共享
3.1 软件包安装与环境准备
RHEL系操作系统的NFS服务端由nfs-utils这个包提供。通常系统最小化安装时不会自带,需要手动装一下:
bash复制dnf install -y nfs-utils
systemctl enable --now nfs-server
如果你用的发行版是Debian/Ubuntu系,对应的包名略有不同,服务名和配置路径也稍有出入,但概念上是一致的。安装完成后,先确认服务状态:
bash复制systemctl status nfs-server
另外,在RHEL 8/9里,即使你把nfs-server服务拉起来了,别忘了rpcbind服务也会自动启动(NFSv3时需要),可以用下面命令观察端口监听情况:
bash复制ss -tlnp | grep -E '^(2049|111)'
3.2 exports文件:NFS共享的核心配置
NFS服务端要共享哪些目录、允许哪些客户端访问、权限怎么控制,全部定义在/etc/exports文件里。这是整个NFS配置中最重要的一个文件,格式非常精简,每一行代表一条共享规则:
code复制共享目录 允许访问的客户端(选项1,选项2,...)
最典型的例子:
code复制/data/software 192.168.1.0/24(rw,sync,no_root_squash)
/data/upload 10.0.0.0/16(rw,sync,root_squash)
/home/nfs *(ro,sync)
逐项拆解一下:
- 共享目录:必须是一个真实存在的本地目录,可以给多个客户端(多行)分别配置。
- 允许访问的客户端:可以写单个IP、网段、主机名、域名通配符,或者像第三行那样用表示"所有人"。生产环境强烈不建议用,最好是精确到具体的网段或IP,避免暴露风险。
- 选项部分:多个选项用半角逗号分隔,不能有空格。
常用的选项我整理了一张表,方便你查阅:
| 选项 | 含义 | 典型场景 |
|---|---|---|
| rw / ro | 读写 / 只读 | 按需求选择,简单明了 |
| sync / async | 写操作同步落盘 / 先写缓存再异步落盘 | 默认用sync,防掉电丢数据 |
| root_squash | 客户端root被压制为nobody | 默认选项,安全 |
| no_root_squash | 客户端root保持root身份 | 特殊场景,慎用 |
| no_all_squash | 普通用户按自身UID/GID访问 | 默认行为 |
| all_squash | 所有用户统一映射为nobody | 公共共享目录防越权 |
| no_subtree_check | 关闭子目录检查 | 提升性能,NFSv4默认开启 |
| anonuid / anongid | 指定squash后映射的UID/GID | 配合all_squash使用 |
配置完成后,需要执行exportfs -r让规则生效,然后查看导出列表确认无误:
bash复制exportfs -r
exportfs -v
3.3 防火墙与SELinux的配合
RHEL这类系统默认开着firewalld,如果不放行NFS相关服务,客户端能看到导出列表,但实际mount会卡住,或者挂载成功但读写超时。正确放行方式是:
bash复制firewall-cmd --permanent --add-service=nfs
firewall-cmd --permanent --add-service=rpc-bind
firewall-cmd --permanent --add-service=mountd
firewall-cmd --reload
如果你查看firewalld的配置文件,会发现NFS服务默认放开的是2049/tcp,rpc-bind放开的是111/tcp,mountd是20048/tcp。这一套放行完,NFSv3和v4都能正常工作。
SELinux方面,RHEL默认开启SELinux enforcing模式,NFS共享目录如果标记得不对,客户端即使挂载成功,也会出现Permission denied之类的诡异问题。需要确保共享目录的SELinux类型正确:
bash复制semanage fcontext -a -t public_content_rw_t "/data/upload(/.*)?"
restorecon -Rv /data/upload
如果不想深入SELinux,最粗暴的排查方式是临时setenforce 0看看是否恢复,如果是,那就是SELinux类型的问题。生产环境不要长期关闭SELinux,正确设置上下文才是正解。
这里想多说一句:很多人在学习阶段图省事,一遇到权限问题就关SELinux,这其实是给自己埋坑。RH134考试后面几章全都是围绕SELinux的题目,如果你连NFS和SELinux的协作都没体验过,到考试时会非常被动。建议从学习阶段就养成好习惯,用semanage和restorecon解决问题,而不是setenforce 0。
3.4 服务端排错:先确认导出是否正常
服务端配置完,可以先用showmount命令在服务端自查:
bash复制showmount -e localhost
这条命令会列出本机当前导出的共享列表。如果这里就报错或列表不对,说明exports文件或服务状态有问题,赶紧先修服务端,别急着去客户端折腾。
4. 客户端挂载:把远程目录变成"本地"目录
4.1 手工挂载与NFS版本协商
客户端这边也需要先安装nfs-utils(或者至少nfs-client),基本包安装:
bash复制dnf install -y nfs-utils
先看点目录有哪些共享:
bash复制showmount -e 172.16.140.200
然后直接挂载:
bash复制mkdir /mnt/nfsdata
mount -t nfs 172.16.140.200:/data/upload /mnt/nfsdata
df -h | grep nfsdata
这里有个容易搞混的点:mount命令里冒号前面的是服务端IP,冒号后面紧跟的是服务端上的共享目录路径,不是本地路径。这个路径必须和exports文件里写的完全一致,否则会报mount.nfs: access denied或者类似错误。
挂载完成后,mount命令的输出会显示详细的挂载信息。如果是RHEL 9默认配置,通常走的是NFSv4,输出类似:
code复制172.16.140.200:/data/upload on /mnt/nfsdata type nfs4 (rw,relatime,sync,vers=4.2,rsize=1048576,wsize=1048576,...)
如果你希望强制走NFSv3,可以加上vers选项:
bash复制mount -t nfs -o vers=3 172.16.140.200:/data/upload /mnt/nfsdata
4.2 挂载选项详解:rsize、wsize、hard、soft、timeo
NFS挂载选项很多,我挑几个生产环境最容易踩坑的说:
- rsize/wsize:单次RPC读写请求的最大字节数。默认值通常足够大(1MB),在局域网内基本不用调。但如果跨广域网或小带宽环境,把rsize/wsize调小(比如32768)反而更稳定。
- hard/soft:hard模式下如果NFS服务端无响应,客户端会持续重试,进程可能卡死;soft模式下超时后返回IO错误,应用脚本能感知并处理。生产环境一般用hard + intr(中断),或者干脆hard,避免数据丢失;测试环境可以用soft。
- timeo:超时时间,单位是0.1秒,默认600(即60秒)。如果你在共享存储故障时希望客户端快速失败,可以把timeo调小。
- noatime:不更新访问时间戳,减少不必要的写IO,性能有小幅提升。
4.3 开机自动挂载:fstab的正确写法
如果希望重启后自动挂载,可以把条目写进/etc/fstab:
code复制172.16.140.200:/data/upload /mnt/nfsdata nfs defaults,_netdev 0 0
这里有两个需要解释的选项:
- _netdev:告诉系统这是一个网络设备,在网络就绪前不要挂载。不写这个的话,开机时如果网络还没起,系统会等很久甚至挂载失败。
- 建议把defaults后面加上auto,确保开机自动挂载。
写完fstab后,可以用mount -a测试一遍配置是否正确。这条命令会把fstab里所有未挂载的条目尝试挂载一遍,是个好习惯。如果这里报错,千万别直接重启,不然系统可能卡在挂载阶段,起不来。
4.4 匿名访问与权限映射
客户端访问共享时,身份判断的规则如下:如果客户端请求来自root且服务端开启了root_squash(默认),那么实际落盘的用户是nobody;如果是普通用户,则按客户端的UID/GID映射到服务端上同UID/GID的用户。
这里就引出一个最常见的坑:服务端上根本没有UID 1000这个用户(或者UID对应的人不对),客户端以UID 1000写入的文件,在服务端上显示属主是一个未知用户。这不会导致写失败,但会带来后续管理混乱。解决办法是保证服务端和客户端对同一个用户使用相同的UID/GID,或者用all_squash + anonuid把所有人映射到固定用户。
我在项目里最常用的做法是:单独建一个nfsuser用户,固定UID(比如6000),然后在共享目录上chown nfsuser:nfsuser,再在exports里写上all_squash,anonuid=6000,anongid=6000。这样不管客户端是什么身份,写进来的文件全是nfsuser的,权限控制一目了然。
5. autofs:让挂载跟着需求走
5.1 为什么要用autofs,而不是fstab
每个NFS共享都在开机时全量挂载,管理简单,但也存在几个问题:机器上挂载点越来越多,占着本地路径;如果NFS服务端临时不可用,开机等超时,整个系统启动都可能被拖慢;一些共享可能一周都用不上一次,白白占着网络连接。
autofs的思路正好反着来:不提前挂载,当进程真正访问某个挂载点时,它才在那一刻触发挂载,经过一定时间空闲后再自动卸载。这对用户完全透明,体验比fstab好得多。
5.2 autofs的核心配置文件
autofs的配置分两层:主配置文件master和映射文件map。主配置文件定义了挂载点的父目录,映射文件定义了具体子目录对应哪个服务端共享。
安装和启动:
bash复制dnf install -y autofs
systemctl enable --now autofs
主配置文件在/etc/auto.master,默认内容里有一行:
code复制/misc /etc/auto.misc
意思是:/misc这个目录下的所有子目录都由/etc/auto.misc这个映射文件来控制。比如/etc/auto.misc里有这么一行:
code复制upload -fstype=nfs,rw,sync 172.16.140.200:/data/upload
那么当你访问/misc/upload的一瞬间,autofs会自动执行mount -t nfs -o rw,sync 172.16.140.200:/data/upload /misc/upload。你什么都不用管,直接cd /misc/upload就能用。
如果你想直接用IP路径访问,而不是子目录名,可以在auto.master里增加独立的挂载根:
code复制/- /etc/auto.direct
然后auto.direct里写成:
code复制/mnt/upload -fstype=nfs,rw,sync 172.16.140.200:/data/upload
这种写法叫直接映射(direct map),适合挂载点分布在不同目录的场景。
5.3 验证autofs:从触发挂载到空闲卸载
autofs有个特性:直接ls /misc并不会触发挂载,只有当ls /misc/upload这种真正进入子目录的请求出现时才会触发。可以用以下命令验证:
bash复制ls /misc/upload
mount | grep upload # 此时应该能看到挂载记录
等一下再执行mount,如果一段时间没有访问,autofs会自动卸载。这个空闲时间默认是300秒,可以在/etc/autofs.conf里通过TIMEOUT变量调整。
5.4 autofs结合LDAP/网络用户
在企业环境里,autofs经常配合LDAP或NIS的自动挂载映射,实现用户在任意一台客户端登录都能自动挂载自己的home目录。RH134只要求掌握本地配置文件,但这个场景在真实工作中非常常见。考完试后建议自己动手试一下:服务端导出一份用户home池,客户端上配合SSSD做LDAP认证,然后配置autofs直接按用户名映射挂载,体验一把"任意机器登录都有完整家目录"的丝滑感。
6. 常见问题与排查技巧实录
6.1 经典疑难:nfs: server 172.16.140.200 not responding, timed out
这个报错大概是NFS新手遇见的第一个“鬼故事”:客户端挂载后一切正常,但过一会儿开始疯狂刷"nfs: server 172.16.140.200 not responding, timed out",应用直接卡死,甚至整个系统都变得很卡。
排查思路按以下顺序走:
- 物理链路:先在客户端ping服务端IP,丢包率高不高?再检查网线和交换机状态。
- 服务端负载:服务端CPU、内存、磁盘IO是否被打满?如果服务端磁盘故障或IO满,NFS请求排队是必然结果。
- 防火墙/安全组:确认2049、111、20048端口是否都放行。有时只放行了2049,但v3的mountd端口没放,会间歇性出问题。
- 超时参数:如果网络环境有小波动,把timeo调大,soft模式配好,客户端就不容易卡死。
- NFS版本协商问题:客户端的v4.2和服务端的v4.0在某些老内核上可能有兼容性问题,尝试强制vers=4.0或者vers=3。
这里我最想强调的是:不要把not responding单纯当成网络问题。有一次我排查半天,最后发现是服务端在做一个大文件rsync,磁盘队列深度被打满,NFS请求全部排队。解决方案不是调网络,而是错峰备份或者限流。
6.2 showmount看到共享但mount卡住:多半是mountd端口问题
showmount -e能列出共享,说明rpcbind和NFS服务端基本正常。但mount时如果卡住不动,或者报RPC: Timed out,大概率是服务端的mountd端口没有被防火墙放行。RHEL 9里mountd默认监听20048/tcp,记得放行。
另一个可能是服务端的/etc/exports配置里客户端的访问权限不对。exports里的IP匹配是精确匹配的,如果你写的是192.168.1.10,而客户端实际是从192.168.1.11发起的请求,那就没法访问。用showmount -e只能看到"有哪些共享",看不到"你这个IP能否访问",所以最好在服务端用exportfs -v看看每条规则的生效范围。
6.3 WSL环境怎么创建一个NFS服务端
有同学私信问过,在Windows的WSL里能不能搭NFS服务端。答案是:WSL1不行,因为它没有真正的Linux内核;WSL2可以,因为WSL2跑在轻量虚拟机里的完整Linux内核之上。
WSL2里搭NFS服务端和普通Linux几乎一样:
bash复制sudo apt update
sudo apt install -y nfs-kernel-server
sudo mkdir -p /data/nfs
echo "/data/nfs *(rw,sync,no_subtree_check,no_root_squash)" | sudo tee /etc/exports
sudo exportfs -r
sudo service nfs-kernel-server start
需要注意两点:一是WSL2默认不会自动启动systemd(老版本),所以用service命令而不是systemctl;二是Windows侧的防火墙可能会拦截来自局域网其他机器的NFS请求,需要在Windows防火墙里放行WSL虚拟网卡所在网段。
6.4 银河麒麟V10的NFS离线安装包
国产化环境是现在绕不开的话题。银河麒麟V10(或者统信UOS)这类基于Linux内核的发行版,在离线环境下装NFS让人头大,因为没有外网,dnf源根本用不了。
我的经验是:如果手头有一台同样版本、能上网的机器,可以先从在线源把rpm包下载下来,再用U盘拷进离线机器安装:
bash复制dnf install --downloadonly --downloaddir=/tmp/nfs-rpms nfs-utils
然后到离线机器上执行:
bash复制rpm -ivh /tmp/nfs-rpms/*.rpm
如果机器恰好有麒麟的本地安装光盘镜像,也可以把光盘挂载后配成本地yum源,再从源里装nfs-utils。这个方案比拷rpm更省事,因为依赖关系能自动解析。
6.5 NFS常用终端命令速查表
把NFS调试过程里最高频的命令整理成一张速查表,建议收藏:
| 命令 | 用途 |
|---|---|
| exportfs -v | 查看当前导出的所有共享及其选项 |
| exportfs -r | 重新加载/etc/exports配置 |
| exportfs -a | 导出所有配置的共享 |
| showmount -e IP | 查看指定服务端导出的共享列表 |
| mount -t nfs IP:/共享 本地挂载点 | 挂载NFS共享 |
| umount 挂载点 | 卸载NFS共享 |
| nfsstat -m | 查看当前NFS挂载统计信息 |
| rpcinfo -p IP | 查看服务端RPC服务的端口映射 |
| systemctl status nfs-server | 检查NFS服务端状态 |
| mount | grep nfs |
6.6 权限问题排查专用路径
每当遇到"挂载成功但写不进去"或者"能写但属于主不对"这类问题,我一般固定走这套流程:
- 先在服务端本地测试共享目录本身的权限:cd到共享目录,touch一个测试文件,确认文件系统本身没问题。
- 再到客户端touch一个文件,然后到服务端看文件的UID/GID,跟预期的用户比对。
- 检查exports里的squash选项,确认root_squash是否生效。
- 检查SELinux上下文:ls -Zd 看看目录的type是不是public_content_rw_t或者nfs_t。
- 如果还不行,在服务端临时tcpdump抓包,看客户端的RPC请求里携带的UID是多少。
这套流程走完,99%的NFS权限问题都能定位。
7. 写在最后:NFS只是开始,存储知识体系得连起来学
RH134第九章的内容,说白了就是"配置NFS服务端+客户端+autofs自动挂载"这三大块。但学习的时候千万别局限于"考试会考什么",你得把这个知识点放回整个存储体系里看:本地磁盘、LVM、文件系统、NFS、iSCSI、Samba,这些技术在真实生产环境里是互补组合的关系。NFS适合类Unix主机之间的共享,Samba适合和Windows主机互通,iSCSI则是把远端磁盘当成一块裸设备来用。理清各自的适用场景,比死记命令重要得多。
我个人学会NFS后最大的收获,并不是会敲那几条命令,而是终于明白了"文件系统其实可以虚构成一层网络服务"这个思路。这个思路延伸到容器卷、云存储之后,你会发现底层逻辑是相通的:存储的提供方和使用方可以分离,关键在共享机制和权限模型。
如果你也在学RH134,建议动手做一个完整的练习:在一台机器上配好NFS服务端,客户端用autofs自动挂载,然后故意搞坏一个配置,尝试用journalctl和mount输出排查问题。这个过程比做十遍题都管用。后面我还会继续整理RH134剩余章节的学习笔记,如果你对autofs深入配置、NFSv4的Kerberos安全认证有兴趣,也欢迎交流,这几个话题单独拎出来都能写很长。
