Debian服务器运行一段时间后,/var/log目录膨胀是几乎所有运维都会遇到的问题。很多人等到磁盘告警才手动删几个大文件,这种做法既危险又低效。日志清理的核心不是“删”,而是建立一套可预期、可追溯的轮转与留存机制。Debian系发行版默认已经安装了logrotate,但默认配置远不足以应对生产环境的安全留存要求。下面直接讲具体怎么做。
先搞清楚哪些日志在快速增长在动手配置之前,先用du和find定位空间占用大头。执行以下命令查看/var/log下各子目录和文件的大小:
du -sh /var/log/* 2>/dev/null | sort -rh | head -20
通常nginx、apache2、mysql、postgresql、journal目录以及kern.log、syslog会是主要占用来源。还要注意一些服务可能把日志写到非标准路径,比如应用目录下的logs文件夹。用find搜索大于100MB的日志文件:
find /var/log -type f -size +100M -exec ls -lh {} \; 2>/dev/null
这一步的目的是建立一份“日志资产清单”。你不需要记在脑子里,但要知道哪些服务在产生日志、增长速度多快、单文件能长到多大。没有这个基础,后面配置轮转策略就是拍脑袋。
logrotate的核心配置逻辑Debian的logrotate主配置文件是/etc/logrotate.conf,里面通常会包含/etc/logrotate.d目录下的所有配置文件。先看主配置:
cat /etc/logrotate.conf
默认配置一般包含weekly、rotate 4、create、include /etc/logrotate.d这几项。weekly表示每周轮转一次,rotate 4表示保留4个历史文件。对于生产服务器,这个默认值往往不够。比如nginx访问日志在流量大的情况下一天就能产生几个GB,等一周再轮转,磁盘早就撑不住了。
正确的做法是按日志类型分别设置策略。在/etc/logrotate.d/目录下为每个关键服务单独建配置文件。以nginx为例,创建/etc/logrotate.d/nginx-custom:
/var/log/nginx/*.log {
daily
rotate 14
missingok
notifempty
compress
delaycompress
sharedscripts
postrotate
if [ -f /var/run/nginx.pid ]; then
kill -USR1 `cat /var/run/nginx.pid`
fi
endscript
}
这里daily表示每天轮转,rotate 14保留14天的压缩历史。compress启用gzip压缩,delaycompress让最近一次轮转的文件延迟到下一次才压缩,这样nginx在轮转后仍能继续写入未压缩的.1文件,避免需要重启服务。missingok和notifempty是安全选项,防止日志文件不存在或为空时报错。postrotate里的kill -USR1是通知nginx重新打开日志文件,这是nginx热重载日志的标准做法。
安全留存的关键:时间与空间的双重控制很多教程只讲rotate数量,但安全留存还要考虑两个维度:一是留存时间要满足合规或审计要求,二是磁盘空间要有硬性上限。rotate 14只控制文件数量,如果某天日志特别大,14个文件加起来依然可能占满磁盘。更稳妥的做法是同时使用maxsize和maxage参数。
maxsize控制单个日志文件达到多大就强制轮转,不管是否到了daily或weekly周期。例如:
/var/log/nginx/access.log {
daily
rotate 30
maxsize 500M
maxage 30
compress
delaycompress
missingok
notifempty
}
maxsize 500M表示只要access.log超过500MB,即使还没到每天轮转的时间点也会立即轮转。maxage 30表示超过30天的日志文件会被删除,即使rotate数量还没达到。这两个参数配合使用,可以确保日志既不会因为单文件过大撑爆磁盘,也不会因为历史文件过多而无限堆积。
对于数据库日志,比如MySQL的慢查询日志和错误日志,建议单独配置。MySQL的日志轮转需要特别注意,不能简单地mv文件,因为MySQL进程持有文件句柄。正确做法是在postrotate中使用mysqladmin flush-logs:
/var/log/mysql/mysql-slow.log /var/log/mysql/mysql-error.log {
daily
rotate 14
maxsize 200M
compress
delaycompress
missingok
notifempty
sharedscripts
postrotate
if [ -x /usr/bin/mysqladmin ]; then
/usr/bin/mysqladmin flush-logs
fi
endscript
}
PostgreSQL的日志轮转则更推荐使用数据库自带的log_rotation_age和log_rotation_size参数,而不是用logrotate去切文件。在postgresql.conf中设置:
log_rotation_age = 1d log_rotation_size = 100MB log_truncate_on_rotation = on
然后配合logrotate只做压缩和清理,不做轮转动作,避免双重轮转导致日志丢失。
systemd journal日志的专项清理Debian从9开始默认使用systemd,journal日志存储在/var/log/journal目录下,不受logrotate管理。如果不加限制,journal日志会持续增长。查看当前journal占用:
journalctl --disk-usage
修改/etc/systemd/journald.conf,设置以下参数:
SystemMaxUse=500M SystemKeepFree=1G MaxFileSec=1week MaxRetentionSec=2week
SystemMaxUse限制journal日志总占用不超过500MB,SystemKeepFree确保磁盘至少保留1GB空闲空间,MaxFileSec控制单个journal文件最多保留一周,MaxRetentionSec控制日志最长保留两周。修改后重启systemd-journald服务:
systemctl restart systemd-journald
如果磁盘已经快满了,可以立即清理旧的journal日志:
journalctl --vacuum-size=200M journalctl --vacuum-time=7d
这两个命令分别按大小和时间清理journal日志,可以写入cron定时任务中作为兜底。
定时任务与自动化兜底logrotate本身由cron.daily触发,Debian默认在/etc/cron.daily/logrotate中执行。但生产环境建议增加一个自定义的定时检查脚本,处理logrotate覆盖不到的情况。创建/usr/local/bin/log-cleanup.sh:
#!/bin/bash
# 清理超过30天未修改的压缩日志
find /var/log -type f -name "*.gz" -mtime +30 -delete 2>/dev/null
# 清理超过7天的临时日志
find /var/log -type f -name "*.tmp" -mtime +7 -delete 2>/dev/null
# 清理journal日志,保留500MB
journalctl --vacuum-size=500M 2>/dev/null
# 检查磁盘使用率,超过80%时强制清理
USAGE=$(df /var/log | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$USAGE" -gt 80 ]; then
find /var/log -type f -name "*.log" -size +1G -exec truncate -s 0 {} \; 2>/dev/null
fi
赋予执行权限并加入crontab:
chmod +x /usr/local/bin/log-cleanup.sh echo "0 3 * * * /usr/local/bin/log-cleanup.sh" | crontab -
这个脚本每天凌晨3点执行,处理logrotate可能遗漏的压缩文件、临时文件,并在磁盘使用率超过80%时对超过1GB的日志文件做truncate操作。truncate比直接rm更安全,因为正在写入日志的进程不会因为文件被删除而出现写入错误。
安全留存中的审计与不可篡改考量如果服务器需要满足等保或行业合规要求,日志留存不仅仅是“保留多少天”的问题,还涉及日志的完整性和不可抵赖性。单纯靠logrotate轮转和压缩,无法防止日志被恶意删除或篡改。对于高安全要求的场景,建议将关键日志实时同步到独立的日志服务器或对象存储。
使用rsync配合cron实现日志同步:
rsync -avz --delete /var/log/nginx/ user@log-server:/backup/logs/nginx/
但rsync同步存在时间窗口,攻击者如果在同步前删除日志,就无法恢复。更可靠的方式是使用syslog或rsyslog的远程转发功能,让日志在产生的同时就发送到远端。在/etc/rsyslog.conf中添加:
*.* @192.168.1.100:514
这样所有日志都会实时发送到日志服务器。日志服务器上再配置独立的留存策略和访问控制,实现日志与业务服务器的物理隔离。
对于本地留存的日志文件,可以通过设置文件属性来防止误删。使用chattr命令:
chattr +a /var/log/nginx/access.log
加a属性后,文件只能追加写入,不能删除或修改已有内容。但要注意,logrotate在轮转时需要先移除这个属性,否则轮转会失败。可以在logrotate的prerotate脚本中处理:
prerotate
chattr -a /var/log/nginx/access.log
endscript
轮转完成后再通过postrotate重新加上a属性。这种机制适合对日志完整性要求极高的场景,但会增加运维复杂度,需要谨慎评估。
验证配置与监控告警配置完成后,不要直接等生产环境验证。先用logrotate的debug模式测试:
logrotate -d /etc/logrotate.conf
这个命令会模拟执行轮转过程,输出详细的处理信息,但不会真正修改文件。确认无误后再手动强制执行一次:
logrotate -f /etc/logrotate.conf
执行后检查/var/log目录下的文件变化,确认轮转、压缩、删除都符合预期。同时检查相关服务是否正常工作,特别是nginx、mysql这类需要重新打开日志文件的服务。
最后,建议在监控系统中添加磁盘使用率告警,阈值设置在75%左右。日志清理策略再完善,也可能遇到突发流量或异常攻击导致日志暴增。监控告警能让你在磁盘真正写满之前介入处理,而不是等到服务全部宕机才发现问题。
日志管理是一个持续调整的过程。每个季度检查一次日志增长趋势,根据实际情况调整轮转周期、留存数量和大小限制。不要指望一次配置就永远不用管,业务变化、流量增长、新服务上线都会影响日志的产生速度。把日志清理纳入日常运维巡检的一部分,才能让Debian服务器长期稳定运行。
