ELK 效能(4) — 大規模 Elasticsearch 叢集效能的最佳實踐

Richaaaard發表於2016-12-01

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 Size)=(日儲存量 / 分片的數量)
    • (每個節點上分片的數量 * 片大小)/ (磁碟寫速度 / 節點分片的數量)
  • 例如: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%

    ELK 效能(4) — 大規模 Elasticsearch 叢集效能的最佳實踐

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

      -w <num_cpu_cores>

    ELK 效能(4) — 大規模 Elasticsearch 叢集效能的最佳實踐

外掛效能 kv
  • 加一個 kv 過濾器

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

  • 吞吐量變化較大主要因為 GC 的壓力

    ELK 效能(4) — 大規模 Elasticsearch 叢集效能的最佳實踐

  • 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 ,或更多
    • 將 idle_flush_time 從 1s 改到 5s ,
    • 增加輸出執行緒 workers
    • 結果受日誌行的影響
      • 調整,等待 15 分鐘,然後觀察

當使用預設的 500 flush_size 時,Logstash 叢集的峰值會達到 50% ,處理能力在每秒 ~40k 日誌行。將這個值改到 10k 時,同時增加 idle_flush_time 到 5s 。處理能力在每秒 ~150k 日誌行,同時 CPU 佔用會下降到 25% 。

ELK 效能(4) — 大規模 Elasticsearch 叢集效能的最佳實踐

Pipeline 管道效能

  • Logstash 2.3 之前

      …/vendor/…/lib/logstash/pipeline.rb
      SizedQueue.new(20)
      -> SizedQueue.new(500)
  • 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% 下降

    ELK 效能(4) — 大規模 Elasticsearch 叢集效能的最佳實踐

伺服器上的配置
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

ELK 效能(4) — 大規模 Elasticsearch 叢集效能的最佳實踐

影響索引的因素
  • 日誌行的長度與分析,預設對映
  • 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 頻率值
基準

為什麼這麼慢?

ELK 效能(4) — 大規模 Elasticsearch 叢集效能的最佳實踐

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

ELK 效能(4) — 大規模 Elasticsearch 叢集效能的最佳實踐

第一步:將分片數從 1 提升到 5

第二步:禁用 merge throttling(ES < 2.0)

index.store.throttle.type: none

ELK 效能(4) — 大規模 Elasticsearch 叢集效能的最佳實踐

拆分 Hosts

當 CPU 接近最大時,需要加入更多節點

ELK 效能(4) — 大規模 Elasticsearch 叢集效能的最佳實踐

在不同 Hosts 上執行 Elasticsearch 以及 Logstash

ELK 效能(4) — 大規模 Elasticsearch 叢集效能的最佳實踐

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

ELK 效能(4) — 大規模 Elasticsearch 叢集效能的最佳實踐

超執行緒 Hyperthreading

超執行緒可以提升 20% 的效能

ELK 效能(4) — 大規模 Elasticsearch 叢集效能的最佳實踐

CPU 治理

~15-30% 的效能提升。

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

ELK 效能(4) — 大規模 Elasticsearch 叢集效能的最佳實踐

儲存

磁碟 I/O

ELK 效能(4) — 大規模 Elasticsearch 叢集效能的最佳實踐

建議

  • 使用 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
    • 高速寫,代價高

基準

ELK 效能(4) — 大規模 Elasticsearch 叢集效能的最佳實踐

降低間隔後,基本仍然維持在 ~20-25k,但更平滑

ELK 效能(4) — 大規模 Elasticsearch 叢集效能的最佳實踐

為什麼提升很小?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" : "*"
}

有那麼一點幫助

ELK 效能(4) — 大規模 Elasticsearch 叢集效能的最佳實踐

索引的效能
  • 增加 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 的索引速率

    ELK 效能(4) — 大規模 Elasticsearch 叢集效能的最佳實踐

  • 截斷

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

    ELK 效能(4) — 大規模 Elasticsearch 叢集效能的最佳實踐

  • 讓 Logstash 做更多的事情

    ELK 效能(4) — 大規模 Elasticsearch 叢集效能的最佳實踐

索引的大小
  • 按索引來調優分片

      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

結束

相關文章