阅读 583

elkb实践经验,再赠送一套复杂的配置文件

原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处。

宝剑锋从磨砺出,梅花香自苦寒来。

诗人白居易,三月下江南,看到沿路开放的桃花,心潮澎湃,作下了这首流传千秋的诗。表现了诗人对美好事物的向往,以及对其背后苦情的感慨。算了...我编不下去了。

前半句还好,是因果关系。但诗的后半句,却狗屁不通,典型的从结果找原因的思路。哪怕改成“狗屎臭自菊花来”,也比这通畅的多。

这就是理论和实践的区别,只靠臆想,是撑不起现实的骨感的。

本篇文章,将会介绍一个常见的、烂大街的elkb方案,并附赠一套精致的配置文件,以便减少重复工作。

ELKkB

不久之前,elkb还是叫elkbeats系列是最近几年才发展起来的,目的是为了替换flume等收集组件。但为了让这个过程更加平滑具有扩展性,一般会加入一个叫做kafka的组件。所以整体看起来是这样的。

简单点评一下几个组件。

1)filebeat。用于收集日志组件,经测试其使用简单,占用资源比flume更少。但是对资源的占用不是那么智能,需要调整一些参数。filebeat会同时耗费内存和cpu资源,需要小心。

2)kafka。流行的消息队列,在日志收集里有存储+缓冲的功能。kafka的topic过多,会有严重的性能问题,所以需要对收集的信息进行归类。更进一步,直接划分不同的kafka集群。kafka对cpu要求较低,大内存和高速磁盘会显著增加它的性能。

3)logstash。主要用于数据的过滤和整形。这个组件非常贪婪,会占用大量资源,千万不要和应用进程放在一块。不过它算是一个无状态的计算节点,可以根据需要随时扩容。

4)elasticsearch。可以存储容量非常大的日志数据。注意单个索引不要过大,可以根据量级进行按天索引或者按月索引,同时便于删除。

5)kibana。和es集成度非常好的展示组件,为此,xjjdog还有一篇专门的文章。《你的野花,朕的kibana》

选择的组件越多,整个过程会越优雅。尤其是kafka的加入,会让整个链条的头和屁股都变得完美可换,比较魔幻。一个晋级方式就是:ELK->ELKB->ELKkB。

实践旅程

日志格式

为了串联我们的这些组件,需要准备一些小小的数据。其中,nginx日志是最常见的,它已经默认成为了http服务的负载均衡器。

首先,需要对它的日志格式进行一下规整,我这里有一个比较好用的配置。

log_format  main 
'$time_iso8601|$hostname|$remote_addr|$upstream_addr|$request_time|'
'$upstream_response_time|$upstream_connect_time|$status|$upstream_status|'
'$bytes_sent|$remote_user|$uri|$query_string|$http_user_agent|$http_referer|$scheme|'
'$request_method|$http_x_forwarded_for' ;

access_log logs/access.log main;
复制代码

最终,生成的日志可能会长下面这个样子,内容还是比较全的。这种格式的日志,无论是交给程序处理,还是使用脚本处理,都方便的多。

2019-11-28T11:26:24+08:00|nginx100.server.ops.pro.dc|101.116.237.77|10.32.135.2:41015|0.062|0.060|0.000|200|200|13701|-|/api/exec|v=10&token=H8DH9Snx9877SDER5627|-|-|http|POST|112.40.255.152
复制代码

收集器

接下来,需要配置filebeat组件。上面也提到了,由于这个东西是部署在业务机器上的,那就需要严格控制它的资源。完整配置文件可以在附件中获取。

比如cpu资源限制。

max_procs: 1
复制代码

内存资源限制。

queue.spool:
  file:
    path: "${path.data}/spool.dat"
    size: 512MiB
    page_size: 32KiB
  write:
    buffer_size: 10MiB
    flush.timeout: 5s
    flush.events: 1024
复制代码

另外,还可以增加一些额外的字段。

fields:
  env: pro
复制代码

接下来需要配置kafka。由于日志量级一般都比较大,又没有非常明显的意义,所以副本数超过2,没有什么用处,反而会增加故障恢复的时间。

过滤器

logstash的配置可能是最让人迷惑的地方了,这也是我们主要介绍的点。因为上面的nginx日志,将会被解析成elasticsearch能够识别的json串。

通过input部分,就可以接入一些数据源。在这里,我们的数据源变成了kafka。如果你有多个kakfa,或者多个数据源,都可以在这里定义。

然后,就可以在filter部分,定义一些数据的清洗动作。这里有着非常蛋疼的语法,用着非常蹩脚的api,尤其是日期处理方面。如果代码没有格式化,嵌套的层次会让人发晕。据说使用的是ruby语法。

注意,event是个内置变量,代表的是当前的一行数据,包括一些基础属性。可以通过get方法获取一些值。

比如,获取最主要的body信息,也就是具体的行信息。

body = event.get('message')
复制代码

然后,把它解析成相应的key/value值。这个分隔符|就是我们nginx日志的分割符。有没有一股蓬勃而出的冲动?

reqhash = Hash[@mymapper.zip(message.split('|'))]

query_string = reqhash['query_string']

reqhash.delete('query_string')
复制代码

日期处理也是让人心碎的旅程。

time_local = event.get('time_local')
datetime = DateTime.strptime(time_local,'%Y-%m-%dT%H:%M:%S%z')
source_timestamp = datetime.to_time.to_i * 1000
source_date = datetime.strftime('%Y-%m-%d')      event.set('source_timestamp',source_timestamp)
event.set('source_date',source_date)
复制代码

假如想要解析query param,这个也是有的,不过依然比较绕。

query_string = reqhash['query_string']
query_param = CGI.parse(query_string)
query_param.each { |key,value| query_param[key]=value.join() }
reqhash['query_param'] = query_param
buffer_map = LogStash::Event.new(reqhash)
event.append(buffer_map)
复制代码

这么多奇形怪状的函数,是从何而来呢?logstash不会告诉你,我是从ruby官方查的。可能是L并不屑于与我们交流。

https://ruby-doc.org/core-2.5.1/
复制代码

如果你的日志格式定义的比较怪异,或者嵌套层次比较深,要注意了。注定解析是要下一番功夫了。

不过logstash有一个output,叫做stdout,可以实时的调试这个过程,你需要肉眼判断结果。这就比较考验一次性编程成功的能力了。

End

所以,我分享了这几个配置文件,以便你在实施的时候。能够进行参考。仓库地址,也可点击原文查看:

https://github.com/xjjdog/elkb.cnf.pro
复制代码

如果你对理论性的东西不是很熟悉,这里也有一些已经完成的文章。包括整体思路,以及调研性质的。 《这么多监控组件,总有一款适合你》 《你的野花,朕的kibana》

其实elkb的实施,困难不在于方案上,而在于集成上面。第一,这种旁路的应用,是不能够影响原有服务的正常运行的,也就是不能喧宾夺主;第二,一般玩这个的,机器数量也特别的大,如何部署,更新,是需要一番研究的。

不过过滤那一部分,不管是flume,还是logstash,都是绕不过的坎。

作者简介:小姐姐味道 (xjjdog),一个不允许程序员走弯路的公众号。聚焦基础架构和Linux。十年架构,日百亿流量,与你探讨高并发世界,给你不一样的味道。我的个人微信xjjdog0,欢迎添加好友,​进一步交流。​