前几天半夜被同事一个电话叫起来,说某台线上服务器的磁盘满了,应用写不进去数据。我登上去一看,好家伙,一个 Nginx 的 access.log 已经撑到了 30 多 GB,再往前翻,这个文件从部署那天起就没动过。那台机器数据盘一共才 40 GB,一个日志文件就占了四分之三。
这种问题相信不少人都遇到过。日志文件如果不加控制,它会理所当然地把你的磁盘吃干抹净,而 logrotate 就是解决这个问题的标准工具。它是 Linux 系统里默认自带的一个日志切割、轮转工具,几乎每个发行版都有。Ubuntu 20.04 上也内置了它,只是很多人从来没有主动去看过它到底在干什么。
这篇文章我就从 logrotate 的核心机制讲起,把配置文件语法掰开揉碎,再给几套可以直接抄的实战配置,最后聊一聊我踩过的那些坑。不管你是刚接触服务器的新手,还是已经在生产环境摸爬滚打了一段时间的运维,这篇文章应该都能给你一些有用的参考。
1. 日志持续增长背后:必须切割的三个原因
先说结论:日志切割不是一个附加的"锦上添花"功能,而是线上服务稳定运行的基础设施之一。很多人觉得日志嘛,写就写了,反正硬盘那么大,直到某一天磁盘 IO 和空间双双告警,才意识到问题有多严重。
1.1 磁盘空间被拖垮的速度比你想象中快
一个最普通的业务接口,假设 QPS 只有 50,每行 access log 平均 200 字节,你算一下一天会产生多少日志: 50 × 86400 × 200 / 1024 / 1024 ≈ 823 MB。也就是说,一个日均请求量并不算高的服务,一天就能产生接近 1 GB 的日志。
这还只是一个接口。现实生产环境里,一个应用往往有 access log、error log、业务流水、慢查询日志、调试日志好几路输出,叠加起来就是几 GB 甚至几十 GB 一天。如果一个月不去处理,磁盘被写满是确定性事件,只是时间早晚的问题。
1.2 单个超大文件带来的连锁反应
日志文件大了以后,不只是占空间这么简单。第一个受害者是排查问题的效率:用 tail 看一眼还好,但如果你想 grep 某个时段的关键字,一个 30 GB 的文件能把整个磁盘的 IO 拖垮。因为 grep 要顺序扫描整个文件,哪怕只是找一个关键词,也得等它从头扫到尾。
第二个受害者是日志采集和归档。现在稍微正规一点的环境都会接日志采集,像 Filebeat、Logstash 这类 agent 读取超大文件时,要维护文件读取位置(offset),文件原地继续写没问题,但如果因为磁盘满了导致写入中断,位置就错乱了,采集端可能重复读或者漏读一大段。
第三个受害者是文本编辑器、less、vim 这类日常排查工具,打开大文件要么巨慢,要么直接内存溢出。
1.3 切割的最终目的:把巨型文件变成可管理的小文件
所谓日志切割,核心就两件事:一是按时间或按大小把日志文件切分成一段一段的小文件;二是设置保留策略,只保留最近 N 份,更老的日志自动删除或压缩归档。
这样磁盘空间始终处于可控状态,排查问题时可以只针对某一天的日志文件进行精准检索,采集端也能稳定地读取完整文件。logrotate 这个工具就是专门干这个的,它不会面向业务逻辑做任何事,只关心文件本身。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. logrotate 的执行机制:谁在背后调用它
很多初学者有一个误区,以为 logrotate 是一个常驻后台的守护进程。其实不是。它本质上是一个命令行工具,需要别人定期触发它执行。在 Ubuntu 20.04 上,这个"别人"是 cron 和 systemd timer 两套机制配合完成的。
2.1 系统默认的触发链路
Ubuntu 20.04 安装 logrotate 之后,会生成两个关键文件:
/etc/cron.daily/logrotate:cron 的每日任务脚本/lib/systemd/system/logrotate.timer和/lib/systemd/system/logrotate.service:systemd 的定时器单元
其中 /etc/cron.daily/logrotate 这个脚本的内容核心只有一行:
bash复制/usr/sbin/logrotate /etc/logrotate.conf
也就是说,系统每天会在 cron 的 daily 时段执行一次 logrotate,读取主配置文件 /etc/logrotate.conf。而 /etc/logrotate.conf 里有一行关键配置:
code复制include /etc/logrotate.d
它的作用是把 /etc/logrotate.d/ 目录下所有配置文件都加载进来。所以你在 /etc/logrotate.d/ 里放的任何配置文件,都会被每天自动执行一遍。
同时,Ubuntu 20.04 默认启用了 logrotate.timer,它也会定时触发 logrotate 的执行。cron 和 systemd timer 两套机制同时存在,正常情况下不会冲突,因为 systemd timer 设计了随机延迟和幂等机制。你可以用下面的命令确认 timer 状态:
bash复制systemctl status logrotate.timer
systemctl list-timers logrotate.timer
输出会显示 timer 的下一次触发时间。如果 cron 先跑了,logrotate 已经处理完毕,timer 再触发时,因为发现没有需要轮转的文件,就会直接跳过。
2.2 读取顺序与配置生效逻辑
logrotate 的执行顺序是先读主配置 /etc/logrotate.conf,再按字母顺序读取 /etc/logrotate.d/ 下的所有配置文件。如果同一个日志文件在多个配置块里被重复定义,logrotate 的处理策略是:后面的配置会重置前面配置的状态。注意,不是"合并",是"整个配置块被重置"。
这个特性非常容易踩坑。比如你在 /etc/logrotate.d/nginx 里定义了对 /var/log/nginx/*.log 的轮转规则,又在 /etc/logrotate.d/myapp 里也写了一个针对 /var/log/nginx/access.log 的规则,那么后读取的那个配置会完全覆盖前一个,前一个配置对 access.log 的所有参数都会失效。
2.3 手动执行与强制轮转
既然是命令行工具,logrotate 当然支持手动触发,这在调试配置时极其重要。常用参数如下:
bash复制logrotate -d /etc/logrotate.conf # debug 模式,只输出执行计划,不实际执行
logrotate -f /etc/logrotate.conf # 强制轮转,即使还没到轮转条件
logrotate -v /etc/logrotate.conf # 详细输出
logrotate -s /var/lib/logrotate/status /etc/logrotate.conf # 指定状态文件
其中 -d 是我最常用的调试参数。它会模拟一遍执行流程,打印出每一步会做什么,但不会真正移动或创建文件。强烈建议每次修改完配置后都用它跑一遍,比你直接 -f 强制轮转安全得多。
logrotate 还有一个状态文件,默认在 /var/lib/logrotate/status。它记录了两个关键信息:每个日志文件上次轮转的日期,以及当前已轮转的次数。logrota
