1. 程式人生 > >elasticsearch 運維記錄

elasticsearch 運維記錄

目錄 (elasticsearch 基礎知識彙總)

索引分片: 從策略層面,控制分片分配的選擇

  • 磁碟限額 為了保護節點資料安全,ES 會定時(cluster.info.update.interval,預設 30 秒)檢查一下各節點的資料目錄磁碟使用情況。在達到 cluster.routing.allocation.disk.watermark.low (預設 85%)的時候,新索引分片就不會再分配到這個節點上了。在達到 cluster.routing.allocation.disk.watermark.high (預設 90%)的時候,就會觸發該節點現存分片的資料均衡,把資料挪到其他節點上去。這兩個值不但可以寫百分比,還可以寫具體的位元組數。有些公司可能出於成本考慮,對磁碟使用率有一定的要求,需要適當擡高這個配置:
# curl -XPUT localhost:9200/_cluster/settings -d '{
    "transient" : {
        "cluster.routing.allocation.disk.watermark.low" : "85%",
        "cluster.routing.allocation.disk.watermark.high" : "10gb",
        "cluster.info.update.interval" : "1m"
    }
}'
  • 熱索引分片不均 預設情況下,ES 叢集的資料均衡策略是以各節點的分片總數(indices_all_active)作為基準的。這對於搜尋服務來說無疑是均衡搜尋壓力提高效能的好辦法。但是對於 Elastic Stack 場景,一般壓力集中在新索引的資料寫入方面。正常執行的時候,也沒有問題。但是當叢集擴容時,新加入叢集的節點,分片總數遠遠低於其他節點。這時候如果有新索引建立,ES 的預設策略會導致新索引的所有主分片幾乎全分配在這臺新節點上。整個叢集的寫入壓力,壓在一個節點上,結果很可能是這個節點直接被壓死,叢集出現異常。 所以,對於 Elastic Stack 場景,強烈建議大家預先計算好索引的分片數後,配置好單節點分片的限額。比如,一個 5 節點的叢集,索引主分片 10 個,副本 1 份。則平均下來每個節點應該有 4 個分片,那麼就配置:
# curl -s -XPUT http://127.0.0.1:9200/logstash-2015.05.08/_settings -d '{
    "index": { "routing.allocation.total_shards_per_node" : "5" }
}'

注意,這裡配置的是 5 而不是 4。因為我們需要預防有機器故障,分片發生遷移的情況。如果寫的是 4,那麼分片遷移會失敗。
此外,另一種方式則更加玄妙,Elasticsearch 中有一系列引數,相互影響,最終聯合決定分片分配:
cluster.routing.allocation.balance.shard 節點上分配分片的權重,預設為 0.45。數值越大越傾向於在節點層面均衡分片。
cluster.routing.allocation.balance.index 每個索引往單個節點上分配分片的權重,預設為 0.55。數值越大越傾向於在索引層面均衡分片。
cluster.routing.allocation.balance.threshold 大於閾值則觸發均衡操作。預設為1。

reroute 介面

  • reroute 介面支援五種指令:allocate_replica, allocate_stale_primary, allocate_empty_primary,move 和 cancel。
  • 常用的一般是 allocate 和 move:
    allocate_* 指令
  • 因為負載過高等原因,有時候個別分片可能長期處於 UNASSIGNED 狀態,我們就可以手動分配分片到指定節點上。預設情況下只允許手動分配副本分片(即使用 allocate_replica),所以如果要分配主分片,需要單獨加一個 accept_data_loss 選項:
# curl -XPOST 127.0.0.1:9200/_cluster/reroute -d '{
  "commands" : [ {
        "allocate_stale_primary" :
            {
              "index" : "logstash-2015.05.27", "shard" : 61, "node" : "10.19.0.77", "accept_data_loss" : true
            }
        }
  ]
}'
  • 因為負載過高,磁碟利用率過高,伺服器下線,更換磁碟等原因,可以會需要從節點上移走部分分片:
curl -XPOST 127.0.0.1:9200/_cluster/reroute -d '{
  "commands" : [ {
        "move" :
            {
              "index" : "logstash-2015.05.22", "shard" : 0, "from_node" : "10.19.0.81", "to_node" : "10.19.0.104"
            }
        }
  ]
}'

