meta 日志按天切分后,我用 logrotate 的 copytruncate 验证了一件事

meta 的日志之前一直是一整块,跑到第 9 个月时单文件到了 3.4GB,grep 一次要等十几秒,才决定按天切。环境是 Debian 12,meta 进程自己不做 reopen,切完不重启就得让 logrotate 替它把文件描述符保住。

先说我最后用的配置,直接能抄:

/var/log/meta/*.log {
    daily
    rotate 14
    size 200M
    missingok
    notifempty
    compress
    delaycompress
    copytruncate
    dateext
    dateformat -%Y%m%d
}

关键就是 copytruncate。手动跑一次 logrotate -d /etc/logrotate.d/meta 看 dry-run 的输出,确认 rotate 和 postrotate 的顺序没问题,再 -f 强制跑一次,然后用 ls -l /proc/$(pgrep -f meta)/fd | grep log 确认进程还指着同一个 inode。这一步不做,后面出问题根本分不清是切分还是进程自己的锅。

为什么选 copytruncate 而不是 create:meta 没有 SIGHUP 重开日志的路径,重启一次要重新加载索引,3.4GB 日志对应的那批记录加载回来大约要 2 分钟,白天不能随便停。copytruncate 的机制是先 cp 再 truncate 原文件,进程的文件描述符一直有效,写的还是同一个 inode,所以不用重启。代价是 cp 和 truncate 之间有个窗口,这期间写进来的行会丢——我们的日志量大概每秒几十行,窗口在毫秒级,偶尔丢一两行能接受,但我确实在压测时见过丢掉整段几十行的,所以对账类日志我没敢用这套,单独走了另一条路径。

会失效的地方有三个,都是我踩过的:一是文件特别大的时候 copytruncate 的 cp 本身很慢,3.4GB 那次切了快 20 秒,期间日志是双写的,磁盘 IO 直接顶满,后来把 size 压到 200M 就没再出现;二是 dateext 配合 copytruncate,同一天强制跑两次会覆盖前一份,我第一次测试就把刚切的文件冲掉了,得加 -f 之前先看 ls;三是 meta 有些子进程会自己再打开一次日志,copytruncate 对它们无效,这些子进程切完后仍然写旧 inode,表现出来就是新文件一直是空的,lsof +L1 能看到 deleted 的文件还挂着。

现在的状态是每天切一次、保留 14 天,grep 单日文件基本在 1 秒内。想问一句:你们那边 meta 的日志切分是靠进程自己 reopen,还是也用 copytruncate 这种带窗口的方案?如果进程支持 reopen,你们是怎么确认它真的重开了而不是继续写旧 inode 的?

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

0 条评论

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

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