使用Filebeat和AWS CloudWatch Logs将EC2上的Tomcat的access_log传送到ELasticsearch中并使用ILM完成日志的自动管理

  • Post author:
  • Post category:其他


这一篇是对

使用Filebeat和AWS CloudWatch Logs将EC2上的Tomcat的access_log传送到ELK

的拓展。

为什么要拓展呢?在上一篇文章中,Filebeat拉到日志之后还要送到Logstash中进行处理。但是Logstash的缺点是它太费资源了,远不如Filebeat轻量。

基于这个原因开始尝试使用Filebeat将Tomcat的access_log直接发送到Elasticsearch中并且其结构

符合ECS规范

.



使用dissect processor解构access_log

processors:
  - dissect:
      tokenizer: '%{client.ip} - - [%{access_timestamp}] %{response_time|integer} %{session_id} "%{http.request.method} %{url_original} %{http.version}" %{http.response.status_code|integer} %{http.response.bytes} "%{http.request.referrer}" "%{user_agent.original}"'
      field: "message"
      target_prefix: ""
      ignore_failure: false
  - drop_event:
      when:
        contains:
          # drop PCI scanner http request event
          user_agent.original: "AlertLogic"
  - if:
      contains:
        url_original: '?'
    then:
      - dissect:
          tokenizer: '%{path}?%{query}'
          field: "url_original"
          target_prefix: "url"
    else:
      - copy_fields:
          fields:
            - from: url_original
              to: url.path
          fail_on_error: false
          ignore_missing: true
  - timestamp:
      field: "access_timestamp"
      layouts:
        - '2006-01-02T15:04:05Z'
        - '2006-01-02T15:04:05.999Z'
        - '2006-01-02T15:04:05.999-07:00'
      test:
        - '2019-06-22T16:33:51Z'
        - '2019-11-18T04:59:51.123Z'
        - '2020-08-03T07:10:20.123456+02:00'
  - drop_fields:
      fields: [ "agent","log","cloud","event","message","log.file.path","access_timestamp","input","url_original","awscloudwatch","host" ]
      ignore_missing: true
  - add_tags:
      when:
        network:
          client.ip: [ private, loopback ]
      tags: [ "private internets" ]
  - add_tags:
      tags: [ "aws_access_log" ]
  - replace:
      when:
        contains:
          http.response.bytes: "-"
      fields:
        - field: "http.response.bytes"
          pattern: "-"
          replacement: "0"
      ignore_missing: true
  - convert:
      fields:
        - { from: "http.response.bytes", type: "integer" }
      ignore_missing: false
      fail_on_error: false

对以上配置做一个简单的说明:

  1. 大部分字段都是以ECS规范命名的,这是因为Filebeat

    默认已经内置了ECS的所有字段

    .不需要像Logstash那样-即不能识别 点. 只能以下划线来命名。Filebeat直接默认就是点.而且点会自动解析为嵌套结构
  2. 使用

    Condition

    来对不同的字段进行处理。对于ip类型的字段,Filebeat会自动解析成IP类型,使用

    network condition

    来给内网或本机IP打上标签
  3. 当http.response.bytes字段是- 时,将其替换为字符串0,然后将其转换为整数类型



output修改为Elasticsearch

output.elasticsearch:
  hosts: ["elasticsearch:9200"]
  username: elastic
  password: ${ELASTIC_PASSWORD}



将日志设置为DataStream并启用索引生命周期周期管理ILM



为什么要使用DataStream?

A data stream lets you store append-only time series data across multiple indices while giving you a single named resource for requests. Data streams are well-suited for logs, events, metrics, and other continuously generated data

来自

Elasticsearch官方文档DataStream部分


对于日志类数据,官方是建议使用Data Stream



为什么要使用ILM?

我们的Elasticsearch的存储空间有限,不可能将日志数据一直存在ES中。我们默认只保留2天的日志数据。为此,我们需要2天之后ES自动清理掉过期的日志数据。

可以通过编程,启动一个定时任务来调用Elasticsearch API达到清理过期日志的目的。但是既然ES已经提供了这个工具,我想我们可以直接用而不用再去编程了



日志数据配置ILM


Filebeat配置ILM官方文档

