现场:/ 分区 99%
机器操作明显卡顿。先用 df -h 检查磁盘,发现 400GB 的 / 分区几乎被写满。继续排查 /var/log 后,确认 /var/log/opensm.log 单文件达到 326GB。
当时的处置动作是先将日志备份到 /public,再删除原文件。但再次执行 df -h 后,/ 仍显示占满。
随后用目录级统计交叉验证:
du -sh /{var,opt,usr,home,root,tmp} 2>/dev/null
df -h /
如果 du 看到的目录占用明显小于 df 中的文件系统占用,通常要优先怀疑两类情况:
- 文件已从目录树删除,但仍被进程打开。
- 某个挂载点覆盖了原目录,使目录统计和底层文件系统统计不一致。
本次更像第一种。
反常:删除后 df -h 仍满
用 lsof 检查 deleted 文件句柄:
lsof +L1 | grep opensm.log
+L1 会列出 link count 小于 1 的打开文件。看到类似 (deleted) 的结果,说明文件名已经从目录中消失,但进程仍持有对应 inode 的文件描述符。
这时不要反复重建同名 /var/log/opensm.log。新建出来的是另一个文件,旧 inode 仍然被进程占用,空间不会因为同名文件重新出现而释放。
原理:为什么“删了还不回收空间”
Linux 文件系统中,目录项、inode、数据块、进程文件描述符是分开的。
rm /var/log/opensm.log 删除的是目录项,也就是文件名到 inode 的引用。只要还有进程打开这个 inode,内核仍会保留数据块。结果就是:
ls /var/log/opensm.log看不到文件。du -sh /var/log统计不到这个文件。df -h /仍显示空间被占用。lsof +L1能看到被删除但仍打开的文件。
这类问题在日志文件上很常见,尤其是服务持续写日志时直接 rm 大日志文件。
应急处置:释放空间
方案一:可中断服务时,停服务后删除
这是最稳妥的流程:
systemctl stop opensm
rm -f /var/log/opensm.log
systemctl start opensm
df -h /
停服务会关闭文件描述符,删除动作才能真正释放底层数据块。
方案二:已经误删时,重启服务
如果文件已经被删除,但 OpenSM 仍在持有旧句柄,重启服务通常可以释放空间:
systemctl restart opensm
df -h /
本次现场采用该方法后,/ 分区空间恢复正常。
方案三:不重启时清空进程 FD
如果业务要求尽量不重启 OpenSM,可以先定位 OpenSM 持有的文件描述符:
pidof opensm
ls -l /proc/$(pidof opensm)/fd | grep opensm.log
确认实际 FD 后,通过 /proc 清空对应文件内容:
cat /dev/null > /proc/$(pidof opensm)/fd/1
df -h /
上面的 1 只是示例,必须替换成现场看到的实际 FD。误清空错误 FD 可能影响标准输出、错误输出或其他日志流,操作前要确认目标。
追因:OpenSM 为什么会产生日志海啸
释放空间只是止血,还要解释为什么 OpenSM 会写出 326GB 日志。常见原因有三类。
链路 flap
InfiniBand 端口反复在 DOWN、INIT、UP 之间变化,会触发频繁的 subnet 变化和拓扑重扫,OpenSM 可能持续刷日志。
快速体检:
iblinkinfo | grep -E "LinkUp|Down"
ibstat
关注 HCA 是否处于 Active / LinkUp,以及交换机端口是否频繁异常。
Verbose 参数过高
如果 OpenSM 启动参数带 -v、-V 或类似 verbose 级别,日志量会被显著放大。调试阶段可以短期开启,长期运行不应保留高详细度日志。
检查启动方式:
ps -ef | grep '[o]pensm'
systemctl cat opensm 2>/dev/null || true
在一些老系统或厂商环境中,OpenSM 可能由 SysV 脚本启动:
sed -n '1,160p' /etc/rc.d/init.d/opensmd
多个 Subnet Manager 竞争
同一 IB fabric 中多个 SM 竞争 master,可能导致主从切换和重复日志。至少要确认运行中的 OpenSM 实例数量:
ps -ef | grep '[o]pensm'
如果存在多个管理节点或交换机内置 SM,也要确认 master/subordinate 关系是否符合预期。
治本:降低日志量和稳定链路
降低 OpenSM 日志级别
如果是 SysV 脚本场景,可以编辑启动脚本,去掉长期运行不需要的 -v / -V:
vi /etc/rc.d/init.d/opensmd
# 示例:仅保留后台运行和日志路径
# /usr/sbin/opensm -B -f /var/log/opensm.log
systemctl restart opensm
实际参数应以现场发行版、OFED 包和厂商文档为准。
稳定 IB 链路
如果日志来自链路 flap,应继续检查:
- 光模块、铜缆、HCA、交换机端口是否异常。
- 是否有长期空闲但反复抖动的端口。
- 交换机固件、HCA 固件和 OFED 版本是否存在已知问题。
- 是否只有一个期望的 master SM。
可选:把日志迁到大容量分区
如果 /var/log 空间较小,可以把 OpenSM 日志迁移到更大的分区,并保留兼容路径:
mkdir -p /public/logs
systemctl stop opensm
mv /var/log/opensm.log /public/logs/opensm.log
ln -sf /public/logs/opensm.log /var/log/opensm.log
systemctl start opensm
这只是降低根分区风险,不替代日志级别治理和 logrotate。
防复发:按大小触发 logrotate
日志暴涨通常不是按周发生的。只做 weekly 轮转,遇到链路风暴时仍可能在一个周期内把根分区写满。更适合 OpenSM 这类场景的是按大小触发。
示例配置:
# /etc/logrotate.d/opensm
/var/log/opensm.log {
size 50M
rotate 10
missingok
notifempty
copytruncate
compress
delaycompress
create 644 root root
postrotate
systemctl reload opensm >/dev/null 2>&1 || true
endscript
}
注意 copytruncate 的取舍:它适合不方便让服务重新打开日志文件的程序,但在复制和截断之间可能丢失极少量日志。对诊断日志通常可以接受;对审计日志要谨慎。
演练:
logrotate -d /etc/logrotate.d/opensm
logrotate -f /etc/logrotate.d/opensm
确认自动执行:
systemctl list-timers | grep logrotate
cat /var/lib/logrotate/logrotate.status | grep opensm.log
复盘
本次问题的关键不是“日志太大”本身,而是几个信号连在一起:
df -h显示/接近满。/var/log/opensm.log单文件异常膨胀。- 删除后
df不下降,而du统计不到对应空间。 lsof +L1看到 deleted 文件仍被 OpenSM 持有。- 重启 OpenSM 释放句柄后,空间恢复。
- 后续通过降低日志级别、稳定链路和按 size 轮转防复发。
经验是:当 df 和 du 明显背离时,优先查 lsof +L1。删除正在写入的日志只是删除文件名,不等于释放空间。眼前止血靠关闭文件句柄,长期治理靠降低日志源头、稳定 IB fabric 和配置按大小轮转。