Debian服务器的/var/log目录膨胀速度往往超出预期,运维人员最头疼的场景莫过于凌晨收到磁盘使用率告警,登录后发现某个应用日志把根分区撑爆了。这不是假设,是生产环境里频繁发生的事故。解决这个问题的核心机制就是logrotate,而Debian默认已经安装了这个工具,问题在于默认配置过于保守,根本应对不了稍具规模的生产环境。
logrotate的核心运行机制logrotate不是守护进程,它依赖cron定时触发。Debian系统里,主配置文件位于/etc/logrotate.conf,而/etc/logrotate.d/目录下的每个文件对应一个日志轮转策略。执行流程是:cron每天运行/etc/cron.daily/logrotate脚本,该脚本调用logrotate命令,读取主配置并合并子目录下所有配置文件,然后逐一处理。理解这个调用链很重要,因为很多人在/etc/logrotate.d/里写了配置却发现不生效,原因往往是主配置文件里的全局参数覆盖了子配置,或者cron执行时间没到。
查看logrotate的实际执行情况,可以手动运行带调试参数的命令:
logrotate -d /etc/logrotate.conf
-d参数是debug模式,只模拟执行不实际轮转,输出会详细展示每个日志文件的处理决策。加上-v参数可以显示详细信息,-f参数则是强制执行,忽略时间条件。排查配置问题时这三个参数组合使用非常有效。
轮转策略的核心参数配置一个生产级的日志轮转配置必须精确控制四个维度:轮转周期、保留数量、压缩策略、触发条件。先看一个典型的Nginx日志配置示例:
/var/log/nginx/*.log {
daily
missingok
rotate 90
compress
delaycompress
notifempty
create 640 www-data adm
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
endscript
}
这个配置逐项拆解:daily表示每天轮转一次,对于日均访问量大的站点这是合理的频率。rotate 90保留90天的日志,按日轮转意味着保留三个月,满足大多数合规要求。compress开启gzip压缩,delaycompress则延迟一天压缩最新轮转出的文件,因为轮转后服务可能还在写这个文件,立即压缩会导致文件句柄错乱。create 640 www-data adm指定新建日志文件的权限和属主属组,这行经常被忽略但极其关键,权限不对会导致服务无法写入新日志。
notifempty表示空文件不轮转,避免产生大量零字节的轮转文件。sharedscripts让postrotate脚本在所有匹配的日志文件处理完后只执行一次,而不是每个文件都执行一次。postrotate里的kill -USR1是通知Nginx优雅地重新打开日志文件,这是信号机制而非重启服务,不会中断连接。
时间条件与大小条件的组合使用仅靠时间触发轮转存在风险,某个日志文件可能在一天内暴增几十GB。需要引入size参数做双重保险:
/var/log/app/*.log {
daily
size 500M
rotate 30
compress
missingok
copytruncate
}
daily和size 500M同时存在时是“或”关系,满足任一条件即触发轮转。这意味着即使没到24小时,只要文件超过500MB就会轮转。对于写入量波动大的应用,这种组合策略能有效防止单文件过大。copytruncate参数适用于不支持信号重载日志的服务,它的工作原理是复制日志内容到轮转文件后截断原文件,而非移动文件。缺点是截断瞬间可能丢失极少量日志,但对于不重要的应用日志完全可以接受。
压缩策略与存储空间精算日志压缩是空间治理的核心环节。gzip默认压缩文本日志能达到10:1甚至更高的压缩比,一个1GB的日志文件压缩后通常只有100MB左右。但压缩时机需要仔细考量,delaycompress前面提过,还有一个关键参数compresscmd可以指定压缩程序:
compresscmd /usr/bin/zstd compressoptions -T2 -3 compressext .zst
zstd是比gzip更现代的选择,压缩速度更快、压缩比相当或更好,-T2使用两个线程并行压缩,-3是压缩级别。对于每天产生几十GB日志的服务器,换用zstd能显著减少压缩时的CPU耗时。compressext指定压缩文件后缀,默认.gz改为.zst保持一致性。
解决日志轮转后服务不写日志的经典问题这是最常被问到的故障场景:logrotate执行后,服务不再写入新日志。根本原因在于服务进程持有的是旧日志文件的文件描述符,当logrotate通过mv移动文件后,服务仍然向已重命名的文件写入。解决方法取决于服务类型:
第一类,支持信号重载的服务如Nginx、Apache,使用postrotate发送重载信号,前面示例已展示。第二类,通过管道或syslog写入的服务,这类服务通常能自动感知文件变化。第三类,不支持信号也不支持管道的老旧服务,必须使用copytruncate参数。还有一种情况是使用systemd-journald的系统,日志由journald管理,logrotate处理的是journald导出的文本日志,需要额外配置journald的存储上限。
systemd-journald的日志空间治理Debian默认同时运行rsyslog和systemd-journald,两者都会产生日志存储开销。journald的二进制日志存放在/var/log/journal/,其空间占用经常被忽视。配置/etc/systemd/journald.conf:
SystemMaxUse=2G SystemKeepFree=5G SystemMaxFileSize=200M MaxRetentionSec=30day Compress=yes
SystemMaxUse限制journald总存储上限为2GB,SystemKeepFree保证磁盘至少保留5GB空闲空间,两者共同作用时取更严格的条件。SystemMaxFileSize限制单个journal文件大小,MaxRetentionSec设置最长保留30天。配置生效需要重启systemd-journald服务。这套参数能确保journald不会成为磁盘空间的隐形杀手。
自定义轮转脚本处理特殊日志有些日志不适合用logrotate直接处理,比如需要先解析再归档的业务日志。可以在/etc/logrotate.d/里配置一个预处理脚本:
/var/log/custom/*.log {
daily
rotate 30
prerotate
/usr/local/bin/log-parser.sh $1
endscript
postrotate
/usr/local/bin/log-uploader.sh $1
endscript
}
prerotate在轮转前执行,可以解析日志提取关键数据入库;postrotate在轮转后执行,可以将归档文件上传到对象存储。$1是logrotate传入的当前处理的日志文件路径。这种模式适合需要长期归档但本地存储有限的场景,轮转后立即上传然后本地文件按rotate数量自然淘汰。
监控与告警:让日志轮转可观测配置完轮转策略不代表万事大吉,必须建立监控机制。三个关键监控点:第一,监控/var/log目录的磁盘使用率,设置85%告警阈值;第二,监控logrotate的执行状态,检查/var/lib/logrotate/status文件,这个文件记录了每个日志最后一次轮转的时间戳,如果某个日志长时间未轮转说明配置可能有问题;第三,监控轮转后服务日志写入是否正常,可以在postrotate里加入验证逻辑:
postrotate
sleep 2
if [ ! -f /var/log/nginx/access.log ]; then
echo "Log rotation failed" | mail -s "Alert" admin@example.com
fi
endscript
sleep 2等待服务重新打开文件,然后检查新日志文件是否存在。这种简单的健康检查能及时发现轮转导致的服务中断。
空间应急处理:手动清理与临时扩容当磁盘已经告警,需要立即释放空间。优先清理的目标是:已轮转但未压缩的大文件、journald日志、apt缓存、core dump文件。快速定位大文件命令:
find /var/log -type f -size +100M -exec ls -lh {} \; | sort -k5 -h
列出/var/log下大于100MB的文件并按大小排序。对于已经轮转的日志文件(带日期后缀或数字后缀),如果磁盘空间紧迫可以直接删除,但要保留当前正在写入的文件。清理journald日志可以立即生效:
journalctl --vacuum-size=500M
这条命令将journald日志缩减到500MB以内,清理的是最旧的条目。如果频繁遇到空间不足,根本解决方案是调整logrotate的rotate参数,减少保留天数,或者将归档日志转移到单独的存储卷。
日志归档的分区隔离策略生产环境应该将/var/log挂载到独立分区或磁盘,这样即使日志写满也不会影响根分区和业务数据分区。Debian安装时可以选择LVM布局,预留调整空间。如果系统已经运行,可以使用logrotate的olddir参数将归档文件转移到其他挂载点:
/var/log/app/*.log {
daily
rotate 60
olddir /data/logs/archive
compress
delaycompress
}
olddir指定归档目录,当前日志仍在原位置,轮转后的文件移动到/data/logs/archive。这个参数需要目标目录存在且权限正确,logrotate不会自动创建目录。通过这种方式,/var/log只保留当前活跃日志,历史归档全部转移到容量更大的存储上,实现冷热分离。
日志轮转配置本质上是对存储成本、合规要求和运维便利性的权衡。rotate设太大浪费存储,设太小丢失历史;compress省空间但消耗CPU;daily保证时效但可能漏掉突发增长。没有一套配置适用所有场景,理解每个参数的实际效果,根据自己服务器的日志量和磁盘容量做精算,才能写出真正有效的配置。最后提醒一点,修改/etc/logrotate.d/下的配置后,用logrotate -d做一次模拟运行,确认输出符合预期再让cron自动执行,这个习惯能避免很多生产事故。
