那天报警响了:服务器磁盘使用率 100%。我登上机器,敲下最熟的两条命令,结果把自己绕进去了。
df -h # 显示 / 已用 100%,只剩 0 字节
du -sh /* # 加起来的占用,只有 12G ……
文件系统说满了,目录加起来却对不上。差了将近 30G。这就是运维里最经典的"幽灵磁盘"问题。
为什么会这样
Linux 里,一个文件其实分两部分:目录项(文件名 → inode)和 inode + 数据块(真正占空间的东西)。
当你 rm 一个文件时,如果还有进程正打开着它,内核只会把目录项删掉,inode 和数据块会一直保留,直到那个进程关闭文件。这时候:
du 找不到它(目录里已经没有这个名字了)
df 却算着它(空间确实还被占着)
这不是 bug,是 Unix 的设计。
三步找到它
第一步:确认是不是这个原因
lsof +L1 2>/dev/null | head -20 # 列出被删除但仍被占用的文件
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
nginx 1421 root 12w REG 253,3 27.4G 0 655431 /var/log/nginx/access.log (deleted)
第二步:确认是哪个进程
# 直接进 /proc 看,比 lsof 更快(不用额外装包)
for pid in /proc/[0-9]*; do
ls -l $pid/fd 2>/dev/null | grep deleted
done
小技巧:
/proc/<pid>/fd 是内核暴露的"进程打开文件表"。就算系统里没装 lsof,这条路也能走通。第三步:释放空间
注意:不能再 rm 一次(已经没有目录项了)。得让那个进程重新打开文件:
| 方法 | 命令 | 适用场景 |
|---|---|---|
| 平滑重载 | systemctl reload nginx | 服务支持 reload(首选) |
| 重启服务 | systemctl restart nginx | 不支持 reload 时 |
| 清空文件 | : > /path/to/file | 文件还有目录项时 |
| 强制杀进程 | kill -9 <pid> | 最后手段,可能丢数据 |
为什么优先 reload?reload 是平滑重载:nginx 启动新 worker 接管新请求,老 worker 处理完手头的再退出,用户无感知。restart 是先停后起,中间有服务中断。
下次怎么避免
根因其实是日志没轮转——nginx 的 access.log 一直写,从来没切过。
/var/log/nginx/*.log {
daily # 每天轮转
rotate 14 # 保留 14 份
missingok
notifempty
compress
delaycompress
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
endscript
}
关键在 kill -USR1:告诉 nginx "重新打开日志文件"。没这一步,nginx 会继续往已被 rename 的旧文件写,问题照样出现。
总结
- df 和 du 对不上,先想到"已删除但被占用"
- 没有 lsof 也能查 ——
/proc/<pid>/fd是万能后门 - 治标更要治本 —— 配好 logrotate 才是根治
排障最有价值的,往往不是"怎么修好的",而是"为什么会这样"。