印度节点上 logrotate 把正在写的日志转走后,我丢了六小时审计

在印度孟买的一台 4C8G 的应用节点上,Ubuntu 22.04,跑一个每天写 8GB 左右业务日志的 Go 服务。某天照例用 logrotate 做切割,配置里 daily、rotate 7、compress、postrotate 发 USR1 让进程重开文件。结果第二天查审计事件,发现切割点之后六小时的日志完全找不到,旧文件里也没有,新文件里只有切割后的内容。

先看当时的配置,问题就出在这一段:

/var/log/app/*.log {
    daily
    rotate 7
    compress
    delaycompress
    copytruncate
    postrotate
        /bin/kill -USR1 $(cat /run/app.pid)
    endscript
}

copytruncate 和 postrotate 里的重开信号同时存在,等于让两套机制抢同一个文件。logrotate 先复制内容、再把原文件截断到 0;进程收到 USR1 后关掉旧 fd、按路径重新打开,但截断已经发生,中间那几百毫秒写入的缓冲数据落在已被截断的 inode 上。更糟的是 delaycompress 让最近一份归档还叫 .1,运维脚本按 .gz 找文件,自然“看不见”那部分内容。

我的做法是二选一,不再混用。保留应用自己重开文件的能力,去掉 copytruncate,改成 create:

/var/log/app/*.log {
    daily
    rotate 7
    compress
    missingok
    notifempty
    create 0640 app app
    sharedscripts
    postrotate
        /bin/kill -USR1 $(cat /run/app.pid) 2>/dev/null || true
    endscript
}

改完用 logrotate -d /etc/logrotate.d/app 干跑一遍,确认 rename 而不是 copy,再用 lsof -p $(cat /run/app.pid) 看 fd 是否指向新 inode。补了一步校验:切割后 60 秒内比对 /proc/<pid>/fd 里的日志路径和目录下的实际文件名,不一致就告警。

代价要说清楚:create 模式下,如果应用不支持重开文件,或者信号处理有 bug,日志会继续写进已被 rename 的旧 inode,磁盘空间不释放,df 看着正常但文件已经“漂走”。所以必须确认进程真的响应信号,不能只依赖配置正确。另一个边界是 NFS 挂载的日志目录,rename 语义和本地盘不同,这时 copytruncate 反而更稳,但就得接受截断窗口内的丢数据风险,二者不可兼得。

我还踩过一个坑:sharedscripts 没加时,多文件匹配会让 postrotate 执行多次,信号发重了,进程重开两次文件,第二次打开的 fd 指向刚创建的空文件,前一次的内容又对不上。加上 sharedscripts 后只执行一次才正常。

现在这台节点的审计日志切割后能对上时间线了,但我不确定在容器里跑、日志走 stdout 再由 sidecar 收集的场景,这套信号方案还成不成立。你们在印度节点上是怎么处理“应用自己管文件”和“采集器管文件”这两种模式的边界?

0
赞同度 100% · 0 条评论 · 1 阅读

0 条评论

支持 Markdown · 回复会实时推送给对方

还没有评论,写下第一条。