在java化项目中,部署到k8s集群后,一度出现无法定位问题的情况。起初怀疑是日志调用链改造导致的,也修复了一些问题,但没有解决根本问题
问题表现
- 日志搜集不够实时
在k8s中执行kubectl logs --tail=500 test-6bb5648c4c-4fmrr -n testns
随机取一条记录,在kibana界面无法查询 - 日志没有被搜集
上个问题或许过一段时间能查询到,但有些日志则是完全无法查到,就算过了10分钟也无法查到 -
查看近几天的日志,发现如下规律:
由上图可知,日志在某个时间正常,然后越来越少。
问题定位
- 首先查看elasticsearch
- 查看索引状态,一切正常
get http://localhost :9200/test* green open test-2020.09.08 6xRJEYkbRa-EIJRwFvXaow 3 1 37078449 2 205.3gb 97.7gb green open test-2020.09.07 RrUCdqUFR26FBKcM91yMiA 3 1 39189071 0 190.2gb 95.1gb
- 查看日志:除了个几个由于mapping 解析错误的日志,没有异常
- 再定位logstash日志
- 曾发现有大量的grokFailure日志,这个问题也会导致日志丢失,修正了grok,但依然出现如上问题
- 也曾发现由于匹配规则导致的一些日志遗漏。但同样没有解决问题
- 再查看kafka是否异常
- 首先定位消费是否有延后,执行如下命令
./kafka-consumer-groups.sh --bootstrap-server 10.64.175.183:9092 --describe --group test_k8
发现一切正常,并没有阻塞导致日志查不到的现象。
- 首先定位消费是否有延后,执行如下命令
- 定位filebeat
通过以上定位,那基本出问题的可能只能是filebeat了。- 查看filebeat日志
kubectl logs filebeat-7sx7r -n kube-syste
{"monitoring": {"metrics": {"beat":{"cpu":{"system":{"ticks":660940,"time":{"ms":2108}},"total":{"ticks":7503770,"time":{"ms":25202},"value":7503770},"user":{"ticks":6842830,"time":{"ms":23094}}},"handles":{"limit":{"hard":1048576,"soft":1048576},"open":21},"info":{"ephemeral_id":"3d0c7300-0a5b-405f-8fba-356fc3b3c085","uptime":{"ms":12930017}},"memstats":{"gc_next":131488384,"memory_alloc":74631384,"memory_total":1093590548216},"runtime":{"goroutines":128}},"filebeat":{"events":{"active":145,"added":61940,"done":61795},"harvester":{"open_files":13 1,"running":131,"started":2}},"libbeat":{"config":{"module":{"running":0}},"output":{"events":{"acked":61793,"batches":47,"total":61793}},"outputs":{"kafka":{"bytes_read":51946,"bytes_write":9380778}},"pipeline":{"clients":4,"events":{"active":573,"filtered":2,"published":61938,"total":61940},"queue":{"acked":61793}}},"registrar":{"states":{"current":39,"update":61795},"writes":{"success":49,"total":49}},"system":{"load":{"1":3.62,"15":3.97,"5":4.14,"norm":{"1":0.2263,"15":0.2481,"5":0.2588}}}}}}```
"harvester":{"open_files":131,"running":131,"started":2}
发现filbeat居然持有这么多文件,也就是说还有131个文件还在搜集的状态。- 回到对应节点,查看文件状态。
lsof |grep deleted
之所以有这个步骤,1是因为之前cmo发现k8s报错了,大量的文件显示被删除,但实际没有删除,仍然被filebeat持有。不过因为当时只发现只有某个节点存在这种现象,不是普遍现象,所以没有在意。2.是filbeat持有这么多文件不正常,现在发现是只要部署了该应用的节点就有这种现象,也就是filebeat启动了搜集。
- 查看filebeat日志
终极原因
定位到上面,其实已经差不多了,但要想揭开真相,还需要解决以下三个问题:
- 为什么持有这么多文件?
- 为什么持有这么多文件会导致上面的一系列问题呢?
- 还有没有其他问题呢?
除了第三个我们可以再解决了前面两个问题之后观察就能看出,那么我们重点就是上面两个问题了。
出现上述情况,直接原因我们可想而知是在##系统删除了相关的日志文件之后,filbeat还持有文件。
这里可能有两个原因:
- filebeat持有的文件没有搜集完,所以继续持有,也就是那么多文件它都没有搜集完。
- filebeat已经搜集完了,但仍然没有释放文件。
最开始我们怀疑是因为filebeat没有配置正确,比如close_*配置选项。最后结合k8s中日志的轮转方式排查,filebeat采取的是默认配置:
- close_renamed :这种情况在日志轮转的时候发生,默认值是flase。
- close_removed: 这个一般发生在日志真正删除后。 默认值是true。也就是会出现上述情况,日志被删除了,filebeat仍继续持有文件。
- close_inactive: 默认值是5分钟
理论上说这个配置是正确的,因为要保证日志搜集完,且发生轮转的时候日志依然继续搜集,也能基本上保证搜集完以后关闭harvest。那么我们暂时排除第二种可能。
filebeat搜集速度过慢
我们继续定位第一种可能:filebeat持有的文件没有搜集完,所以继续持有,也就是那么多文件它都没有搜集完。
-
如何定位?
现在单从filebeat的日志已经无法看出了。于是想到去看filebeat的注册表,filebeat的registry文件中存放的是被采集的所有日志的相关信息。- 进入到k8s中相应的filebeat节点
kubectl exec -it filebeat-lh2bq -n kube-system /bin/bash - 查看其中registry文件,截取一条
{"source":"/data/docker/containers/0eb0397149bcd687a40cd4313b853354ff43e41a6257ccb5ab972d7170881037/0eb0397149bcd687a40cd4313b853354ff43e41a6257ccb5ab972d7170881037-json.log","offset":114912226,"timestamp":"2020-09-08T08:43:41.079295459Z","ttl":-1,"type":"container","meta":null,"FileStateOS":{"inode":8143887,"device":64512}}
重点关注inode和offset,inode代表某个文件,offset代表filebeat读到该文件的位置,字节数。
-
结合使用stat查看所在node的真正文件状态,持续观察(也就是每隔一段更新registry信息):
发现,当文件被删除时,inode依然还在,同时filebeat中的注册表信息依然在更新,也就是offset持续增长,
114912226->117408238->122393532。最后缓慢增长到198596250,也就是200M。至此filebeat关闭这个文件,文件被真正删除。
- 进入到k8s中相应的filebeat节点
docker中的配置:
"storage-opts":["overlay2.override_kernel_check=true"],
"log-driver": "json-file",
"log-opts": {
"max-size": "200m",
"max-file": "3"
}
至此就可以解释上面两个问题了,filebeat没有搜集完导致一直持有文件,文件轮转越快,filebeat持有的文件数越多,搜集性能相应的下降,因此越来越慢,最终导致日志不能实时的查询,以及日志丢失的问题(也许并不会丢失,只是长时间查不到)。
解决问题
既然发现了这个问题就要着手解决
最开始想到两个解决方案:
- 日志轮转文件数设置更大
- 日志级别设置设置高一点,之前是debug
读者可以想一想这两个方案有没有问题?
显然是有问题的,这两个方案并没有真正解决问题,只是延缓了问题的发生。第一个问题照旧,第二个在并发量暴增或者日志需要放开是就有问题。
问题真正的根因所在是filebeat搜集的速度跟不上日志打印的速度。 我们必须找出filebeat慢的原因,按理说读文件应该相当快才对,肯定是有哪里的配置不对,导致filebeat的性能如此的低。
最终重新审视filebeat部署文档,发现我们部署的时候限制了filebeat的性能:
# filebeat.yaml部分片段
resources:
limits:
memory: 500Mi
cpu: 200m
requests:
cpu: 200m
memory: 500Mi
这里的200m代表了200*1/1000 个cpu,也就是只有20%的cpu,这样显然filebeat在多线程处理的时候性能被大大压缩,也就无法跟上我们日志输出的速度。而且k8s具有cpu压缩的问题,它在低cpu的时候仍然能运行,我们无法监控到错误,所以很难发现。
修改配置文件取消限制,重新部署filebeat 解决
再次查看cpu:
再次查看日志,果然 飞起!