设为首页 收藏本站
查看: 2271|回复: 0

[经验分享] ELK 性能(4)

[复制链接]

尚未签到

发表于 2017-6-9 07:09:10 | 显示全部楼层 |阅读模式
ELK 性能(4) — 大规模 Elasticsearch 集群性能的最佳实践

介绍
  集群规模


  • 集群数:6
  •   整体集群规模:

    • 300 Elasticsearch 实例
    • 141 物理服务器
    • 4200 CPU 核心
    • 38TB RAM
    • 1.5 Pb 存储

  •   索引日志:

    • 100 亿/天
    • 400k/秒

内容

开场白
  健康提示


  • 将 Elasticsearch 集群的名称 “elasticsearch” 进行重命名。当网络内有两个以上的集群时,就会发现这样做所带来的好处。
  •   为了防止误删除,设置参数
      

    action.destructive_requires_name=true
  • 始终使用 SSD 。这并不是可选的。
  • 需要至少 10G 的带宽。
  •   采用监护人制度,开发并发布自己的版本。

扩展

扩展 Elasticsearch 集群

影响到 Elasticsearch 集群的因素


  •   CPU

    • 核心数 > 时钟速度

  •   内存

    • 文档的数量
    • 分片的数量

  •   磁盘 I/O

    • SSD 持续写的速率

  •   网络带宽

    • 至少 10G 的带宽保证快速恢复与重新索引

影响到集群内存的因素


  • 段内存(segment memory):~4b RAM/文档 = ~4Gb/10亿行日志
  • 字段数据内存(field data memory):几乎与段内存相当
  • 过滤器缓存(filter cache):~1/4 到 1/2 的段内存,取决于搜索的内容
  • 剩下的所有(50% 的系统内存)用作操作系统文件的缓存
  • 无法获得足够的内存
