前言:
在 Linux 运维日常工作中,logrotate 是处理日志轮转的标配工具。不少运维工程师都遇到过一个十分诡异的问题:明明配置里明确写了 daily 每日切割,却时常出现一天生成多份压缩日志、日志序号异常跳变的情况。排查到最后往往会发现:问题根本不在切割规则本身,而是系统里同时存在两套独立的调度机制在重复触发 logrotate。
一、问题表象与本质
logrotate 本质上只是一个普通的命令行工具,自身不具备定时调度能力,必须依赖外部定时器触发执行。正常情况下,它会读取 /var/lib/logrotate/status 中的时间戳记录,判断是否达到切割周期,设计上具备幂等性。
但当同一台机器上同时存在两套独立的调度入口时,幂等性就会被打破,出现典型的重复切割现象:
- 同一个日志文件,同一天内生成多个
.gz压缩包 - 归档日志序号不连续、异常跳号
- 查看状态文件,同一条日志路径一天内出现多次时间戳更新
- 部分场景下出现目标文件已存在的执行报错
二、根本原因:两套调度的历史遗留与时序陷阱
1. 调度方案的两代演进
Linux 系统的日志切割调度经历了两代技术方案的迭代:
- 传统 Cron 方案 :通过
/etc/cron.daily/logrotate脚本,由 cron/anacron 每日定时执行,这是沿用了数十年的经典实现,几乎所有老版本发行版都默认采用这套机制。 - Systemd Timer 方案 :近年来主流发行版(RHEL 8+、Ubuntu 20.04+)开始使用
logrotate.timer接管调度。相比 cron 方案,它具备执行日志可追溯、关机错过任务可补执行、支持资源隔离、自带随机偏移避免集群IO尖峰等优势。
2. 为什么两套会同时存在?
在全新安装的高版本系统中,默认只会保留 systemd timer 一套调度。但在大量生产环境中,两套机制会同时并存,主要有三类场景:
- 系统跨代升级:操作系统从低版本大版本升级时,包管理器不会主动删除旧的 cron 配置文件,新系统又自带 timer 机制,形成并存。
- 镜像模板继承:定制化系统镜像、企业内部模板继承了旧版本的 cron 脚本,部署到高版本系统后,与原生 timer 机制重叠。
- 手动运维遗留:早期运维人员手动部署过 cron 定时任务,后续系统升级或架构调整后未做清理。
3. 最容易忽略的「开机时序竞争」
很多人看过 /etc/cron.daily/logrotate 的脚本内容,会发现里面有判断逻辑:检测到 systemd 运行就直接退出。按理说不会重复执行,但实际生产中依然会出现偶发的重复切割。
核心问题在于开机阶段的时序竞争 :
cron.daily 的执行时机可能早于 /run/systemd/system 目录的创建。此时脚本检测不到 systemd 标记,就会完整执行一次 logrotate;等 systemd 完全就绪、timer 到点后,又会执行第二次。这种偶发的时序漏洞,正是很多人"查配置没问题,但就是偶尔重复切"的根源。
三、一键排查:快速定位重复调度
使用下面这条组合命令,可以一次性完整排查 logrotate 的配置、调度入口、执行产物和状态记录,快速定位问题:
bash
echo "=== logrotate 业务配置 ==="; cat /etc/logrotate.d/你的业务配置名 2>/dev/null; \
echo -e "\n=== systemd timer 状态 ==="; systemctl list-timers logrotate.timer; \
echo -e "\n=== cron.daily 入口 ==="; ls -l /etc/cron.daily/logrotate 2>/dev/null; \
echo -e "\n=== 最近切割产物 ==="; ls -l --time-style=long-iso /你的业务日志路径/*.gz 2>/dev/null | tail -5; \
echo -e "\n=== logrotate 状态记录 ==="; grep "你的日志关键词" /var/lib/logrotate/status 2>/dev/null
判断标准
如果同时满足以下两点,即可确认存在重复调度风险:
logrotate.timer处于active (waiting)状态且已设置为开机启用/etc/cron.daily/logrotate文件存在且具备可执行权限
四、生产级根治方案
生产环境推荐保留 systemd timer,禁用 cron 入口,兼顾稳定性、可维护性与可回滚性。
推荐操作:可回滚式禁用
不直接删除文件,而是通过改名的方式禁用,保留原始文件便于后续追溯与回滚:
bash
mv /etc/cron.daily/logrotate /etc/cron.daily/logrotate.disabled
为什么不直接删除?
- 保留原文件可追溯变更来源,符合生产环境变更审计要求
- 后续如需回滚方案,只需改回文件名即可,无需重新编写脚本
- 避免误删系统原生组件,降低操作风险
备选方案:保留 Cron 禁用 Timer
如果环境依赖传统 cron 体系,也可以选择关闭 systemd timer:
bash
systemctl disable --now logrotate.timer
五、操作验证与回滚预案
必做三项验证
操作完成后,务必执行以下三步校验,确保日志切割正常运行:
- 确认 cron 入口已禁用
bash
ls -l /etc/cron.daily/logrotate*
输出应仅存在 logrotate.disabled,无执行态的 logrotate 文件。
- 确认 timer 正常运行
bash
systemctl status logrotate.timer
服务需处于 active (waiting) 状态,且显示明确的下次执行时间。
- 全量配置语法校验
bash
# 调试模式校验全量配置,只打印动作不实际执行
logrotate -d /etc/logrotate.conf
输出无 error 即为配置正常,所有系统日志与业务日志规则均有效。
异常回滚预案
若出现异常情况,可快速恢复到原有状态:
bash
mv /etc/cron.daily/logrotate.disabled /etc/cron.daily/logrotate
systemctl disable --now logrotate.timer
六、生产环境最佳实践
- 纳入基线巡检:将"两套调度并存"加入系统基线巡检脚本,检测到同时存在则自动告警或自动修复,批量服务器尤其适用。
- 镜像规范清理 :制作系统模板时,统一清理遗留的
/etc/cron.daily/logrotate,仅保留 systemd timer 一套调度,从源头避免问题。 - 效果持续观测 :修复后观察 1-2 天,查看
/var/lib/logrotate/status,每条日志每日仅更新一次时间戳即为正常。 - 执行日志溯源 :通过
journalctl -u logrotate查看 timer 执行记录,相比传统 cron 排查问题更高效、信息更完整。
结语
logrotate 重复切割看似是不起眼的小问题,背后其实是 Linux 初始化体系从 cron 向 systemd 过渡的历史遗留问题。很多时候故障不在配置规则本身,而在调度入口的隐形重叠。
对于运维而言,排查这类问题不能只盯着配置文件,更要向上追溯执行链路与调度机制。通过"禁用冗余入口 + 单一套调度"的思路,可以彻底根治重复切割问题,同时保留完整的日志轮转规则与系统兼容性。
