Kubernetes 日志采集 配置避坑指南
最近给集群上日志采集,本来以为写个 DaemonSet 跑个 Fluent Bit 就完事儿了,结果日志不是缺、就是乱、要么就是疯狂重复,前前后后折腾了快一个礼拜... 把踩过的坑都记在这了,配置全部实测能跑,直接抄就行,不谢。
先搞清楚运行时是啥,别拿 docker 的解析规则硬套
最开始在测试环境一切正常,因为测试集群跑的还是 docker,日志文件是标准的 json 格式,{"log":"xxx","stream":"stdout","time":"..."},配个 json parser 就搞定。后来推到生产环境,日志全部解析失败,进节点一看,生产用的是 containerd,日志长这样:
2024-05-20T08:10:11.123456789Z stdout F this is a log line
之前配的 json parser 直接歇菜,所有日志全进了死信队列。另外中间那个 F 不是日志内容,是 CRI 的标志位,F 代表完整一行,P 代表半行,单行超过 16K 会被切开,前半段标 P,最后一段才标 F,不做处理的话一条日志就碎成好几条了。
Fluent Bit 配个 CRI 的 parser:
[PARSER]
Name cri
Format regex
Regex ^(?<time>[^ ]+) (?<stream>stdout|stderr) (?<logtag>[^ ]*) (?<message>.*)$
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L%z
要是集群里 docker 和 containerd 混着跑,那就两套 parser 按节点分别指定,别指望一套配置走天下,没准儿下个集群又不一样了。
DaemonSet 挂载少一个目录,日志就集体消失
推上生产后发现一大半 pod 没日志,采集器进程活得好好的,手动 tail 宿主机上的日志文件也有内容,就是采不到。查了半天,原因特别蠢:/var/log/containers/ 下面全是软链接,真正的文件在 /var/log/pods/ 里,而我只挂了前者,进容器一看,软链接全断了。
老实把两个都挂上:
volumeMounts:
- name: varlogcontainers
mountPath: /var/log/containers
readOnly: true
- name: varlogpods
mountPath: /var/log/pods
readOnly: true
volumes:
- name: varlogcontainers
hostPath:
path: /var/log/containers
- name: varlogpods
hostPath:
path: /var/log/pods
如果节点还是 docker 运行时,还得额外挂一个 /var/lib/docker/containers,不然软链照样断。
Java 堆栈,采集完碎成渣
Java 应用一抛异常就是十几行的堆栈,采集完变成十几条独立日志,ES 里翻日志翻得人头晕。得配多行合并,把不以外层时间戳开头的行拼回上一条:
[MULTILINE_PARSER]
Name java_stack
Type regex
Flush_Timeout 1000
Rule "start_state" "/^\d{4}-\d{2}-\d{2}/" "cont"
Rule "cont" "/^\s+(at|\.\.\.|Caused by)/" "cont"
用的时候在 INPUT 里加一行 multiline.parser java_stack 就行。注意这玩意儿吃内存,limit 给小了,赶上日志洪峰直接 OOMKill。更坑的是 kubelet 默认单文件 10Mi、最多留 5 个文件就轮转,采集器挂一会儿没接上,轮转一圈日志就真没了,找都没地方找去。
采集器把自己采集了,无限套娃
某天发现 ES 磁盘增速不太对劲,一查全是同一条 fluent-bit 的日志在翻倍。原因也很离谱:采集器自己的日志打到 stdout,然后被自己采集了,采完又打一条处理成功的日志... 开始无限套娃。直接在 INPUT 里把自家的日志排除:
[INPUT]
Name tail
Path /var/log/containers/*.log
Exclude_Path /var/log/containers/*_logging_*.log
我这边采集器放在 logging 这个 namespace,所以这么写就够了,你们按自己的 namespace 改。顺带一提,采集器的日志级别别开 debug,真顶不住。
时区,永远是时区
日志时间全按 UTC 存的,上午十点出的问题,去 ES 里搜 10:00 搜了半天啥也没有,因为日志里写的是凌晨两点... 要么查询前自己心里换算,要么采集端直接转掉。Fluent Bit 里加个偏移就完事:
[INPUT]
Name tail
Path /var/log/containers/*.log
Time_Offset +0800
控制平面节点的日志,默认是没人采的
有次要排查 apiserver 的问题,翻了一圈发现控制平面节点上压根没有采集器在跑。原因也简单,master 默认带 taint,DaemonSet 默认容忍不了,加个 tolerations 就好:
tolerations:
- operator: Exists
全容忍虽然糙了点,但是省心,免得以后集群加了个带污点的节点又忘掉这茬。
最后
以上坑都是挨个踩出来的,配置抄了就能用。日志稳定灌进 ES 的那一刻,感觉这礼拜没白熬... 然后两周后磁盘满了,那就是另一个故事了。
评论
还没有评论。
发表评论
提交后评论将经过自动审核,审核通过后公开展示。