开放问答
K8s 容器日志被 json-file/logrotate 轮转后 otel collector 漏采故障窗口日志,inode 变了 fd 失效,怎么定位和根治?
该问题的正文、回答与评论。
提问者 沈星河 · 状态 open
Kubernetes日志采集otellogrotateinode
背景
12 节点集群(16C64G,Kubernetes 1.28.9,containerd 1.7.13,内核 5.15),日志链路是:业务容器 stdout 走 containerd 的 json-file 驱动(--log-opt max-size=10m --log-opt max-file=3),容器内应用文件日志由 otel-collector(filelog receiver)tail 采集,最终落 Loki。我们没跑 filebeat,纯 otel 采集。
现象
一次支付超时故障复盘时,后端(Loki)能查到故障前后各约 1 分钟的日志,但中间 40 秒是断的。而同一时段 kubectl logs、crictl logs、节点上 cat 原始 json 文件都能完整看到那 40 秒的日志——说明日志确实写出来了,是采集侧丢了,不是应用没打。
已尝试的排查
- 排除 otel 进程 OOM:
kubectl describe pod无 OOMKill,otel 副本资源充足,无重启记录。 - 确认 rotate 行为:故障窗口正好落在一次
max-size触发点,节点docker/ctr日志目录能看到xxx.json被 rename 成xxx.json.1、同时生成新的xxx.json。 - inotify 上限怀疑:
cat /proc/sys/fs/inotify/max_user_watches是默认 8192,节点容器多,怀疑逼近上限,但没拿到"watch 耗尽"的坐实证据。 - 最可疑的:otel filelog 的 tail 还握着旧 inode 的 fd,文件被 rename 后,新写入落到新 inode 的文件,旧 fd 指向被搬走的文件 → 新日志没被采。但我不确定 otel 到底是靠 inotify 的 MOVED/CREATE 事件重挂,还是靠 poll 扫描目录,以及它 poll 的默认间隔是多少。
卡点 我基本判断是 rotate 产生新 inode,而 otel 的 file tail 没对新文件重新建立 watch,导致新写入的日志漏采。但 otel filelog 和 filebeat 的 reopen 机制不一样(filebeat 有明确的 registry + inotify 重挂),我没吃透 otel 这边的实现,也没法定位到具体是哪一环没跟上。
期望得到的答案形态
- otel filelog receiver 在文件被 rotate(rename)后,到底靠什么发现新文件?inotify 事件还是 poll?默认
poll_interval/ 相关配置项是多少、默认值是什么? - 生产里怎么把"rotate 后漏采"根治:是调 json-file 驱动的
max-size/max-file、改 otel 的start_at/flush 配置,还是干脆换 node agent(filebeat)?各自的取舍? - 给一段可复现的最小验证:怎么人为制造一次 rotate,然后抓 otel 的 debug 日志 / inotify 事件,坐实"是不是 inode/fd 没跟上"?
- 有没有监控手段能提前发现"采集断流":比如 otel 暴露的 ingestion / scraped 指标、按文件 mtime 算采集 lag 的告警,怎么落地?
想听听大家生产里踩过的坑,以及你们最终是靠 otel 配置兜住,还是回到了 filebeat。
评论
暂无评论。