rm大日志文件不释放空间,因进程仍持文件句柄;必须用kill -USR1等信号通知进程reopen日志,或通过logrotate配postrotate发送信号,才能真正释放磁盘空间并保障服务连续性。

rm 大日志文件,也不能无条件清空;必须保留 inode、通知进程重载、控制保留策略,并全程可追溯。
为什么 rm 一个 5G 的 access.log 可能导致服务中断
进程(如 Nginx、Java 应用)持续向日志文件写入时,文件被打开并持有 inode。执行 rm 并不会立即释放磁盘空间——只要进程没关闭 fd,数据块仍被占用;更危险的是,rm 后进程继续写,会触发“已删除但未释放”状态,df 显示空间未回收,而 lsof | grep deleted 能看到残留句柄。此时若磁盘已满,新日志写入失败,可能引发服务 panic 或静默丢日志。
- 真正释放空间的前提是:进程主动 close fd,或收到信号后 reopen 日志文件
-
rm不触发 reopen,truncate和echo -n >也不触发——它们只改长度,不碰 inode - 只有
kill -USR1(Nginx)、kill -HUP(rsyslog、Tomcat)等信号,才能让进程放弃旧文件、打开新文件
logrotate 配置中必须显式声明的 4 个关键项
靠手动清理不可持续,logrotate 是生产环境唯一推荐的自动化方案。但默认配置极易出错,以下四项必须显式写进 /etc/logrotate.d/myapp:
-
daily或size 100M:二选一,禁止同时用;混用会导致轮转时机混乱 -
rotate 30:明确保留份数,避免无限堆积;数值需结合磁盘容量与合规要求(如等保要求日志留存 180 天,则此处至少设为 180) -
compress:必须开启,否则归档体积爆炸;确认系统装有gzip或在配置中指定compresscmd /usr/bin/zstd -
postrotate ... endscript:必须包含有效信号指令,例如/bin/kill -USR1 `cat /var/run/nginx.pid 2>/dev/null` || true;漏掉这步,轮转后新日志停写
应急清空大日志时,truncate 比 echo -n > 更可控
当磁盘突然告警、必须秒级释放空间时,truncate 是首选。它比 echo -n > 多出两个关键能力:
-
truncate -s 0 /var/log/app/error.log:精准截为 0 字节,无换行符风险(echo默认加\n,可能干扰后续日志解析) -
truncate -s -1000 /var/log/app/access.log:保留最后 1000 字节,方便现场诊断——echo做不到 - 两者都保持 inode 不变,进程无需重启;但
truncate是专为此设计的 POSIX 命令,语义清晰、行为稳定 - 注意:
truncate不触发inotify事件,若下游有日志采集 agent(如 filebeat),需额外 reload 或发送 SIGHUP
清理后验证是否真正释放空间的三步检查法
执行完 logrotate -f /etc/logrotate.d/myapp 或手动 truncate 后,不能只看 df -h ——那只是文件系统视角。必须交叉验证:
- 查文件大小:
ls -lh /var/log/app/*.log*,确认主日志已清空或轮转出新文件 - 查进程句柄:
lsof -n +D /var/log/app/ 2>/dev/null | grep -E "(deleted|LOG)",确保无残留 deleted 条目 - 查磁盘真实释放:
du -sh /var/log/app/对比执行前后,排除其他子目录干扰;再运行sync && echo 3 > /proc/sys/vm/drop_caches(仅调试用,勿放入脚本)强制刷缓存
最容易被忽略的是第三点:很多团队看到 df 没变化就反复操作,其实是因为内核 page cache 还缓存着旧块元数据,du 才反映真实占用。生产环境切忌只盯 df。


