setup.template.settings:
  index.number_of_shards: 1
  index.number_of_replicas: 0
setup.ilm.overwrite: true
setup.ilm.policy_file: /usr/share/filebeat/filebeat-lifecycle-policy.json

生命周期策略json文件,当index达到2gb或者2天的时候,进行rollover。idnex超过两天就删除

{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "rollover": {
            "max_primary_shard_size": "2gb",
            "max_age": "2d"
          }
        }
      },
      "warm": {
        "min_age": "2d",
        "actions": {
          "readonly": {},
          "set_priority": {
            "priority": 50
          }
        }
      },
      "delete": {
        "min_age": "2d",
        "actions": {
          "delete": {
            "delete_searchable_snapshot": true
          }
        }
      }
    }
  }
}

为什么要设置index.number_of_shards和index.number_of_replicas,因为Elasticsearch在清理过期的index时,该index的状态必须是健康的,也就是green,我们的ELK是单机的,因为只用来查询日志,所以我们认为单机就够了,所以这里设置number_of_replicas为0。如果index是yellow的,那么index就会一直保留在ES中,因为无法被清掉。


这一说明可以在官方文档中找到

However, because Elasticsearch can only perform certain

clean up tasks on a green cluster

, there might be unexpected side effects

这里index template用的就是Filebeat的默认的,如果只有一种日志,那么用默认的足够了。

想要对ILM更多了解的同学,请移步

Elasticsearch DataManagement部分



性能调优

经过前面的配置,便可以从AWS CloudWatch Logs上拉日志并送到Elasticsearch中去了。

但是在实际测试过程中,发现在ES里拿到的日志和原始日志延迟较大,基本2分钟以上了,这是不能接受的。于是开始调优

首先在Elasticsearch Blog中找到了一篇文章

How to Tune Elastic Beats Performance

,这篇文章的思路对我帮助很大.



配置Filebat的内部队列大小

看完这篇文章之后,同时又参考了Filebeat官方文档

内部队列的文档

。简而言之就是,Filebeat从input获得events,但是它不会接收到一条event立刻发送到output中,它是等待一批events然后再发送到output处理,如果一段时间内达不到批量数,则会等待一定时间之后发送。

events的默认是4096,我认为这个值太小了。同时我还注意到官方文档提到的一句话

If the queue is full, no new events can be inserted into the memory queue. Only after the signal from the output will the queue free up space for more events to be accepted

如果队列满了,那么后续的数据是进不来的。

为什么我认为默认队列数对于我们的日志量来说是设置过小呢?

因为我们有几十台服务器吧,我在input部分配置的是每

10秒

去AWS上拉取一次日志。通过预估每台服务器的平均请求数并且反复的测试,我认为12288是合理且满足的。也就是说,10秒中拉一次,这一次的数据量基本在12288左右,不会太多,大多数情况下能hold住。这样的话,我拉取一次日志,基本可以全部放在

内存队列

里。然后马上处理,不会阻塞后续events入队列

# Reference https://www.elastic.co/guide/en/beats/filebeat/current/configuring-internal-queue.html
# queue.mem.events = number of servers * average requests per second per server * scan_frequency(10s). I think 12288 is more reasonable now
# queue.mem.events = output.worker * output.bulk_max_size
# queue.mem.flush.min_events = output.bulk_max_size
queue.mem:
 events: 12288
 flush.min_events: 4096
 flush.timeout: 1s



如何验证queue.mem是合理且正确的?

上述配置完毕,如何确定,在Filebeat部分确实是做到了基本没有延迟的呢?

我先将output部分改为输出到文件中,并一直观察文件内容和Filebeat拉取日志时间。通过不断的测试和调整,最终确定在基于上述配置下,Filebeat每10秒拉取一次日志,可以非常快速的将内容写到文件中去。而基于默认的4096配置,写文件时出现了比较长的延迟。

同时在调试过程中修改了下列

registry.flush配置

# Reduce the frequency of Filebeat refreshing files to improve performance
filebeat.registry.flush: 30s

原因是

Filtering out a huge number of logs can cause many registry updates, slowing down processing. Setting registry.flush to a value >0s reduces write operations, helping Filebeat process more events

