Linux 日志轮转实战:logrotate 配置、Nginx 日志管理与排错指南
很多 VPS 跑着跑着磁盘就满了,排查下来罪魁祸首往往是不断膨胀的日志文件。Nginx 的 access.log 一天能写几十 MB,MySQL 的 error.log 会在故障时暴涨,而 systemd 的 journal 默认还会长期占用磁盘。本文从零开始讲透 Linux 日志轮转:logrotate 的核心机制、Nginx 日志实战配置、journald 清理,以及最常见的报错与排错方法。
一、为什么日志必须轮转
日志文件如果不做轮转(log rotation),会带来三个典型问题:
- 磁盘耗尽:单个日志无限制增长,最终写满 / 分区,导致服务无法写文件、数据库崩溃。
- 排错困难:日志文件过大后,grep、tail 都变慢,历史关键信息被淹没。
- 权限与删除问题:进程持有文件句柄时,直接 rm 无法释放磁盘空间,必须靠 copytruncate 或信号通知重开。
轮转的本质是:按时间或大小定期把当前日志改名归档、压缩保留一定份数,再让进程写入新的日志文件。Linux 下最通用的工具就是 logrotate。
二、logrotate 配置文件结构解析
logrotate 由 cron 每日调度执行,主配置是 /etc/logrotate.conf,各服务的独立配置放在 /etc/logrotate.d/ 目录下,主配置末尾会用 include 引入该目录。
先看主配置的关键默认项:
# /etc/logrotate.conf
weekly # 默认轮转周期:每周
rotate 4 # 默认保留 4 份归档
create # 轮转后创建新的空日志文件,权限沿用原文件
dateext # 归档文件名带日期,如 access.log-20261022
compress # 归档默认使用 gzip 压缩
include /etc/logrotate.d
单个服务配置的通用语法如下:
/var/log/example/*.log {
daily
rotate 7
maxsize 100M
compress
delaycompress
missingok
notifempty
copytruncate
create 0640 www-data www-data
dateext
}
常用指令含义:
daily / weekly / monthly:按时间轮转周期。rotate N:最多保留 N 份归档,超出删除最老的。maxsize S:文件超过 S 大小也会触发轮转(与周期取先到者)。compress:归档 gzip 压缩;delaycompress表示压缩推迟一轮,避免影响正在写的进程。missingok:文件不存在时跳过,不报错。notifempty:文件为空不轮转。copytruncate:先复制后清空,适合不支持重开日志句柄的进程。dateext:用日期命名归档,避免轮转时逐级改名覆盖。
三、实战一:Nginx 访问日志与错误日志轮转
Nginx 的 access.log 是最典型的磁盘杀手。官方推荐做法是轮转后用信号通知 Nginx 重开日志句柄,这样不需要重启服务。创建配置 /etc/logrotate.d/nginx:
/var/log/nginx/*.log {
daily
rotate 14
maxsize 200M
compress
delaycompress
missingok
notifempty
create 0640 www-data adm
sharedscripts
postrotate
if [ -f /var/run/nginx.pid ]; then
kill -USR1 `cat /var/run/nginx.pid`
fi
endscript
}
要点说明:
sharedscripts:匹配多个日志文件时,postrotate 脚本只执行一次,避免重复发信号。kill -USR1:Nginx 收到 USR1 信号会重新打开日志文件,继续写入新的 access.log。delaycompress:刚轮转出的文件等下一轮再压缩,防止 Nginx 还没切换句柄就压缩导致日志丢失。
如果不想用信号,也可以换成 copytruncate,代价是可能丢失极少量在复制间隙写入的日志:
/var/log/nginx/*.log {
daily
rotate 14
compress
copytruncate
missingok
}
四、实战二:MySQL 数据库错误日志与慢查询轮转
MySQL 的 error.log 和 slow.log 在故障或压测时可能瞬间膨胀。注意:MySQL 8 之后可以使用 audit/error 日志的 flush 命令配合轮转,最稳妥的方式仍是 copytruncate,避免依赖 MySQL 信号机制。配置 /etc/logrotate.d/mysql:
/var/log/mysql/*.log {
daily
rotate 7
maxsize 500M
compress
missingok
notifempty
copytruncate
create 0640 mysql mysql
dateext
}
手动验证 MySQL 日志轮转后空间是否释放:
# 查看当前日志大小
ls -lh /var/log/mysql/error.log
# 查看归档及压缩情况
ls -lh /var/log/mysql/ | grep -E "gz|error"
如果慢查询日志长期不需要,可直接关闭:在 my.cnf 的 [mysqld] 段设置 slow_query_log = 0,或用 SET GLOBAL 动态关闭:
mysql -e "SET GLOBAL slow_query_log = 0;"
五、systemd journald 日志清理与限额
使用 systemd 的系统里,journald 会把所有服务的输出存到 /var/log/journal,默认可能占到分区 10% 甚至更多,且不会自动缩小。通过修改 /etc/systemd/journald.conf 限制大小:
# /etc/systemd/journald.conf
[Journal]
SystemMaxUse=200M # journal 最大占用 200M
SystemMaxFileSize=50M # 单个 journal 文件上限
MaxRetentionSec=2week # 保留两周
Compress=yes
修改后重启 journald 并立即清理:
systemctl restart systemd-journald
# 立即清空 journal(谨慎:会删除所有历史日志)
journalctl --vacuum-size=100M
# 或按时间清理,保留最近 3 天
journalctl --vacuum-time=3d
日常排查 journal 占用:
journalctl --disk-usage
du -sh /var/log/journal/
六、手动触发与调试:logrotate -d / -f
配置完成后不要等 cron,先用调试模式验证语法,再强制跑一次:
# 调试模式:只输出执行计划,不真正轮转(关键排错手段)
logrotate -d /etc/logrotate.conf
# 强制轮转单个配置
logrotate -f /etc/logrotate.d/nginx
# 强制轮转并输出详细日志
logrotate -v -f /etc/logrotate.conf
常见调试输出示例:
reading config file /etc/logrotate.d/nginx
reading config info for /var/log/nginx/*.log
Handling 1 logs
rotating pattern: /var/log/nginx/*.log after 1 days (14 rotations)
强制轮转后立即检查结果:
ls -lh /var/log/nginx/
# 期望看到 access.log 被重命名为 access.log-20261022,新 access.log 由 Nginx 重新创建
七、常见报错与排错方法
1. 权限不足:error: failed to change mode of ... to 0640
logrotate 由 root 的 cron 执行,但目标目录权限不对也会失败。检查目录与文件属主,并用 create 指令显式指定属主属组:
chown -R www-data:www-data /var/log/nginx/
chmod 750 /var/log/nginx/
2. 重复轮转或跳过:logrotate: error: skipping ... because parent directory is insecure
配置所在目录如果被其他用户可写,logrotate 出于安全考虑会跳过。确认 /etc/logrotate.d/ 目录权限为 root 所有且不可被他人写:
chown root:root /etc/logrotate.d/
chmod 755 /etc/logrotate.d/
3. 磁盘空间一直没释放
进程仍持有旧文件句柄。用 lsof | grep deleted 查找被删除但仍被占用的文件,重启对应服务或按上文方式发信号重开日志:
lsof +L1 | grep -i nginx
4. 归档被压缩成 .1.gz 而不是 .gz
这是 delaycompress 的正常表现:当轮发生成的 .1 文件要等下一轮才被压缩,无需处理。
5. cron 没生效
确认 logrotate 的 cron 任务存在,并检查系统日志:
ls -l /etc/cron.daily/logrotate
grep logrotate /var/log/syslog /var/log/cron 2>/dev/null
八、总结与自查清单
一套健康的日志轮转方案应该满足:日志按周期归档、保留份数可控、归档自动压缩、磁盘占用有上限、不丢关键日志、轮转后服务句柄正确切换。部署后建议对照以下清单自查:
# 1. 所有大日志都有轮转配置
du -sh /var/log/* | sort -rh | head -20
# 2. 配置语法正确
logrotate -d /etc/logrotate.conf 2>&1 | grep -i error
# 3. journal 占用受限
journalctl --disk-usage
# 4. 被删除仍占空间的句柄
lsof +L1 | wc -l
把 logrotate、journald 限额和定期磁盘巡检组合起来,VPS 的磁盘基本不会再被日志“吃满”。后续可以结合监控工具(如 node_exporter + Prometheus)对 filesystem_avail_bytes 设置告警,磁盘接近阈值时第一时间收到通知。


