ELK stack at weibo.com

50 %
50 %
Information about ELK stack at weibo.com

Published on September 12, 2015

Author: chenryn

Source: slideshare.net

1. 实时⽇日志检索分析应⽤用 ELKstack@sina

2. ⾃自我介绍 • 先后给 CloudEx, China.com, RenRen, Sina 重启 服务器; • 精通 echo/say/puts/console.log(“Hello World”); • 写过《⽹网站运维技术与实践》; • 翻过《Puppet 实战⼿手册》; • 微博账号:@ARGV,求关注。

3. 内容概要 • ELKstack集群概况 • ELKstack场景⽰示例 • 从ELK到ERK的演进 • LERK性能优化细节

4. ERK集群规模 • 26 个datanode: • 2.4Ghz*8, 42G, 300G *10 RAID5 • 25种⽇日志,7天,650亿条数据,6万个字段 • 单⽇日数据8TB,写⼊入峰值19万qps • 10 个rsyslog/logstash • rsyslog/logstash/kibana都有⼆二次开发 • 使⽤用⼈人员:故障管理组,客户端开发,服务端开发,运维 • 维护⼈人员:我*0.8

5. kopf 数据状态和设置调整

6. bigdesk 节点性能数据实时监控

7. zabbix trapper 关键指标的监控和报警

8. But,为什么要ELK?

9. 先说说⽇日志能⼲干嘛 • 找问题 • data-driven develop/test/operate • 安全审计 • Laws of Marcus J. Ranum • 监控 • Monitoring is the aggregation of health and performance data, events, and relationships delivered via an interface that provides an holistic view of a system's state to better understand and address failure scenarios. @etsy

10. ⽇日志分析的难点(1) • timestamp + data = log • 好,昨晚23:12到23:29那会⼉儿⽇日志有啥异常?

11. ⽇日志分析的难点(2) • ⽂文本内容⾮非结构化数据

12. ⽇日志分析的难点(2) • grep/awk只能单机跑

13. ⽇日志分析的难点(3) • 格式复杂不⽅方便可视化效果

14. So... • 我们需要⼀一个 实时⼤大数据搜 索平台 • 不过,splunk好贵好贵... • 只能⾃自⼰己拼开源玩具了~

15. ELKstack简明⼊入⻔门