默认刷新是1秒,我认为太频繁了。所以为了避免registry文件刷新太快影响Filebeat速度,改为30s

到这一步Filebeat这一部分经过验证和调试,最终确保不会出现非常大的延迟



配置output部分的worker和bulk_max_size

Filebeat部分调整完毕之后,我将output部分改为Elasticsearch继续测试,发现还是有比较大的延迟。说明该调优output部分的Elasticsearch

主要原则是





q

u

e

u

e

.

m

e

m

.

e

v

e

n

t

s

=

w

o

r

k

e

r

s

×

b

u

l

k

_

m

a

x

_

s

i

z

e

(1)

queue.mem.events = workers \times bulk\_max\_size \tag{1}






q


u


e


u


e


.


m


e


m


.


e


v


e


n


t


s




=








w


or


k


ers




×








b


u


l


k


_


ma


x


_


s


i


ze







(



1



)








让min_events和bulk_max_size相等,此结论来自于上述官方博客。按照官方博客的建议,应该是如下公式





q

u

e

u

e

.

m

e

m

.

e

v

e

n

t

s

=

2

×

w

o

r

k

e

r

s

×

b

a

t

c

h

s

i

z

e

q

u

e

u

e

.

m

e

m

.

f

l

u

s

h

.

m

i

n

_

e

v

e

n

t

s

=

b

a

t

c

h

s

i

z

e

(2)

queue.mem.events = 2 \times workers \times batch size \tag{2} \\ queue.mem.flush.min\_events = batch size






q


u


e


u


e


.


m


e


m


.


e


v


e


n


t


s




=








2




×








w


or


k


ers




×








ba


t


c


h


s


i


ze








q


u


e


u


e


.


m


e


m


.


f


l


u


s


h


.


min


_


e


v


e


n


t


s




=








ba


t


c


h


s


i


ze







(



2



)








不过实际情况,我使用公式1延迟更小,可能跟具体的硬件、内存大小都有一定的关系.

同时开启压缩

所以最终output部分的配置就是

output.elasticsearch:
  hosts: ["elasticsearch:9200"]
  username: elastic
  password: ${ELASTIC_PASSWORD}
  worker: 3
  bulk_max_size: 4096
  compression_level: 3



测试结果

经过上述配置完毕之后,再次测试,同时将拉取日志间隔由10秒降为5秒。这次日志延迟在5s到15秒之间。这是可以接受的,为什么说可以接受呢?因为假设现在是4:30:30,那么这是Filebeat拉取的日志是过去5秒的日志,等到在ES里看到4:30:30秒的日志的时候,时间在4:30:35秒左右,所以我认为是可以接受的,最终再去提高1-2秒没多大意义了。



最终效果

把Filebeat和Es、Kibana用docker compose启动起来之后,可以看到日志以可接受的延迟写入到ES的Data Stream中。

可以在kibana的Index Management > Data Stream中看到。点击后面的index数字可以跳转到相应的back index。

当index超过2天,则index会自动删除。删除时ES并不是到了2天就删,可能要等个几分钟才删除,因为ES删除index也是需要做一些操作和时间的。


注意

:如果ES在删除过期index时出现异常了,例如内存溢出之类的,可以尝试降低一下index的大小,在上述ILM json文件中配置。这条建议本人并没有严格验证过,只是因为我测试的时候出过一次这种情况,我就把index降低了,就没再出现过这种错误,也没有把参数再调整回去。有时间的同学可以验证一下。



总结

把一个东西用起来仅仅是最初级的部分,如何把一个东西用好并比较好的支持当前所需场景才是最重要的。

如果在调试过程中遇到什么问题,没有头绪的时候,一定要去看看服务器日志,例如Filebeat的日志或者ES的日志。切记不要把自己的需求放到网上去Google,例如为什么Filebeat延迟很高之类的,因为同样的需求面对的场景不一样,给出的答案完全不一样且不一定适用你。

大部分关键的配置已经都给了出来,所以这里就不再提供源码了。因为上述配置都在filebeat.yml。



有用的文章

  • https://www.elastic.co/cn/blog/troubleshooting-elasticsearch-ilm-common-issues-and-fixes



版权声明:本文为dghkgjlh原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。