影响到集群I/O的因素


  • SSD 持续写速率
  • 计算片恢复的速度(假设一个节点失败):

    • 片大小(Shard>
    • (每个节点上分片的数量 * 片大小)/ (磁盘写速度 / 节点分片的数量)

  • 例如:30Gb 分片,每个节点 2 个分片,250Mbps 的写速度:

    • (2 * 30Gb)/ 125Mbps = 8 mintues

  • 恢复弹性所能忍受的时间
  • 可以忍受失去多少节点
  • 一台服务器多个节点会增加恢复所需的时间
影响到网络的因素


  • 10G 至少
  • 10 分钟恢复 vs 50+ 分钟恢复
  • 1G 瓶颈:网络上线
  • 10G 瓶颈:磁盘速度
扩展 Logstash 集群

扩展 Logstash 的 CPU


  • 规则 1:买所能承受的尽可能快的 CPU 核心
  • 规则 2:参见第一条
  • 更多的过滤 = 更多的 CPU
监控

Marvel
自研
易用
需要花时间开发
数据存入 ES
与自己的系统集成
很多分析度量
没有集成
重复造轮子
成本高
免费
监控 Elasticsearch


  •   度量在多个地方都有暴露:

    •   _cat API
        包括了大多数度量,易读

    •   _stats API,_nodes API
        涵盖所有,JSON格式,易于解析


  • 发送到 Graphite  


  •   创建 dashboards

监控系统


  • SSD 性能
  • 监控 Logstash 报管道阻塞的频率,并找出原因
  • 动态的磁盘空间阀值
  • ((服务器的数量 - 失败的数量)/ 服务器的数量)- 15%

    • 100 服务器
    • 最多允许 6 个失败
    •   磁盘空间预警的阀值 =((100 - 6)/ 100)- 15%
        磁盘空间预警的阀值 = 79%


  • 根据集群增加与移除节点的数量配置并管理系统
  • 额外的 15% 是用来提供申请并准备更多节点的时间
扩展 Logstash

影响 Logstash 性能的因素


  • 日志行的长度
  • Grok 模式的复杂度 - 正则表达式非常慢
  • 插件的使用
  •   GC

    • 增加的堆大小

  •   超线程

    • 度量,并关闭

重复测量
  将日志以 JSON 格式输出并没有带来很大的好处,除非不使用 grok,kv 等。Logstash 还是需要将字符串转换成为 ruby 的 hash

GC 垃圾回收


  • 缺省配置通常是可以的
  • 确保记录了 GC 的图
  • Ruby 会很容易的创建很多对象:在做伸缩扩展时需要监控 GC
  •   在写插件时需要时刻记住 GC

    •   不好的:1_000_000.times { "This is a string" }

      user
      time
      0.130000
    •   好用法:foo = 'This is a string'; 1_000_000.times { foo }

      user
      time
      0.060000

插件性能基准


  • 如何建立基准
  • 度量某些过滤器
  • 度量更多的过滤器
  • 计算每个过滤器的成本
  • 社区提供的过滤器只是在大多数情况下适用

    • 对于特殊的场景需要自己开发
    • 易于使用

  • 在测评时执行至少 5 分钟的时间,使用大数据集
  • 建立基准的吞吐量:Python,StatsD,Graphite
  •   Logstash 简单配置,10m 行 apache 日志,没有过滤:
      

    input {  file {
      path => "/var/log/httpd/access.log"
      start_position => "beginning"
      }
      
    }
      
    output {
      stdout { codec => "dots" }
      
    }

  •   Python 脚本将 Logstash 输出到 statsd :
      

    sudo pip install statsd  

      
    #!/usr/bin/env python
      
    import statsd, sys
      
    c = statsd.StatsClient('localhost', 8125)
      
    while True:
      sys.stdin.read(1)
      c.incr('logstash.testing.throughput', rate=0.001)

  •   为什么我们不用 statsd 输出插件?它会降低输出的速度!

  •   放在一起
      

    logstash -f logstash.conf | pv -W | python throughput.py  

      
    ![](http://images2015.cnblogs.com/blog/613455/201612/613455-20161201113003006-798605241.png)

插件性能 Grok


  •   增加一个简单的 Grok
      

    grok { match => [ "message", "%{ETSY_APACHE_ACCESS}" ] }
  •   在只有一个 worker 时,性能下降 80%
    DSC0000.png


  •   增加 worker 的数量,吞吐量仍然下降了 33%:65k/s -> 42k/s
      

    -w <num_cpu_cores>  

    DSC0001.png


插件性能 kv


  •   加一个 kv 过滤器
      

    kv { field_split => "&" source => "qs" target => "foo" }
  •   吞吐量基本不变,有 10% 的下降(40k/s)

  •   吞吐量变化较大主要因为 GC 的压力
    DSC0002.png


  •   kv 很慢,以下是一个用来查询字符串的 splitkv 插件
      

    kvarray = text.split(@field_split).map { |afield|  pairs = afield.split(@value_split)
      if pairs[0].nil? || !(pairs[0] =~ /^[0-9]/).nil? || pairs[1].nil? ||
      (pairs[0].length < @min_key_length && !@preserve_keys.include?(pairs[0]))
      next
      end
      if !@trimkey.nil?
      # 2 if's are faster (0.26s) than gsub (0.33s)
      #pairs[0] = pairs[0].slice(1..-1) if pairs[0].start_with?(@trimkey)
      #pairs[0].chop! if pairs[0].end_with?(@trimkey)
      # BUT! in-place tr is 6% faster than 2 if's (0.52s vs 0.55s)
      pairs[0].tr!(@trimkey, '') if pairs[0].start_with?(@trimkey)
      end
      if !@trimval.nil?
      pairs[1].tr!(@trimval, '') if pairs[1].start_with?(@trimval)
      end
      pairs
      
    }
      
    kvarray.delete_if { |x| x == nil }
      
    return Hash[kvarray]

  splitkv 之前的 CPU 占用率是 100% ,之后的占用率是 33% 。


Elasticsearch 的输出


  • Logstash 的输出设置直接影响了 Logstash 所在机器的 CPU

    • 将 flush_size 从 500 改到 5000 ,或更多
    • 将>
    • 增加输出线程 workers
    • 结果受日志行的影响

      • 调整,等待 15 分钟,然后观察



  当使用缺省的 500 flush_size 时,Logstash 集群的峰值会达到 50% ,处理能力在每秒 ~40k 日志行。将这个值改到 10k 时,同时增加>
DSC0003.png


Pipeline 管道性能


  •   Logstash 2.3 之前
      

    …/vendor/…/lib/logstash/pipeline.rb  
    SizedQueue.new(20)

      
    ->>
  •   Logstash 2.3 之后
      

    —pipeline-batch-size=500
  最好在调优最后改变这个参数。管道的性能受输出插件性能的影响。


测试配置变更

增加上下文


  •   发现管道的延迟
      

    mutate { add_field =>  [ "index_time", "%{+YYYY-MM-dd HH:mm:ss Z}" ]
      
    }

  •   logstash 服务器处理日志行
      

    mutate { add_field =>  [ "logstash_host", "<%= node[:fqdn] %>" ]
      
    }

  •   对日志行进行哈希,实现重放
      hashid 插件可以避免重复行

  •   ~10% 下降
    DSC0004.png


服务器上的配置
  

describe package('logstash'),  :if => os[:family] == 'redhat' do
  it { should be_installed }
  
end
  

  
describe command('chef-client') do
  its(:exit_status) { should eq 0 }
  
end
  

  
describe command('logstash -t -f ls.conf.test') do
  its(:exit_status) { should eq 0 }
  
end
  

  
describe command('logstash -f ls.conf.test') do
  its(:stdout) { should_not match(/parse_fail/) }
  
end
  

  
describe command('restart logstash') do
  its(:exit_status) { should eq 0 }
  
end
  

  
describe command('sleep 15') do
  its(:exit_status) { should eq 0 }
  
end
  

  
describe service('logstash'),
  :if => os[:family] == 'redhat' do
  it { should be_enabled }
  it { should be_running }
  
end
  

  
describe port(5555) do
  it { should be_listening }
  
end
  

Input
  

input {  generator {
  lines => [ '<Apache access log>' ]
  count => 1
  type => "access_log"
  }
  generator {
  lines => [ '<Application log>' ]
  count => 1
  type => "app_log"
  }
  
}
  

Filter
  

filter {  if [type] == "access_log" {
  grok {
  match => [ "message", "%{APACHE_ACCESS}" ]
  tag_on_failure => [ "parse_fail_access_log" ]
  }
  }
  if [type] == "app_log" {
  grok {
  match => [ "message", "%{APACHE_INFO}" ]
  tag_on_failure => [ "parse_fail_app_log" ]
  }
  }
  
}
  

Output
  

output {  stdout {
  codec => json_lines
  }
  
}
  

小结


  •   更快的 CPU
      CPU 核心数 > CPU 时钟速度

  • 增加管道的大小
  •   更多内存
      18Gb+ 防止频繁 GC

  • 横向扩展
  • 为日志行添加上下文
  • 编写自己的插件
  •   对所有的东西进行性能评测

扩展 Elasticsearch

默认基准
  Logstash 输出: 默认选项 + 4 workers
  Elasticsearch: 默认选项 + 1 shard, no replicas
DSC0005.png


影响索引的因素


  • 日志行的长度与分析,默认映射
  • doc_values - 必须

    • 使用更多的 CPU 时间
    • 索引时使用更多的磁盘空间,磁盘 I/O
    • 有助于降低内存的使用
    • 如果发现 fielddata 使用过多内存,定位占用最多的,然后将它们移到 doc_values

  • 为恢复保留足够的带宽
  • CPU

    • 分析
    •   映射
        默认映射会创建大量 .raw 字段

    • doc_values
    • 合并
    • 恢复

  • 内存

    • 索引的缓冲
    • GC
    • 段(segment)数量和未优化的索引

  • 网络

    •   恢复的速度
        更快的网络 == 更短的恢复延迟


影响内存的因素


  •   以 32Gb 堆为例的分布情况:

    • Field data: 10%
    • Filter cache: 10%
    • Index buffer: 500Mb  


    • Segment cache (~4 bytes per doc):  
      每个节点可存储的文档数


  • 32Gb - ( 32G / 10 ) - ( 32G / 10 ) - 500Mb = ~25Gb (段内存)
  • 25Gb / 4b = 6.7bn 个文档(所有片的总和)
  • 10bn docs / day, 200 shards = 50m docs/shard

    • 1 daily shard per node: 6.7bn / 50m / 1 = 134 days
    • 5 daily shards per node: 6.7bn / 50m / 5 = 26 days

Doc Values


  • Doc values 可以降低内存开销
  • Doc values 会消耗 CPU 和存储

    •   部分字段使用 doc_values:
        1.7G Aug 11 18:42 logstash-2015.08.07/7/index/_1i4v_Lucene410_0.dvd

    •   所有字段使用 doc_values:
        106G Aug 13 20:33 logstash-2015.08.12/38/index/_2a9p_Lucene410_0.dvd


  •   不要盲目地为所有字段开启 doc_values

    • 找到使用最频繁的字段,然后将它们转换成 Doc Values
    • curl -s 'http://localhost:9200/_cat/fielddata?v' | less -S

  •   示例

    total
    request_uri
    _size
    owner
    ip_address
    117.1mb
    11.2mb
    28.4mb
    8.6mb
    4.3mb
    96.3mb
    7.7mb
    19.7mb
    9.1mb
    4.4mb
    93.7mb
    7mb
    18.4mb
    8.8mb
    4.1mb
    139.1mb
    11.2mb
    27.7mb
    13.5mb
    6.6mb
    96.8mb
    7.8mb
    19.1mb
    8.8mb
    4.4mb
    145.9mb
    11.5mb
    28.6mb
    13.4mb
    6.7mb
    95mb
    7mb
    18.9mb
    8.7mb
    5.3mb
    122mb
    11.8mb
    28.4mb
    8.9mb
    5.7mb
    97.7mb
    6.8mb
    19.2mb
    8.9mb
    4.8mb
    88.9mb
    7.6mb
    18.2mb
    8.4mb
    4.6mb
    96.5mb
    7.7mb
    18.3mb
    8.8mb
    4.7mb
    147.4mb
    11.6mb
    27.9mb
    13.2mb
    8.8mb
    146.7mb
    10mb
    28.7mb
    13.6mb
    7.2mb
内存小结


  • 实例使用 128Gb 或 256Gb RAM
  •   根据硬件配置优化 RAM
      Haswell/Skylake Xeon CPUs 有 4 个内存通道

  •   Elasticsearch 多个实例
      为每个实例分配自己的名称 node.name

CPU


  •   CPU 密集型操作

    • 索引:分析,合并,压缩
    • 搜索:计算,解压缩

  •   写压力

    • CPU 核心数受并发的索引操作影响
    • 核心数 优于 CPU 频率值

基准
  为什么这么慢?
DSC0006.png

  

[logstash-2016.06.15][0] stop throttling indexing:  numMergesInFlight=4, maxNumMerges=5
  

合并
DSC0007.png

  第一步:将分片数从 1 提升到 5
  第二步:禁用 merge throttling(ES < 2.0)
  

index.store.throttle.type: none  

DSC0008.png


拆分 Hosts
  当 CPU 接近最大时,需要加入更多节点
DSC0009.png

  在不同 Hosts 上运行 Elasticsearch 以及 Logstash
DSC00010.png

  吞吐量有 50% 的提升:13k/s -> 19k/s
DSC00011.png


超线程 Hyperthreading
  超线程可以提升 20% 的性能
DSC00012.png


CPU 治理
  ~15-30% 的性能提升。
  

# echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor  

DSC00013.png


存储
  磁盘 I/O
DSC00014.png

  建议


  • 使用 SSD
  • RAID 0
  • 软 RAID 足够
  更多的建议


  •   好的 SSD 非常重要
      廉价 SSD 会大大降低性能

  •   不要使用多个数据路径,使用 RAID 0
      大量的 translog 写磁盘操作会是瓶颈

  •   如果有大量段合并,但是 CPU 和 磁盘 I/O 还有空闲:
      可以尝试提升值
      

    index.merge.scheduler.max_thread_count
  •   降低间隔(Durability)
      

    index.translog.durability: async      

      Translog fsync() 值为 5s ,足够

  •   集群的恢复会吃掉大量磁盘 I/O
      需要在恢复前后调整相应的参数
      

    indices.recovery.max_bytes_per_sec: 300mb  
    cluster.routing.allocation.cluster_concurrent_rebalance: 24
      
    cluster.routing.allocation.node_concurrent_recoveries: 2   

  •   任何的持续 I/O 等待都意味着存在一个次优状态

SSD 的选择


  •   消费级

    • 慢速写
    • 廉价
    • 低耐久性,每天相对较少的写次数

  •   企业级

    • 快速写
    • 昂贵
    • 高耐久性,每天相对较高的写次数

  •   大量读

    • 低耐久性,1-3 DWPD
    • 低速读,代价小

  •   混合使用

    • 中度耐久性,10 DWPD
    • 平衡读写,中等价位

  •   大量写

    • 高耐久性,25 DWPD
    • 高速写,代价高

  基准
DSC00015.png

  降低间隔后,基本仍然维持在 ~20-25k,但更平滑
DSC00016.png

  为什么提升很小?Merging
  

$ curl -s 'http://localhost:9200/_nodes/hot_threads?threads=10' | grep %  73.6% (367.8ms out of 500ms) 'elasticsearch[es][bulk][T#25]'
  66.8% (334.1ms out of 500ms) 'elasticsearch[es][[logstash][1]: Lucene Merge Thread #139]'
  66.3% (331.6ms out of 500ms) 'elasticsearch[es][[logstash][3]: Lucene Merge Thread #183]'
  66.1% (330.7ms out of 500ms) 'elasticsearch[es][[logstash][1]: Lucene Merge Thread #140]'
  66.1% (330.4ms out of 500ms) 'elasticsearch[es][[logstash][4]: Lucene Merge Thread #158]'
  62.9% (314.7ms out of 500ms) 'elasticsearch[es][[logstash][3]: Lucene Merge Thread #189]'
  62.4% (312.2ms out of 500ms) 'elasticsearch[es][[logstash][2]: Lucene Merge Thread #160]'
  61.8% (309.2ms out of 500ms) 'elasticsearch[es][[logstash][1]: Lucene Merge Thread #115]'
  57.6% (287.7ms out of 500ms) 'elasticsearch[es][[logstash][0]: Lucene Merge Thread #155]'
  55.6% (277.9ms out of 500ms) 'elasticsearch[es][[logstash][2]: Lucene Merge Thread #161]'
  

  分层存储


  • 将更多访问的索引放在更多的服务器上,并分配更多的内存以及更快的 CPU
  • 将 “冷” 索引独立存储(SSD下仍然需要这么做)
  • 设置 index.codec: best_compression
  • 移动索引,重新优化
  • 构建 elasticsearch-curator 可以让事情变得简单
  为什么默认的配置 Merging 如此多?
  

$ curl 'http://localhost:9200/_template/logstash?pretty'  

  看到了吗?
  

"string_fields" : {  "mapping" : {
  "index" : "analyzed", // <--- see?
  "omit_norms" : true,
  "type" : "string",
  "fields" : {
  "raw" : {
  "ignore_above" : 256, // <--- see?
  "index" : "not_analyzed", // <--- see?
  "type" : "string"  // <--- see?
  }
  }
  },
  "match_mapping_type" : "string",
  "match" : "*"
  
}
  

  使用自定义映射
  

"string_fields" : {  "mapping" : {
  "index" : "not_analyzed",
  "omit_norms" : true,
  "type" : "string"
  },
  "match_mapping_type" : "string",
  "match" : "*"
  
}
  

  有那么一点帮助
DSC00017.png


索引的性能


  •   增加 bulk 线程池可以控制索引的爆发
      但同时也要注意,这会隐藏性能的问题

  • 增加索引的缓冲
  • 增加刷新的时间,1s 到 5s
  • 将索引请求发送到多个 hosts
  •   增加 worker 直到没有明显的性能提升为止
      num_cpu / 2

  •   增加 flush_size 知道没有明显的性能提升为止
      10,000

  • 磁盘 I/O 性能
  •   索引协议

    • HTTP
    • Node
    • Transport

  • Transport 仍然是性能最好的,但是 HTTP 已经非常接近了
  • Node 基本上不会使用
  •   自定义映射模板

    • 默认模板为每个字段额外生成 not_analyzed.raw 字段
    • 分析每个字段会占用 CPU
    • 额外的字段会吃掉更多磁盘空间
    • 动态字段和 Hungarian 标记

  • 使用开启了动态字段的自定义映射模板,但是将它们设置为 non_analyzed 剔除 .raw 字段,除非真的需要它。
  • 这可以将 Elasticsearch 集群的 CPU 的使用率从 28% 降到 15%
  •   消息的复杂度也十分相关
      加 20k 的新行与平均 1.5k 的索引速率
    DSC00018.png


  •   截断
      

    ruby { code =>  "if event['message'].length > 10240 then
      event['message'] = event['message'].slice!(0,10240)
      end"
      
    }
      

    DSC00019.png


  •   让 Logstash 做更多的事情
    DSC00020.png


索引的大小


  •   按索引来调优分片
      

    num_shards = (num_nodes - failed_node_limit) / (number_of_replicas + 1)  

      50 个节点,并允许最多 4 个节点失败,replication 为 1x:
      

    num_shards = (50 - 4) / (1 + 1) = 23
  •   如果分片大于 25Gb ,需要相应增加分片数

  •   调优 indices.memory.index_buffer_size
      

    index_buffer_size = num_active_shards * 500Mb  

      其中“active_shards”:指任何 5 分钟内更新的分片

  •   调试 refresh_interval

    • 默认 1s - 过于频繁
    • 增加到 5s
    • 更高的值会导致磁盘抖动
    •   目标:将磁盘里的缓冲尽可能的移储
        例如:Samsung SM863 SSDs

      •   DRAM buffer: 1Gb

      •   Flush speed: 500Mb/sec



参考
  参考来源:
  2016.6 ELK: Moose-ively scaling your log system

结束

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-385277-1-1.html 上篇帖子: 开源服务专题之------ssh防止暴力破解及fail2ban的使用方法 下篇帖子: Linux实战教学笔记07:Linux系统目录结构介绍
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表