记一次NFS下的权限踩坑:从"Operation not permitted"到安装成功的折腾实录
事情是这样开始的
上周五晚上,快下班的时候,同事在群里发了个截图,配文是"这数据库装不上了,帮忙瞅一眼"。
我点开一看,命令行界面躺着一串红色报错:
bash
-bash-4.4$ sh setup.sh
-bash: /usr/bin/sh: Operation not permitted
-bash-4.4$ chmod -R 777 setup.sh
chmod: changing permissions of 'setup.sh': Read-only file system
-bash-4.4$ ./setup.sh
Now launch installer...
tee: .installer.log: Read-only file system
看到这几行,我心里大概有数了。不是脚本本身的问题,是环境的问题。
我问他:"你这个目录在哪儿?" "NFS挂的共享存储。"
果然。
先说说NFS是个什么情况
NFS这东西,说白了就是通过网络把一台机器上的目录"借"给另一台机器用。你在客户端看到的是本地文件夹,实际读写操作都要通过网络发到服务端去执行。
这种机制带来了一个挺隐蔽的问题:权限判断不是客户端说了算的。
你在本地执行chmod 777,这个命令会打包成网络请求发给服务端。服务端收到后,用自己那套规则来判断要不要执行。如果服务端那边挂载时配的是只读,那不管你本地怎么改,写操作都会被拒绝。
同事遇到的情况就是典型的例子。Read-only file system这个提示已经说得很明白了------文件系统是只读的,任何写入都不让干。
排查过程:一步步找问题
我先让同事确认了几件事。
第一步,看看挂载状态:
bash
mount | grep cluster_nfs
输出里如果有ro这个标志,说明挂载的时候就是只读模式。如果没有ro而是rw,那可能是别的原因。
第二步,试试能不能写文件:
bash
touch /home/test/pan/cluster_nfs/test.txt
这个命令直接报错的话,就坐实了只读的问题。
第三步,检查一下环境变量:
bash
echo $PATH
which sh
同事执行完告诉我,/usr/bin/sh这个路径是存在的,但执行时就是报Operation not permitted。这个报错比普通的Permission denied更底层------普通权限问题系统会说"不允许",但Operation not permitted往往意味着操作被内核或安全模块拦截了。
问题到底出在哪儿
我把几个线索串起来想了想:
- 文件系统是只读的------这解释了为什么
chmod改不了权限 - NFS挂载的------这解释了权限判断为什么绕弯子
- 执行
sh setup.sh时报Operation not permitted------这比只读更奇怪,因为执行脚本本身是读操作,不是写操作
后来同事跟我说,执行了一下source .bashrc,然后就好了。
我愣了一下。就这么简单?
仔细想想,source .bashrc干的事情是重新加载当前用户的Shell配置。.bashrc这个文件里通常放着环境变量、别名、还有------自定义的NFS挂载参数。
如果登录的时候.bashrc没被自动加载(这种情况很常见,比如su切用户或者某些非交互式登录),那NFS挂载可能用的是一套默认参数。重新加载之后,正确的参数(包括rw读写模式)才生效。
说白了,问题不是出在权限本身,而是出在环境没准备好。
后来我整理了一份NFS环境下的检查清单
踩过一次坑,就得记住教训。我把这次遇到的问题和解决过程整理成了几条检查项,以后在NFS上装东西之前先过一遍。
第一项:确认挂载是读写的
bash
mount | grep "你的目录" | grep -E "rw|ro"
看到rw才算正常。
第二项:实际测一下能不能写
bash
touch 你的目录/test.txt && rm 你的目录/test.txt && echo "可以写"
能写入才能继续。
第三项:确认Shell环境是完整的
bash
echo $PATH
如果PATH看起来不正常,执行一下source ~/.bashrc重新加载。
第四项:检查NFS挂载参数
bash
cat /proc/mounts | grep nfs
这里能看到更详细的挂载参数。对于数据库安装这种场景,建议带上这些参数:
rw:读写hard:硬挂载,出错了会持续重试vers=3:用NFS版本3,比版本4更稳tcp:用TCP协议noatime:不记录文件访问时间,减少网络开销
第五项:检查用户ID是否一致
bash
id
NFS环境下,客户端的UID需要和服务端匹配。不匹配的话,可能出现"明明是我创建的文件,系统却不认"的怪事。
顺手写了个预检脚本
被折腾过一次之后,我写了个小脚本,每次在NFS上装东西之前跑一遍,提前发现问题:
bash
#!/bin/bash
echo "=== NFS环境预检 ==="
# 检查挂载状态
if mount | grep -q "nfs.*ro,"; then
echo "[失败] 目录是只读挂载的,需要改成rw"
exit 1
else
echo "[通过] 目录是读写挂载"
fi
# 检查写入权限
if touch .test_$$ 2>/dev/null; then
rm -f .test_$$
echo "[通过] 可以正常写入"
else
echo "[失败] 无法写入,请检查挂载状态或磁盘空间"
exit 1
fi
# 检查磁盘空间
AVAIL=$(df --output=avail . | tail -1)
if [ $AVAIL -lt 10485760 ]; then
echo "[警告] 磁盘空间不足10GB,只剩$((AVAIL/1024))MB"
else
echo "[通过] 磁盘空间充足"
fi
echo "=== 预检完成 ==="
几个有用的排查命令
这些命令是平时积累下来的,遇到NFS相关的问题时基本都会用到:
bash
# 看NFS统计信息,判断有没有丢包或重试
nfsstat -c
# 看服务端导出了哪些目录
showmount -e NFS服务器IP
# 看系统日志里的NFS报错
tail -f /var/log/messages | grep nfs
# 测试NFS读写速度
dd if=/dev/zero of=test.img bs=1M count=100
总结一下这次的经验
折腾了一圈,最后解决问题的就是一行source .bashrc。但这一行命令背后,折射出的是NFS环境的特殊性:
- 权限是两层判断------客户端和服务端共同决定,不能只看本地
- 环境变量要到位 ------很多自定义配置藏在
.bashrc里,没加载就等于没用 - 挂载参数要正确 ------
rw和ro差一个字,结果天差地别
数据库安装这种事情,很多时候问题不在安装包本身,而在环境准备。NFS、网络存储这类特殊环境,更容易出一些"本地想不到"的问题。
希望这篇踩坑记录能帮到遇到类似问题的人。下次在NFS上装东西之前,记得先跑一遍预检脚本,省得半夜还在排查。