服务器上的应用日志如果不加管理,几天就能把一个磁盘写满,甚至触发监控告警、拖垮整个节点。Logrotate 是 Linux 发行版自带的标准日志轮转(log rotation)工具,能在日志达到时间或大小阈值时自动切割、压缩、删除旧文件,并平滑地让程序继续往新文件写入。本文用 Nginx、Docker 等真实场景,把它的配置语法、核心指令和最容易踩的坑一次讲透。
一、为什么必须做日志轮转
很多新手以为”日志多占点空间没关系”,但生产环境的真实情况要残酷得多。第一,单个文件持续追加会无限膨胀,几 GB 的日志文件既难用 grep 检索,也难用 tail 跟踪。第二,Linux 删除被进程占用的文件并不会立即释放磁盘空间,只有进程关闭句柄或重启后空间才回收,这会让 df 与 du 显示的结果严重不一致。第三,磁盘写满后,数据库、消息队列这类强依赖磁盘的服务会直接拒绝写入,引发雪崩。
日志轮转的本质,是在”可检索的历史”和”可控的磁盘占用”之间做平衡:把旧日志改名压缩、保留 N 份、超期删除,同时保证应用进程无感地切到新文件。Logrotate 就是干这件事的事实标准,它通常挂在系统的 /etc/cron.daily 里每天跑一次,也可以通过 anacron 或 systemd timer 触发。
二、Logrotate 的工作机制与目录约定
2.1 主配置与 /etc/logrotate.d
Logrotate 有两个入口:全局配置 /etc/logrotate.conf 定义默认策略,而具体的服务轮转规则放在 /etc/logrotate.d/ 目录下,每个文件对应一个应用。主配置里常见的全局设置包括 weekly、rotate 4、create、compress 等,但更推荐把每个服务的规则独立成文件,便于维护和排障。
2.2 一个最小可用配置
# /etc/logrotate.d/myapp
/var/log/myapp/*.log {
daily
rotate 7
compress
delaycompress
missingok
notifempty
create 0640 www-data www-data
}
这段配置的含义是:每天轮转一次、保留 7 份、压缩旧日志、延迟一轮再压缩(delaycompress,给还在写的进程留缓冲)、日志不存在也不报错、空文件不轮转、轮转后新建 0640 权限的文件。下一节我们把这些指令逐一拆开。
三、核心指令速查
Logrotate 的指令很多,但日常 90% 的场景只需要下面这一组。理解它们各自的默认值和副作用,比死记硬背更重要。
| 指令 | 作用 | 常见取值 | 注意点 |
|---|---|---|---|
| daily / weekly / monthly | 按时间触发轮转 | 三选一 | 基于文件 mtime,配合 size 可变成”或”关系 |
| size | 达到指定大小才轮转 | 100M / 1G | 与 daily 是”或”逻辑,满足任一即轮转 |
| rotate N | 保留几份历史 | 7 / 30 | 超过 N 份的最旧文件被删除 |
| compress / nocompress | 是否 gzip 压缩 | 默认关闭 | 压缩后旧日志变成 .gz |
| delaycompress | 延迟一轮再压缩 | — | 必须与 compress 同用,避免压缩正在写的文件 |
| copytruncate | 拷贝后截断原文件 | — | 不重命名,适合无法重载的服务,有极小数据丢失窗口 |
| create mode owner group | 轮转后新建文件 | 0640 user group | 与 copytruncate 互斥 |
| postrotate / endscript | 轮转后执行脚本 | reload 服务 | 常用于让服务重新打开日志文件 |
四、实战一:Nginx 访问日志按大小轮转
Nginx 默认会把访问日志和错误日志持续追加到同一个文件,高流量站点一天就能产生数 GB。下面这个配置让它在”每天”或”超过 200M”时任一条件满足就轮转,避免磁盘被瞬间打满。
# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily
size 200M
rotate 14
missingok
notifempty
compress
delaycompress
sharedscripts
postrotate
if [ -f /var/run/nginx.pid ]; then
kill -USR1 $(cat /var/run/nginx.pid)
fi
endscript
}
这里的 kill -USR1 是 Nginx 官方推荐的”重新打开日志文件”信号,相当于平滑重载日志,不会断连接。如果你的 Nginx 是通过反向代理对外提供服务的,保持访问日志可控同样重要,否则一次流量高峰就可能把代理节点写挂。
五、实战二:Docker 容器日志轮转
Docker 容器默认使用 json-file 日志驱动,日志会原样写进 /var/lib/docker/containers/<id>/ 下的 JSON 文件,而且 Docker 本身不会帮你轮转。正确做法是先在 daemon.json 里给容器日志设上限,再用 Logrotate 兜底。
// /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "5"
}
}
改完配置需要 systemctl restart docker 才能对新容器生效(已存在的容器要重建)。但 json-file 的 max-size 是按容器单文件算的,多个容器叠加仍可能占满磁盘,所以再补一条 Logrotate 规则清掉磁盘上的 JSON 残留:
# /etc/logrotate.d/docker-containers
/var/lib/docker/containers/*/*.log {
daily
rotate 7
compress
delaycompress
copytruncate
missingok
}
这里用 copytruncate 而不是 create,是因为 Docker 守护进程握着这些 JSON 文件的句柄,重命名会失效。关于 Docker 的更多生产实践,可以参考多环境部署那篇。
六、实战三:postrotate 平滑重载服务
很多程序(Nginx、rsyslog、MySQL)打开的是文件描述符而非文件名。Logrotate 默认用 rename(mv)把旧日志改名,但进程还在往旧的文件描述符写,结果就是:磁盘上看到 access.log.1,可日志依旧写进那个已经被改名的文件里,轮转形同虚设。解决办法就是在 postrotate 里让服务重新打开文件。
# /etc/logrotate.d/rsyslog
/var/log/syslog {
daily
rotate 7
compress
delaycompress
missingok
notifempty
postrotate
/usr/bin/systemctl kill -s HUP rsyslog.service >/dev/null 2>&1 || true
endscript
}
HUP 信号让 rsyslog 重新打开日志文件,是这类”按文件名持有”服务的标准做法。同样地,当你在服务器上用Fail2ban 阻断暴力破解时,它的日志也需要被正确轮转,否则被封禁记录查无可查。
七、delaycompress 与 copytruncate 的坑
这两个指令是新手最常踩坑的地方,用错会直接导致磁盘空间不释放或日志丢失。下表把典型误区列清楚。
| 误区 | 现象 | 正确做法 |
|---|---|---|
| 开了 compress 却没开 delaycompress | 当天的 .1 文件被立即压缩,postrotate 里用文件名 reload 的服务找不到文件 | 需要 postrotate 读旧文件时,务必配合 delaycompress |
| 对无法 reload 的服务用 create | 进程仍写旧文件,新文件是空的,日志”消失” | 改用 copytruncate,牺牲极小窗口换取无感 |
| size 与 daily 误以为是”与”关系 | 以为”每天且超过 200M 才转”,实际任一满足就转 | 想要”且”语义需写自定义脚本判断 |
| 忘记 sharedscripts | 每个匹配文件都执行一次 postrotate,reload 多次 | 多文件场景加 sharedscripts 只执行一次 |
# copytruncate 示例:Tomcat 这类不支持信号重载的服务
/opt/tomcat/logs/catalina.out {
daily
size 300M
rotate 10
compress
copytruncate
missingok
notifempty
}
八、调试与强制执行
写好配置后千万别直接等它自动跑,先用 debug 模式验证一遍,确认轮转逻辑符合预期再放手。
# 只模拟执行,不真正轮转,能看到每一步决策
logrotate -d /etc/logrotate.d/nginx
# 强制立即轮转一次(排障或测试用)
logrotate -f /etc/logrotate.d/nginx
# 指定 state 文件,避免污染系统默认状态
logrotate -f -s /tmp/lr.state /etc/logrotate.d/nginx
# 结合 verbose 看详细信息
logrotate -dv /etc/logrotate.d/nginx
-d 是”dry run”,能帮你发现配置语法错误或逻辑冲突而不动真实文件,强烈建议每次新增规则都先跑一遍。如果某条规则一直不生效,多半是权限问题或被 state 文件里的”上一次轮转时间”挡住了——用 -s 指定独立 state 可以排除干扰。
九、落地检查清单
- 确认 /etc/cron.daily/logrotate 或 systemd timer 在正常运行(
systemctl status logrotate.timer)。 - 每个新增服务都在 /etc/logrotate.d/ 下有独立规则,不堆在主配置里。
- 需要服务重新打开日志的,务必配 postrotate + sharedscripts。
- 无法信号重载的服务用 copytruncate,但要知道它有极小的丢日志窗口。
- 上线前用
logrotate -d验证,用-f做一次真实演练。 - 把磁盘用量接入Grafana 监控和Prometheus,设置”剩余空间 < 20%”告警,别等写满才发现。
日志轮转看似是个小配置,却是运维稳定性的第一道防线。把它和监控、告警串起来,才能在你睡觉的时候真正放心。




