周一早上七点,手机弹了条磁盘告警:/var 使用率 92%。
登上去看谁在长:
du -sh /var/log/* | sort -rh | head -5
# 9.1G /var/log/nginx
/etc/logrotate.d/nginx 是有的,cat 出来也正常,daily、rotate 14、compress 都写着。配置在,日志还在涨,说明中间断了一环。
我按下面的顺序查,二十分钟定位到根因。
一、先确认它到底跑没跑
很多人直接改配置,其实第一步该看的是执行记录:
systemctl list-timers --all | grep -i logrotate
tail -5 /var/lib/logrotate/status
status 文件里是一条条记录,格式是「日志路径 + 上次轮转的日期」。如果某条日志后面跟着一个很旧的日期,说明它被跳过、或者压根没被匹配到。这个文件比日志本身更能说明问题,因为它记的是 logrotate 自己的判断。
顺带看一眼执行日志,有没有报错:
journalctl -u logrotate --since "-7 days" --no-pager | tail -20
二、size 会把 daily 盖掉
这是最常被写错的一处。三个指令的语义不一样:
| 写法 | 触发条件 |
|---|---|
只写 daily | 每天执行一次,不看大小 |
写 size 100M | 只看大小,时间条件被忽略 |
写 minsize 100M | 时间到了、且大于这个值才转 |
size 和 daily 同时写,生效的是 size,daily 被忽略——但 size 又只在 logrotate 被调用时判断,所以表现成「我设了每天转,它偏不转」。要的是「每天一次、太小就不转」,正确写法是 daily + minsize。
三、配置文件的权限会被直接忽略
ls -l /etc/logrotate.d/
# 正确 -rw-r--r-- 1 root root
# 危险 -rw-rw-rw- 或 -rw-rw-r--
组可写或其他人可写的配置文件,logrotate 会跳过并提示 Ignoring ... because of bad file mode。这个提示在 logrotate -d 的输出中间,不在末尾,所以只看最后一行的人会漏掉。
四、同一个日志被两份配置匹配
grep -rn "access.log" /etc/logrotate.d/
两份都匹配同一个路径时,后读到的覆盖前面的,不是合并。/etc/logrotate.conf 里 include /etc/logrotate.d 是按文件名顺序读的,所以「z-nginx」会盖掉「nginx」。表现就是:改了配置没反应,因为改的那份被别人盖了。
五、nginx 的日志要发信号,不然白转
nginx 的日志文件是被 master 进程一直开着的。文件改名了,进程还往旧 inode 里写,du 看不到新文件变大,df 也不减——空间被一个「已经改名」的文件占着。
标准写法是这样:
/var/log/nginx/*.log {
daily
rotate 14
missingok
notifempty
compress
delaycompress
sharedscripts
postrotate
[ -f /run/nginx.pid ] && kill -USR1 $(cat /run/nginx.pid)
endscript
}
postrotate 那段是核心,sharedscripts 保证多个日志只发一次信号。delaycompress 让最新一份不压缩,方便 tail 正在写的那个。
另一条路是 copytruncate:复制一份再清空原文件,不需要信号。代价是复制和清空之间那一瞬的写入会丢,审计类日志不要用。
六、dateext 撞名会静默跳过
用 dateext 时文件名是 access.log-20251104。同一天手动 logrotate -f 第二次,目标文件已存在,它直接跳过,不报错。所以「我强制跑了一次,怎么没变化」多半是这个原因。要么按小时加后缀,要么别反复 -f。
这台机器就是这一条:我前一天调试时 -f 过两次,第二次之后 status 里的日期停在三天前。
日志涨起来那几天,我是开着极目Linux桌面连上去看哪个文件在长,比对着几个目录 tail 快;也仅此而已,轮转规则照样得自己一行行写。
七、被删掉的日志还在占空间
lsof +L1 | grep -i log
进程还持有已删除文件的句柄,空间就不释放。这不属于 logrotate 的问题,但表现完全一样:文件列表里没有大文件,df 却下不去。
八、验证顺序
logrotate -d 看配置解析 → logrotate -f 强制跑一次 → ls -l 看新文件属主 → df -h 看空间 → tail /var/lib/logrotate/status 看时间戳有没有更新。
-d 是空跑,只打印将要做什么,不改任何文件,但它会把「权限不对」「重复定义」「找不到文件」全列出来。养成先 -d 再 -f 的习惯,能省掉很多「跑完才发现配错」的时间。
九、长期怎么盯
日志这件事的难点不在配置,在「配置好了没人看」。容量慢慢涨、单个文件不超限,等告警来的时候往往已经 90%。
要细粒度指标就装 agent 采集,每台机器一个常驻进程、一个监听端口,还得跟着版本升级。不想装的话,另一条路是不装采集 agent,直接通过 SSH 读日志目录、inode 占用和 status 文件——这类「无 agent 监控」的界面在本地渲染、只走现成的 SSH 通道,服务器侧不装 Web 服务、也不新增端口。
选不选它,我的判断只有一条:看它是否在服务器上新增对外端口。 端口多一个,长期要盯的东西就多一样。
FAQ
Q1:logrotate 必须机器连续开机 24 小时才会触发 daily 吗?
不需要连续开机。/etc/cron.daily 由 anacron 驱动时,关机错过的那次会在下次开机后补跑;纯 cron 驱动的机器不补。常年开机的云主机基本不涉及这条,容器里则通常没有 cron.daily,得自己在外面挂一个定时任务调用 logrotate /etc/logrotate.conf。
Q2:轮转后新文件属主变成 root,写不进去怎么办?
用 create 指定模式和属主:create 0640 www-data adm。不写 create 时,logrotate 按原文件属性重建;写了 create 但不带参数,就是默认的 0644 root root,服务进程立刻失去写权限。这是「轮转之后网站 502」的常见原因。
Q3:copytruncate 会丢日志吗?
会。复制完成到清空之间写入的行会丢,窗口通常在毫秒级,但高写入场景一天能丢几十行,而且丢得毫无痕迹。计费、审计、需要取证的日志不要用它,改用信号让进程重开文件。
Q4:多个配置文件匹配同一个日志,以哪个为准?
以最后被读到的为准,是覆盖不是合并。include 目录按文件名顺序读取,所以名字靠后的文件生效。改完不生效,先 grep -rn 看有没有第二份配置在管同一个路径,再确认 include 的顺序。
Q5:改完配置需要重启什么服务吗?
不需要。logrotate 是一次性执行的程序,由 timer 或 cron 触发,每次执行都重新读配置。改完用 logrotate -d /etc/logrotate.conf 验证解析结果就行,没有常驻进程要重启。
Q6:日志保留 30 天,磁盘该留多大?
按日增量乘天数再乘压缩比。文本日志 gzip 后大约是原来的 1/8 到 1/12。日增 300M、留 30 天、开压缩,实际占用 1G 出头;不开压缩就是 9G 左右。规划时按算出来的两倍留余量,因为峰值日往往不是平均值。