0x01 概述
前段时间监控到公司站点的手机号是否存在的接口被单IP高频率访问,站点解析到腾讯云上,LB后是20+腾讯云主机做Nginx代理,使用Filebeat+ELK收集监控此类异常行为,当前最新的是6.2,有些改动,我还是用5.5版本。
ELK可以当作一个MVC模型,logstash是controller层,Elasticsearch是一个model层,kibana是view层。
使用三台机器,配置和角色如下:
10.59.0.248(32核+64G内存) Logstash Elasticsearch(Master) Kibana
10.59.0.116(24核+32G内存) Logstash Elasticsearch(Data)
10.211.0.107(64核+64G内存) Logstash Elasticsearch(Data)
0x02 Filebeat
Filebeat的性能消耗要比logstash小的多,配置如下:
data/registry记录了文件读取的offset,如果文件data/registry不存在,则会重新发送文件。
配置tail_files: true 仅发送新的数据
0x03 Logstash
在logstash-5.4.0/bin/logstash.lib.sh文件第一行添加
所遇到的问题:
1)ELK部署运行后,非常常见的一个现象是429错误,如下所示:
[2018-04-04T09:08:16,479][INFO ][logstash.outputs.elasticsearch] retrying failed action with response code: 429 ({"type"=>"es_rejected_execution_exception", "reason"=>"rejected execution of org.elasticsearch.transport.TransportService$7@4e1276c3 on EsThreadPoolExecutor[bulk, queue capacity = 200, org.elasticsearch.common.util.concurrent.EsThreadPoolExecutor@4892020c[Running, pool size = 24, active threads = 24, queued tasks = 200, completed tasks = 4786420]]"})
表示Elasticsearch处理不过来了。
在ES5.0以后,Elasticsearch将bulk、flush、get、index、search等线程池完全分离,自身的写入不会影响其他功能的性能。
来查询一下ES当前的线程情况:
其中
最需要关注的是rejected。当某个线程池active==threads时,表示所有线程都在忙,那么后续新的请求就会进入queue中,即queue>0,一旦queue大小超出限制,那么elasticsearch进程将拒绝请求(bulk HTTP状态码429),相应的拒绝次数就会累加到rejected中。
解决方案为优化logstash.yml的参数,修改batch.size参数为3000,增加每次发送的事件数,从而降低调用ES的频率。另外修改worker/output.workers为CPU数。
logstash.yml配置如下:
2)增加Logstash JVM内存,Logstash报错内存溢出
需要修改logstash启动文件bin/logstash
Logstash配置文件如下:
input {
beats {
port => 5044
}
}
filter{
grok{
match => {
"message" => "%{IP:client-ip}(,\s)?(?<proxy-ip>[\d\.,\s]*) (%{USER:ident}|-) (%{USER:auth}|-) \[%{HTTPDATE:timestamp}\] \"(?:%{WORD:verb} %{URIPATH:uri}%{DATA:parameter}(?: HTTP/%{NUMBER:http_version})?|-)\" %{NUMBER:status-code} %{NUMBER:bytes} \"(%{GREEDYDATA:referer}|-)\" \"(%{GREEDYDATA:user-agent}|-)\" (%{BASE16FLOAT:response_time}|-) (%{BASE16FLOAT:request_time}|-) \"(%{GREEDYDATA:cookie}|-)\" \[%{GREEDYDATA:servername}\]"
}
}
if ([uri] =~ "\.(js|css)$"){
drop {}
}
date {
match => ["timestamp", "dd/MMM/yyyy:HH:mm:ss Z"]
target => "@timestamp"
}
ruby {
code => "event.set('timestamp', event.get('@timestamp').time.localtime + 8*60*60); event.set('@timestamp', event.get('timestamp'))"
}
if ([verb] == "POST")
{
mutate {
add_field => {"dynamic" => 1}
}
}
else if ([verb] == "GET" and [parameter])
{
mutate {
add_field => {"dynamic" => 1}
}
}
else {
mutate {
add_field => {"dynamic" => 0}
}
}
mutate {
remove_field => [ "message" ]
}
}
output {
if "_grokparsefailure" not in [tags] {
elasticsearch {
hosts => "10.59.0.248:9200"
index => "nginx_%{+YYYY.MM.dd}"
}
}
}
这里grok正则匹配非常耗费性能,可以使用dissect替换。
0x04 ELasticsearch
ElasticSearch是一个基于Lucene的搜索服务器,Lucene是一个开源的全文检索引擎工具包(类似于Java api),而Elasticsearch底层是基于这些包,对其进行了扩展,提供了比Lucene更为丰富的查询语言,可以非常方便的通过Elasticsearch的HTTP接口与底层Lucene交互。Elasticsearch是Lucene面向企业搜索应用的扩展,极大的缩短研发周期。
在if [ -x “$JAVA_HOME/bin/java” ]; then上添加两行
启动时遇到其他问题汇总:
1) 启动 elasticsearch 如出现异常 can not run elasticsearch as root
解决方法:创建ES 账户,修改文件夹 文件 所属用户 组
2) 启动异常:ERROR: bootstrap checks failed
system call filters failed to install; check the logs and fix your configuration or disable system call filters at your own risk
解决方法:在elasticsearch.yml中配置bootstrap.system_call_filter为false,注意要在Memory下面:
3) 启动后,如果只有本地可以访问,尝试修改配置文件 elasticsearch.yml中network.host(注意配置文件格式不是以 # 开头的要空一格, : 后要空一格)
为 network.host: 0.0.0.0
默认端口是 9200
注意:关闭防火墙 或者开放9200端口
4) ERROR: bootstrap checks failed
解决方法:切换到root用户,编辑limits.conf 添加类似如下内容
添加如下内容:
5)
解决:切换到root用户,进入limits.d目录下修改配置文件。
修改如下内容:
#修改为
6)
解决:切换到root用户修改配置sysctl.conf,添加下面配置:
并执行命令:
然后,重新启动elasticsearch,即可启动成功。
Elasticsearch Master配置文件如下:
0x05 ELasticsearch plugins
1) Head插件
下载Node.js:
配置node.js环境变量:
执行source /etc/profile使环境变量生效
查看当前head插件目录下有无node_modules/grunt目录:
没有则执行命令创建:
安装head插件:
编辑Gruntfile.js
文件93行添加
检查head根目录下是否存在base文件夹,没有:将 _site下的base文件夹及其内容复制到head根目录下
修改elasticsearch.yml,添加:
启动grunt server:在head下运行
访问head插件:http://localhost:9100
2) bigdesk插件
bigdesk是elasticsearch的一个集群监控工具,可以通过它来查看es集群的各种状态,如:cpu、内存使用情况,索引数据、搜索情况,http连接数等。
安装步骤:
git clone https://github.com/hlstudio/bigdesk
cd bigdesk/_site/
python -m SimpleHTTPServer
0x06 ELasticsearch 优化
1) shard与Replicas
shard不能修改,一个node不要超过2个shard。
replica只会参与读操作,它的主要作用就是提高集群错误恢复的能力,并且可以在集群建立之后变更。
2) 索引存储
最好是使用SSD,没有的话,最好将es数据节点配置多个数据存储路径,尽量避免使用远程文件系统存储,如NFS 或 SMB。
3) “refresh_interval”: “30s”
优化点: 减少刷新频率,降低潜在的写磁盘性能损耗
另外如果需要一次加载较大的数据量进 index 里面时,可以先禁用 refresh ,把 index.refresh_interval 设置成为 -1 ,把 index.number_of_replicas 设置成 0。暂时把多个shard副本关闭,这样做可以大大加快索引速度。当初始化索引完成,可以将 index.refresh_interval 和 index.number_of_replicas 设置回原来的值。
4) translog优化
Lucene只有在commit的时候才会把之前的变更持久化存储到磁盘(每次操作都写到磁盘的话,代价太大),在commit之前如果出现故障,上一次commit之后的变更都会丢失
为了防止数据丢失,Lucene会把变更操作都记录在translog里,在出现故障的时候,从上次commit起记录在translog里的变更都可以恢复,尽量保证数据不丢失
Lucene的flush操作就是执行一次commit,同时开始记录一个新的translog,所以translog是用来记录从上次commit到下一次commit之间的操作的
flush操作的频率是通过translog的大小控制的,当translog大小达到一定值的时候就执行一次flush,对应参数为index.translog.flush_threshold_size,默认值是512mb,这里调整为1gb,减少flush的次数
translog本身是文件,也需要存储到磁盘,它的存储方式通过index.translog.durability和index.translog.sync_interval设定。默认情况下,index.translog.durability=request,意为每次请求都会把translog写到磁盘。这种设定可以降低数据丢失的风险,但是磁盘IO开销会较大
这里采用异步方式持久化translog,每隔30秒写一次磁盘
{
"index": {
"translog": {
"flush_threshold_size": "1gb",
"sync_interval": "30s",
"durability": "async"
}
}
}
5) 429错误
增加bulk的queue大小
改配置会增加JVM内存,修改config/jvm.options
-Xms8g
-Xmx8g
建议配置为物理内存的一半,因为文件系统缓存是为了缓冲磁盘的IO操作。至少确保有一半机器的内存保留给操作系统,并且JVM内存不要超过32G。
6)
index由多个shard组成,每个shard又分成很多segment,segment是index数据存储的最小单位。segment比较多的时候会影响搜索性能,ES通过merge对小的segment进行合并,优化查询性能。但是合并过程中会消耗较多磁盘IO,会影响查询性能。Elasticsearch 5 采用了多线程去执行merge,可以通过修改index.merge.scheduler.max_thread_count 来动态调整这个线程数,默认的话是通过下面公式去计算:
要注意的是如果你是用HDD而非SSD的磁盘的话,最好是用单线程为妙。
另外也可以手工进行merge操作,这里有3个参数可以用
max_num_segments 期望merge到多少个segments,1的意思是强行merge到1个segment
only_expunge_deletes 只做清理有deleted的segments,即瘦身
flush 清理完执行一下flush,默认是true
你可以用下面的URL来执行强行的merge
[root@localhost elasticsearch55]# curl -XPOST "http://localhost:9200/nginx_2018.04.12/_forcemerge?max_num_segments=1"
{"_shards":{"total":5,"successful":5,"failed":0}}
7) 避免内存交换
设置为true来锁住内存不进行swapping,因为当jvm开始swapping时es的效率会降低。
0x07 告警配置
告警方面可以选择Elastalert
这里我以单IP高频请求api监控为例,查询语句如下:
{
"size": 0,
"query": {
"bool": {
"must": [
{
"range": {
"timestamp": {
"from": "%d",
"to": "%d"
}
}
},
{
"term": {
"status-code": {
"value": "200"
}
}
},
{
"term": {
"dynamic": {
"value": 1
}
}
}
]
}
},
"aggs": {
"group_by_clientip": {
"terms": {
"field": "client-ip",
"order": {
"_count": "desc"
},
"min_doc_count": %d
},
"aggs": {
"group_by_servername": {
"terms": {
"field": "servername",
"size": 2,
"order": {
"_count": "desc"
}
}
}
}
}
}
}
参考文章:
https://cloud.tencent.com/developer/article/1006124
https://www.jianshu.com/p/9b872a41d5bb