
有段时间没整理NFS了最近在内部环境搭一套共享存储又把手头的NFS服务端操作手册翻了出来。NFSNetwork File System说白了就是让不同机器像访问本地目录一样去读远端文件服务端这一侧配置得好不好直接决定客户端挂载稳不稳、权限乱不乱。我见过太多人卡在exports文件上也见过权限映射搞反导致数据目录被写坏的案例。这篇就把我长期使用的服务端运维操作整理成一份能直接照着做的体系化指南。这套内容适合正在搭NFS共享存储的运维、测试环境管理员也适合刚接触Linux共享文件系统、想知道exports每一列到底什么意思的开发者。我会把环境准备、导出配置、服务管理、权限映射、性能调优和故障排查这几块串起来尽量把操作背后的原因也讲清楚而不是只丢命令。1. 先捋清楚NFS服务端到底在解决什么问题1.1 这套手册的适用人群和边界NFS服务端做的事情用一句话概括就是把服务器上的某个目录“导出”给网络内的其他主机让客户端通过挂载方式远程使用。它不是分布式文件系统没有副本和容错解决的是共享、集中管理和权限收敛的问题。比如多台应用服务器需要读写同一份上传文件、日志目录或者要在测试环境里把构建产物集中存放NFS都是成本最低的方案。我写这篇手册的预设环境是RHEL系兼容发行版命令以dnf/yum和systemctl为主Debian系的操作思路一样只是包名和部分服务名略有差异。阅读前你需要先明确三件事客户端和服务端的操作系统版本大致相同、网络是互通的、导出目录没有特殊文件系统限制。如果是在容器或虚拟化环境里跑还需要确认内核模块和端口策略这部分后面会展开。边界也要说清楚NFS不解决数据备份问题也不能替代对象存储。它适合文件级共享不适合数据库数据文件这类高并发随机写入场景。我见过有人把数据库目录直接丢在NFS上结果锁和延迟一堆问题这就是选型没想明白。1.2 我实际调通的一个存储场景我前段时间处理过一个典型场景三台应用服务器要把用户上传的头像和附件写到同一个目录原先各自存本地后来要做灰度发布和迁移就必须把存储抽出来。方案就是在其中一台性能较好的机器上导出/data/appdata其余机器通过内网挂载到同一路径。这个场景看起来简单但真正配置时牵涉到不少细节客户端用哪个用户写文件、服务端怎么识别这些用户、目录权限怎么设计、要不要开同步写、防火墙要放行哪几个服务。每一步选错都可能让客户端挂载成功但写入失败或者写入成功但文件属主错乱。下面我按实际操作顺序把服务端从安装到最终验证的流程完整走一遍。2. 动手前的环境准备与安装2.1 网络、主机名和目录规划正式安装前先把网络和目录规划好这样能省掉后面大量排查时间。NFS服务端最好使用静态IP或DHCP保留地址不要用会漂移的地址主机名也要固定因为NFSv4的一些操作会用到主机名标识。我给客户端规划的是独立内网网段服务端双网卡环境里对外共享的IP要提前确认是哪一块。目录规划方面我习惯单独划分一个分区或逻辑卷给共享数据而不是直接用根目录下的普通文件夹。原因有三个第一共享目录的写入量可能快速增长独立分区能避免把根分区写满第二后续要扩容时逻辑卷比目录迁移简单得多第三可以针对该分区单独设置挂载参数比如noexec、nodev这类加固项。实际操作中我会按用途拆分子目录。比如上面说的场景我建了/data/appdata/uploads和/data/appdata/logs这样后续如果要给不同客户端不同权限可以直接在exports里按子目录分别导出而不是把所有权限绑在同一个根目录上。目录权限我建议先统一设为root所有后面再按实际映射的用户调整。2.2 服务端依赖安装与内核模块确认在RHEL系环境里NFS服务端需要安装nfs-utils这个包它包含nfs-server服务、exportfs、showmount等工具。安装命令很简单sudo dnf install -y nfs-utils安装完成后我建议先确认内核是否已经加载了NFS相关模块。虽然现代发行版基本都预置了但如果你用的是裁剪过的内核或者容器宿主机可能缺少模块。检查方法cat /proc/filesystems | grep nfs lsmod | grep nfs如果/proc/filesystems里能看到nfsd和nfs说明文件系统支持已经就绪。接着启动服务并设置开机自启sudo systemctl enable --now nfs-server sudo systemctl status nfs-server注意RHEL系环境里nfs-server服务会依赖rpcbind所以我还习惯确认一下rpcbind状态。rpcbind的作用是管理RPC服务端口映射NFSv3依赖它NFSv4虽然不依赖但为了兼容性和排查方便保持它运行是稳妥做法。Debian系环境服务名可能是nfs-kernel-server操作思路一样。3. exports文件核心配置的每一行3.1 共享目录、客户端匹配与选项语法NFS服务端最核心的配置文件是/etc/exports每行定义一条导出规则。语法看起来简单但每个字段都有讲究。我常写的格式是这样/data/appdata 192.168.10.0/24(rw,sync,no_root_squash,no_all_squash) /data/appdata/uploads 192.168.10.31(rw,sync,all_squash,anonuid1001,anongid1001)第一列是导出的绝对路径第二列是允许访问的客户端网段或IP括号里是导出选项。客户端匹配支持单个IP、网段、DNS主机名和通配符。我在生产上尽量只用IP或网段不用主机名因为DNS解析故障会导致客户端无法访问。关于exportfs -rav我每次改完exports都会执行它来重新加载配置。有个坑必须提醒exportfs -a只会根据exports文件增量导出不会把已经从文件里删除的目录自动取消导出。如果你发现旧共享还在要先执行exportfs -r让配置完全重置或者用exportfs -u指定取消某个导出。常用选项我整理成了一张速查表方便对照选项作用我的建议rw / ro读写或只读按需选择默认是ro别忽略sync / async同步落盘或先回内存数据重要选sync追求性能可asyncwdelay合并多个写入请求延迟落盘默认开启小文件多可关掉root_squash将客户端root映射为匿名用户强烈建议开启安全底线no_root_squash保留客户端root权限只在可信内网按需使用all_squash所有用户映射为匿名用户适合公共共享目录anonuid / anongid指定匿名映射后的UID/GID配合all_squash使用subtree_check检查子目录是否在导出范围内新内核默认no_subtree_checkno_subtree_check关闭子树检查提升稳定性默认推荐3.2 权限映射和anonuid/anongid权限映射是NFS最容易出问题的地方因为客户端和服务端的用户ID可能不一样。NFS协议传输的是UID和GID数字不是用户名。如果客户端用户A的UID是1000服务端恰好是另一个用户的UID那写出来的文件属主就会“乱掉”。解决思路是保证客户端和服务端对应用户的UID/GID一致。比如我在场景里要求客户端上传目录以UID 1001写入那我会在服务端创建一个UID 1001的用户同时要求客户端侧也创建一个UID 1001的用户。这样配置sudo groupadd -g 1001 appuser sudo useradd -u 1001 -g 1001 -s /sbin/nologin appuser sudo chown -R appuser:appuser /data/appdata如果客户端用户不确定甚至希望所有共享用户都收敛到同一个账号就用all_squash配合anonuid/anongid。比如公共上传目录无论客户端用哪个UID过来都映射成服务端的appuser/data/public 192.168.10.0/24(rw,sync,all_squash,anonuid1001,anongid1001)这里有个细节很多人不知道exportfs -rav之后用exportfs -v查看实际生效的选项会发觉内核默认会附加一些你没写的选项比如wdelay、no_subtree_check、secsys这是正常现象。如果你发现写的是all_squash但显示no_all_squash说明配置没生效或者被后读的配置覆盖了要按顺序检查是否有重复的导出行。4. 服务启动、防火墙与开机自启4.1 服务启动顺序与exportfs校验NFS服务有依赖顺序rpcbind先起来然后nfs-server加载导出配置。RHEL系通过systemd管理正常执行systemctl enable --now nfs-server就行。但如果你改了exports文件一定要按这个顺序操作sudo exportfs -rav sudo systemctl reload nfs-serverexportfs -rav会解析所有导出配置并应用reload会重新读取服务配置。很多情况下只执行exportfs -rav就够了但我在CentOS系遇到过exportfs报错提示配置语法错误而服务还在跑旧配置的情况。这时候systemctl reload能暴露错误信息比挂载失败后去排查更直接。校验导出项是否生效用sudo exportfs -v sudo showmount -e localhostshowmount -e显示的是通过mountd服务查询到的导出列表。如果这里看不到你刚写的目录说明配置没有被正确加载。注意showmount只显示当前服务端导出的信息需要rpcbind正常运行如果你把rpcbind停了showmount也会失败但NFSv4挂载可能还能用这种“假故障”很容易误导排查方向。4.2 防火墙放行与客户端验证在RHEL系环境如果开了firewalldNFSv3会涉及多个动态端口最省事的方式是直接放行所需服务sudo firewall-cmd --permanent --add-servicenfs sudo firewall-cmd --permanent --add-servicemountd sudo firewall-cmd --permanent --add-servicerpc-bind sudo firewall-cmd --reload这里mountd和rpc-bind是NFSv3要用的NFSv4主要放行nfs服务就够了。我建议即使你只用NFSv4也把这三个都放行否则客户端用showmount时会超时排查起来容易走弯路。服务端配置完成后我在客户端做三件事。第一确认能查到导出项showmount -e 192.168.10.5第二挂载并写入测试文件sudo mkdir -p /mnt/appdata sudo mount -t nfs 192.168.10.5:/data/appdata /mnt/appdata echo nfs test /mnt/appdata/test.txt第三检查文件属主和回读内容。如果看到服务端/data/appdata下出现nfs_test文件且属主符合预期说明整条链路通了。挂载完成后df -h能看到NFS挂载点状态列显示网络文件系统。要彻底卸载测试目录记得先删除文件再卸载避免服务端残留垃圾数据。5. 性能调优与安全加固一起做5.1 rsize/wsize/同步模式怎么选NFS挂载参数里最常用到的是rsize和wsize它们代表客户端和服务端之间单个RPC读写请求的最大字节数。服务端侧无法直接强制这些参数但服务端的导出选项会影响实际表现尤其是sync和async。sync模式要求服务端把数据写入稳定存储后才给客户端返回写完成数据安全性高但网络往返延迟会叠加。async模式允许先回响应再落盘性能和吞吐更好但一旦服务端宕机客户端以为写成功的数据可能丢失。我在共享上传目录这种非核心数据场景敢用async数据库备份目录这类数据必须用sync。客户端实际挂载时可以显式指定rsize和wsize比如sudo mount -t nfs -o rw,rsize1048576,wsize1048576,hard,intr 192.168.10.5:/data/appdata /mnt/appdata这里1MB的读写块大小在现代网络和内核上是比较稳妥的选择不必去追求过大的值因为超过底层RPC限制反而会被截断。老手册里常写rsize32768那是早期内核的约束。如果你不确定服务端支持的最大值可以先用默认参数挂载再用nfsstat观察RPC统计逐步调整。还有两个客户端选项我建议关注hard和intr。hard表示服务端恢复后客户端自动重连避免文件系统报I/O错误intr允许中断卡住的NFS操作防止进程永久卡死。现在很多发行版默认启用hard但intr要看情况。测试环境我会加上生产环境要评估中断NFS操作可能带来的数据不一致风险。5.2 访问控制、root_squash与网络隔离NFS本身没有加密所以安全主要靠网络层控制和权限配置。我坚持三条原则共享网络必须是内网可信网段、客户端必须明确列出、root_squash必须开启。root_squash太重要了。它把客户端的root用户映射成服务端的匿名用户nfsnobody防止客户端root直接以服务端root身份读写所有文件。如果你配置了no_root_squash客户端root就拥有服务端root的写权限那基本等于对整台机器放开了一个远程root入口。我只有在完全可信的实验环境才用no_root_squash平时一律不开。NFSv4支持更细粒度的权限域通过sec选项可以指定安全机制比如seckrb5p提供加密和完整性校验。但部署Kerberos复杂度比较高我在内网可信环境一般不强求主要依靠防火墙限制源IP。如果你的NFS要跨不可信网络请优先考虑SSHFS或其他加密方案不要裸奔NFS。还有一个加固点容易被忽略导出目录的权限紧缩到最小。比如/data/appdata我只给appuser写权限其他用户一律不可写sudo chmod 750 /data/appdata同时在服务端开启noexec挂载选项如果该目录不是单独分区可以投递到systemd挂载配置里避免共享目录里的未知文件被直接执行。配置完成后用exportfs -v确认实际生效选项里有root_squash和sync这才算收尾。6. 常见故障排查记录6.1 挂载失败、超时与无权限问题我整理了几个高频故障都是实际踩过的。第一种是客户端挂载时卡住或超时报mount.nfs: Operation not permitted或timed out。这多半是防火墙没放行或者rpcbind出了问题。先看服务端rpcinfo -p是否正常再确认firewalld是否有nfs、mountd、rpc-bind服务。第二种是挂载成功但写入提示只读文件系统。这种十有八九是exports里写了ro或者客户端挂载参数带了ro。用mount | grep nfs查看实际挂载选项再回到exports确认。另外如果导出目录的属主不是客户端写入用户的映射UID也会出现类似“权限不足”的表现这时用ls -n看属主数字和exports里的anonuid对比。第三种是showmount能看到共享但mount时不认路径。这种一般是exports里目录不存在或者exportfs没生效。我在某次操作中因为路径少写了一个字母后来用exportfs -v才发现导出的是另一个目录。我把排查命令按优先级排了个序照着执行基本能定位问题操作命令定位方向服务端导出状态exportfs -v确认选项实际生效RPC服务状态rpcinfo -p确认rpcbind/mountd在线导出列表查询showmount -e 服务端IP确认客户端视角服务端日志journalctl -u nfs-server查看认证和导出日志防火墙列表firewall-cmd --list-services确认端口放行日志这个手段很多人不用但NFS的报错最后都会落到system日志里。我遇到客户端报access denied时直接在服务端执行journalctl -u nfs-server --since 10 min ago经常能看到具体是IP被拒还是选项不匹配比瞎猜快得多。6.2 NFS服务异常与inode问题NFS服务端偶尔会遇到nfs-server状态显示active但客户端挂载全部卡住的情况。这通常是服务端的NFS线程被阻塞或者网络文件系统依赖的锁恢复问题。我建议先用nfsstat -s看服务端RPC统计如果大量请求在排队检查是否有人把NFS导出的目录本身做了一次递归压缩或大量小文件写入把磁盘I/O打满了。另一个常见问题是客户端卸载时提示target is busy。这种一般是有进程还占用挂载点用lsof D /mnt/appdata或fuser -mv /mnt/appdata找出占用进程杀掉或正常结束再卸载。不要直接umount -l来强制卸载延迟卸载虽然能让系统恢复正常但进程后续访问文件会报错容易留下脏数据。inode问题主要出在大量小文件场景。NFS服务端文件系统如果用的是早期文件系统inode耗尽后即使磁盘还有空间也写不进新文件。排查时用df -i看inode使用率同时确认服务端目录所在分区的空间余量。如果inode快满了就只能清理旧文件或扩展分区这类问题跟NFS配置本身无关但经常被误判为NFS故障。7. 最后分享几个实操习惯写完这套手册再补充几个我长期养成的小习惯不一定写在教科书里但对维护NFS很有帮助。第一个习惯是每次修改exports后把旧配置备份成带时间戳的文件再执行exportfs -rav同时立刻在客户端跑一轮读写测试。这样如果改坏了可以秒回滚配置不用靠记忆恢复。第二个习惯是把NFS共享目录的挂载信息写入客户端的/etc/fstab时我特意加上_netdev选项。这个选项告诉系统网络就绪后再挂载避免开机时网络还没起来就尝试挂载导致启动卡顿。在虚拟化环境里这个选项尤其重要。第三个习惯是定期执行exportfs -v导出当前状态把它和上一周的记录做对比。这么做的原因很简单NFS是静默型服务除非客户端写爆磁盘否则服务端很难感知异常。提前掌握共享目录的文件数、空间增长和客户端连接状态比出问题再救火要省事得多。NFS服务端的核心就是把“目录导出”和“权限映射”两件事做对再配上清晰的可视化验证手段。按这篇手册走一遍你至少能避开我踩过的绝大多数坑。如果后面用到NFSv4的Kerberos加固或者多节点高可用我再单独写一篇展开。