Shell脚本如何轮转日志避免撑爆磁盘:从原理到生产级配置全解析
目录导读
- 为什么需要日志轮转?——磁盘撑爆的代价
- 日志轮转的核心原理:切割、压缩与清理
- 实战:Shell脚本实现三种日志轮转方案
- 高频问题FAQ:轮转脚本常见陷阱与解决方案
- 生产环境最佳实践:结合logrotate与系统级优化

为什么需要日志轮转?——磁盘撑爆的代价
问题场景:某金融系统后台每天生成约2GB的access_log,运行30天后,/var/log分区直接爆满,导致服务无法写入新日志,最终数据库连接池耗尽,业务中断超4小时。
核心风险:
- 日志文件持续增长(单个日志可达百GB)
- 磁盘空间耗尽后,应用可能崩溃(如MySQL、Nginx)
- 运维人员无法及时发现告警(凌晨突发概率高)
轮转的本质:将单一日志文件切割为多个带时间戳的小文件,并按策略清理最旧的归档日志,确保磁盘占用始终在可控阈值内。
日志轮转的核心原理:切割、压缩与清理
1 三个关键动作
| 动作 | 说明 | 典型实现 |
|---|---|---|
| 切割 | 将当前日志重命名,创建新空日志文件 | mv access.log access_2025-03-01.log; touch access.log |
| 压缩 | 对归档文件进行gzip/bzip2压缩,节省80%空间 | gzip access_2025-03-01.log |
| 清理 | 删除超过保留天数的归档文件 | find ./ -name "access_*.gz" -mtime +30 -delete |
2 两种主流轮转策略
- 按大小轮转:日志达到指定size(如100MB)即触发切割
- 按时间轮转:每小时/天/周固定时间点切割(更可控)
生产建议:优先采用按时间轮转,配合大小兜底(如当日日志超1GB强制切割),避免单文件过大。
实战:Shell脚本实现三种日志轮转方案
1 方案一:基于日期的简单轮转(适合中小型应用)
#!/bin/bash
# rotate_date.sh - 按日期轮转,保留最近30天
LOG_PATH="/var/log/myapp"
LOG_FILE="app.log"
RETENTION_DAYS=30
DATE=$(date +%Y%m%d)
cd $LOG_PATH || exit 1
# 切割:将当前日志移动到带日期的归档文件
[ -f "$LOG_FILE" ] && mv "$LOG_FILE" "${LOG_FILE}.${DATE}"
# 创建新空日志文件(保持权限)
touch "$LOG_FILE"
chmod 644 "$LOG_FILE"
# 压缩旧日志(跳过前一天的日志,因为可能正在写入)
find . -name "${LOG_FILE}.*" -mtime +0 -exec gzip {} \; 2>/dev/null
# 删除超过保留天数的压缩日志
find . -name "${LOG_FILE}.*.gz" -mtime +${RETENTION_DAYS} -delete
# 发送SIGHUP给监听进程(如syslog-ng)
if [ -f /var/run/syslog-ng.pid ]; then
kill -HUP $(cat /var/run/syslog-ng.pid)
fi
适用场景:自定义应用日志(Java/Python),无需重载服务即可继续写入。
2 方案二:带大小检测的混合轮转(避免单文件过大)
#!/bin/bash
# rotate_mixed.sh - 每天轮转一次,但当日志超1GB时立即触发
MAX_SIZE=$((1024*1024*1024)) # 1GB
while true; do
CURR_SIZE=$(stat --format=%s "$LOG_PATH/$LOG_FILE" 2>/dev/null || echo 0)
if [ $CURR_SIZE -gt $MAX_SIZE ]; then
echo "[$(date)] 日志超1GB,强制执行大小轮转" >> /var/log/rotate_history.log
# 执行切割(使用当前时间戳)
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
mv "$LOG_PATH/$LOG_FILE" "$LOG_PATH/${LOG_FILE}.${TIMESTAMP}"
touch "$LOG_PATH/$LOG_FILE"
# 压缩旧文件(排除刚刚切割的)
gzip "$LOG_PATH/${LOG_FILE}.${TIMESTAMP}"
fi
sleep 300 # 每5分钟检查一次
done
关键优化:使用while sleep实现守护进程化,结合cron每天固定时间轮转一次。
3 方案三:集成压缩与磁盘告警(生产级)
#!/bin/bash
# rotate_prod.sh - 带磁盘阈值警告的完整轮转脚本
THRESHOLD_WARN=80 # 磁盘使用率超过80%触发告警
THRESHOLD_CRIT=90 # 超过90%强制删除非必要日志
check_disk_usage() {
local usage=$(df /var/log | awk 'NR==2{print $5}' | sed 's/%//')
if [ $usage -ge $THRESHOLD_CRIT ]; then
echo "[ERROR] 磁盘使用率 ${usage}%,大于 ${THRESHOLD_CRIT}%,删除7天前日志"
find $LOG_PATH -name "*.gz" -mtime +7 -delete
find $LOG_PATH -name "*.bz2" -mtime +7 -delete
elif [ $usage -ge $THRESHOLD_WARN ]; then
echo "[WARN] 磁盘使用率 ${usage}%,接近告警阈值"
# 可选择发送邮件:mail -s "Log Rotate Warning" admin@domain.com
fi
}
# 主流程:先检查磁盘,再轮转
check_disk_usage
# 常规轮转逻辑(参考方案一)
# ......
为什么需要磁盘阈值:防止日志量暴增(如攻击流量)导致轮转脚本还在运行,磁盘已被撑满。
高频问题FAQ:轮转脚本常见陷阱与解决方案
Q1:为什么我轮转后,应用还在写旧文件?
A:应用持有旧文件描述符,需在切割后发送SIGHUP信号或在脚本中执行kill -USR1 <PID>(如Nginx),或使用logrotate的copytruncate指令(复制后截断原文件)。
Q2:压缩后日志无法正常查看?
A:部分应用框架(如Apache)会持续写入文件直到压缩完成,解决方案:先使用mv重命名,压缩时排除/proc目录下的文件,或等1-2分钟再压缩。
Q3:脚本执行时,日志文件正在被写入,会不会丢数据?
A:使用mv命令是原子操作,不会丢数据,但可能丢失最后几十毫秒的日志,生产环境建议在业务低谷期执行轮转(如凌晨3点)。
Q4:如何避免重复轮转?
A:在脚本开头添加锁机制:
LOCKFILE="/var/log/rotate.lock"
if [ -f $LOCKFILE ]; then
echo "轮转脚本已在运行,退出"
exit 0
fi
trap "rm -f $LOCKFILE" EXIT
touch $LOCKFILE
生产环境最佳实践:结合logrotate与系统级优化
1 为什么推荐使用logrotate而非纯Shell脚本?
- logrotate是Linux标准日志轮转工具,支持:
- 多应用预设配置(Nginx/MySQL/PostgreSQL)
- 自动检测夏令时
- 邮局通知(配合mail命令)
- Shell脚本适合高度定制场景,如:
- 需要控制云监控告警
- 日志文件数量需精细备份
2 一个完整的logrotate配置示例(/etc/logrotate.d/myapp)
/var/log/myapp/app.log {
daily # 每日轮转
rotate 30 # 保留30个归档
compress # 启用压缩
delaycompress # 延迟一天压缩(避免正在写入时压缩)
missingok # 如果日志缺失,不报错
notifempty # 空文件不轮转
create 0644 root root # 创建新文件时设置权限
postrotate # 轮转后执行脚本
/usr/bin/killall -HUP myapp 2>/dev/null || true
endscript
size 500M # 大小超过500M也触发轮转
maxage 30 # 删除超过30天的归档
}
3 监控报警与自动化
# 在crontab中添加每5分钟一次的磁盘检查
*/5 * * * * /root/scripts/check_disk_alert.sh
# check_disk_alert.sh内容:
#!/bin/bash
USAGE=$(df /var/log | awk 'NR==2{print $5}' | sed 's/%//')
if [ $USAGE -gt 85 ]; then
# 发送到企业微信/钉钉机器人
curl -X POST "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx" \
-H "Content-Type: application/json" \
-d '{"msgtype":"text","text":{"content":"日志磁盘使用率 '$USAGE'%,请检查轮转脚本"}}'
fi
4 性能优化建议
- 使用异步压缩:压缩操作可能消耗CPU,建议在低负载时段执行(如cron脚本中
ionice -c 3指定空闲I/O优先级) - 分目录存储:按应用/日期创建子目录(如
/var/log/myapp/2025/03/),避免单目录文件过多 - 限制归档数量:即使保留30天,若日志量极大,可配置
rotate 90并配合maxsize 100M防止单个归档过大