節點下線

  • 叢集中個別節點出現故障預警等情況,需要下線,也是 Elasticsearch 運維工作中常見的情況。如果已經穩定執行過一段時間的叢集,每個節點上都會儲存有數量不少的分片。這種時候通過 reroute 介面手動轉移,就顯得太過麻煩了。這個時候,有另一種方式:
curl -XPUT 127.0.0.1:9200/_cluster/settings -d '{
  "transient" :{
      "cluster.routing.allocation.exclude._ip" : "10.0.0.1"
   }
}'

Elasticsearch 叢集就會自動把這個 IP 上的所有分片,都自動轉移到其他節點上。等到轉移完成,這個空節點就可以毫無影響的下線了。和 _ip 類似的引數還有 _host, _name 等。此外,這類引數不單是 cluster 級別,也可以是 index 級別。下一小節就是 index 級別的用例。

冷熱資料的讀寫分離

Elasticsearch 叢集一個比較突出的問題是: 使用者做一次大的查詢的時候, 非常大量的讀 IO 以及聚合計算導致機器 Load 升高, CPU 使用率上升, 會影響阻塞到新資料的寫入, 這個過程甚至會持續幾分鐘。所以,可能需要仿照 MySQL 叢集一樣,做讀寫分離。1

實施方案

N 臺機器做熱資料的儲存, 上面只放當天的資料。這 N 臺熱資料節點上面的 elasticsearc.yml 中配置 node.tag: hot
之前的資料放在另外的 M 臺機器上。這 M 臺冷資料節點中配置 node.tag: stale
模板中控制對新建索引新增 hot 標籤:
{
“order” : 0,
“template” : “*”,
“settings” : {
“index.routing.allocation.require.tag” : “hot”
}
}
每天計劃任務更新索引的配置, 將 tag 更改為 stale, 索引會自動遷移到 M 臺冷資料節點

# curl -XPUT http://127.0.0.1:9200/indexname/_settings -d'
{
"index": {
   "routing": {
      "allocation": {
         "require": {
            "tag": "stale"
         }
      }
  }
}
}'

叢集自動發現

  • fd 是 fault detection
  • 考慮到節點有時候因為高負載,慢 GC [垃圾回收] 等原因會偶爾沒及時響應 ping ,一般建議稍加大 Fault Detection 的超時時間。
  • discovery.zen.ping_timeout 僅在加入或者選舉 master 主節點的時候才起作用;
  • discovery.zen.fd.ping_timeout 在穩定執行的叢集中,master檢測所有節點,以及節點檢測 master是否暢通時長期有用
discovery.zen.ping.unicast.hosts: ["es0","es1", "es2","es3","es4"]    # 叢集自動發現
discovery.zen.fd.ping_timeout: 120s                # 超時時間(根據實際情況調整)
discovery.zen.fd.ping_retries: 6                   # 重試次數,防止GC[Garbage collection]節點不響應被剔除
discovery.zen.fd.ping_interval: 30s                # 執行間隔

API 介面

資料寫入

# curl -XPOST http://127.0.0.1:9200/logstash-2015.06.21/testlog -d '{
    "date" : "1434966686000",
    "user" : "chenlin7",
    "mesg" : "first message into Elasticsearch"
}'

命令返回響應結果為:

{"_index":"logstash-2015.06.21","_type":"testlog","_id":"AU4ew3h2nBE6n0qcyVJK","_version":1,"created":true}

資料讀取

# curl -XGET http://127.0.0.1:9200/logstash-2015.06.21/testlog/AU4ew3h2nBE6n0qcyVJK
# curl -XGET http://127.0.0.1:9200/logstash-2015.06.21/testlog/AU4ew3h2nBE6n0qcyVJK/_source 

GET apache-2016.12.30/testlog/AVlPEWLkYdfL4HTWelef/_source
GET apache-2016.12.30/testlog/AVlPEWLkYdfL4HTWelef?fields=user  # 指定欄位

資料刪除

# curl -XDELETE http://127.0.0.1:9200/logstash-2015.06.21/testlog/AU4ew3h2nBE6n0qcyVJK
刪除不單針對單條資料,還可以刪除整個整個索引。甚至可以用萬用字元。
# curl -XDELETE http://127.0.0.1:9200/logstash-2015.06.0*