18. How scaling 19. Talk is cheap, 
 show me the case! 20. php记录的⽇日志 21. logstash相关配置 22. Kibana3最终效果 服务端开发和运维⽤用来观测定位接⼜⼝口和应⽤用错误 23. 以及Kibana4效果 好吧,k4还没优化到配⾊色⽅方⾯面 24. PHP的slowlog 25. 经过multiline处理后 运维可以快速对⽐比各机房的php慢函数堆栈异常, 以及单机异常数据的排名。 26. 发现单机异常后的诊断 27. Nginx的errorlog 28. grok { match => { "message" => "(?<datetime>d{4}/dd/dd dd:dd:dd) [(? <errtype>w+)] S+: *d+ (?<errmsg>[^,]+), (?<errinfo>.*)$" } } mutate { gsub => [ "errmsg", "too large body: d+ bytes", "too large body" ] } if [errinfo] { ruby { code => "event.append(Hash[event['errinfo'].split(', ').map{|l| l.split(': ')}])" } } grok { match => { "request" => '"%{WORD:verb} %{URIPATH:urlpath}(?:?% {NGX_URIPARAM:urlparam})?(?: HTTP/%{NUMBER:httpversion})"' } } kv { prefix => "url_" source => "urlparam" field_split => "&" } date { locale => 'en' match => [ "datetime", "yyyy/MM/dd HH:mm:ss" ] } 29. 运维对性能数据做优化调研时, 可以同时参照多个维度的数据。 30. 时间段变化导致的各维度差异 31. app crash堆栈 客户端开发关注堆栈排⾏行, 堆栈内容是在logstash⾥里过滤掉系统函数之后的结果。 32. 发布新版,即时过滤,关注堆栈 33. 故障管理组和开发通过快捷搜索, 快速定位投诉⽤用户的报错类型和内容。 34. H5开发关注的⾸首⻚页接⼝口性能趋势数据 35. 响应时间的概率分布 平均时间不靠谱,划分区间又不能拍脑门 36. ELK到ERK的优化之路 37. 别⼈人家孩⼦子 38. 穷⼈人家孩⼦子 39. WHY? 40. 优劣对⽐比 logstash • 设计:多线程共享 SizedQueue • 语⾔言:JRuby开发 • 语法:DSL • 环境:jre1.7 • 队列:依赖外部系统 • 正则:ruby实现 • 输出:同⺴⽹网段内⾛走java协议写ES • 插件:182个 • 监控:暂⽆无 rsyslog • 设计:多线程共享 mainQ 接收 • 语⾔言:C开发 • 语法:rainerscript • 环境:rhel6⾃自带 • 队列:内置异步队列 • 正则:ERE • 输出:使⽤用HTTP协议写ES • 插件:57个 • 监控:有pstats数据 41. Logstash性能已知问题 • Input/syslog性能极差,改⽤用input/tcp+多线程filter/grok • Filter/geoip性能较差,开发⼀一个filter/geoip2 • Filter/grok费CPU,改⽤用filter/ruby⾃自⼰己写split • Input/tcp有内存溢出(1.4.2版本之前) • Output/elasticsearch有内存溢出(1.5.0之前) • Output/elasticsearch的retry逻辑跟stud的SizedQueue重复(⾄至今) 42. LogStash问题(1) • LogStash::Inputs::Syslog性能极差 • logstash的pipeline介绍: • input thread
 -> filterworker threads * Num
 -> output thread • ⽽而Inputs::Syslog的逻辑是: • TCPServer/accept
 -> client thread -> filter/grok -> filter/date
 -> filterworker threads • 也就是在单线程中,要完成正则和时间转换两个极废资源的操作。 • 经测试,TCPServer能到50k的qps,经过filter/grok后下降成6k,再经filter/date后下降成 0.7k! 43. LogStash问题(1) • LogStash::Inputs::Syslog性能极差 • 解决办法: • 把grok和date搬出来,⾃自⼰己通过配置实现这个功能: input { tcp { port => 514 } } filter { grok { match => ["message", "%{SYSLOGLINE}"] } syslog_pri { } date { match => ["timestamp", "ISO8601"] } } • 使⽤用logstash -w 20运⾏行,可以达到30k的qps。 44. LogStash问题(2) • LogStash::Filters::Grok性能 • Grok是在标准正则基础上,增加了正则预定义和引⽤用 功能: • 预定义好:NUMBER d+
 使⽤用%{NUMBER:score} 等价于 (?<score>d+) • 采⽤用正则处理⽇日志,虽然灵活,但是消耗CPU⽐比较严 重。⽽而且grok原先是C程序(现在通过epel库还可以直 接 yum install libgrok) 45. LogStash问题(2) • LogStash::Filters::Grok性能 • 解决办法: • 对有明显格式的⽇日志,避免采⽤用grok,⽐比如有固定分隔符的: filter { ruby { init => "@kname = ['datetime','uid','limittype','limitkey','client','clientip','request_time','url']" code => "event.append(Hash[@kname.zip(event['message'].split('|'))])" } mutate { convert => ["request_time", "float"] } } • 实践证明:采⽤用split代码可以降低20%的cpu使⽤用。 46. LogStash问题(3) • LogStash::Filters::GeoIP性能 • 即使在logstash -w 30的情况下,也只能到7k的 qps。 • MaxMind公司后来推出的MaxMindDB格式,即 GeoIP2地址库,性能有极⼤大提⾼高。但是因为 GeoIP2-Lite的发布协议只允许⼀一年内使⽤用,不⽅方 便分发,所以LogStash开发者⽆无法使⽤用。 47. LogStash问题(3) • LogStash::Filters::GeoIP性能 • 解决办法: • MaxMindDB提供了Writer⼯工具(感谢陈刚童鞋)。转 换我司ip.db成ip.mmdb,300MB->50MB • JRuby平台采⽤用java_import导⼊入maxminddb-java 库实现LogStash::Filters::MaxMindDB,性能提⾼高 到 28k 的 qps。 48. LogStash问题(4) • LogStash::Outputs::Elasticsearch稳定性 • 到⺫⽬目前为⽌止,踩过三个bug: 1. logstash1.4.2采⽤用的ftw-0.0.39有内存泄露问题,跑⼏几个⼩小时就堵死了 ; 2. logstash1.5.0beta1改⽤用了Manticore,但是retry逻辑和logstash pipeline中使⽤用的ThreadQueue库的retry逻辑有重复,导致反复发送; 3. logstash1.5.0rc1记录ES错误响应时,⽆无法匹配reject错误的429状态 码,看着报错⽇日志⾥里"got response of . source:",完全茫然。 • ⺫⽬目前最新的logstash1.5.0rc3版本内已解决1和3。 49. LogStash问题(5) • LogStash::Pipeline的隐藏问题 • 前⾯面检结果pipeline的逻辑。但是这⾥里Logstash对filterworkers没有做 supervisor。所以,⼀一旦配置写得不全⾯面,有可能导致thread异常退出 的话,慢慢的所有filterworkers都退出掉了,logstash就处于⼀一个堵死但 进程还活着的状态! • 官⽅方插件的代码⼀一般都还⽐比较可靠。但是前⾯面介绍的采⽤用filter/ruby来⾃自 ⼰己操作`event['field']`,就很可能碰到了。所以,⼀一定要先检查有没有这 个field存在! if [url] { ruby { code => "event['urlpath']=event['url'].split('?')[0]" } } 50. LogStash问题(6) • LogStash::Pipeline的⼜又⼀一个隐藏问题 • logstash1.5.0之前,filter阶段,某个插件代码内如 果⽣生成新event,通过`yield`加⼊入pipeline的时候, 并不会继续配置中书写在该插件配置块后⾯面的其他 filter插件流程,⽽而是直接进⼊入output thread。 • 官⽅方插件中,split和clone都有⽤用到。我们⾃自定义 插件json2也是。 51. Rsyslog性能优化 • action采⽤用linkedlist异步队列 • imfile启⽤用合适的statepresistinterval保证异常重启不重复发送太多⽇日志 • omfwd采⽤用rebindinterval保证负载均衡 • 配置合适的global.maxmessagesize⼤大⼩小 • 配置合适的queue.size和queue.highwatermask • 采⽤用CEE格式记录⽇日志,配合mmjsonparse解析 • 利⽤用mmfields切割固定分隔符的⽇日志 • 利⽤用rainerscript语法做⼆二次处理 • 利⽤用property replacer⼿手⼯工拼接JSON字符串 • 开发mmdb插件做ip地址解析 52. rsyslog问题(1) • 在源码的测试集中发现rsyslog8.7新加实验性的foreach指令,正好 ⽤用来处理客户端⽇日志的JSON数组,使⽤用中发现三个相关bug: 1.foreach 操作没有判断传⼊入变量是不是数组; 2.action() 不会复制消息内容⽽而是直接引⽤用指针,这样在 foreach ⾥里发 送消息数组⾥里单个元素,使⽤用 linkedlist 异步⽅方式的 action 队列,就 会出问题。之前测试 omfile 不会有问题,因为 omfile 默认是同步⽅方 式。对此,给 action() 新增⼀一个 copymsg 参数控制; 3.omelasticsearch 插件在记录 errorfile 的逻辑⾥里发现⼀一个未初始化的 变量。 修复的补丁已经merge进rsyslog8.10,5⽉月20⽇日发布。 53. rsyslog问题(2) • message modification插件较少,mmexternal插件 ⺫⽬目前不稳定,对复杂需求需要⼆二次开发。 • ⺫⽬目前完成rsyslog-mmdb插件开发,在rsyslog中完 成clientip的解析,5⽉月下旬上线稳定运⾏行。 54. input( type=“imtcp” port=“514” ) template( name=“clientlog" type="list" ) { constant(value="{"@timestamp":"") property(name="timereported" dateFormat="rfc3339") constant(value="","host":"") property(name="hostname") constant(value="",“mmdb":") property(name="!iplocation") constant(value=",") property(name="$.line" position.from="2") } ruleset( name=“clientlog” ) { action(type="mmjsonparse") if($parsesuccess == "OK") then { foreach ($.line in $!msgarray) { if($.line!rtt == “-”) then { set $.line!rtt = 0; } set $.line!urlpath = field($.line!url, 63, 1); set $.line!urlargs = field($.line!url, 63, 2); set $.line!from = ""; if ( $.line!urlargs != "***FIELD NOT FOUND***" ) then { reset $.line!from = re_extract($.line!urlargs, "from=([0-9]+)", 0, 1, ""); } else { unset $.line!urlargs; } action(type=“mmdb” key=“.line!clientip” fields=[“city”,“isp”,“country”] mmdbfile="./ip.mmdb") action(type="omelasticsearch" server=“1.1.1.1“ bulkmode=“on“ template=“clientlog” queue.size="10000" queue.dequeuebatchsize="2000“ ) } } } if ($programname startswith “mweibo_client”) then { call clientlog stop } 55. ES性能优化 • 不要照抄⽹网上的说法!! • 单机单索引单分⽚片零副本的测试数据做基线 • ⽤用unicast,加⼤大fd.ping_timeout •doc_values是稳定性的基础 • 加快恢复:gateway,recovery和allocation相关参数 • 适当加⼤大refresh_interval和flush_threshold_size • 适当加⼤大store.throttle.max_bytes_per_sec • 升级到1.5.1以上 • 新加节点:限制索引级别的分⽚片策略 • 要bulk,不要multithreads,更不要async •⽤用curator做定时optimize • 简单⽇日志可以不要_all 56. ES稳定性问题(1) • OOM • segment⾥里的fielddata是ES做即时搜索和聚合的来源,默认 是在query的时候,从term加载到内存。所以数据量⼀一⼤大, 就会OOM。 • Kibana3⾥里⼲⼴广泛采⽤用facet_filter⽅方式,意味着QUERY阶段数 据量不做过滤,加重了出问题的可能。 • 在新版本中,引⼊入了circuit breaker概念,fielddata到heap 的60%的时候,直接断掉查询,报错: Data too large, data for field [@timestamp] would be larger than limit of[639015321/609.4mb]] 57. ES稳定性问题(1) • OOM 解决办法: • 启⽤用doc_values设置。该设置让ES在indexing的 时候提前做好fielddata到磁盘上。以后query的就 是读取磁盘内容。性能⽅方⾯面则主要靠⽂文件系统的缓 存。 • 这样意味着反⽽而不⽤用设置太⼤大的heap(所谓的 31GB问题),多留⼀一些内存给⽂文件系统。 58. ES稳定性问题(2) • 数据迁移和恢复导致的停机不可⽤用 • 默认策略: • 节点启动即开始做恢复 • 集群内同时只能迁移⼀一个shard • 迁移限速20MB • translog在flush后就清除,所以replica是全量从primary⾛走⺴⽹网络 复制! • 所以只要有宕机,集群就可能⼏几个⼩小时不可⽤用…… 59. ES稳定性问题(2) • 数据迁移和恢复导致的停机不可⽤用的解决办法 • 节点启动即开始做恢复 • gateway相关参数,强制等集群数量够了再恢复 • 集群内同时只能迁移⼀一个shard • cluster.routing.allocation相关参数,加⼤大并发 • 迁移限速20MB • indices.recovery相关参数,放宽限速 • ⺫⽬目前30个节点的集群,全集群重启20分钟恢复完毕(副本分⽚片恢复速度⺫⽬目前⽆无解,在 ES2.0计划中有对⽇日志场景冷索引关闭IndexWriter结构后加快恢复的计划)。 • 注:1.5.1之前,ES另外还有⼀一个bug导致分⽚片恢复过程卡在translog阶段过不去。⽽而 1.4.0之前默认开启的bloom filter偏巧可以掩盖这个问题。 60. ES稳定性问题(3) • 新增节点被压死 • ES集群对分⽚片分布的默认策略: • 以节点为统计单位,尽量每个节点的分⽚片总数均衡 • 节点磁盘使⽤用率达到阈值的时候,不接新分⽚片 • 所以新增节点会被尽量分配多分⽚片来做到总数均衡。但 是⽇日志场景下,只有当天热索引的分⽚片有IO压⼒力。所以 新增节点的第⼆二天,全部热索引分⽚片都集中在这台机器 上,直接被干死! 61. ES稳定性问题(3) • 新增节点被压死的解决办法 1. 在新建索引出来之前,完成relocation。 2. 通过index.routing.allocation.total_shards_per_node 配置强制每个索引在单节点上的分⽚片数。 • 注1:强制要放宽余量,不然在故障的时候,副本要迁 移却没地⽅方允许它落地…… • 注2:已经悲剧了的索引就不要亡⽺羊补牢了,结果只会 对新节点IO雪上加霜。 62. ES稳定性问题(4) • replica的async⽅方式 • ES默认要求数据在发送给replica shards的时候,达到⼀一半以上shards成功,才返回写⼊入 成功。 • 看似如果采⽤用async⽅方式,能提⾼高写⼊入性能? • 实际上,async⽅方式没有数据校验,没法流控。⼀一旦因为负载⾼高导致某个segment有偏 差,反复修复会导致恶性循环,cpu util%飙升,反⽽而写⼊入性能下降。 • 注:ES2.0⾥里将取消这个功能。 63. ES性能问题(1) • bulk批量写⼊入,经常返回状态码429,队列容量不 ⾜足。 • 我们单条⽇日志⻓长度较⼤大,nginx⽇日志平均600B, client⽇日志平均2KB。尤其是client_net_fatal_error 有时候单条过MB。 • ES接收HTTP请求是可以101接收,但是处理HTTP 完整body的上限是100MB。所以bulk_size需要根 据⽇日志单条⻓长度仔细计算。 64. ES性能问题(2) • 数据容量膨胀率较⾼高(默认logstash模板膨胀到3倍⼤大),对IO和容量都有压 ⼒力。要减少冗余数据,减少不必要的分词。 • _source: 存储源JSON • _all: 存储各field的term⽅方便在不指定field的时候直接搜索单词 • multi-field: logstash默认模板会对每个字段附加⼀一个.raw不分词字段 • 所以: • 对于字段很明确的⽇日志,如nginx⽇日志,舍弃_all。 • 对于没啥⽂文本,都是数值的聚合需求的监控数据,可以放弃掉_source。 • 对于⼤大多数字段内容很明确不会有拆分搜索和统计的直接设置不分词。 65. ES性能问题(3) • ⻓长期处于CPU util%⾼高⽔水位运⾏行。 • CPU主要就是segment merge(永远的hot threads)。默认规则是: • 单个segment上限5GB • 单个segment下限2MB • 考虑⽇日志场景不要求多么实时,所以最简单的办法:加⼤大refresh(默认1s) 和flush(默认200MB)的interval。 66. cluster.name: es1003 cluster.routing.allocation.node_initial_primaries_recoveries: 30 cluster.routing.allocation.node_concurrent_recoveries: 5 cluster.routing.allocation.cluster_concurrent_rebalance: 5 cluster.routing.allocation.enable: all node.name: esnode001 node.master: false node.data: data node.max_local_storage_nodes: 1 index.routing.allocation.total_shards_per_node : 3 index.merge.scheduler.max_thread_count: 1 index.refresh_interval: 30s index.number_of_shards: 26 index.number_of_replicas: 1 index.translog.flush_threshold_size : 5000mb index.translog.flush_threshold_ops: 50000 index.search.slowlog.threshold.query.warn: 30s index.search.slowlog.threshold.fetch.warn: 1s index.indexing.slowlog.threshold.index.warn: 10s indices.store.throttle.max_bytes_per_sec: 1000mb indices.cache.filter.size: 10% indices.fielddata.cache.size: 10% indices.recovery.max_bytes_per_sec: 2gb indices.recovery.concurrent_streams: 30 path.data: /data1/elasticsearch/data path.logs: /data1/elasticsearch/logs bootstrap.mlockall: true http.max_content_length: 400mb http.enabled: true http.cors.enabled: true http.cors.allow-origin: "*" gateway.type: local gateway.recover_after_nodes: 30 gateway.recover_after_time: 5m gateway.expected_nodes: 30 discovery.zen.minimum_master_nodes: 3 discovery.zen.ping.timeout: 100s discovery.zen.ping.multicast.enabled: false discovery.zen.ping.unicast.hosts: ["10.19.0.97","10.19.0.98","10.19.0.99"] monitor.jvm.gc.young.warn: 1000ms monitor.jvm.gc.old.warn: 10s monitor.jvm.gc.old.info: 5s monitor.jvm.gc.old.debug: 2s 67. ES的搜索问题(1) • 搜索结果跟过滤条件“明显不对”。 curl es.domain.com:9200/logstash-accesslog-2015.04.03/nginx/_search?q=_id:AUx- QvSBS-dhpiB8_1f1&pretty -d '{ "fields": ["requestTime"], "script_fields" : { "test1" : { "script" : "doc["requestTime"].value" }, "test2" : { "script" : "_source.requestTime" }, "test3" : { "script" : "doc["requestTime"].value * 1000" } } }' 68. NOT schema free! "hits" : { "total" : 1, "max_score" : 1.0, "hits" : [ { "_index" : "logstash-accesslog-2015.04.03", "_type" : "nginx", "_id" : "AUx-QvSBS-dhpiB8_1f1", "_score" : 1.0, "fields" : { "test1" : [ 4603039107142836552 ], "test3" : [ -8646911284551352000 ], "requestTime" : [ 0.54 ], "test2" : [ 0.54 ], } } ] } 69. ES的搜索问题(2) • 部分⽇日志搜不着! • 因为ES要求同⼀一个_index下同⼀一个_type的同⼀一个名字的field, mapping type必须⼀一致。否则整条数据直接写⼊入失败! • client_net_fatal_error⽇日志中,某次发版后字段内容变了: • {"reqhdr":{"Host":"api.weibo.cn"}} • {"reqhdr":"{"Host":"api.weibo.cn"}"} • 设置object的mapping为{"enabled":false},跳过indexing阶段。 意味着这部分内容不能搜索,只能在_source JSON中可见。 70. ES的搜索问题(3) •部分⽇日志还是搜不着! •logstash⾃自带template中对不分词的字段定义了ignore_above:256。也就是字段内 容超过256字节,就⾃自动跳过indexing阶段。没有fielddata,针对这条记录的这个 字段的搜索和统计都是⽆无效的。 curl 10.19.0.100:9200/logstash-mweibo-2015.05.18/mweibo_client_crash/_search?q=_id:AU1ltyTCQC8tD04iYBIe&pretty -d '{ "fielddata_fields" : ["jsoncontent.content", "jsoncontent.platform"], "fields" : ["jsoncontent.content","jsoncontent.platform"] }' ... "fields" : { "jsoncontent.content" : [ "dalvik.system.NativeStart.main(Native Method)nCaused by: java.lang.ClassNotFoundException: Didn't find class "com.sina.weibo.hc.tracking.manager.TrackingService" on path: DexPathList[[zip file "/data/app/com.sina.weibo-1.apk", zip file "/data/data/com.sina.weibo/code_cache/secondary- dexes/com.sina.weibo-1.apk.classes2.zip", zip file "/data/data/com.sina.weibo/app_dex/ dbcf1705b9ffbc30ec98d1a76ada120909.jar"],nativeLibraryDirectories=[/data/app-lib/com.sina.weibo-1, /vendor/lib, / system/lib]]" ], "jsoncontent.platform" : [ "Android_4.4.4_MX4 Pro_Weibo_5.3.0 Beta_WIFI", "Android_4.4.4_MX4 Pro_Weibo_5.3.0 Beta_WIFI" ] } 71. Kibana⼆二次开发 • 升级k3的elastic.js版本,到ES1.2的API。可以使⽤用ES1.0版本新增的 aggs接⼝口(新写了percentile和range两个panel,重写了histogram panel,添加了cardinality metric)。 • 给table添加export as csv功能。 • 给bettermap添加地图切换功能。 • 给map添加term_stats功能,添加china选项。 • 通过mapping多选框⾃自助⽣生成querystring。 • 给terms panel添加script field功能,添加either filtering多选功能。 • 还有⼗十余项其他优化详⻅见<https://github.com/chenryn/kibana> 72. 参考阅读 •《Elasticsearch 服务器开发(第2版)》 •《⽇日志管理与分析权威指南》 •《数据之魅:基于开源⼯工具的数据分析》 •《⽹网站运维:保持数据实时的秘笈》 •《Web 容量规划的艺术》 •《⼤大规模 Web 服务开发技术》 •https://codeascraft.com/ •http://calendar.perfplanet.com •http://kibana.logstash.es 73. –JordanSissel@logstash.net “If a newbie has a bad time, it's a bug.”

18. How scaling

19. Talk is cheap, 
 show me the case!

20. php记录的⽇日志

21. logstash相关配置

22. Kibana3最终效果 服务端开发和运维⽤用来观测定位接⼜⼝口和应⽤用错误

23. 以及Kibana4效果 好吧,k4还没优化到配⾊色⽅方⾯面

24. PHP的slowlog

25. 经过multiline处理后 运维可以快速对⽐比各机房的php慢函数堆栈异常, 以及单机异常数据的排名。

26. 发现单机异常后的诊断

27. Nginx的errorlog

28. grok { match => { "message" => "(?<datetime>d{4}/dd/dd dd:dd:dd) [(? <errtype>w+)] S+: *d+ (?<errmsg>[^,]+), (?<errinfo>.*)$" } } mutate { gsub => [ "errmsg", "too large body: d+ bytes", "too large body" ] } if [errinfo] { ruby { code => "event.append(Hash[event['errinfo'].split(', ').map{|l| l.split(': ')}])" } } grok { match => { "request" => '"%{WORD:verb} %{URIPATH:urlpath}(?:?% {NGX_URIPARAM:urlparam})?(?: HTTP/%{NUMBER:httpversion})"' } } kv { prefix => "url_" source => "urlparam" field_split => "&" } date { locale => 'en' match => [ "datetime", "yyyy/MM/dd HH:mm:ss" ] }

29. 运维对性能数据做优化调研时, 可以同时参照多个维度的数据。

30. 时间段变化导致的各维度差异

31. app crash堆栈 客户端开发关注堆栈排⾏行, 堆栈内容是在logstash⾥里过滤掉系统函数之后的结果。

32. 发布新版,即时过滤,关注堆栈

33. 故障管理组和开发通过快捷搜索, 快速定位投诉⽤用户的报错类型和内容。

34. H5开发关注的⾸首⻚页接⼝口性能趋势数据

35. 响应时间的概率分布 平均时间不靠谱,划分区间又不能拍脑门

36. ELK到ERK的优化之路

37. 别⼈人家孩⼦子

38. 穷⼈人家孩⼦子

39. WHY?

40. 优劣对⽐比 logstash • 设计:多线程共享 SizedQueue • 语⾔言:JRuby开发 • 语法:DSL • 环境:jre1.7 • 队列:依赖外部系统 • 正则:ruby实现 • 输出:同⺴⽹网段内⾛走java协议写ES • 插件:182个 • 监控:暂⽆无 rsyslog • 设计:多线程共享 mainQ 接收 • 语⾔言:C开发 • 语法:rainerscript • 环境:rhel6⾃自带 • 队列:内置异步队列 • 正则:ERE • 输出:使⽤用HTTP协议写ES • 插件:57个 • 监控:有pstats数据

41. Logstash性能已知问题 • Input/syslog性能极差,改⽤用input/tcp+多线程filter/grok • Filter/geoip性能较差,开发⼀一个filter/geoip2 • Filter/grok费CPU,改⽤用filter/ruby⾃自⼰己写split • Input/tcp有内存溢出(1.4.2版本之前) • Output/elasticsearch有内存溢出(1.5.0之前) • Output/elasticsearch的retry逻辑跟stud的SizedQueue重复(⾄至今)

42. LogStash问题(1) • LogStash::Inputs::Syslog性能极差 • logstash的pipeline介绍: • input thread
 -> filterworker threads * Num
 -> output thread • ⽽而Inputs::Syslog的逻辑是: • TCPServer/accept
 -> client thread -> filter/grok -> filter/date
 -> filterworker threads • 也就是在单线程中,要完成正则和时间转换两个极废资源的操作。 • 经测试,TCPServer能到50k的qps,经过filter/grok后下降成6k,再经filter/date后下降成 0.7k!

43. LogStash问题(1) • LogStash::Inputs::Syslog性能极差 • 解决办法: • 把grok和date搬出来,⾃自⼰己通过配置实现这个功能: input { tcp { port => 514 } } filter { grok { match => ["message", "%{SYSLOGLINE}"] } syslog_pri { } date { match => ["timestamp", "ISO8601"] } } • 使⽤用logstash -w 20运⾏行,可以达到30k的qps。

44. LogStash问题(2) • LogStash::Filters::Grok性能 • Grok是在标准正则基础上,增加了正则预定义和引⽤用 功能: • 预定义好:NUMBER d+
 使⽤用%{NUMBER:score} 等价于 (?<score>d+) • 采⽤用正则处理⽇日志,虽然灵活,但是消耗CPU⽐比较严 重。⽽而且grok原先是C程序(现在通过epel库还可以直 接 yum install libgrok)

45. LogStash问题(2) • LogStash::Filters::Grok性能 • 解决办法: • 对有明显格式的⽇日志,避免采⽤用grok,⽐比如有固定分隔符的: filter { ruby { init => "@kname = ['datetime','uid','limittype','limitkey','client','clientip','request_time','url']" code => "event.append(Hash[@kname.zip(event['message'].split('|'))])" } mutate { convert => ["request_time", "float"] } } • 实践证明:采⽤用split代码可以降低20%的cpu使⽤用。

46. LogStash问题(3) • LogStash::Filters::GeoIP性能 • 即使在logstash -w 30的情况下,也只能到7k的 qps。 • MaxMind公司后来推出的MaxMindDB格式,即 GeoIP2地址库,性能有极⼤大提⾼高。但是因为 GeoIP2-Lite的发布协议只允许⼀一年内使⽤用,不⽅方 便分发,所以LogStash开发者⽆无法使⽤用。

47. LogStash问题(3) • LogStash::Filters::GeoIP性能 • 解决办法: • MaxMindDB提供了Writer⼯工具(感谢陈刚童鞋)。转 换我司ip.db成ip.mmdb,300MB->50MB • JRuby平台采⽤用java_import导⼊入maxminddb-java 库实现LogStash::Filters::MaxMindDB,性能提⾼高 到 28k 的 qps。

48. LogStash问题(4) • LogStash::Outputs::Elasticsearch稳定性 • 到⺫⽬目前为⽌止,踩过三个bug: 1. logstash1.4.2采⽤用的ftw-0.0.39有内存泄露问题,跑⼏几个⼩小时就堵死了 ; 2. logstash1.5.0beta1改⽤用了Manticore,但是retry逻辑和logstash pipeline中使⽤用的ThreadQueue库的retry逻辑有重复,导致反复发送; 3. logstash1.5.0rc1记录ES错误响应时,⽆无法匹配reject错误的429状态 码,看着报错⽇日志⾥里"got response of . source:",完全茫然。 • ⺫⽬目前最新的logstash1.5.0rc3版本内已解决1和3。

49. LogStash问题(5) • LogStash::Pipeline的隐藏问题 • 前⾯面检结果pipeline的逻辑。但是这⾥里Logstash对filterworkers没有做 supervisor。所以,⼀一旦配置写得不全⾯面,有可能导致thread异常退出 的话,慢慢的所有filterworkers都退出掉了,logstash就处于⼀一个堵死但 进程还活着的状态! • 官⽅方插件的代码⼀一般都还⽐比较可靠。但是前⾯面介绍的采⽤用filter/ruby来⾃自 ⼰己操作`event['field']`,就很可能碰到了。所以,⼀一定要先检查有没有这 个field存在! if [url] { ruby { code => "event['urlpath']=event['url'].split('?')[0]" } }

50. LogStash问题(6) • LogStash::Pipeline的⼜又⼀一个隐藏问题 • logstash1.5.0之前,filter阶段,某个插件代码内如 果⽣生成新event,通过`yield`加⼊入pipeline的时候, 并不会继续配置中书写在该插件配置块后⾯面的其他 filter插件流程,⽽而是直接进⼊入output thread。 • 官⽅方插件中,split和clone都有⽤用到。我们⾃自定义 插件json2也是。

51. Rsyslog性能优化 • action采⽤用linkedlist异步队列 • imfile启⽤用合适的statepresistinterval保证异常重启不重复发送太多⽇日志 • omfwd采⽤用rebindinterval保证负载均衡 • 配置合适的global.maxmessagesize⼤大⼩小 • 配置合适的queue.size和queue.highwatermask • 采⽤用CEE格式记录⽇日志,配合mmjsonparse解析 • 利⽤用mmfields切割固定分隔符的⽇日志 • 利⽤用rainerscript语法做⼆二次处理 • 利⽤用property replacer⼿手⼯工拼接JSON字符串 • 开发mmdb插件做ip地址解析

52. rsyslog问题(1) • 在源码的测试集中发现rsyslog8.7新加实验性的foreach指令,正好 ⽤用来处理客户端⽇日志的JSON数组,使⽤用中发现三个相关bug: 1.foreach 操作没有判断传⼊入变量是不是数组; 2.action() 不会复制消息内容⽽而是直接引⽤用指针,这样在 foreach ⾥里发 送消息数组⾥里单个元素,使⽤用 linkedlist 异步⽅方式的 action 队列,就 会出问题。之前测试 omfile 不会有问题,因为 omfile 默认是同步⽅方 式。对此,给 action() 新增⼀一个 copymsg 参数控制; 3.omelasticsearch 插件在记录 errorfile 的逻辑⾥里发现⼀一个未初始化的 变量。 修复的补丁已经merge进rsyslog8.10,5⽉月20⽇日发布。

53. rsyslog问题(2) • message modification插件较少,mmexternal插件 ⺫⽬目前不稳定,对复杂需求需要⼆二次开发。 • ⺫⽬目前完成rsyslog-mmdb插件开发,在rsyslog中完 成clientip的解析,5⽉月下旬上线稳定运⾏行。

54. input( type=“imtcp” port=“514” ) template( name=“clientlog" type="list" ) { constant(value="{"@timestamp":"") property(name="timereported" dateFormat="rfc3339") constant(value="","host":"") property(name="hostname") constant(value="",“mmdb":") property(name="!iplocation") constant(value=",") property(name="$.line" position.from="2") } ruleset( name=“clientlog” ) { action(type="mmjsonparse") if($parsesuccess == "OK") then { foreach ($.line in $!msgarray) { if($.line!rtt == “-”) then { set $.line!rtt = 0; } set $.line!urlpath = field($.line!url, 63, 1); set $.line!urlargs = field($.line!url, 63, 2); set $.line!from = ""; if ( $.line!urlargs != "***FIELD NOT FOUND***" ) then { reset $.line!from = re_extract($.line!urlargs, "from=([0-9]+)", 0, 1, ""); } else { unset $.line!urlargs; } action(type=“mmdb” key=“.line!clientip” fields=[“city”,“isp”,“country”] mmdbfile="./ip.mmdb") action(type="omelasticsearch" server=“1.1.1.1“ bulkmode=“on“ template=“clientlog” queue.size="10000" queue.dequeuebatchsize="2000“ ) } } } if ($programname startswith “mweibo_client”) then { call clientlog stop }

55. ES性能优化 • 不要照抄⽹网上的说法!! • 单机单索引单分⽚片零副本的测试数据做基线 • ⽤用unicast,加⼤大fd.ping_timeout •doc_values是稳定性的基础 • 加快恢复:gateway,recovery和allocation相关参数 • 适当加⼤大refresh_interval和flush_threshold_size • 适当加⼤大store.throttle.max_bytes_per_sec • 升级到1.5.1以上 • 新加节点:限制索引级别的分⽚片策略 • 要bulk,不要multithreads,更不要async •⽤用curator做定时optimize • 简单⽇日志可以不要_all

56. ES稳定性问题(1) • OOM • segment⾥里的fielddata是ES做即时搜索和聚合的来源,默认 是在query的时候,从term加载到内存。所以数据量⼀一⼤大, 就会OOM。 • Kibana3⾥里⼲⼴广泛采⽤用facet_filter⽅方式,意味着QUERY阶段数 据量不做过滤,加重了出问题的可能。 • 在新版本中,引⼊入了circuit breaker概念,fielddata到heap 的60%的时候,直接断掉查询,报错: Data too large, data for field [@timestamp] would be larger than limit of[639015321/609.4mb]]

57. ES稳定性问题(1) • OOM 解决办法: • 启⽤用doc_values设置。该设置让ES在indexing的 时候提前做好fielddata到磁盘上。以后query的就 是读取磁盘内容。性能⽅方⾯面则主要靠⽂文件系统的缓 存。 • 这样意味着反⽽而不⽤用设置太⼤大的heap(所谓的 31GB问题),多留⼀一些内存给⽂文件系统。

58. ES稳定性问题(2) • 数据迁移和恢复导致的停机不可⽤用 • 默认策略: • 节点启动即开始做恢复 • 集群内同时只能迁移⼀一个shard • 迁移限速20MB • translog在flush后就清除,所以replica是全量从primary⾛走⺴⽹网络 复制! • 所以只要有宕机,集群就可能⼏几个⼩小时不可⽤用……

59. ES稳定性问题(2) • 数据迁移和恢复导致的停机不可⽤用的解决办法 • 节点启动即开始做恢复 • gateway相关参数,强制等集群数量够了再恢复 • 集群内同时只能迁移⼀一个shard • cluster.routing.allocation相关参数,加⼤大并发 • 迁移限速20MB • indices.recovery相关参数,放宽限速 • ⺫⽬目前30个节点的集群,全集群重启20分钟恢复完毕(副本分⽚片恢复速度⺫⽬目前⽆无解,在 ES2.0计划中有对⽇日志场景冷索引关闭IndexWriter结构后加快恢复的计划)。 • 注:1.5.1之前,ES另外还有⼀一个bug导致分⽚片恢复过程卡在translog阶段过不去。⽽而 1.4.0之前默认开启的bloom filter偏巧可以掩盖这个问题。

60. ES稳定性问题(3) • 新增节点被压死 • ES集群对分⽚片分布的默认策略: • 以节点为统计单位,尽量每个节点的分⽚片总数均衡 • 节点磁盘使⽤用率达到阈值的时候,不接新分⽚片 • 所以新增节点会被尽量分配多分⽚片来做到总数均衡。但 是⽇日志场景下,只有当天热索引的分⽚片有IO压⼒力。所以 新增节点的第⼆二天,全部热索引分⽚片都集中在这台机器 上,直接被干死!

61. ES稳定性问题(3) • 新增节点被压死的解决办法 1. 在新建索引出来之前,完成relocation。 2. 通过index.routing.allocation.total_shards_per_node 配置强制每个索引在单节点上的分⽚片数。 • 注1:强制要放宽余量,不然在故障的时候,副本要迁 移却没地⽅方允许它落地…… • 注2:已经悲剧了的索引就不要亡⽺羊补牢了,结果只会 对新节点IO雪上加霜。

62. ES稳定性问题(4) • replica的async⽅方式 • ES默认要求数据在发送给replica shards的时候,达到⼀一半以上shards成功,才返回写⼊入 成功。 • 看似如果采⽤用async⽅方式,能提⾼高写⼊入性能? • 实际上,async⽅方式没有数据校验,没法流控。⼀一旦因为负载⾼高导致某个segment有偏 差,反复修复会导致恶性循环,cpu util%飙升,反⽽而写⼊入性能下降。 • 注:ES2.0⾥里将取消这个功能。

63. ES性能问题(1) • bulk批量写⼊入,经常返回状态码429,队列容量不 ⾜足。 • 我们单条⽇日志⻓长度较⼤大,nginx⽇日志平均600B, client⽇日志平均2KB。尤其是client_net_fatal_error 有时候单条过MB。 • ES接收HTTP请求是可以101接收,但是处理HTTP 完整body的上限是100MB。所以bulk_size需要根 据⽇日志单条⻓长度仔细计算。

64. ES性能问题(2) • 数据容量膨胀率较⾼高(默认logstash模板膨胀到3倍⼤大),对IO和容量都有压 ⼒力。要减少冗余数据,减少不必要的分词。 • _source: 存储源JSON • _all: 存储各field的term⽅方便在不指定field的时候直接搜索单词 • multi-field: logstash默认模板会对每个字段附加⼀一个.raw不分词字段 • 所以: • 对于字段很明确的⽇日志,如nginx⽇日志,舍弃_all。 • 对于没啥⽂文本,都是数值的聚合需求的监控数据,可以放弃掉_source。 • 对于⼤大多数字段内容很明确不会有拆分搜索和统计的直接设置不分词。

65. ES性能问题(3) • ⻓长期处于CPU util%⾼高⽔水位运⾏行。 • CPU主要就是segment merge(永远的hot threads)。默认规则是: • 单个segment上限5GB • 单个segment下限2MB • 考虑⽇日志场景不要求多么实时,所以最简单的办法:加⼤大refresh(默认1s) 和flush(默认200MB)的interval。

66. cluster.name: es1003 cluster.routing.allocation.node_initial_primaries_recoveries: 30 cluster.routing.allocation.node_concurrent_recoveries: 5 cluster.routing.allocation.cluster_concurrent_rebalance: 5 cluster.routing.allocation.enable: all node.name: esnode001 node.master: false node.data: data node.max_local_storage_nodes: 1 index.routing.allocation.total_shards_per_node : 3 index.merge.scheduler.max_thread_count: 1 index.refresh_interval: 30s index.number_of_shards: 26 index.number_of_replicas: 1 index.translog.flush_threshold_size : 5000mb index.translog.flush_threshold_ops: 50000 index.search.slowlog.threshold.query.warn: 30s index.search.slowlog.threshold.fetch.warn: 1s index.indexing.slowlog.threshold.index.warn: 10s indices.store.throttle.max_bytes_per_sec: 1000mb indices.cache.filter.size: 10% indices.fielddata.cache.size: 10% indices.recovery.max_bytes_per_sec: 2gb indices.recovery.concurrent_streams: 30 path.data: /data1/elasticsearch/data path.logs: /data1/elasticsearch/logs bootstrap.mlockall: true http.max_content_length: 400mb http.enabled: true http.cors.enabled: true http.cors.allow-origin: "*" gateway.type: local gateway.recover_after_nodes: 30 gateway.recover_after_time: 5m gateway.expected_nodes: 30 discovery.zen.minimum_master_nodes: 3 discovery.zen.ping.timeout: 100s discovery.zen.ping.multicast.enabled: false discovery.zen.ping.unicast.hosts: ["10.19.0.97","10.19.0.98","10.19.0.99"] monitor.jvm.gc.young.warn: 1000ms monitor.jvm.gc.old.warn: 10s monitor.jvm.gc.old.info: 5s monitor.jvm.gc.old.debug: 2s

67. ES的搜索问题(1) • 搜索结果跟过滤条件“明显不对”。 curl es.domain.com:9200/logstash-accesslog-2015.04.03/nginx/_search?q=_id:AUx- QvSBS-dhpiB8_1f1&pretty -d '{ "fields": ["requestTime"], "script_fields" : { "test1" : { "script" : "doc["requestTime"].value" }, "test2" : { "script" : "_source.requestTime" }, "test3" : { "script" : "doc["requestTime"].value * 1000" } } }'

68. NOT schema free! "hits" : { "total" : 1, "max_score" : 1.0, "hits" : [ { "_index" : "logstash-accesslog-2015.04.03", "_type" : "nginx", "_id" : "AUx-QvSBS-dhpiB8_1f1", "_score" : 1.0, "fields" : { "test1" : [ 4603039107142836552 ], "test3" : [ -8646911284551352000 ], "requestTime" : [ 0.54 ], "test2" : [ 0.54 ], } } ] }

69. ES的搜索问题(2) • 部分⽇日志搜不着! • 因为ES要求同⼀一个_index下同⼀一个_type的同⼀一个名字的field, mapping type必须⼀一致。否则整条数据直接写⼊入失败! • client_net_fatal_error⽇日志中,某次发版后字段内容变了: • {"reqhdr":{"Host":"api.weibo.cn"}} • {"reqhdr":"{"Host":"api.weibo.cn"}"} • 设置object的mapping为{"enabled":false},跳过indexing阶段。 意味着这部分内容不能搜索,只能在_source JSON中可见。

70. ES的搜索问题(3) •部分⽇日志还是搜不着! •logstash⾃自带template中对不分词的字段定义了ignore_above:256。也就是字段内 容超过256字节,就⾃自动跳过indexing阶段。没有fielddata,针对这条记录的这个 字段的搜索和统计都是⽆无效的。 curl 10.19.0.100:9200/logstash-mweibo-2015.05.18/mweibo_client_crash/_search?q=_id:AU1ltyTCQC8tD04iYBIe&pretty -d '{ "fielddata_fields" : ["jsoncontent.content", "jsoncontent.platform"], "fields" : ["jsoncontent.content","jsoncontent.platform"] }' ... "fields" : { "jsoncontent.content" : [ "dalvik.system.NativeStart.main(Native Method)nCaused by: java.lang.ClassNotFoundException: Didn't find class "com.sina.weibo.hc.tracking.manager.TrackingService" on path: DexPathList[[zip file "/data/app/com.sina.weibo-1.apk", zip file "/data/data/com.sina.weibo/code_cache/secondary- dexes/com.sina.weibo-1.apk.classes2.zip", zip file "/data/data/com.sina.weibo/app_dex/ dbcf1705b9ffbc30ec98d1a76ada120909.jar"],nativeLibraryDirectories=[/data/app-lib/com.sina.weibo-1, /vendor/lib, / system/lib]]" ], "jsoncontent.platform" : [ "Android_4.4.4_MX4 Pro_Weibo_5.3.0 Beta_WIFI", "Android_4.4.4_MX4 Pro_Weibo_5.3.0 Beta_WIFI" ] }

71. Kibana⼆二次开发 • 升级k3的elastic.js版本,到ES1.2的API。可以使⽤用ES1.0版本新增的 aggs接⼝口(新写了percentile和range两个panel,重写了histogram panel,添加了cardinality metric)。 • 给table添加export as csv功能。 • 给bettermap添加地图切换功能。 • 给map添加term_stats功能,添加china选项。 • 通过mapping多选框⾃自助⽣生成querystring。 • 给terms panel添加script field功能,添加either filtering多选功能。 • 还有⼗十余项其他优化详⻅见<https://github.com/chenryn/kibana>

72. 参考阅读 •《Elasticsearch 服务器开发(第2版)》 •《⽇日志管理与分析权威指南》 •《数据之魅:基于开源⼯工具的数据分析》 •《⽹网站运维:保持数据实时的秘笈》 •《Web 容量规划的艺术》 •《⼤大规模 Web 服务开发技术》 •https://codeascraft.com/ •http://calendar.perfplanet.com •http://kibana.logstash.es

73. –JordanSissel@logstash.net “If a newbie has a bad time, it's a bug.”

Add a comment

Related pages

Elk Stack | LinkedIn

... Introduce ELK stack Explain how to start it Start a WildFly instance to send log messages ... ELK stack at weibo.com. 907 Views. angadsg. Scaling ELK ...
Read more

在CaaS环境下部署ELK stack的实践经验总结 ...

至此,ELK stack在alauda平台的搭建已经结束。由于本实验只是一个PoC的功能,所以logstash的输入是tcp ... Weibo; Wechat;
Read more

-Y口Z口B-的微博_微博

10月21日 17:47 来自 微博 weibo.com. ... 新作《ELK Stack权威指南》上架。对海量日志实时处理感兴趣的童鞋走过路过不要 ...
Read more

selboo的微博_微博

新作《ELK Stack权威指南》上架。对海量日志实时处理感兴趣的童鞋走过路过不要错过~~ ... 3月4日 14:57 来自 微博 weibo.com.
Read more

Elasticsearch、Logstash & Kibana 和 Docker的结合 ...

而为了监控基础设施,尤其是HTTP响应,我试着使用了一下著名的的ELK Stack。ELK 代表着 Elasticsearch ... Weibo; Wechat;
Read more

RateCity Pty Ltd · GitHub

Elasticsearch ELK Stack ... now with Kibana 4 goodness! Updated Sep 23, 2015 ... Weibo, Douban, QQ ... Updated Aug 25, 2013. Ruby 1 15 ...
Read more