一、为什么日志必须轮转而非无限膨胀
服务器上 Nginx、PHP、MySQL、应用自己的日志,若任由其增长,几个月就能吃掉几十 GB 磁盘,轻则磁盘告警、重则分区写满导致服务崩溃。logrotate 是 Linux 自带的日志管理工具,它按时按量对日志做"切分 + 压缩 + 清理 + 重载服务",把日志维护变成自动化。几乎所有主流发行版都默认带 crontab + logrotate 日报,我们只需把规则写好,它每天自动执行。
二、logrotate 工作原理与主配置
logrotate 通过"配置文件 + 每日定时任务"运作:logrotate -d 可调试,主配置需按设定频率运行。默认由 crontab 驱动(/etc/cron.daily/logrotate),也可手动执行:
# 手动执行(-f 强制轮转,-d 不真正执行只演示,-v 显示过程) sudo logrotate -dv /etc/logrotate.conf sudo logrotate -f /etc/logrotate.conf
核心配置项都放在 /etc/logrotate.conf,自定义规则建议写进 /etc/logrotate.d/。自定义规则优先级高于默认段,新增规则无需改主文件、便于管理和备份。
三、为 Nginx 配置日志轮转
在 /etc/logrotate.d/nginx 写一条规则,分钟轮转频率由 daily/weekly 决定:
/var/log/nginx/*.log {
daily # 每天轮转一次
rotate 14 # 保留最近 14 份
compress # 轮转后 gzip 压缩
missingok # 文件不存在不报错
notifempty # 空日志不轮转
dateext # 用日期命名,如 access.log-20260928
sharedscripts # 下面脚本只在所有日志轮转完后执行一次
postrotate
[ ! -f /var/run/nginx.pid ] || kill -USR1 `cat /var/run/nginx.pid`
endscript
}
关键点是 `postrotate` 里的指令:Nginx 打开日志文件后不会再重新打开同名字符串,必须发送 `USR1` 信号让它重新打开新日志文件,否则日志会被反复写进同一个已轮转的文件,甚至因删除句柄而中断记录。sharedscripts 确保这个"重载"动作只在全部日志处理完毕后执行一次,而不是每个文件都执行。
四、PHP-FPM 与应用日志配置
PHP-FPM 日志、应用自己输出的日志同样可以管理:
# /etc/logrotate.d/php-fpm
/var/log/php-fpm.log /var/log/php-fpm-slow.log {
daily
rotate 14
compress
missingok
notifempty
create 0640 www-data www-data # 轮转后以指定权限重建新日志
postrotate
[ ! -f /run/php/php8.2-fpm.pid ] || kill -USR2 `cat /run/php/php8.2-fpm.pid`
endscript
}
# /etc/logrotate.d/myapp 自定义应用日志
/var/log/myapp/*.log {
weekly
rotate 4
compress
missingok
notifempty
copytruncate # 先复制再清空,避免重启应用也能轮转
}
`copytruncate` 适用于"无法通过信号重载日志"的应用:先复制当前日志为旧文件,再把原文件截断为空。它的代价是复制瞬间可能丢掉极少量记录,但对那些不提供日志重载信号的应用是必要手段。能用 postrotate 重载信号的应用优先用信号方式。
五、压缩、清理与大小阈值
compress # gzip 压缩旧日志 compresscmd /usr/bin/gzip delaycompress # 轮转后不立即压缩,留一份供追查 size 100M # 按大小触发:达到 100M 即轮转(与 daily 可同时写) maxsize 100M # 到达大小即轮转,避免单份过大 age 7 # 至少 7 天前的旧日志才轮转
`delaycompress` 配合 postrotate 重载很有用:轮转当天保留未压缩的旧日志,便于当天排错,第二天再压缩。size 与 daily 同时存在时按"先到先触发"执行;想"每天且超 100M 才轮转"应改用 maxsize,语义更符合直觉。
六、排错与避坑建议
# 调试:只预览将执行的操作,不真正轮转 sudo logrotate -d /etc/logrotate.d/nginx # 强制轮转并显示详细输出 sudo logrotate -vf /etc/logrotate.d/nginx
- 轮转没生效:检查 /etc/cron.daily/logrotate 权限(需可执行)、用
-d排查规则是否匹配到文件。 - 日志写进旧文件:多半是没配 postrotate 重载 Nginx/PHP-FPM,补上信号即可。
- error: stat of failed:日志路径不存在,加
missingok容忍。 - 磁盘仍满:rotate 份数 + 单份大小相乘估算占用,别把天数据留太多天。
- 权限问题:logrotate 通常以 root 跑,但 `create` 可指定新建日志属主,避免 Web/PHP 用户写不动。
写在最后
logrotate 是"花十分钟配置、长期受益"的后台工程。核心就三件事:为每种日志写一条规则(频率、保留份数、压缩)、必要服务的 postrotate 重载信号、以及用 -d/-v 调试验证。不建议把所有日志一股脑套用同一套参数——Nginx/PHP 重载信号各有差异,应用日志可能要走 copytruncate。配好后,磁盘空间与日志文件就不会再成为你半夜惊醒的理由。



