摘自designing the perfect elasticsearch cluster
本文包含了设计elasticsearch cluster需要了解的方方面面,本文不会告诉你如何设计一个完美的es集群。
elasticsearch是一个搜索引擎,不是数据库,不会替代mysql elasticsearch是一个弹性搜索引擎,弹性体现在水平扩展、索引分片;elastic将索引拆分成多个分片存储在不同的host上,分片数量默认为5,需要根据实际workload调整以达到最优性能
高可用设计
elasticsearch集群包含一下4种节点类型: master nodes: 主节点 data node: 数据节点 http node: 查询节点 coordinating node: 协作节点 最小高可用集群设计: 1. 3数据中心 2. 3主节点,奇数个主节点防止脑裂,将3个主机点分散在各个数据中心 3. 2查询节点,在主节点数据中心各一个 4. 任意数量的数据节点 ES的分片分配算法允许分片分配在不同区域内,根据rack配置将主分片、备份分片分配在不同的区域中 cluster.routing.allocation.awareness.attributes: "rack_id" node.attr.rack_id: "dontsmokecrack" 理解luence: elasticsearch使用了luence库
1. 每个elasticsearch分片是一个luence索引,一个luence索引最大可包含2,147,483,519条记录。 2. luence将索引划分为多个段,luence顺序搜索这些段;当新写请求来时会创建段,写提交或关闭时, 段即不可变,有新记录添加到elasticsearch index时,luence会创建新的段,luence会不断的将小的段 合并成大的段。luence搜索段是串行的,所以段越多延迟越大。当luence合并时会占用CPU与I/O进而降 低index速度。 3. 当更新或删除文档时luence执行copy on write操作,删除只将文档标记为已删除,索引磁盘占用会 一直增长,除非整个索引删除。 4. luence执行merge时,会将两个段合并成一个新的段后再删除旧的段,merge要确保磁盘空间足够 硬件
CPU: 复杂查询 ElasticSearch主要通过thread_pools使用CPU资源,主要的thread_pool有generic、index、get、bulk、 search等,可用通过GET _nodes/thread_pool?pretty查看当前集群各个节点的thread_pool情况; thread_pool的线程数、队列数可以通过配置设置,默认不会超过32;当队列满时请求会被ES拒绝,可以 适当调整queue的数值。 Memory: ElasticSearch作为Java应用程序,内存分配与回收非常关键;ES使用CMS作为默认的垃圾收集器,而CMS 的最大问题是会导致JVM停止,在堆内存大于4G不推荐;Java8推出了新的垃圾收集器G1,专门针对堆大于 4G的情况,G1GC将堆划分为1-32M的区,然后利用后台线程先扫描含垃圾最多的区,不会造成stop the world 的情况;另外由于指针大小的原因不要将堆设置超过31G;禁止swap。 -XX:+UseG1GC -XX:MaxGCPauseMillis=400 Network: 带宽越大越好;可以增加mtu值,以减少网络延时 Storage 存储往往是ES的瓶颈;尽量不要使用机械磁盘,SSD是最好的选择。 在对数据完整性要求不高时,raid0是最佳选择;裸磁盘成本低,故障只会丢失一块盘的数据,但读写速率低;raid10成本高。 Software
起因 最近Logstash经常打印如下日志内容,虽然只是INFO级别的错误但还是引起了我的注意,大概意思是ES的bulk thread pool大小为32已经满负荷,队列也满了,所以拒绝了Logstash的bulk请求
[2018-06-23T08:00:47,281][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@177d229b on EsThreadPoolExecutor[bulk, queue capacity = 1000, org.elasticsearch.common.util.concurrent.EsThreadPoolExecutor@66a1a8c3[Running, pool size = 32, active threads = 32, queued tasks = 1019, completed tasks = 832616617]]"}) 解决思路 扩大处理bulk的thread pool线程数量
当前使用的服务器CPU核数为176(cat /proc/cpuinfo),而在ES源码中为了避免引起Java oom线程数会取min(32, 核数),所以造成默认的thread_pool.bulk.size为32,为了扩大线程容量需在ES中添加以下配置 processors: 96 thread_pool.bulk.size: 96 增加等待队列长度
thread_pool.bulk.queue_size: 3000 更新配置 这些配置不能通过ES Setting API来更改,只能通过滚动重启的方式,下面简单记录下过程
禁止分配分片
curl -X PUT "ES_HOST:9200/_cluster/settings" -H 'Content-Type: application/json' -d' { "transient": { "cluster.
考虑到目前使用的ELK集群版本与开源版本的版本差距有点大,而ELK5.6相较2.3版本性能有较大提升,尤其是Logstash grok插件,最近对测试环境的两个ELK集群进行了升级,对升级过程进行一个记录;升级主要参考了官方文档
当前系统运行版本 filebeat:1.2.3 kibana:4.5.1 logstash:2.3.2 elasticsearch:2.3.3 JVM: 1.7 升级顺序 jvm(略) elasticsearch kibana logstash beats 升级Elasticsearch 升级之前 升级前使用Elasticsearch Migration Plugin查看潜在问题 最好先在测试环境升级ELK 在升级之前先备份数据 升级(跨大版本整个集群需重启) 禁止自动分配分片,在停止elasticsearch时减少不必要的I/O
curl -XPUT 'localhost:9200/_cluster/settings?pretty' -H 'Content-Type: application/json' -d' { "persistent": { "cluster.routing.allocation.enable": "none" } }' 同步刷新以加快分片恢复
curl -XPOST 'localhost:9200/_flush/synced?pretty' 停止并升级各个节点(安装新的rpm或deb,更新配置文件,配置文件有较大改动)
/etc/init.d/elasticsearch stop apt-get pruge elasticsearch(所有deb包已在本地apt源中,删除原配置文件) apt-get install elasticsearch=5.6.5 升级插件(需删除旧的插件)
/usr/share/elasticsearch/bin/elasticsearch-plubin remove license /usr/share/elasticsearch/bin/elasticsearch-plubin remove marvel /usr/share/elasticsearch/bin/elasticsearch-plubin install x-pack 启动集群各个节点,保证所有节点均加入集群(优先启动master node)
最近接手维护了几个ELK集群,对接的是IaaS、PaaS服务日志,简单的ELK架构,通过filebeat采集日志,发送到logstash结构化日志然后发送到ElasticSearch,用户可以通过Kibana查看服务日志定位问题、做一些性能分析图表等,同时利用elastalert做日志报警。
在ElasticSearch须要对用户的索引建立合适的类型映射(尤其是int类型),才可以在kibana中对数据进行分析,关于ES的映射类型可以看这里,需指出的是一旦某个field字段类型确定就很难更改该字段的类型(需reindex)。
Logstash将非结构化数据转化为结构化数据,通过JSON将数据发送给ElasticSearch,所有字段都会被当作string来处理,而ElasticSearch在自动判断字段类型建立映射这方面做的不足,与我们需求不符,那么如何正确建立索引类型映射呢?
一、通过Logstash的grok、mutate确定字段类型
grok 根据grok官方文档,在grok正则表达式后可以添加一个数据类型,默认是string类型,如果你想使字段类型为int,你可以在表达式后加int,例如%{NUMBER:num:int},那么num字段会从string类型变为int类型,目前只支持int和float。
mutate 通过mutate可以将field转化为integer、float、string,例如:
filter { mutate { convert => { "num" => "integer" } } } 二、ElasticSearch mapping template
映射(mappings)决定了一个字段(field)如何被ElasticSearch解释、存储,例如数据{“ip”:“223.5.5.5”}发送给ES,ES会将ip字段存储为string类型,而不是ip类型,不能做IP范围查询同时造成存储空间浪费、查询效率低等。不管在Logstash如何转换类型,ElasticSearch不会知道你的用意除非你正确映射。所有整型会存为long,小数会存为float或double,关于最小类型可以看这里,使用integer而不是long会有效的减少ELasticSearch负担。
映射模版 编写模版文件my_template.json
{ "index_patterns" : "index*", "version" : 1, "settings" : { "index.refresh_interval" : "5s" }, "mappings" : { "_default_" : { "properties":{ "host": { "type" : "string"}, "ip": {"type" : "ip"}, .... } } } } 上传template.
Logstash过滤插件grok正则解析 一、grok介绍 grok是Logstash中用来解析非结构化日志数据,将日志转化为可查询的结构化数据的最佳方法,可以用来处理syslog日志、apache等webserver日志、mysql日志以及用户自定义日志。
Logstash自带有120多种预定义好的正则表达式方便用户使用,你可以在这里查看这些正则表达式,你也可以添加自己的匹配规则。
有两个网站可以帮助我们来构建正则表达式去匹配我们的日志:
http://grokdebug.herokuapp.com http://grokconstructor.appspot.com (推荐) 二、grok基础 grok匹配模式语法为:%{SYNTAX:SEMANTIC:TYPE}
SYNTAX: 正则表达式、预定义的正则表达式名称
SEMANTIC: 标识符,标识匹配后的数据
TYPE: 可选的类型,目前支持int、float
例如:NUMBER可以匹配3.44,IP可以匹配:192.168.21.2
一个复杂的日志格式如下:
192.168.21.2 GET /index.html 15823 0.023 grok匹配模式可以为:
${IP:client} %{WORD:method} %{URIPATHPARAM:request} %{NUMBER:bytes} %{NUMBER:duration} 更加实际的,该条日志可能来自一个文件:
input { file { path => "/var/log/http.log" } } filter { grok { match => { "message" => "%{IP:client} %{WORD:method} %{URIPATHPARAM:request} %{NUMBER:bytes} %{NUMBER:duration}" } } } 在grok过滤后,可以得到额外一下字段:
client: 192.168.21.2 method: GET request: /index.html bytes: 15823 duration: 0.