背景
使用Filebeat 5.6.4收集业务日志,配置文件比较简单:
filebeat.prospectors:
- input_type: log
paths:
- /path/logs/access.log
output.elasticsearch:
hosts: ["http://x.x.x.x:9200"]
index: "accesslog-%{+yyyy.MM.dd}"
其中,access.log是业务主日志,按天进行滚动,日志文件最大为128MB, 当天日志超出128MB后也进行滚动,最多保留15个日志文件;滚动后的日志文件会立即进行压缩。
启动filebeat后几个月过去了,发现filebeat目录下的data/registry文件容量越来越大。registry本身是用来记录日志文件的state信息,比如记录读取到文件位置的的offset,文件的inode、modify time等,通过查看registry文件内容看到,该文件中保存了从filebeat启动后的所有经过滚动并删除的日志文件的state信息,registry文件大小也到了几百KB,虽然看起来问题不大,但是越来越大的文件势必在每次更新时要消耗更多的系统资源,所以需要想办法优化,解决registry文件越来越大的问题。
解决办法
通过查看filebeat 5.6.4文档看到,有两个参数clean_removed和clean_inactive可以清除掉registry文件中无用的state信息。
- clean_removed: 默认打开,清除已经被删除文件的state信息
- clean_inactive:默认关闭,清除掉已经不活跃的文件的state信息,必须配合ignore_older参数使用,并且ignore_older +scan_frequency必须小于clean_inactive,否则可能造成日志重复收集。
既然clean_removed参数是默认打开的,并且可以清除掉已经被删除文件的state信息,那为什么在上述场景下并没有生效呢?原因就是日志滚动时是先rename重命名了access.log文件为acess.log.1, 然后把acess.log.1文件压缩成了acess.log.1.gz之后再删除acess.log.1文件,也就是说并没有直接删除掉access.log文件,而clean_removed参数对重命名的文件是不起作用的,所以state信息没有被清除。
5.6版本中有关clean_removed参数的描述:
This setting does not apply to renamed files or
files that were moved to another directory that is still visible to Filebeat
但是这个问题已经在6.0之后的版本得到了修复,即使文件被重命名,在收集完该文件后后续state信息也会被清理。
6.0版本中有关clean_removed参数的描述:
When this option is enabled, Filebeat cleans files from the registry if they
cannot be found on disk anymore under the last known name. This means also files
which were renamed after the harvester was finished will be removed.
解决办法1
通过使用6.4.3版本的filebeat重新采集access.log日志,发现registry文件越来越大的问题已经得到了解决,所以最好的解决办法是把filebeat升级到6.4.3版本。
解决办法2
如果不能立即升级filebeat怎么办?那就可以通过配置ignore_older+clean_inactive参数解决问题.
解释一下ignore_older参数:
- ignore_older: 默认为0不启用,启用该参数可以忽略掉比较旧的日志文件(根据modify time判断),从较新的文件开始收集日志。如果日志文件第一次被采集到但是modify time超出了ignore_older,则会从该文件末尾开始采集日志,registry文件中的state信息为日志文件的末尾;如果日志文件的state信息已经存在,但是文件的modify time超出了ignore_older,则继续从state中记录的offset开始读取日志。ignore_older必须大于close_inactive, 以确保文件在被忽略之前已经被关闭。如果文件modify time已经超出了ignore_older, 但是仍然在被采集中,此时filebeat会把文件读取完毕后等待close_inactive时间后关闭该文件。
最终针对业务场景,新的基于5.6.4版本的filebeat的配置文件如下:
filebeat.prospectors:
- input_type: log
paths:
- /path/logs/access.log
ignore_older: 24h
clean_inactive: 36h
output.elasticsearch:
hosts: ["http://x.x.x.x:9200"]
index: "accesslog-%{+yyyy.MM.dd}"
因为日志文件是按天滚动的,ignore_older设置为24h,clean_inactive设置为36h,清理掉registry文件中距离最近一次更新超出36小时的日志文件的state信息.
根据上述配置,分别验证了两个场景,证明registry文件是可以正常清理旧的state,并且日志采集也不受影响:
-
场景1:日志较少,可能十天半月才有一条日志
这种场景下,因为日志文件一直没有更新,registry中始终记录的有日志文件的state信息,offset指向文件末尾。为什么日志文件的state信息一直没有被清理,因为更新registry文件是在读取文件之后进行的,filebeat每次扫描文件时发现文件没有被更新,就直接结束本次scan了。经过了十天半月,日志文件中产生了日志,此时会先根据registry中的state信息从文件末尾读取日志,不会从头开始读取,从而不会造成日志重复读取的情况。
场景2:日志较多,滚动较快,当天的日志都能滚动15次以上
这种场景下,每次滚动后新产生的日志文件被从头开始读取,旧的日志文件被重命名后即便被删除,因为filebeat此时并没有释放文件句柄,所以也可以被持续读取直至文件末尾。旧的日志文件的state信息在滚动发生后36h会被清理掉,从而避免了registry文件越来越大的情况发生。另外需要注意的是,这种场景下因为filebeat会占用已经删除文件的句柄直至文件读取完毕并且close_inactive到期,整个过程中磁盘资源是不会释放的,所以可以通过合理配置close_timeout参数及时释放掉文件句柄。