在 Elasticsearch 2.x 之前,可以通過查詢語句刪除,也可以刪除某個 _type 內的資料。現在都已經不再內建支援,改為 Delete by Query 外掛。因為這種方式本身對效能影響較大!

資料更新

已經寫過的資料,同樣還是可以修改的。有兩種辦法,一種是全量提交,即指明 _id 再發送一次寫入請求。
# curl -XPOST http://127.0.0.1:9200/logstash-2015.06.21/testlog/AU4ew3h2nBE6n0qcyVJK -d '{
    "date" : "1434966686000",
    "user" : "chenlin7",
    "mesg" " "first message into Elasticsearch but version 2"
}'

另一種是區域性更新,使用 /_update 介面:
# curl -XPOST 'http://127.0.0.1:9200/logstash-2015.06.21/testlog/AU4ew3h2nBE6n0qcyVJK/_update' -d '{
    "doc" : {
        "user" : "someone"
    }
}'

或者
# curl -XPOST 'http://127.0.0.1:9200/logstash-2015.06.21/testlog/AU4ew3h2nBE6n0qcyVJK/_update' -d '{
    "script" : "ctx._source.user = \"someone\""
}'

分片儲存設定

PUT /_cluster/settings{
  "transient": {
    "cluster.routing.allocation.disk.watermark.low": "80%",
    "cluster.routing.allocation.disk.watermark.high": "5gb",
    "cluster.info.update.interval": "1m"
  }
}

搜尋請求

