在印度机房用 ncdu 清磁盘,删完 df 还是 100%
印度机房一台跑日志归档的机器,Ubuntu 20.04,/var 分区 500G,报警时 df 显示 98%。用 ncdu 扫了一遍,/var/log 下有个归档目录占了 210G,里面是三个月前的 .tar.gz,确认没人要,直接 rm -rf 清掉了。
清完再跑 df -h,数字没动。不是没刷新,是 inode 还被占着。
先确认是谁占着:
lsof +L1 | grep -i deleted
输出里能看到那个归档进程和几条刚删掉的 tar.gz,还在往写。这台机器上有个自制的归档脚本,用 tar czf 写到临时文件再 mv,但那次跑挂了,进程没退,文件句柄一直开着。
处理方式:
# 先看进程和文件对应关系
lsof +L1 | awk '{print $2, $7, $9}'
# 确认可以重启后,直接 kill 掉持有句柄的进程
kill -TERM <pid>
# 等两秒再查
df -h /var
kill 完 df 立刻回到 42%。
为什么之前 ncdu 看不出来:ncdu 扫的是目录项,文件已经 unlink 了,目录里没有它,但 inode 还挂在进程的 fd 上,磁盘块没释放。df 看的是块使用,不是目录大小,所以两边对不上。这个机制本身不复杂,坑在于「我删了但没删干净」这件事在目录层面完全不可见。
代价是:kill 进程有风险,如果那个进程正在写别的东西,会丢数据。这次是归档脚本,写的是已经决定删掉的临时文件,可以接受。如果换成数据库或消息队列,就不能这么干,得先让它自己关闭句柄,或者用 truncate -s 0 先把文件清空再等它自然释放。
边界条件也说一下。lsof +L1 在容器里经常看不到宿主机的句柄,如果进程跑在 Docker 里,得进容器或者用 ls /proc/<pid>/fd 交叉查。另外,如果删的是 NFS 挂载上的文件,句柄释放可能延迟很久,kill 进程也不一定马上反映到 df。还有一次,是另一个同事用 find -delete 删小文件,inode 释放很慢,df 也是滞后十几分钟才降,那种不是句柄问题,是文件系统自己的回收节奏。
后来我把归档脚本改成先写 .tmp,写完 sync 再 mv,跑完主动 close,并且加了个超时 kill 的兜底。但这只是补丁,真正的教训是:在印度这种网络和机器都不太稳的环境里,删大文件前先 lsof +L1 看一眼有没有进程开着,比事后排查便宜得多。
你们那边遇到 df 和 ncdu 对不上的时候,是先 kill 进程还是先重启机器?我一般不敢直接重启,怕把别的服务带下去。
0 条评论
还没有评论,写下第一条。