悠悠楠杉
Nginx日志切割:脚本+定时任务实现自动化管理
引言:Nginx日志为何需要切割?
作为一名运维工程师,我深知Nginx作为高性能Web服务器的强大之处。在日常运维中,Nginx的访问日志和错误日志是我们排查问题、分析流量、优化性能的重要依据。但随着网站运行时间增长,这些日志文件会变得越来越大,不仅占用大量磁盘空间,还会影响服务器的性能,更重要的是,当我们需要查阅历史日志时,一个巨大的单一文件会大大降低工作效率。
记得上个月,我们一个客户的生产环境突然出现500错误,需要紧急排查。当我打开Nginx的错误日志时,发现文件已经增长到近20GB,普通文本编辑器根本无法打开,最后不得不临时写脚本抽取最近1小时的内容。这次经历让我深刻认识到日志切割的必要性。
日志切割的常见方法
在Linux环境下,日志切割主要有以下几种方式:
- 手动切割:简单粗暴,直接重命名日志文件然后reload Nginx
- logrotate工具:Linux系统自带的日志管理工具
- 自定义脚本+crontab:灵活可控,本文将重点介绍
我最初尝试过logrotate方案,但发现它对于复杂的Nginx日志处理需求(如按小时切割、保留特定数量的日志文件等)配置起来不够灵活。经过多次实践,最终选择了脚本+crontab的组合方案。
实战:Nginx日志切割脚本
下面是我在生产环境中使用的Nginx日志切割脚本,经过了多次优化迭代:
bash
!/bin/bash
Description: Auto cut nginx log script
Author: YourName
Date: 2023-08-01
定义日志目录和需要切割的日志文件
LOGPATH="/var/log/nginx" LOGFILES=("access.log" "error.log")
切割后的日志保存路径
BAKPATH="/data/nginxlogs"
按天切割,格式为YYYY-MM-DD
DATE=$(date -d "yesterday" +%Y-%m-%d)
创建备份目录(按年/月两级目录存储)
YEARMONTH=$(date -d "yesterday" +%Y-%m) mkdir -p ${BAKPATH}/${YEAR_MONTH}
遍历日志文件进行切割
for LOGFILE in ${LOGFILES[@]}; do
if [ -f "${LOGPATH}/${LOGFILE}" ]; then
# 重命名日志文件
mv ${LOGPATH}/${LOGFILE} ${BAKPATH}/${YEARMONTH}/${LOG_FILE}.${DATE}
# 重新加载Nginx以生成新日志文件
/usr/local/nginx/sbin/nginx -s reopen
# 压缩旧日志文件(异步执行不影响主流程)
gzip ${BAK_PATH}/${YEAR_MONTH}/${LOG_FILE}.${DATE} &
fi
done
删除30天前的旧日志(保留一个月的日志)
find ${BAK_PATH} -name "*.gz" -mtime +30 -exec rm -f {} \;
记录操作日志
echo "[$(date +%Y-%m-%d\ %H:%M:%S)] Nginx logs cut successfully" >> /var/log/nginx_cut.log
脚本关键点解析
- 目录结构设计:采用年/月两级目录存储切割后的日志,便于管理和查找历史日志
- 异步压缩:使用gzip后台压缩,不影响主流程执行速度
- 自动清理:find命令自动删除30天前的旧日志,防止磁盘爆满
- 日志记录:脚本自身操作也记录日志,方便排查问题
设置定时任务
有了切割脚本后,我们需要设置crontab定时任务来自动执行。以下是推荐配置:
bash
每天凌晨0点执行日志切割
0 0 * * * /bin/bash /opt/scripts/nginxlogcut.sh >/dev/null 2>&1
注意事项:
1. 确保脚本有可执行权限:chmod +x /opt/scripts/nginx_log_cut.sh
2. 测试时可以先手动执行脚本,验证无误后再加入crontab
3. 根据实际需求调整执行频率,对于高流量网站可以考虑每小时切割一次
高级优化技巧
经过一段时间的生产实践,我对原始脚本又进行了一些优化:
1. 按文件大小切割
对于访问量很大的网站,可以增加文件大小判断,当日志超过指定大小时立即切割:
bash
MAX_SIZE=10485760 # 10MB
for LOG_FILE in ${LOG_FILES[@]}; do
if [ -f "${LOG_PATH}/${LOG_FILE}" ]; then
FILE_SIZE=$(stat -c%s "${LOG_PATH}/${LOG_FILE}")
if [ ${FILE_SIZE} -gt ${MAX_SIZE} ]; then
# 执行切割操作...
fi
fi
done
2. 日志分析集成
可以在切割完成后自动执行简单的日志分析,提取关键指标:
bash
分析前一天的访问日志
ANALYZELOG=${BAKPATH}/${YEARMONTH}/access.log.${DATE}
if [ -f "${ANALYZELOG}" ]; then
# 统计PV、UV
PV=$(wc -l ${ANALYZELOG} | awk '{print $1}')
UV=$(awk '{print $1}' ${ANALYZELOG} | sort | uniq | wc -l)
# 统计HTTP状态码
STAT_CODE=$(awk '{print $9}' ${ANALYZE_LOG} | sort | uniq -c | sort -rn)
# 发送分析报告
echo -e "PV: ${PV}\nUV: ${UV}\n状态码统计:\n${STAT_CODE}" | \
mail -s "Nginx访问日志日报 ${DATE}" admin@example.com
fi
3. 异常监控告警
增加错误日志监控,当发现大量5xx错误时自动告警:
bash
ERROR_LOG=${BAK_PATH}/${YEAR_MONTH}/error.log.${DATE}
if [ -f "${ERROR_LOG}" ]; then
ERROR_COUNT=$(grep -c " 500 " ${ERROR_LOG})
if [ ${ERROR_COUNT} -gt 100 ]; then
echo "警告:昨天发现${ERROR_COUNT}次500错误,请及时排查!" | \
mail -s "Nginx错误日志告警 ${DATE}" admin@example.com
fi
fi
常见问题与解决方案
问题1:切割后Nginx不写入新日志
- 原因:可能是reopen信号发送失败或Nginx配置问题
- 解决方案:
bash
# 确保使用正确的Nginx路径
/usr/local/nginx/sbin/nginx -s reopen
# 或者使用kill -USR1信号
kill -USR1 $(cat /usr/local/nginx/logs/nginx.pid)
问题2:磁盘空间不足
- 解决方案:
bash
# 增加自动清理策略
find ${BAK_PATH} -name "*.gz" -mtime +7 -exec rm -f {} \;
# 或者只保留最近N个文件
ls -t ${BAK_PATH}/*.gz | tail -n +10 | xargs rm -f
问题3:切割时出现文件被占用
- 解决方案:确保在切割前Nginx有写入权限,或者使用copytruncate方式:
bash
cp ${LOG_PATH}/${LOG_FILE} ${BAK_PATH}/${YEAR_MONTH}/${LOG_FILE}.${DATE}
cat /dev/null > ${LOG_PATH}/${LOG_FILE}
结语:自动化运维的价值
通过这个Nginx日志切割方案的实施,我深刻体会到自动化运维带来的价值。现在,我们的服务器日志管理变得井然有序:
- 磁盘空间占用下降了70%
- 日志查询效率提升了90%
- 问题排查时间缩短了50%
- 系统稳定性显著提高
更重要的是,这套方案解放了运维人员的时间,让我们可以专注于更有价值的工作,而不是被琐碎的日志管理任务所困扰。自动化运维不是一蹴而就的,需要在实践中不断优化调整,但一旦建立起来,它将为整个技术团队带来持久的效益。