全文搜尋
# curl -XGET http://127.0.0.1:9200/logstash-2015.06.21/testlog/_search?q=first
# curl -XGET http://127.0.0.1:9200/logstash-2015.06.21/testlog/_search?q=user:"chenlin7"
{
  "took": 2,   Elasticsearch 執行這個搜尋的耗時,以毫秒為單位
  "timed_out": false,  搜尋是否超時 
  "_shards": { 指出多少個分片被搜尋了,同時也指出了成功/失敗的被搜尋的shards 的數量
    "total": 5,  
    "successful": 5,    
    "failed": 0     
  },
  "hits" : {   搜尋結果
    "total" : 579,  匹配查詢條件的文件的總數目
    "max_score" : 0.12633952,
    "hits" : [   真正的搜尋結果陣列(預設是前10個文件)
      {
        "_index" : "logstash-2015.06.21",
        "_type" : "logs",
        "_id" : "AVmvfgGaSF0Iy2LFN0EH",
        "_score" : 0.12633952,
        "_source" : {
          "user" : "chenlin7"
        }
      }
    ......

querystring 語法

  • 上例中,?q=後面寫的,就是 querystring 語法。鑑於這部分內容會在 Kibana 上經常使用,這裡詳細解析一下語法:
  • 全文檢索:直接寫搜尋的單詞,如上例中的 first;
  • 單欄位的全文檢索:在搜尋單詞之前加上欄位名和冒號,比如如果知道單詞 first 肯定出現在 mesg 欄位,可以寫作 mesg:first;
  • 單欄位的精確檢索:在搜尋單詞前後加雙引號,比如 user:”chenlin7”;
  • 多個檢索條件的組合:可以使用 NOT, AND 和 OR 來組合檢索,注意必須是大寫。比如 user:(“chenlin7” OR “chenlin”) AND NOT mesg:first;
  • 欄位是否存在:exists:user 表示要求 user 欄位存在,missing:user 表示要求 user 欄位不存在;
  • 萬用字元:用 ? 表示單字母,* 表示任意個字母。比如 fir?t mess*;
  • 近似搜尋:用 ~ 表示搜尋單詞可能有一兩個字母寫的不對,請 ES 按照相似度返回結果。比如 frist~;
  • 範圍搜尋:對數值和時間,ES 都可以使用範圍搜尋,比如:rtt:>300,date:[“now-6h” TO “now”} 等。其中,[] 表示端點數值包含在範圍內,{} 表示端點數值不包含在範圍內;

term query 的寫法

# curl -XGET http://127.0.0.1:9200/_search -d '
{
  "query": {
    "term": {
      "user": "chenlin7" 
    }
  }
}'

叢集健康狀態

$ curl -XGET 127.0.0.1:9200/_cluster/health?pretty

  • green 綠燈,所有分片都正確執行,叢集非常健康。
  • yellow 黃燈,所有主分片都正確執行,但是有副本分片缺失。這種情況意味著 ES 當前還是正常執行的,但是有一定風險。注意,在 Kibana4 的 server 端啟動邏輯中,即使是黃燈狀態,Kibana 4 也會拒絕啟動,死迴圈等待叢集狀態變成綠燈後才能繼續執行。
  • red 紅燈,有主分片缺失。這部分資料完全不可用。而考慮到 ES 在寫入端是簡單的取餘演算法,輪到這個分片上的資料也會持續寫入報錯。

level 請求引數

介面請求的時候,可以附加一個 level 引數,指定輸出資訊以 indices 還是 shards 級別顯示。當然,一般來說,indices 級別就夠了。

節點狀態監控介面

對不瞭解 JVM 的 GC 的讀者,這裡先介紹一下 GC(垃圾收集)以及 GC 對 Elasticsearch 的影響。
Java is a garbage-collected language, which means that the programmer does not manually manage memory allocation and deallocation. The programmer simply writes code, and the Java Virtual Machine (JVM) manages the process of allocating memory as needed, and then later cleaning up that memory when no longer needed. Java 是一個自動垃圾收集的程式語言,啟動 JVM 虛擬機器的時候,會分配到固定大小的記憶體塊,這個塊叫做 heap(堆)。JVM 會把 heap 分成兩個組:
Young 新例項化的物件所分配的空間。這個空間一般來說只有 100MB 到 500MB 大小。Young 空間又分為兩個 survivor(倖存)空間。當 Young 空間滿,就會發生一次 young gc,還存活的物件,就被移入倖存空間裡,已失效的物件則被移除。
Old 老物件儲存的空間。這些物件應該是長期存活而且在較長一段時間內不會變化的內容。這個空間會大很多,在 ES 來說,一節點上可能就有 30GB 記憶體是這個空間。前面提到的 young gc 中,如果某個物件連續多次倖存下來,就會被移進 Old 空間內。而等到 Old 空間滿,就會發生一次 old gc,把失效物件移除。
聽起來很美好的樣子,但是這些都是有代價的!在 GC 發生的時候,JVM 需要暫停程式執行,以便自己追蹤物件圖收集全部失效物件。在這期間,其他一切都不會繼續執行。請求沒有響應,ping 沒有應答,分片不會分配……
當然,young gc 一般來說執行極快,沒太大影響。但是 old 空間那麼大,稍慢一點的 gc 就意味著程式幾秒乃至十幾秒的不可用,這太危險了。
JVM 本身對 gc 演算法一直在努力優化,Elasticsearch 也儘量複用內部物件,複用網路緩衝,然後還提供像 Doc Values 這樣的特性。但不管怎麼說,gc 效能總是我們需要密切關注的資料,因為它是叢集穩定性最大的影響因子。
如果你的 ES 叢集監控裡發現經常有很耗時的 GC,說明叢集負載很重,記憶體不足。嚴重情況下,這些 GC 導致節點無法正確響應叢集之間的 ping ,可能就直接從叢集裡退出了。然後資料分片也隨之在叢集中重新遷移,引發更大的網路和磁碟 IO,正常的寫入和搜尋也會受到影響。

叢集更新節點配置

  • 剛上線的服務,最近一再的更新基礎配置(硬碟)
1. 關閉分片自動均衡
PUT /_cluster/settings
{
    "transient" : {
        "cluster.routing.allocation.enable" : "none"
    }
}

2). 升級重啟該節點,並確認該節點重新加入到了叢集中

3). 其他節點重複第2步,升級重啟。

4. 最後所有節點配置更新完成後,重啟叢集的shard均衡

curl -XPUT http://192.168.1.2/_cluster/settings -d'
{
    "transient" : {
        "cluster.routing.allocation.enable" : "all"
    }
}'

Grafana 監控ES

Kibana 2.x

  • Sence
  • Marvel
  • 5.x 版本( x-pack )