3. 采集器层故障(二):日志文件轮转(Log Rotation)导致的数据丢失或重复采集,配置检查与 inode 追踪

日志文件轮转(Log Rotation)是运维中常见的文件管理策略,用于防止单个日志文件无限增长。然而,轮转操作会改变文件路径、文件名甚至文件 inode,如果采集器(如 Filebeat、Fluent Bit、Logstash)没有正确处理这些变化,就会引发数据丢失或重复采集。本节深入分析轮转场景下的故障根因,并给出基于 inode 的排查方法。

3.1 轮转场景下的数据管道故障模型

日志轮转通常通过 logrotate 工具执行,常见策略包括:

  • copytruncate:先复制原文件,再截断原文件(inode 不变)。
  • create:重命名原文件,创建同名新文件(inode 改变)。
  • compress:轮转后压缩旧文件(文件名改变)。

采集器依赖文件路径或 inode 来追踪读取位置。当轮转策略与采集器追踪机制不匹配时,故障模式如下表所示:

轮转策略 采集器追踪方式 潜在故障 影响范围
copytruncate inode 追踪 正常(inode 不变) 无
copytruncate 文件名追踪 正常(文件名不变) 无
create 文件名追踪 正常(新文件同名) 无
create inode 追踪 数据丢失:采集器仍追踪旧 inode(已被重命名),新文件 inode 未被识别 新写入日志丢失
rename + compress 文件名追踪 重复采集:压缩文件被当作新文件重新读取 数据重复
rename + compress inode 追踪 数据丢失:旧 inode 文件被压缩删除,采集器无法继续读取 轮转期间数据丢失
关键结论: 使用 create 策略时,采集器必须采用 文件名追踪 或 inode + 文件名双校验 机制;使用 copytruncate 时,inode 追踪更可靠。生产环境中,不推荐 采集器仅依赖单一追踪方式。

3.2 故障排查:inode 追踪与注册表文件分析

大多数现代采集器(如 Filebeat)使用 注册表文件(registry file) 来持久化记录每个被追踪文件的 inode、设备号、偏移量等信息。当怀疑轮转导致数据丢失或重复时,第一步是检查注册表内容。

3.2.1 检查 Filebeat 注册表

Filebeat 的注册表默认位于 data/registry/filebeat/log.json。使用 jq 解析关键字段:

# 查看注册表中所有活跃条目
jq '.[] | {source: .source, inode: .inode, offset: .offset, state: .state}' \
  /var/lib/filebeat/registry/filebeat/log.json | head -20

输出示例:

{
  "source": "/var/log/app/access.log",
  "inode": 1234567,
  "offset": 1048576,
  "state": "active"
}
{
  "source": "/var/log/app/access.log.1",
  "inode": 1234567,
  "offset": 2097152,
  "state": "active"
}

异常模式识别:

  • 数据丢失:注册表中 source 指向旧文件名(如 access.log.1),但 inode 与当前 access.log 的 inode 不同,且 offset 未更新 → 采集器未切换到新文件。
  • 重复采集:注册表中出现两个条目,source 分别为 access.log 和 access.log.gz,但 inode 不同 → 压缩文件被当作新文件。

3.2.2 实时 inode 对比命令

在轮转发生后,立即对比文件系统的 inode 与采集器注册表中的 inode:

# 获取当前日志文件的 inode
stat /var/log/app/access.log | grep Inode

# 从注册表中提取对应文件的 inode
jq '.[] | select(.source == "/var/log/app/access.log") | .inode' \
  /var/lib/filebeat/registry/filebeat/log.json

如果两者不一致,说明采集器仍绑定在旧 inode 上,新文件未被追踪。

3.3 配置检查:采集器轮转适配参数

不同采集器提供了专门应对轮转的配置项。以下列出关键参数及推荐值:

采集器 关键配置 推荐值 说明
Filebeat close_inactive 5m 文件无新数据 5 分钟后关闭文件句柄,避免占用过多 fd
Filebeat clean_inactive 72h 注册表中清理超过 72 小时未更新的条目,防止注册表膨胀
Filebeat ignore_older 24h 忽略最后修改时间超过 24 小时的文件,避免读取已轮转的旧文件
Fluent Bit tail.rotate_wait 30 轮转后等待 30 秒再切换新文件,防止数据丢失
Fluent Bit tail.skip_long_lines On 跳过超长行,避免轮转时截断行导致解析错误
Logstash file input sincedb_path 持久化路径 确保 sincedb 文件写入持久化存储,重启后不丢失追踪位置
最佳实践: 在 Filebeat 中启用 close_renamed: true 和 close_removed: true,确保文件被重命名或删除后立即关闭句柄,避免采集器死锁在旧文件上。

3.4 轮转故障排查流程图

下图展示了从发现日志缺失到定位根因的完整排查路径:

日志轮转故障排查流程图 发现日志缺失/重复 检查采集器注册表文件 对比 inode 是否一致? 不一致 → 采集器绑定旧 inode 一致 → 检查轮转策略与配置 方案:重启采集器 / 清理注册表 方案:调整 logrotate 策略 验证:日志恢复连续采集 提示:若注册表损坏,可删除 registry 目录后重启采集器(会丢失历史偏移量)

3.5 预防性配置清单

为避免轮转导致的数据管道故障,建议在部署阶段完成以下检查:

  1. 统一轮转策略:所有日志文件使用 copytruncate 或 create 中的一种,避免混用。
  2. 采集器追踪方式对齐:若使用 create,确保采集器配置为文件名追踪(Filebeat 默认使用 inode + 文件名双校验,无需额外配置)。
  3. 注册表持久化:将采集器的注册表文件存储在持久化磁盘上,避免容器重启后丢失。
  4. 设置合理的 close_inactive:根据日志写入频率调整,避免频繁打开/关闭文件句柄。
  5. 启用轮转后延迟:在 logrotate 配置中添加 delaycompress 选项,避免压缩操作与采集器读取冲突。
案例复盘: 某电商平台在凌晨 2:00 执行 logrotate 后,支付日志采集中断 15 分钟。排查发现:logrotate 使用 create 策略,Filebeat 注册表中 inode 仍指向被重命名的旧文件。解决方案:在 Filebeat 配置中增加 close_renamed: true,并设置 clean_inactive: 24h,问题解决。