5.5.4 日志轮转与持久化实践

日志轮转与持久化的目标是控制日志体积、避免磁盘耗尽、满足审计与排障留痕,并保证脚本运行中断时仍可追溯。建议统一日志路径与命名规范,如 /var/log/ops/脚本名/YYYYMMDD.log,目录权限建议 750,日志文件 640

原理草图(日志产生 → 轮转 → 压缩 → 持久化/集中存储):

文章图片

1) 使用 logrotate(推荐)示例#

安装与确认:

# 安装(基于 Debian/Ubuntu)
sudo apt-get update
sudo apt-get install -y logrotate

# 安装(基于 RHEL/CentOS)
sudo yum install -y logrotate

# 查看版本与主配置
logrotate --version
cat /etc/logrotate.conf

命令解释:logrotate --version 查看版本;主配置包含全局策略(如默认保留周期、压缩)。

创建业务脚本日志与轮转配置:

# 准备日志目录与权限
sudo install -d -m 750 -o root -g ops /var/log/ops/demo
sudo touch /var/log/ops/demo/demo.log
sudo chown root:ops /var/log/ops/demo/demo.log
sudo chmod 640 /var/log/ops/demo/demo.log

# 写入脚本日志示例(脚本将日志写入固定文件)
cat >/usr/local/bin/demo_job.sh <<'EOF'
#!/usr/bin/env bash
LOG=/var/log/ops/demo/demo.log
echo "$(date '+%F %T') [INFO] demo job running" >> "$LOG"
EOF
chmod +x /usr/local/bin/demo_job.sh

# 轮转配置(/etc/logrotate.d/demo_job)
cat >/etc/logrotate.d/demo_job <<'EOF'
/var/log/ops/demo/demo.log {
  daily
  size 50M
  rotate 14
  compress
  delaycompress
  missingok
  notifempty
  create 0640 root ops
  copytruncate
  postrotate
    # 轮转后记录控制日志,便于审计
    echo "$(date '+%F %T') rotated" >> /var/log/ops/demo/control.log
  endscript
}
EOF

运行测试与预期效果:

# 强制执行(-f),并显示调试信息(-d 不实际执行)
sudo logrotate -f /etc/logrotate.d/demo_job
sudo logrotate -d /etc/logrotate.d/demo_job

# 预期:生成 demo.log.1 和 demo.log.1.gz 等文件
ls -lh /var/log/ops/demo/

命令解释:-f 强制轮转,-d 仅调试输出;copytruncate 适合无法通知服务重开文件句柄的场景,但存在丢日志风险。

2) 脚本内轮转(谨慎使用)示例#

适用于无 logrotate 或极简环境。核心是原子 mvtouch、加锁避免并发。

cat >/usr/local/bin/rotate_inline.sh <<'EOF'
#!/usr/bin/env bash
LOG=/var/log/ops/demo/inline.log
LOCK=/var/lock/inline.lock
MAX_MB=10

exec 200>"$LOCK"
flock -n 200 || exit 1

# 创建日志文件
touch "$LOG"
SIZE_MB=$(du -m "$LOG" | awk '{print $1}')
TODAY=$(date +%Y%m%d)
ARCHIVE="${LOG}.${TODAY}"

if [ "$SIZE_MB" -ge "$MAX_MB" ]; then
  mv "$LOG" "$ARCHIVE"
  touch "$LOG"
  gzip -f "$ARCHIVE"
fi

echo "$(date '+%F %T') [INFO] inline log entry" >> "$LOG"
EOF
chmod +x /usr/local/bin/rotate_inline.sh

命令解释:flock -n 非阻塞加锁防止并发冲突;du -m 获取大小;mv 是原子替换;gzip -f 强制压缩。

3) 持久化与集中存储#

分离日志分区与挂载:

# 示例:将 /var/log 挂载到独立卷(假设 /dev/vdb1 已格式化)
sudo mkdir -p /var/log
sudo mount /dev/vdb1 /var/log
df -h /var/log

# 持久化配置(/etc/fstab)
echo "/dev/vdb1 /var/log ext4 defaults 0 2" | sudo tee -a /etc/fstab

命令解释:独立分区可避免日志撑满系统盘;df -h 验证挂载。

集中存储(rsyslog 远程转发示例):

# /etc/rsyslog.d/99-ops.conf
*.* @@10.0.0.10:514

命令解释:@@ 表示 TCP 传输;集中存储用于审计合规与防篡改。

容器场景建议写 stdout/stderr,由宿主采集;如需文件日志,挂载持久化卷并在宿主机统一轮转。

4) 常见问题与排错#

1) 轮转不生效

# 看 logrotate 运行日志或手动调试
sudo logrotate -d /etc/logrotate.d/demo_job
sudo grep -i logrotate /var/log/syslog /var/log/messages 2>/dev/null

排错点:配置语法错误、路径不匹配、权限不足。

2) 日志被清空或丢失
原因:copytruncate 在高并发写入时可能丢数据。
替代:服务支持 SIGHUP 重开日志。

# 例:nginx 重开日志
sudo nginx -s reopen

3) 权限错误

# 确认目录与文件权限
namei -l /var/log/ops/demo/demo.log

排错点:目录权限不足、属主组不匹配、SELinux 限制。

4) 磁盘满导致写入失败

df -h
df -i

排错点:空间或 inode 用尽,需清理旧日志或扩容。

5) 练习#

  1. 编写一个脚本,每分钟写 1 行日志到 /var/log/ops/lab/lab.log,并配置 logrotate:
    - 超过 5MB 或每日轮转
    - 保留 7 份并压缩
    - 轮转后记录控制日志
  2. /var/log 挂载到独立分区并持久化配置到 /etc/fstab
  3. 模拟高频写入,比较 copytruncatenginx -s reopen 的效果差异。
  4. 使用 logrotate -d 解释每个关键参数的执行行为。