logrotate 配完日志却不轮转多半是少了 create 或权限不对
有台服务器跑了大半年没出问题,某天突然磁盘告警。上去一看,一个应用的日志文件 app.log 已经滚到了 30 多 G。我明明在 /etc/logrotate.d/ 下给它配了轮转规则,怎么就没生效?手动跑了一遍才发现问题不在"有没有配置",而在配置里少了关键几行,加上文件属主不对,导致轮转"看起来做了,实际啥也没发生"。
问题现象
为应用配了 logrotate 规则,预期是每天轮转、保留若干份、压缩旧日志。但现实中日志文件只增不减,磁盘被慢慢吃满。查 /var/lib/logrotate/status 发现该日志的轮转记录要么很旧、要么根本没有;手动 logrotate -d 调试时,要么提示"log does not need rotating",要么提示轮转了但文件大小纹丝不动。最诡异的是:日志明明被移走了(或改名了),应用却还在往一个"已不属于它"的路径写,磁盘占用一点没降。
排查过程
先看我的配置:
/var/log/myapp/*.log {
daily
rotate 7
compress
missingok
notifempty
}
手动调试:
logrotate -d /etc/logrotate.d/myapp
输出里反复出现 considering log /var/log/myapp/app.log 然后 log does not need rotating。原因有二:一是我没加 size/minsize 触发条件,默认按 daily 周期走,但 logrotate 通常由 cron 每天调一次,若当天已经跑过、且没到下一个周期,就不会再转;更关键的是,这个应用不像 nginx 那样会重新打开日志文件——logrotate 把 app.log 改名成 app.log.1 后,应用的文件描述符还指向老 inode,继续往那个已被移走名字的文件里写,新生成的 app.log 反而是空的,磁盘占用自然不降。
再看权限:应用以普通用户 appuser 运行,但 logrotate 默认以 root 执行,配置里没有 su 指令,目录下也没有可写的归属,导致 create 出来的新文件属主是 root,应用写不进去,于是它死守着旧 fd。两个坑叠在一起,轮转等于白做。
根因
logrotate 默认行为是"把旧文件改名、然后靠应用重新打开来生成新文件"。如果应用不支持信号重载、不会重新打开日志(很多自研程序都这样),就必须用 copytruncate 或显式 create + 重新加载。而 create 缺省时,新文件属主/权限可能跟应用期望的不一致,应用无法写入,只能一直抱着旧 fd。另外,缺少 size/minsize 等触发条件,或者 cron 没按时调用 logrotate,也会让轮转不按预期发生。这不是 logrotate 坏了,而是配置没贴合应用的日志打开方式。
解决办法
- 不能重载日志的程序,用
copytruncate:它先复制一份再截断原文件,不依赖应用重新打开,最省心。注意复制与截断之间有一瞬间可能丢几行日志,一般可接受。 - 能重载的,配
create+postrotate:create 0644 appuser appuser指定新文件属主,postrotate里发信号或 reload 让应用重开日志(如 nginx 的kill -USR1)。 - 加触发条件:
size 100M或minsize 100M,让日志到一定大小就转,不单靠天级别。 - 权限对齐:若应用非 root,配置里加
su appuser appuser,避免 logrotate 以 root 建的文件应用写不了。 - 调试先行:永远先
logrotate -d <配置>看它会做什么,再去掉-d正式执行;定期检查/var/lib/logrotate/status确认真的转了。
小结
logrotate 不轮转,十有八九是"应用不会重开日志却没配 copytruncate/create"或"权限没对齐"。记住:先看应用是否支持信号重载,不支持就 copytruncate;新文件属主要跟运行用户一致;改完用 -d 调试、看 status 验证。把这几条补齐,日志轮转基本不会再掉链子。
- 点赞
- 收藏
- 关注作者
评论(0)