从零到自动化运维的完整指南
目录导读
为什么需要日志轮转压缩?
在实际运维中,日志文件若不加以管理,会迅速膨胀至数百GB甚至TB级别,导致磁盘空间耗尽、性能下降,甚至引发服务崩溃,编写日志轮转压缩脚本的目的是自动化切割、压缩、归档旧日志,同时清理超期文件。

核心价值:
- 节约存储成本(压缩率可达90%)
- 保证日志可读性(单文件不超过合理大小)
- 符合合规要求(保留周期可控)
典型场景:Web服务器(Nginx/Apache)、数据库(MySQL慢查询日志)、Java应用(Spring Boot)、系统日志(rsyslog)
核心原理与常用工具
实现逻辑
日志文件 → 监测(大小/时间)→ 重命名/切割 → 压缩 → 清理旧文件
工具对比
| 工具 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| logrotate | Linux标准,配置简单 | 依赖系统cron,灵活性差 | 系统级日志 |
| 自定义Shell脚本 | 完全可控,可集成监控 | 需手工处理并发 | 复杂业务需求 |
| Python脚本 | 逻辑清晰,跨平台 | 依赖Python环境 | 需要与API集成 |
推荐方案:用Shell脚本作为基础,结合cron定时任务,辅以gzip或pigz(并行压缩)实现高性能轮转。
手把手编写Shell脚本
第一步:基础版本
#!/bin/bash
LOG_DIR="/var/log/myapp"
LOG_FILE="app.log"
MAX_SIZE="100M" # 轮转阈值
RETENTION_DAYS=30
# 获取当前日志大小
current_size=$(stat --printf="%s" "$LOG_DIR/$LOG_FILE" 2>/dev/null)
max_bytes=$(echo $MAX_SIZE | awk '{print $1 * 1024 * 1024}')
if [ $current_size -ge $max_bytes ]; then
timestamp=$(date +"%Y%m%d_%H%M%S")
mv "$LOG_DIR/$LOG_FILE" "$LOG_DIR/${LOG_FILE}.${timestamp}"
gzip "$LOG_DIR/${LOG_FILE}.${timestamp}" # 压缩
touch "$LOG_DIR/$LOG_FILE" # 创建新空文件
fi
# 清理过期日志
find "$LOG_DIR" -name "${LOG_FILE}.gz" -mtime +$RETENTION_DAYS -delete
改进点
- 增加
copytruncate模式(零停机) - 添加信号处理(防止进程崩溃)
- 支持多日志文件配置
高级策略:按大小/时间轮转
混合策略脚本片段
# 按时间轮转(每日凌晨执行)
if [ "$1" == "daily" ]; then
archive_name="${LOG_FILE}.$(date -d yesterday +%Y%m%d)"
cp "$LOG_DIR/$LOG_FILE" "$LOG_DIR/$archive_name"
:> "$LOG_DIR/$LOG_FILE" # 清空原文件
gzip -9 "$LOG_DIR/$archive_name"
fi
# 按大小轮转(实时监测)
if [ $(stat -c%s "$LOG_DIR/$LOG_FILE") -gt 104857600 ]; then # 100MB
#... 执行切割压缩
fi
关键要点:
- 使用重定向创建空文件(保留文件inode)
- 压缩后删除原日志(避免重复处理)
- 同时支持
rotate_by_size和rotate_by_time
压缩与删除策略优化
高性能压缩技巧
# 使用pigz并行压缩(需安装) command -v pigz &>/dev/null && COMPRESS_CMD="pigz -p $(nproc)" || COMPRESS_CMD="gzip" $COMPRESS_CMD "$log_archive" # 使用zstd实现更快压缩 zstd -q -3 --rm "$log_archive" # 速度比gzip快5倍
删除策略
- 相对时间:
find -mtime +60 - 绝对数量:保留最后10个文件
- 混合删除:对最近30天的文件按天保留,更早的按周保留
# 示例:保留最近30个压缩文件,仅保留最近一周的原始日志
ls -1t "$LOG_DIR"/*.gz | tail -n +31 | xargs rm -f
find "$LOG_DIR" -name "${LOG_FILE}*" ! -name "*.gz" -mtime +7 -delete
错误处理与日志审计
完整错误处理模板
#!/bin/bash
set -euo pipefail
ERROR_LOG="/var/log/rotate_error.log"
log_error() {
echo "[$(date)] ERROR: $*" >> "$ERROR_LOG"
# 可选:发送告警到企业微信/钉钉
}
rotate_log() {
local src=$1 dest=$2
if ! cp "$src" "$dest" 2>/dev/null; then
log_error "Failed to copy $src to $dest"
return 1
fi
# 压缩...
}
trap 'log_error "Script interrupted"' INT TERM
审核要点:
- 记录每次旋转的文件名、大小、耗时
- 检查磁盘剩余空间(小于1%时停止轮转)
- 防止重复执行(使用锁文件)
LOCK_FILE="/var/run/rotate.lock" exec 200>"$LOCK_FILE" flock -n 200 || exit 1 # 仅允许单实例运行
常见问题与解答
Q1:日志轮转后,应用进程仍然往旧文件写日志怎么办?
A:使用copytruncate模式(先复制后清空),或者向进程发送SIGUSR1信号让应用重新打开日志文件。
kill -USR1 $(cat /var/run/myapp.pid)
Q2:压缩后的日志无法直接grep搜索?
A:使用zcat或pigz -dc配合管道实时解压,或配置find ... -exec zcat {} \; | grep ...,建议生成索引文件加速检索。
Q3:脚本执行时间过长导致重叠?
A:添加超时机制timeout 300 ./rotate.sh,并使用flock防止并发冲突。
Q4:如何处理日志文件包含中文字符或特殊符号?
A:在脚本中设置export LANG=C避免locale干扰,文件名中避免使用空格。
Q5:是否有现成的开源工具推荐?
A:除了logrotate,还可以考虑:
cronolog(按时间切分)rotatelogs(Apache自带)savelogs(Python生态)
生产环境部署建议
- 测试验证:先在测试环境运行
./rotate.sh --dry-run模拟执行 - cron配置:编辑
/etc/crontab添加每小时执行35 * * * * root /opt/scripts/log_rotate.sh >> /var/log/rotate_cron.log 2>&1 - 监控集成:加入Prometheus或者Zabbix监控指标
- 磁盘使用率>80%告警
- 轮转失败计数>3触发工单
- 版本控制:脚本纳入Git管理,变更需经过Code Review
通过以上方法,你可以构建一套稳定、可扩展的日志轮转压缩体系,确保服务器长期运行的日志管理自动化、无故障,如有更复杂的需求(如多数据中心、实时流式日志),可以结合ELK或Loki实现云端归档。