ELK運維文件

charlieroro發表於2024-01-25

Logstash

目錄

開源一篇ELK運維文件,如果有描述不對的希望,歡迎大家糾正。

Monitoring API

官方提供了4個Monitoring API,如下:

Node Info API

用於檢視Node級別的基本資訊,選引數為pipelinesosjvm,如下檢視基本的os和jvm資訊:

curl 127.0.0.1:9600/_node/os,jvm

如下展示了pipeline的預設配置,os和jvm的基本資訊。其中一個比較重要的欄位是status,其展示了logstash的當前健康狀態:

{
  "host": "dragon-logging-logstash-c6b8798bf-8rvkp",
  "version": "7.17.9",
  "http_address": "0.0.0.0:9600",
  "id": "4c68e86b-0858-4c01-b52d-f59a346892a4",
  "name": "dragon-logging-logstash-c6b8798bf-8rvkp",
  "ephemeral_id": "804f0684-c2c3-4dea-8033-f368ab029d61",
  "status": "green",
  "snapshot": false,
  "pipeline": {
    "workers": 100,
    "batch_size": 250,
    "batch_delay": 50
  },
  "os": {
    "name": "Linux",
    "arch": "amd64",
    "version": "5.4.0-1094-azure",
    "available_processors": 2
  },
  "jvm": {
    "pid": 1,
    "version": "11.0.18",
    "vm_version": "11.0.18",
    "vm_vendor": "Eclipse Adoptium",
    "vm_name": "OpenJDK 64-Bit Server VM",
    "start_time_in_millis": 1678872170592,
    "mem": {
      "heap_init_in_bytes": 1073741824,
      "heap_max_in_bytes": 1056309248,
      "non_heap_init_in_bytes": 7667712,
      "non_heap_max_in_bytes": 0
    },
    "gc_collectors": [
      "ParNew",
      "ConcurrentMarkSweep"
    ]
  }
}

Plugins Info API

用於檢視外掛的版本資訊:

curl 127.0.0.1:9600/_node/plugins

Node Stats API

用於檢視logstash的執行時狀態,可選引數為:

  • jvm: 檢視jvm的mem和gc情況,可以使用collection_time_in_millis/collection_count檢視GC的速率

  • process:檢視程式概況,如當前開啟檔案控制程式碼數/最大開啟檔案控制程式碼數/允許的最大檔案控制程式碼數,還有當前CPU的百分比和負載

  • events:展示事件相關的資訊,如果queue_push_duration_in_millis大於 duration_in_millis,說明 Logstash 的輸入外掛速率很快,而 filter/output 的處理很慢,導致等待時間非常的長,這時候要注意最佳化後面兩個外掛。可以透過pipelines介面檢視各個外掛處理花費的具體時間。

    • queue_push_duration_in_millis: input階段花費的時間
    • duration_in_millis: filter和output階段花費的時間
      "events": {
        "in": 794568,
        "filtered": 794568,
        "out": 1095644,
        "duration_in_millis": 20695169,
        "queue_push_duration_in_millis": 186248
      }
    
  • flow:logstash 8的功能,可以檢視pipeline的吞吐量資訊,如input_throughputfilter_throughputoutput_throughput

  • pipelines: 展示個每個pipeline各個階段的詳細資訊,如input、filter和output,其中也包含了該pipeline的events和flow資訊,以及output的返回值和失敗次數等資訊。

  • reloads:展示了重新載入配置的成功和失敗次數

  • os:當logstash執行在容器中時,可以展示cgroup的資訊

  • geoip_download_manager

下面用於檢視logstash 的pipeline資訊

curl 127.0.0.1:9600/_node/stats/pipelines

Hot Threads API

用於檢視logstash的熱點執行緒資訊。可以檢視各個執行緒的執行緒ID和佔用的CPU時間以及執行緒狀態,以此可以確認高負載的執行緒:

curl 127.0.0.1:9600/_node/hot_threads	

logstash exporter指標

logstash exporter的指標取自monitoring API的_node/stats介面,採集了jvmeventsprocessreloads這四個維度的資訊

外掛管理

離線安裝外掛

bin/logstash-plugin install file:///path/to/logstash-offline-plugins-8.6.2.zip

更新外掛

bin/logstash-plugin update                       #更新所有外掛
bin/logstash-plugin update logstash-input-github #更新特定外掛

移除外掛

bin/logstash-plugin install /path/to/logstash-output-kafka-1.0.0.gem

使用Gem私有庫

Logstash 外掛管理器會連線到一個Ruby gem倉庫,預設為http://rubygems.org。logstash外掛的gemfile中的source行指定了外掛的位置,如預設的gemfile的source為:

source "https://rubygems.org"

將這一行指向自己的外掛地址即可:

source "https://my.private.repository"

效能調優

Logstash提供了三個引數來除錯pipeline的效能:

  • pipeline.workers:設定了處理filter和output的執行緒數。如果發現事件處理擁塞,或CPU不飽和,可以考慮增大該值。預設等於CPU的個數。
  • pipeline.batch.size :設定了單個worker執行緒在執行filter和output前採集的事件總數。通常batch越大,處理效率越高,但也會增大記憶體開銷。該數值過大可能會導致頻繁GC或JVM出現OOM。預設125。
  • pipeline.batch.delay:該配置基本不需要進行調節。

logstash中inflight的事件(即記憶體佇列中的事件)的總數與pipeline.workerspipeline.batch.size的配置有關。inflight事件過多會導致GC和CPU曲線出現突刺,而合理的inflight事件的場景下,GC和CPU曲線會比較平滑。

Troubleshooting Logstash

下面給出的是原文的部分場景。

  • jvm.options檔案中新增如下配置可以讓logstash在啟動的時候忽略告警。

    --add-opens=java.base/java.security=ALL-UNNAMED
    --add-opens=java.base/java.io=ALL-UNNAMED
    --add-opens=java.base/java.nio.channels=ALL-UNNAMED
    --add-opens=java.base/sun.nio.ch=org.ALL-UNNAMED
    --add-opens=java.management/sun.management=ALL-UNNAMED
    
  • 請求返回429。說明應用繁忙,如elasticsearch在由於ingest佇列滿導致bulk失敗之後會給logstash返回429。

FAQ

logstash可能出現的問題?

一般成熟的架構中,logstash會從訊息佇列(如kafka)中pull資料,然後寫入後端(如elasticsearch),因此logstash承擔的是一個資料處理轉發的功能,其本身一般不會儲存過程資料(除非使用了persistent queue)。

方式1:
logstash比較吃記憶體,首先檢查logstash的jvm記憶體利用率。

方式2:

logstash一般可能會出現input和filter/output處理效率不匹配的問題。假如logstash是從kafka攝取訊息的,可以在kafka上針對logstash消費的訊息做一個lag告警,當lag較大時說明出現lagstash處理不及時,透過logstash消費的topic可以進一步定位出哪個logstash pipeline出現了效能問題。使用/_node/stats/pipelines介面可以得到更細節的資訊,透過增加特定pipeline的pipeline.workerspipeline.batch.size來提高pipeline的吞吐量,也可以透過各個外掛的queue_push_duration_in_millisduration_in_millis找到消耗效能的外掛,針對inputfilteroutputcodec等外掛進行效能調優。

方式3:

logstash處理能力不足,可能是由於其對CPU的和記憶體的利用不足導致的。可以在logstash.ymlpipelines.yml中配置pipeline.workerspipeline.batch.size來提高資源利用率,前者的調節基於CPU飽和度,後者會導致JVM使用量增加。

最佳的pipeline.workerspipeline.batch.size配比應該是使得GC和CPU使用曲線都趨於平滑

如何保證logstash事件不丟失?

預設情況下,logstash使用記憶體佇列來快取pipeline各個階段的事件,記憶體佇列的上限等於pipeline.workers (預設為CPU個數) 乘以 pipeline.batch.size (預設: 125) 個事件數。

在logstash的input接收事件並在事件沒有傳送到output之前出現異常的話可能丟失事件。可以使用persistent queue來防止事件丟失,它位於input和filter階段之間:input → queue → filter + output。當input接收到事件併成功寫入佇列之後,input就可以向事件源返回確認資訊。佇列會記錄事件的處理狀態,只有當filter和output都處理完成之後,該事件才會被標記為"已完成"。當logstash出現異常並重啟之後,會繼續處理那些"未完成"的事件。

在事件成功持久化到persistent queue(PQ)之後,kafka input外掛才會提交offset?

否。kafka input外掛會週期性地提交offset。如果PQ處理慢或被阻塞,那麼會提交沒有達到PQ的事件的offset

logstash是否可以保證訊息處理的順序?

logstash預設不會保證訊息處理的順序的,在如下兩種場景中可能會出現亂序:

  1. filter批次處理過程中可能會出現亂序
  2. 多個批次事件可能會因為處理快慢導致亂序

透過啟動單個logstash例項並設定 pipeline.ordered ⇒ true來保證順序處理。不過一般logstash的事件會包含時間戳,在es側再按照時間或其他維度的資訊進行排序。

logstash是如何退出的?

logstash接收到SIGTERM(kubelet停止pod時也會傳送該訊號)訊號之後會執行如下步驟:

  • 停止所有input、filter和output外掛
  • 處理所有未完成的事件(events)
  • 結束logstash程式

下面因素會影響到logstash的退出:

  • input外掛的事件接收速度慢
  • filter慢
  • output外掛鏈路斷開,等待重連來刷入未完成的事件

可以使用上面的monitor API檢視各個階段的執行情況。可以在啟動時透過指定--pipeline.unsafe_shutdown引數來強制logstash退出,但這種方式可能會導致事件丟失。

TIPS

  • logstash自動載入配置:包含兩個引數config.reload.automaticconfig.reload.interval
  • 建議logstash的記憶體不低於4GB,且不高於8GB
  • logstash的plugin預設位於/usr/share/logstash/vendor/bundle/jruby/2.5.0/gems,不同版本的外掛支援的引數可能不一樣,版本差異可參見integration-kafka-index
  • 不同版本的logstash的jvm配置檔案

Elasticsearch(基於es8)

安裝事項

  • 生產中的核心引數vm.max_map_count最少設定為262144

  • elasticsearch預設使用者elasticsearch,uid:gid為1000:0。elasticsearch需要讀取path.datapath.logs 的許可權。

  • 設定開啟的檔案控制程式碼數--ulimit nofile=65535:65535

  • 設定可建立的執行緒數ulimit -u 4096

  • 推薦使用/usr/share/elasticsearch/config/jvm.options.d來設定JVM引數(不推薦使用ES_JAVA_OPTS)

  • Elasticsearch會使用bin/elasticsearch-keystore create -p來建立keystore。可以使用如下方式新增bootstrap的密碼:

    echo "demopwd"|elasticsearch-keystore add -x "bootstrap.password"
    

    可以使用如下方式檢視keystore中的內容:

    elasticsearch-keystore list
    elasticsearch-keystore show bootstrap.password
    

初始化重要配置

路徑配置

  • path.data:儲存了索引資料和data stream資料
  • path.logs:儲存了叢集狀態和運算元據

叢集名稱

一個es叢集就是配置了相同cluster.name的節點的集合,預設是elasticsearch

節點名稱

node.name,預設為機器的主機名

網路主機配置

elasticsearch預設的繫結地址為127.0.0.1[::1],可以使用network.host來變更elasticsearch的繫結地址。

節點發現和選舉master節點

  • discovery.seed_hosts:設定為叢集的master-eligible節點。可以使IP地址或主機名。

    discovery.seed_hosts:
       - 192.168.1.10:9300
       - 192.168.1.11 
       - seeds.mydomain.com 
       - [0:0:0:0:0:ffff:c0a8:10c]:9301 
    
  • cluster.initial_master_nodes:首次叢集引導時使用。參見叢集引導章節

JVM配置

  • XmsXmx不應超過總記憶體的50%
  • circuit break的設定推薦85%的節點記憶體

網路

elasticsearch有兩個網路介面:HTTP 介面,用於處理客戶端請求;transport 介面,用於和其他節點通訊。常用配置如下:

  • network.host:設定HTTP和transport流量的地址。可以是IP地址,主機名稱,0.0.0.0等。

  • http.port:HTTP客戶端的通訊埠,支援單個值會範圍值。如果指定了範圍,則會繫結範圍中第一個可用的埠。預設 9200-9300

  • transport.port:節點間通訊的埠。配置方式同http.port。在master-eligible 節點上需要設定為單個埠。預設 9300-9400

節點型別

透過elasticsearch.ymlnode.roles欄位來設定節點的角色,主要角色如下:

  • Master-eligible node:角色為master,負責叢集範圍內的輕量工作,如建立或刪除索引,探測節點是否健康,並決定將哪些分片分配到哪些節點。可以被選舉為master節點,master節點需要配置一個path.data目錄來儲存叢集的後設資料,叢集後設資料描述瞭如何讀取data節點上儲存的資料,因此如果後設資料丟失,那麼將es無法讀取資料節點上的資料。生產上推薦給master角色配置單獨的節點,防止節點過載,以保證叢集的穩定。master eligible節點負責master節點的選舉以及新叢集狀態的提交。

    node.roles: [ master ]
    
  • Voting-only master-eligible node:角色為voting_only,只參與選舉但不會變為master的節點。

    node.roles: [ voting_only ]
    
  • Data node:角色為data,Data節點負責資料相關的操作,如CRUD,查詢以及聚合。Data節點是I/O、記憶體和CPU密集的,當監控到這些資源過載之後,需要新增新的Data節點。在多層架構中,Data節點的角色還可以為data_content,data_hot, data_warm, data_cold, 或 data_frozen,但同時只能設定一個Data角色。

    node.roles: [ data ]
    
  • Ingest node:角色為ingest,負責資料採集的節點

  • Coordinating node:查詢或bulk索引都會涉及多臺data節點的資料,接收到客戶端請求的節點稱為coordinating 節點。例如一個查詢包含兩個階段:

    • scatter階段:coordinating節點會將請求轉發到包含資料的data節點,每個data節點會在本地執行查詢,並將結果返回給coordinating節點。

    • gather階段:coordinating節點會將多個data節點返回的內容合併為一個完整的結果。

    每個節點都可能成為coordinating節點,如果一個節點的node.roles為空,則說明該節點只能作為coordinating節點。gather階段會消耗大量CPU和記憶體,因此為了保證叢集的穩定性,不應該將master節點作為coordinating節點。

    node.roles: [ ]
    

一個節點可能配置多個角色,即可能既是master,同時也是data。在實際的使用中,應該把請求(如kibana)傳送給data節點,而不能傳送給master節點。

發現和組建叢集

master-eligible節點需要協作完成master節點的選舉和叢集狀態變更,在選舉新的master或提交新的叢集狀態時,要求voting configuration中至少一半以上的節點同意之後才能執行相應的動作,因此為了保證叢集的穩定性,不能在同一時間停掉voting configuration中一半及以上的節點。

每個elasticsearch叢集都有一個voting configuration,通常voting configuration等同於叢集中的所有master-eligible節點的集合,但某些情況下會有所不同,如節點的加入和離開,以及包含不可用的節點時。當一個節點加入或離開叢集時,elasticsearch會自動調整對應的voting configuration。可以使用如下方式檢視當前的voting configuration:

GET /_cluster/state?filter_path=metadata.cluster_coordination.last_committed_config

voting configuration的片段如下:

    "cluster_coordination": {
      "term": 5,
      "last_committed_config": [
        "kG4FQeW5SYy-5MzYJwJ7sA",
        "Kz2QwUWDS8CsFH-lUTBm1w",
        "wBopsPAPSHeNDC7yK982HA"
      ],
      "last_accepted_config": [
        "kG4FQeW5SYy-5MzYJwJ7sA",
        "Kz2QwUWDS8CsFH-lUTBm1w",
        "wBopsPAPSHeNDC7yK982HA"
      ],
      "voting_config_exclusions": []
    },

透過cluster.auto_shrink_voting_configuration來設定是否允許自動移除voting configuration中的節點(前提是voting configuration中至少有三個節點),預設是true。如果將其設定為false,則必須手動呼叫voting exclusions API來從voting configuration中移除節點。

elasticsearch的叢集的master eligible節點數應該是奇數,但如果配置了偶數個master eligible節點,那麼elasticsearch會將其中一個節點排除在voting configuration之外。當出現網路分割槽問題,可以避免導致兩個分割槽master eligible節點數相同,以此提升叢集的穩定性。

叢集引導(bootstrap)

在叢集引導時需要配置具有投票權的master eligiable節點列表。新啟動的節點可以從叢集的master節點獲取所需的資訊,先前啟動過的節點則將資訊儲存到了磁碟,在重啟之後可以使用這些資訊。

透過 cluster.initial_master_nodes來設定初始的master eligible節點列表,可以是node.name,IP地址或IP:PORT格式的內容。

在叢集形成之後,從各個節點上移除cluster.initial_master_nodes配置,且不要使用該配置來重啟叢集或新增新節點。如果在叢集形成之後還留著該配置,可能會導致未來在已有叢集之後引匯出一個新的叢集,且無法在不丟失資料的情況下恢復回來。

叢集引導只需要配置如下引數既可:

  • discovery.seed_hostsdiscovery.seed_providers

  • cluster.initial_master_nodes

master選舉

elasticsearch在叢集啟動或現有master故障的情況下會啟動master選舉流程。任何master eligible節點都可以參與選舉,通常第一個執行選舉的節點會成為master。但如果兩個節點同時執行選舉,會出現選舉失敗,此時會等待下一次選舉,後續選舉會新增隨機退避時間(隨機時間上限為cluster.election.back_off_time,預設為100ms),防止再次衝突。

  • cluster.election.duration:每次選舉的時間,超過該時間後,節點認為選舉失敗,重新選舉。預設500ms
  • cluster.election.initial_timeout:一開始或master故障的情況下,節點首次嘗試選舉前等待的最長時間。預設100ms
  • cluster.election.max_timeout:設定第一次選舉前節點等待的時間上限。目的是為了在網路分割建立下不會導致選舉頻率過低。

叢集故障檢測

master節點會週期性的檢測叢集中的每個節點是否健康,叢集中的每個節點也會週期性地檢測master是否健康。

master和follower透過cluster.fault_detection.*配置進行故障檢測。

但當master發現一個節點斷開連線之後,它會繞過timeout和retry檢測,並嘗試將該節點從叢集中移除。同樣地,當一個節點檢測到master斷開連線之後,它會繞過timeout和retry檢測並嘗試發現或選舉出新的master。

此外每個節點會透過週期性地往磁碟寫入小檔案然後刪除的方式來檢測其data路徑是否健康,如果檢測到data路徑不健康,則會將其從叢集中移除掉,參見monitor.fs.health 配置

如果一個節點無法在合理的時間內apply更新的叢集狀態,master會將其移除。預設為2分鐘(cluster.publish.timeout + cluster.follower_lag.timeout)。

叢集狀態釋出

master節點是可以變更叢集狀態的唯一節點。master節點會計算出狀態變更,並將一批更新的叢集狀態釋出給叢集中的其他節點。每次釋出時:

  1. master節點會將更新的叢集狀態廣播到叢集的所有節點上
  2. 其他節點在接收到該訊息之後,會回覆一個確認資訊(但還沒有apply接收到的狀態)
  3. 一旦master節點接收到大多數master eligible節點的確認資訊後,則說明提交了新的叢集狀態
  4. master節點發布另一個訊息,讓其他節點apply新提交的狀態。
  5. 其他節點在接收到該訊息之後,會apply新狀態,並再次回覆一個確認資訊。

從第一步開始,到第三步必須在 30s 內完成。這由引數 cluster.publish.timeout 控制,預設30s 。如果超時,則會拒絕此次叢集狀態變更,並認為master節點出現了故障,此時會嘗試選舉一個新的master節點。

如果在cluster.publish.timeout超時之前提交了新的叢集狀態,則master節點會認為變更成功,它會一直等待超時或知道接收到叢集中的所有節點都apply了更新狀態的確認資訊,然後開始處理和釋出下一個叢集狀態更新。如果沒有在cluster.publish.timeout之間內接收到某些確認資訊,則認為這些節點出現了延遲,其叢集狀態落後於master的最新狀態。master節點會等待cluster.follower_lag.timeout(預設90s)來讓出現延遲的節點追趕當前的狀態,如果在超時之前這些節點仍然無法apply新的叢集狀態,則認為其出現故障,master節點會從叢集中移除掉該節點。

叢集狀態變更時,通常會發布相比之前叢集狀態的差異,以降低時間和頻寬。但在節點丟失先前(如節點重新加入)的叢集狀態的情況下,master會發布完整的叢集狀態。

elasticsearch是一個點對點的系統,每個節點會直接與另一個節點進行通訊。高吞吐量的API(index、delete、search)通常不會和master節點互動。master節點的責任是負責維護全域性的叢集狀態,包括在節點加入和離開叢集時分配分片。每次叢集狀態變更時,都會將新的狀態釋出到所有節點。

新增和移除叢集節點

一個在節點加入或離開叢集時,叢集會自動識別到該事件,並將資料平均分發到其他可用節點上。

新增節點

本節是使用enroll的方式新增節點。但大部分情況下使用bootstrap的方式自發現節點(與enroll方式互斥),即:

  • 啟動一個新的elasticsearch例項
  • elasticsearch.yml中指定相同的cluster.name
  • 配置 discovery.seed_hosts 來讓其他節點發現新加的節點

當elasticsearch節點首次啟動時,節點會嘗試啟用自動安全功能,並檢查如下配置,如果檢查失敗,則不會啟用自動安全功能:

  • 節點是否首次啟動
  • 是否配置了安全特性
  • 啟動程式是否可以修改

當啟用自動安全功能時,新節點(elasticsearch和kibana)需要enrollment token才能加入叢集,方式如下:

  1. 在現有節點上執行elasticsearch-create-enrollment-token命令生成一個enrollment token:

    bin\elasticsearch-create-enrollment-token -s node
    
  2. 使用上面生成的enrollment token啟動新節點,elasticsearch會在config\certs中自動生成證照和金鑰

    bin\elasticsearch --enrollment-token <enrollment-token>
    
  3. 重複上述步驟來新增更多新節點。

在如下場景中,將不會啟用自動安全功能:

  • elasticsearch的/data目錄存在但不為空:節點並非首次啟動的重要訊號,該節點可能是叢集的一部分。
  • elasticsearch.yml不存在(或不可讀),或elasticsearch.keystore不可讀:節點啟動的程式沒有足夠的許可權修改節點配置。
  • elasticsearch配置目錄不可寫:可能是管理員配置了目錄只讀許可權,或啟動elasticsearch的用不併不是安全elasticsearch的使用者

如下配置不相容自動安全功能,當存在任一配置時,節點啟動程式會跳過配置自動安全功能階段(自動安全功能會自動配置如下引數):

移除節點

在移除master-eligible節點時,如果叢集中至少有三個master-eligible節點時,通常是一個一個移除,好讓叢集自動對voting configuration進行調整。

當需要在elasticsearch叢集中移除至少一半的master eligible節點時,可以使用Voting configuration exclusions API將需要移除的master eligible節點加入exclusions列表,這樣就可以同時移除這些節點。當一個節點新增到voting configuration exclusion列表後,除叢集不再需要它的投票之外,該節點仍然能正常工作。另外需要注意的是elasticsearch不會自動將voting exclusions列表中的節點新增回voting configuration中。

注意移非master eligible節點不需要呼叫該介面,且移除的master eligible節點少於一半時也不需要呼叫該介面。

使用如下介面將節點從voting configuration中移除,返回成功表示移除成功:

# Add node to voting configuration exclusions list and wait for the system
# to auto-reconfigure the node out of the voting configuration up to the
# default timeout of 30 seconds
POST /_cluster/voting_config_exclusions?node_names=node_name

# Add node to voting configuration exclusions list and wait for
# auto-reconfiguration up to one minute
POST /_cluster/voting_config_exclusions?node_names=node_name&timeout=1m

可以使用如下介面檢視exclusion列表:

curl -X GET "localhost:9200/_cluster/state?filter_path=metadata.cluster_coordination.voting_config_exclusions&pretty"

當一個master節點從voting configuration移除之後,會從voting configuration中選擇另一個master eligible節點作為master。通常在進行維護時會將mater eligible節點加入exclusion列表,在維護結束之後清空exclusion列表。

# Wait for all the nodes with voting configuration exclusions to be removed from
# the cluster and then remove all the exclusions, allowing any node to return to
# the voting configuration in the future.
DELETE /_cluster/voting_config_exclusions

# Immediately remove all the voting configuration exclusions, allowing any node
# to return to the voting configuration in the future.
DELETE /_cluster/voting_config_exclusions?wait_for_removal=false

叢集級別的分片分配和路由配置

分片分配是將分片分配到節點的過程,該過程可能發生在初始恢復階段、副本分配階段、rebalance或增刪節點階段。master節點的一個主要任務就是確定需要將哪些分片分配到哪些節點以及什麼時候在節點之間移動分片,以達到rebalance叢集的目的。

分片分配的結果儲存在cluster state中。

叢集級別的分片分配設定

可以使用如下引數來設定分片分配:

cluster.routing.allocation.enable

  • all - (預設) 允許為所有型別的分配分片
  • primaries - 僅允許分配主分片
  • new_primaries - 僅為主分片的新索引分配分片
  • none - 不允許為任何索引進行任何型別的分片分配

cluster.routing.allocation.node_concurrent_incoming_recoveries

一個節點執行incoming分片分配的併發數,incoming分片是指在節點上分配的目標分片(除非正在重分配分配,一般指副本分片)。預設2。

cluster.routing.allocation.node_concurrent_outgoing_recoveries

一個節點執行outgoing分片分配的併發數,outgoing分片是指在節點上分配的源分片(除非正在重分配分配,一般指主分片)。預設2。

cluster.routing.allocation.node_concurrent_recoveries

設定cluster.routing.allocation.node_concurrent_incoming_recoveriescluster.routing.allocation.node_concurrent_outgoing_recoveries的快捷方式,預設2。

線上ES叢集引數配置引起的業務異常分析一文中就是因為手動設定了較大的cluster.routing.allocation.node_concurrent_recoveries值,導致併發relocate或recovery的分片過多導致磁碟出現問題。

分片的rebalance設定

elasticsearch會自動在節點之間均衡分片,但前提是不能違背分配過濾器cluster.routing.allocation.awareness.force的限制。

用於在叢集節點之間均衡索引的分片。主要配置引數如下:

cluster.routing.rebalance.enable

  • all - (預設) 允許均衡所有型別的分片
  • primaries - 僅均衡主分片
  • replicas - 僅均衡副本分片
  • none - 不均衡任何索引任何型別的分片

cluster.routing.allocation.allow_rebalance:什麼時候均衡分片

  • always - 總是允許執行分片rebalance
  • indices_primaries_active - 只有在叢集的主分片分配之後才進行rebalance
  • indices_all_active - (預設) 在叢集的主分片和副本分片分配之後才進行rebalance

分片均衡的啟發式配置

Rebalance會基於每個節點上分配的分片計算權重,並在節點之間移動分片來降低高權重的節點,並增加低權重的節點。一個節點的重量取決於它所持有的分片的數量,以及這些分片估計的總資源使用量,這些資源使用量為分片所在的磁碟大小以及往分片寫入流量所需的執行緒數量。

用於配置什麼時候會觸發rebalance。有如下三個考量的配置:

cluster.routing.allocation.balance.shard:每個節點上分配的分片總數的權重因子,預設是 0.45f。提高該值可以讓叢集節點上的分片數目趨於一致。

cluster.routing.allocation.balance.index:每個節點上分配的單個索引的分片數的權重因子,預設是 0.55f。提高該值可以讓叢集節點上的每個索引的分片數目趨於一致。

cluster.routing.allocation.balance.disk_usage:根據預測的磁碟位元組大小來均衡分片,預設是2e-11f。提高該值可以讓叢集節點的底盤使用趨於一致。

cluster.routing.allocation.balance.write_load:根據分片所需的索引執行緒的估計數量,預設是10.0f。定義每個分片的寫負載權重因子。提高該值可以讓節點的寫負載趨於一致。

cluster.routing.allocation.balance.threshold:設定觸發rebalance 分片移動的因子(非負浮點數)。預設值為1.0f ,提高該值將導致elasticsearch更快停止rebalance,使叢集處於更加不均衡的狀態

基於磁碟的分配配置

基於磁碟的分片分配是為了保證所有節點都能有足夠的磁碟空間,該分配方式有一對閾值:低水位和高水位,目的是讓節點不超過高水位,或只是暫時超過高水位。如果一個節點超過高水位,elasticsearch會轉移部分分片來解決該問題。如果所有節點都超過高水位,elasticsearch將不會移動任何分片。

該分配模式需要滿足過濾器和forced awareness的約束。

如果節點磁碟的寫入速度高於elasticsearch移動分片的速度,則可能會讓磁碟爆滿。為了防止發生這種問題,elasticsearch使用了flood-stage水位(cluster.routing.allocation.disk.watermark.flood_stage),當磁碟達到該水位之後,elasticsearch會阻止向受影響的節點的索引分片寫入資料,並繼續向其他節點轉移分片。當磁碟低於高水位之後,elasticsearch會自動取消寫阻塞。

  • cluster.routing.allocation.disk.watermark.low:低水位,預設85%。當高於該數值之後,elasticsearch將不會往該節點分配分片
  • cluster.routing.allocation.disk.watermark.high:高水位,預設90%。當高於該數值之後,elasticsearch將會嘗試移除該節點的分片
  • cluster.routing.allocation.disk.watermark.flood_stage:預設95%。當高於該數值之後,elasticsearch會將節點上的分片變為只讀。

使用節點屬性分配分片

該方式需要首先在elasticsearch.yml中設定節點屬性,然後透過cluster.routing.allocation.awareness.attributes配置分片所需的節點屬性,這樣elasticsearch會將分片分配到具有這些屬性的節點上。

在一個節點出現故障之後,elasticsearch預設會將分片轉移到其他節點上,為了防止這種情況發生,可以使用forced-awareness,這樣在節點出現故障時,elasticsearch不會進行分片分配。更多可以參見官方文件。

叢集分片分配過濾器

可以使用分片分配過濾器來控制將索引的分片分配到哪裡。分片分配過濾器可以基於自定義節點屬性或內建的_name, _host_ip, _publish_ip, _ip, _host, _id_tier屬性。

在停用節點時通常會使用到叢集級別的分片分配過濾器。可以建立一個過濾器來排除掉需要停用的節點,此時elasticsearch會將該節點的分片轉移到其他節點上:

PUT _cluster/settings
{
  "persistent" : {
    "cluster.routing.allocation.exclude._ip" : "10.0.0.1"
  }
}

cluster routing設定有如下幾種(attribute可以包含多個,使用逗號分割):

  • cluster.routing.allocation.include.{attribute}:將分片分配到至少包含其中一個{attribute}的節點

  • cluster.routing.allocation.require.{attribute}:將分片分配到包含所有{attribute}的節點

  • cluster.routing.allocation.exclude.{attribute}:將分片分配到不包含任一個{attribute}的節點

叢集分片限制

如果超過如下限制,elasticsearch將不會允許建立新的分片。主要涉及如下兩種型別的分片:

  • cluster.max_shards_per_node:限制了叢集中主分片和副本分片的總數,預設1000。計算方式為:
    cluster.max_shards_per_node * number of non-frozen data nodes
  • cluster.max_shards_per_node.frozen:限制了叢集中frozen型別的主分片和副本分片的總數,預設3000。計算方式為:
    cluster.max_shards_per_node.frozen * number of frozen data nodes

document的讀寫

elasticsearch中的每個索引都會被切分為多個分片,每個分片可以有多份複製,這些複製稱為replication group(包含主分片和副本分片),在新增和刪除document時需要保證replication group的同步,否則會導致讀資料不一致。

elasticsearch的資料複製模型基於主備模型,replication group中的某個複製作為主分片(primary shard),其餘為副本分片(replica shard)。主分片為所有索引操作的入口,負責對索引操作的校驗和分發。

寫模型

Elasticsearch中的每個索引操作首先會透過routing找到對應的replication group,通常會基於對document ID的雜湊。一旦確定了replication group,會將操作路由到該group的主分片上。該索引階段稱為coordinating stage。

image

預設的分片檢索方式如下:

shard_num = hash(_routing) % num_primary_shards

image

索引的下一個階段是由主分片負責的primary stage。主分片負責校驗操作並將其轉發到其他副本,由於副本可能下線,因此主分片不需要將操作複製到所有副本。elasticsearch 的master節點維護了一組可以接受索引操作的副本列表,稱為in-sync副本,該列表中的分片可以保證處理所有使用者所需的索引和刪除操作。主分片負責維護該集合,並將操作複製到集合中的所有副本。

分片的in-sync資訊儲存在cluster state中,因此需要透過/_cluster/state進行查詢。如使用下面命令檢視索引名為my_index的in-sync資訊:

GET /_cluster/state?filter_path=metadata.indices.my_index.in_sync_allocations.*,routing_table.indices.my_index.*

in_sync_allocations中可以看到in-sync的副本的allocation_id,在routing_table中可以看到allocation_id和副本的對應關係:

{
"metadata": {
"indices": {
"my_index": {
    "in_sync_allocations": {
       "0": [
         "HNeGpt5aS3W9it3a7tJusg",
         "wP-Z5fuGSM-HbADjMNpSIQ"
       ]
     }
   }
 }
},
"routing_table": {
"indices": {
"my_index": {
    "shards": {
         "0": [
           {
             "primary": true,
             "state": "STARTED",
             "allocation_id": { "id": "HNeGpt5aS3W9it3a7tJusg" },
             "node": "CX-rFmoPQF21tgt3MYGSQA",
             ...
           },
           {
             "primary": false,
             "state": "STARTED",
             "allocation_id": { "id": "wP-Z5fuGSM-HbADjMNpSIQ" },
             "node": "AzYoyzzSSwG6v_ypdRXYkw",
             ...
           }
         ]
       }
     }
   }
 }
}

也可以使用如下命令檢視所有的index和route table資訊:

curl localhost:9200/_cluster/state?filter_path=metadata.indices.*,routing_table.indices.*|jq

更多參見tracking-in-sync-shard-copies

主分片的流程如下:

  1. 校驗輸入的操作,如果無效則拒絕
  2. 本地執行該操作,如索引或刪除相關的document,該階段也會校驗內容欄位(如keyword的值太長)
  3. 將操作轉發到當前in-sync的副本,如果有多個副本,則並行操作
  4. 一旦所有in-sync的副本完成操作並響應主分片,主分片會向客戶端確認操作成功

每個in-sync的副本都會本地執行索引操作,稱為replica階段。

這些索引階段(coordinating, primary和 replica)是按序執行的。每個階段包含子階段的生命週期。如在所有primary stage結束之前,coordinating stage不會結束(該過程可能會涉及多個主分片)。而在所有in-sync的副本分片完成本地索引之前,primary stage也不會結束。

Refresh和flush

索引操作可以看做是傳統資料系統中的寫操作。分片中的每個索引操作包含兩部分:refresh和flush。

refresh

在索引中新增、更新和刪除document後,並不能立即被search到。這些document首先會被寫入in-memory buffer中,等待refresh(預設1s)。refresh會將in-memory buffer的資料轉化為一個記憶體中的segment(類似倒排索引),並清空buffer,此時才能夠被search到。

shards由多個segments構成,其中包含了索引的變更操作,segments由refresh以及後續的merge操作所建立。segment是不可修改的,因此每次索引操作都會建立新的segment。

image
Flush

如上圖所示,新的被索引的document會被新增到in-memory buffer的同時,還會被寫入分片的translog中。每30min或translog達到512MB時會執行一次flush操作。如下圖所示,在flush時,會將小的segment合併為一個大的segment,然後將合併後的segment同步到磁碟,並清空translog。

image

使用flush API可以提交translog中的操作。

參考:

故障處理

當主分片故障時,所在節點會給master節點傳送訊息,此時索引操作會被中斷(預設最長1分鐘)。master在接收到訊息之後會將一個副本分片提升為新的主分片,然後會將操作轉發到新的節點進行處理。master節點也會監控node的健康狀態,在持有主分片的節點因為網路等問題被隔離情況下,master會主動提升一個主分片。

在主分片的操作完成之後,它需要處理副本分片執行時可能出現的問題,如副本本身的故障或網路原因導致無法連線到副本。此時,主分片會給master傳送從in-sync 副本集中移除故障分片的請求,在master確認移除該分片之後,主分片會最終確認該移除操作。之後,master會在其他節點構建一個新的副本複製來讓叢集恢復到健康狀態。

主分片在給副本分片轉發操作的同時,它也需要副本分片來維護其主分片的角色。當一個主分片由於網路分割(或長時間GC)被隔離之後,在其感知到被降級之前,可能會繼續處理索引操作。副本會拒絕處理來自老的主分片的操作,當主分片接收到其他分片的拒絕響應之後,它會請求master來了解此次變更,之後的操作會被路由到新的主分片。

這部分內容類似kafka的ack功能:

在建立索引時可以指定wait_for_active_shards來提高資料的可靠性。預設情況下,寫操作只要求主分片active即可,透過指定該值可以執行寫操作時要求active的分片數。

預設情況下,當只有主分片時,該分片在處理操作的過程中不再涉及外部校驗。

讀模型

elasticsearch的主備模型可以保證所有分片的複製是相同的,因此in-sync的分片就可以處理讀請求。

當一個coordinating節點接收到讀請求後,該節點會負責將其轉發到持有相關分片的節點,並整理響應,然後將響應轉發給客戶端,基本流程如下:

  1. 將請求解析為相關的分片,由於大部分查詢會涉及一個或多個分片,因此通常需要讀取多個分片,每個分片包含一部分資料。
  2. 為每個相關的分片選擇一個active的複製,可以是主分片也可以是副本分片。elasticsearch預設採用Adaptive replica selection的方式選擇分片複製
  3. 向選擇的複製傳送分片級別的讀請求
  4. 組合結果並作出響應

當一個分片無法響應讀請求時,coordinating節點會將請求傳送到另一個副本複製。

當一個多個分片故障的情況下,如下介面會返回部分結果,其HTTP狀態碼為200,可以透過time_outshards欄位檢視是否有分片故障。

下面是一個search操作的示意圖,分為query 和fetch兩個階段:

image

故障

發生故障時可能會出現如下問題

  • 一個分片拖慢了整個索引操作:每次操作時,由於主分片會得到所有in-sync的分片,因此一個較慢的分片可能會拖慢整個replication group的處理
  • 被隔離的主分片可能會繼續處理無法被確認的寫操作。這是因為被隔離的主分片只有在給其副本傳送請求或連線到master時才會知道它被隔離。此時已經到達該分片的請求可能會被並行讀操作讀取到,elasticsearch透過(預設每秒)ping master和在無法連線到master時拒絕索引操作來緩解這個問題。

Index templates

index template可以讓使用者在建立索引(index)時,引用已儲存的模板來減少配置項,如指定副本數。一個index template可以由多個component template組成。如下定義了兩個component template my-mappingsmy-settings,並在my-index-template中引用它們。

# Creates a component template for mappings
PUT _component_template/my-mappings
{
  "template": {
    "mappings": {
      "properties": {
        "@timestamp": {
          "type": "date",
          "format": "date_optional_time||epoch_millis"
        },
        "message": {
          "type": "wildcard"
        }
      }
    }
  }
}

# Creates a component template for index settings
PUT _component_template/my-settings
{
  "template": {
    "settings": {
      "index.lifecycle.name": "my-lifecycle-policy"
    }
  },
  "_meta": {
    "description": "Settings for ILM",
    "my-custom-meta-field": "More arbitrary metadata"
  }
}

PUT _index_template/my-index-template
{
  "index_patterns": ["my-data-stream*"],
  "data_stream": { },
  "composed_of": [ "my-mappings", "my-settings" ],
  "priority": 500,
  "_meta": {
    "description": "Template for my time series data",
    "my-custom-meta-field": "More arbitrary metadata"
  }
}

ILM: index lifecycle management

ILM用於自動管理索引,如:

  • 在索引達到一定大小或document打到一定數目時建立一個新的索引
  • 按天、周、月來建立新的索引
  • 根據資料retention規則來刪除老的索引

ILM定義瞭如下 lifecycle phases

  • Hot: 索引是活動的,可以被更新和查詢
  • Warm: 索引無法被更新,但可以被查詢
  • Cold: 索引無法被更新,但可以被查詢,且查詢的頻率較低
  • Frozen: 索引無法被更新,但可以被查詢,且查詢的頻率極低
  • Delete: 索引可以被安全地刪除

更新policy

當更新一個索引的policy後,當前phase仍然會使用之前的policy,當索引進入下一個phase後,會使用新的policy。rollover操作會建立一個新的索引,使用新的policy。

Data stream

data steam可以跨索引處理只追加的時序資料,非常適用於日誌、事件、指標和其他持續產生的資料。可以直接向data stream提交索引或查詢請求,data stream會將其自動路由到儲存流資料的後端索引。推薦使用 ILM來在資料達到一定時間或大小時滾動data stream,也可以手動配置roll over(roll over可以在滾動data stream時建立新的索引)

建立data stream 前需要建立一個index template。然後在index template中包含data_stream物件即可。

PUT _index_template/my-index-template
{
  "index_patterns": ["my-data-stream*"],
  "data_stream": { },
  "composed_of": [ "my-mappings", "my-settings" ],
  "priority": 500,
  "_meta": {
    "description": "Template for my time series data",
    "my-custom-meta-field": "More arbitrary metadata"
  }
}

當請求的索引符合index template的索引模式時,就會自動建立一個data stream

POST my-data-stream/_doc
{
  "@timestamp": "2099-05-06T16:21:15.000Z",
  "message": "192.0.2.42 - - [06/May/2099:16:21:15 +0000] \"GET /images/bg.jpg HTTP/1.0\" 200 24736"
}

也可以透過如下介面手動建立data stream:

PUT _data_stream/my-data-stream

運維

elasticsearch的master節點負責建立和維護叢集狀態,master節點的日誌要比其他節點更豐富,因此當叢集不健康時,可以透過檢視master日誌來定位問題。

審計日誌

審計日誌可以記錄安全相關的事件,如認證失敗,連線拒絕和資料訪問等事件。如果需要配置審計,則必須在叢集中的所有節點上都進行配置。對於靜態配置,例如xpack.security.audit.enabled,就需要在所有節點的elasticsearch.yml中進行配置;對於動態配置,則可以使用叢集配置更新API

  • xpack.security.audit.enabled:預設false

叢集

檢視叢集狀態
GET /_cluster/stats
GET _cluster/health
叢集重啟

當一個data節點重啟之後,分配程式在將該節點的分片轉移到其他節點之前,會等待index.unassigned.node_left.delayed_timeout(預設1分鐘),此時會出現大量I/O。但如果節點需要短暫重啟,為了避免出現這類I/O,可以在節點重啟前臨時禁用副本分片分配功能:

PUT _cluster/settings
{
  "persistent": {
    "cluster.routing.allocation.enable": "primaries"
  }
}

重新整理到translog:

POST /_flush

在節點重啟之後,記得恢復預設的分片分配方式:

PUT _cluster/settings
{
  "persistent": {
    "cluster.routing.allocation.enable": null
  }
}

節點

檢視節點磁碟和分片分配
# curl 127.0.0.1:9200/_cat/allocation?v=true
shards disk.indices disk.used disk.avail disk.total disk.percent host        ip          node
  1205      510.7gb   577.4gb    430.3gb   1007.7gb        57 10.157.4.83 10.157.4.83 elkdata01
  1205      406.2gb   470.4gb    537.3gb   1007.7gb        46 10.157.4.82 10.157.4.82 elkdata02
  1205      438.3gb   502.5gb    505.2gb   1007.7gb        49 10.157.4.79 10.157.4.79 elkdata03
  1205      502.1gb   567.4gb    440.3gb   1007.7gb        56 10.157.4.80 10.157.4.80 elkdata04
  1204      530.7gb   595.1gb    412.6gb   1007.7gb        59 10.157.4.21 10.157.4.21 elkdata05
  1205      496.5gb   560.4gb    447.3gb   1007.7gb        55 10.157.4.81 10.157.4.81 elkdata06
  1204      503.4gb     568gb    439.7gb   1007.7gb        56 10.157.4.23 10.157.4.23 elkdata07
  1205      452.2gb   517.1gb    490.6gb   1007.7gb        51 10.157.4.22 10.157.4.22 elkdata08
檢視節點資訊
GET /_nodes
GET /_nodes/<node_id>
GET /_nodes/<metric>
GET /_nodes/<node_id>/<metric>

metrics支援如下選項:

  • aggregations:給出可用的聚合型別資訊

  • http:給出該節點的HTTP介面資訊

  • indices:節點界別的索引資訊

    • total_indexing_buffer: 該節點上的最大索引快取
  • ingest:給出ingest pipelines 和 processors資訊。

  • jvm:jvm的名稱、版本和配置資訊。

  • os:系統資訊。

  • plugins:單個節點安裝的外掛和模組詳情。

  • process:程式資訊。

  • settings:elasticsearch.yml檔案中的所有節點配置。

  • thread_pool:每個執行緒池的資訊。

  • transport:節點的傳輸介面資訊。

索引

列出叢集中的索引
curl -XGET "localhost:9200/_cat/indices?h=index"
檢視分片

增加分片可以提高查詢速度。

target可以是data stream,索引名稱或別名:

GET /_cat/shards/<target>
GET /_cat/shards

主要引數使用的引數為h,其指定了展示的列表名稱,一般使用

  • index, i, idx:索引名稱

  • shard, s, sh:分片名稱

  • prirep, p, pr, primaryOrReplica:分片型別,primaryreplica.

  • state, st:分片狀態

  • INITIALIZING: 正在從同類分片或閘道器初始化分片

    • RELOCATING: 正在分配分片

    • STARTED: 分片已經啟動,說明分片正常工作

    • UNASSIGNED: 無法分配分片

  • ip:節點IP

  • node:節點名稱

  • unassigned.at, ua:分片變為UNASSIGNED狀態的UTC時間

  • unassigned.reason, ur:分片變為UNASSIGNED狀態的原因,原因程式碼參見官方文件

舉例如下:

GET _cat/shards?h=index,shard,prirep,state,node,unassigned.reason
解釋分片的分配情況

對於unassigned的分片,該介面可以解釋為什麼沒有對其進行分配,對於已分配的分片,則解釋為什麼該分片位於當前節點上。

GET _cluster/allocation/explain
{
  "index": "my-index-000001",
  "shard": 0,
  "primary": true
}

如果沒有指定引數,則elasticsearch會隨機檢索一個unassigned的主分片會副本分片,如果沒有檢索到unassigned的分片,則返回400。

檢視分片分配進度

Recovery可能發生在如下場景中:

  • 節點啟動
  • 主分片複製階段
  • 將一個分片轉移到另一個節點
  • 執行快照恢復操作
  • 執行Clone, shrinksplit操作

如下介面可以檢視recovery資訊,主要檢視recovery進度,target可以是索引、data stream或別名。

GET /_cat/recovery/<target>
GET /_cat/recovery
拆分索引

elasticsearch的分片數目是透過index.number_of_shards靜態配置的,但副本數目可以透過index.number_of_replicas動態配置。key使用split功能將現有索引拆分為有更多主分片的新索引,這樣就可以提高資料處理的速度。

POST /<index>/_split/<target-index>
PUT /<index>/_split/<target-index>

首先需要將拆分的索引設定為只讀:

PUT /my_source_index/_settings
{
  "settings": {
    "index.blocks.write": true 
  }
}
POST /my_source_index/_split/my_target_index
{
  "settings": {
    "index.number_of_shards": 2
  }
}

注意:data stream需要配置 rolled over 之後才能拆分data stream中的索引。拆分上限為1024個分片。

索引拆分的工作原理如下:

  • 建立目標索引,除主分片數目不同之外,具有和源所有相同的配置
  • 如果系統支援硬連結,則使用硬連結將源索引的segments連結到目標索引,否則將所有segments複製到新索引。
  • 重新雜湊所有document

可以使用_cat recovery API檢視拆分進度。

收縮索引

與split API相反,該API用減少索引的主分片數目。

Alias

alias可以為index或data stream建立別名。一個alias可以指向多個index或data stream,用於資料的讀寫。

建立別名

POST _aliases
{
  "actions": [
    {
      "add": {
        "index": "logs-nginx.access-prod",
        "alias": "logs"
      }
    }
  ]
}

支援萬用字元模式

POST _aliases
{
  "actions": [
    {
      "add": {
        "index": "logs-*",
        "alias": "logs"
      }
    }
  ]
}

可以在component或index template中為index或data stream建立索引:

# Component template with index aliases
PUT _component_template/my-aliases
{
  "template": {
    "aliases": {
      "my-alias": {}
    }
  }
}

# Index template with index aliases
PUT _index_template/my-index-template
{
  "index_patterns": [
    "my-index-*"
  ],
  "composed_of": [
    "my-aliases",
    "my-mappings",
    "my-settings"
  ],
  "template": {
    "aliases": {
      "yet-another-alias": {}
    }
  }
}

如果需要對alias寫入資料(如使用POST /<allias>/_doc),需要在alias中指定is_write_index的index或data stream:

POST _aliases
{
  "actions": [
    {
      "add": {
        "index": "logs-nginx.access-prod",
        "alias": "logs"
      }
    },
    {
      "add": {
        "index": "logs-my_app-default",
        "alias": "logs",
        "is_write_index": true
      }
    }
  ]
}

可以對alias建立index pattern,用於檢索資料。

document

查詢可以使用search APIdocumen API

查詢索引的所有doc:

curl -X GET "localhost:9200/my-index-000001/_search?pretty"

查詢特定的doc:

curl -X GET "localhost:9200/my-index-000001/_doc/0?pretty"

移動資料

將叢集分片移動到特定節點

該方式並不會區分特定的索引,主要用於故障隔離或節點維護。參見叢集分片分配過濾器

將索引分片移動到特定節點

該方式以索引為單位,可以將特定索引轉移到特定的節點上,主要用於將索引轉移到合適的節點處理(如使用硬體更好的節點來處理某些需要優先保證的索引)。過濾器的屬性可以使用自定義屬性,也可以使用內建屬性,如_name_ip,_host等。如下表示式用於將test索引轉移到IP為192.168.2.*的節點:

PUT test/_settings
{
  "index.routing.allocation.include._ip": "192.168.2.*"
}

叢集分片分配過濾器類似,支援如下三種過濾方式,可以指定多個過濾器,但在執行分片分配時需要同時滿足這些過濾器的要求:

  • index.routing.allocation.include.{attribute}
  • index.routing.allocation.require.{attribute}
  • index.routing.allocation.exclude.{attribute}
手動遷移分片

該方式以分片為單位,可以手動配置分片分配,如將一個分片從一個節點遷移到另一個節點,取消分片分配以及將unassigned的分片分配到特定節點。

需要注意的是在執行reroute命令之後,elasticsearch也會執行rebalance(cluster.routing.rebalance.enable為true)。

在執行reroute時可以使用dry run模式,即在請求時新增?dry_run引數即可,在命令執行後會計算並返回命令應用之後的叢集狀態,但不會真正修改叢集狀態。主要引數如下:

  • dry_run:如上
  • explain:如果為true,則響應中會包含一段針對命令為什麼可以或無法執行的解釋。

請求體的commands支援如下:

  • move:將STARTED狀態的分片從一個節點移到另一個節點,需要的引數為:

    • index:索引名稱
    • shard:索引的分片號
    • from_node:分片所在的節點
    • to_node:遷移分片的目的節點
  • cancel:取消分配分片。用於強制從主分片同步重新同步現有的副本。預設只能取消副本分片,如果需要取消主分片,則需要在請求中指定allow_primary標記。所需引數如下:index:索引名稱

    • shard:索引的分片號
    • node:分片所在的節點
  • allocate_replica:在節點上申請unassigned副本分片。所需引數和cancel相同。如果需要移動主分片,則需要額外的命令。由於主分片通常是由elasticsearch自動管理的,因此不建議對主分片進行操作。但在如下場景下elasticsearch無法自動分配主分片:

    1. 建立了主分片,但沒有找到合適的節點
    2. 當前資料節點上沒有找到最新資料的分片,為了防止資料丟失,系統不會將老分片提升為主分片。

    下面兩條命令可能導致資料丟失,主要用於原始資料無法恢復且能夠接受資料丟失的場景。需要注意的是,在執行如下命令之後,如果新加入了一個包含受影響的分片的節點,那麼該節點上的分片會被刪除或覆蓋。

    • allocate_stale_primary:將主分片分配到一個有舊資料複製的節點。引數同cancel
    • allocate_empty_primary:將空的主分片分配到一個節點。這樣會導致資料全部丟失。
POST /_cluster/reroute?metric=none
{
  "commands": [
    {
      "move": {
        "index": "test", "shard": 0,
        "from_node": "node1", "to_node": "node2"
      }
    },
    {
      "allocate_replica": {
        "index": "test", "shard": 1,
        "node": "node3"
      }
    }
  ]
}

snapshot的備份和恢復

不能透過複製data目錄的方式來備份節點資料,透過這種方式來恢復資料可能會導致資料丟失或不一致。

使用snapshot可以:

  • 定期備份叢集
  • 在資料被刪除或硬體故障的情況下恢復資料
  • 在叢集間傳輸資料
  • 使用searchable snapshots 降低儲存成本

在使用snapshot之前需要註冊snapshot倉庫,之後可以使用snapshot 生命週期管理(SLM)來自動管理snapshot。

snapshot預設包含叢集狀態(include_global_state為true),所有常規的data stream和索引,但不包含節點配置檔案和安全配置檔案。叢集狀態包含:

Elasticsearch 8.0 以及之後的版本中,feature state是唯一可以備份和恢復系統索引和系統data stream的方法。

在備份一個索引時,snapshot會複製該索引的segment並會將其儲存到snapshot倉庫中。由於segment是不可變的,因此snapshot只會複製相比倉庫中新增的segment。每個snapshot邏輯上是獨立的,因此在刪除一個snapshot時不會對倉庫中的其他snapshot造成影響。

snapshot和分片分配

snapshot會從索引的主分片複製segment,當啟動一個snapshot時,elasticsearch會立即從所有可用的主分片上複製segment,如果一個分片正在啟動或relocating,則elasticsearch會等到該流程結束後才啟動複製,如果一個或多個主分片不可用,則snapshot會失敗。

一旦一個snapshot開始複製分片的segment,則elasticsearch不會將該分片轉移到其他節點(即使發生了rebalancing或分片分配設定觸發了reallocation),elasticsearch會在snapshot分片複製結束之後才會移動該分片。

snapshot的相容性

不能將snapshot恢復給一個更早版本的elasticsearch。

索引相容性

從snapshot中恢復的索引必須相容當前的叢集版本

建立snapshot倉庫

Azure repository為例

  • 如果elasticsearch版本小於8.0(8.0及以上版本以及繼承該外掛),則需要在所有節點上安裝repository-azure外掛:

    sudo bin/elasticsearch-plugin install repository-azure
    
  • 所有節點新增storage account認證資訊:

    echo "$(storageaccountName)" | /usr/share/elasticsearch/bin/elasticsearch-keystore add azure.client.default.account
    echo "${azurestorage_key}" | /usr/share/elasticsearch/bin/elasticsearch-keystore add azure.client.default.key 
    
  • 在所有節點的elasticsearch.yml新增 azure.client.default.endpoint_suffix: core.chinacloudapi.cn配置,並重啟服務。使用如下命令可以已檢視節點配置是否生效:

    GET /_nodes/<node_id>/_all/
    
  • 配置倉庫

    • container:Azure Storage account的container名稱,在建立倉庫前需要提前建立好。3-63個字元長度
    • base_path:container中存放備份資料的路徑,下面例子中為backup-container/backups
    curl -X PUT "localhost:9200/_snapshot/my_backup2?pretty" -H 'Content-Type: application/json' -d'
    {
      "type": "azure",
      "settings": {
        "container": "backup-container",
        "base_path": "backups", //container中的路徑
        "chunk_size": "32MB",
        "compress": true
      }
    }
    '
    
  • 檢視倉庫

    GET /_snapshot/<repository>
    GET /_snapshot
    
  • 校驗倉庫

    POST _snapshot/<repository>/_verify
    
  • 刪除倉庫

    DELETE /_snapshot/my_repository
    
備份

備份的請求配置中主要填寫indicesfeature_states,前者預設是空[],不包含所有index和data stream;後者與include_global_state有關,如果include_global_statetrue,則包含所有feature_states,反之不包含任何feature_states

indices欄位可以使用-排除掉不需要備份的索引,如"indices": "*,-.*" 表示備份所有data stream和index,但不包含系統索引以及以.開頭的索引。

SLM 方式自動建立snapshot

建立SLM來管理snapshot,排程時間參考

schedule欄位的含義如下,hours取值為0-23。"schedule": "0 30 1 * * ?", 表示每天1:30觸發排程。

 <seconds> <minutes> <hours> <day_of_month> <month> <day_of_week> [year]

name欄位用於自動生成snapshot名稱,用法參考Data math

SLM_settings配置

PUT _slm/policy/nightly-snapshots
{
  "schedule": "0 30 1 * * ?",       
  "name": "<nightly-snap-{now/d}>", 
  "repository": "my_repository",  //註冊的snapshot倉庫  
  "config": {
    "indices": "*",               //需要儲存的data stream或索引  
    "include_global_state": true    
  },
  "retention": {                    
    "expire_after": "30d",
    "min_count": 5,
    "max_count": 50
  }
}

使用如下命令可以手動觸發建立一個snapshot

curl -X POST "localhost:9200/_slm/policy/nightly-snapshots/_execute?pretty"

使用如下命令配置retention任務:

PUT _cluster/settings
{
  "persistent" : {
    "slm.retention_schedule" : "0 30 1 * * ?"
  }
}

使用如下命令可以立即觸發retention:

POST _slm/_execute_retention

檢視SLM配置

curl -X GET "localhost:9200/_slm/stats?pretty"

檢視slm的策略執行情況,包括策略配置和最近成功和失敗情況:

curl -X GET "localhost:9200/_slm/policy/nightly-snapshots?pretty"
手動建立snapshot

手動呼叫建立snapshot API

PUT _snapshot/my_repository/my_snapshot?wait_for_completion=true

也可以新增配置:

PUT /_snapshot/my_repository/snapshot_2?wait_for_completion=true
{
  "indices": "index_1,index_2",
  "ignore_unavailable": true,
  "include_global_state": false,
  "metadata": {
    "taken_by": "user123",
    "taken_because": "backup before upgrading"
  }
}
備份特定的feature gate

預設情況下叢集狀態的snapshot也會包含所有的feature gate,同樣地,預設排除叢集狀態的snapshot也會排除掉所有feature gate。

檢視支援的feature gate:

curl -X GET "localhost:9200/_features?pretty"

使用如下方式備份特定的feature gate,下面例子中只會備份kibana 和elasticsearch 安全特性:

PUT _slm/policy/nightly-snapshots
{
  "schedule": "0 30 2 * * ?",
  "name": "<nightly-snap-{now/d}>",
  "repository": "my_repository",
  "config": {
    "indices": "-*",
    "include_global_state": true,
    "feature_states": [
      "kibana",
      "security"
    ]
  },
  "retention": {
    "expire_after": "30d",
    "min_count": 5,
    "max_count": 50
  }
}

上述是使用slm方式建立的只包含kibana和elasticsearch安全的 feature gate備份,當然也可以手動建立:

PUT /_snapshot/my_repository/my_repository_2023_6_7?wait_for_completion=true
{
"indices": "-*", 
"ignore_unavailable": true,
"include_global_state": true,
"feature_states": [
    "kibana",
    "security"
]
}
檢視備份狀態

檢視snapshot配置和狀態:

GET _snapshot/<repository>/_current //檢視當前執行的snapshot,沒有執行的則返回空
GET _snapshot/<repository>/_all //檢視所有snapshot配置
GET _snapshot/<repository>/my_snapshot //檢視特定的snapshot配置

檢視snapshot中的分片的詳細資訊(該介面比較耗時):

GET _snapshot/_status //檢視當前執行的snapshot,沒有執行的則返回空
GET _snapshot/<repository>/_status //檢視當前執行的snapshot,沒有執行的則返回空
GET _snapshot/<repository>/<snapshot>/_status //檢視當前特定的snapshot

刪除一個snapshot

curl -X DELETE "localhost:9200/_snapshot/my_repository/my_snapshot_2099.05.06?pretty"
恢復

注意:

  • 只能在 elected master節點上恢復snapshot
  • 如果要恢復一個已存在的索引,要求該索引是closed的,且主分片的數目和snapshot的主分片數目相同
  • 不能恢復open狀態的索引,以及包含backing index的data stream
  • 恢復操作會自動open 被恢復的索引以及backing index

為避免恢復衝突,可以事先刪除叢集中需要恢復的索引

# Delete an index
DELETE my-index

# Delete a data stream
DELETE _data_stream/logs-my_app-default
檢視snapshot
curl -X GET "localhost:9200/_snapshot?pretty"
curl -X GET "localhost:9200/_snapshot/my_repository/*?verbose=false&pretty"
恢復feature gate

使用如下方式檢視一個snapshot中的feature gate

GET _snapshot/my_repository/my_snapshot_2099.05.06

使用snapshot恢復叢集狀態時,預設會恢復所有的feature gates,可以使用如下方式恢復特定的feature gate。在恢復feature gate時,elasticsearch會關閉並覆蓋該feature的現有索引。

curl -X POST "localhost:9200/_snapshot/my_repository/my_snapshot_2099.05.06/_restore?pretty" -H 'Content-Type: application/json' -d'
{
  "feature_states": [ "geoip" ],
  "include_global_state": false, # 排除掉叢集狀態
  "indices": "-*"                   
}
'
恢復整個叢集

用於恢復整個叢集狀態和feature gates,在恢復前需要關閉一些特性,恢復結束之後再開啟。

檢視恢復狀態

檢視叢集和節點狀態:

GET _cluster/health
GET _cat/shards?v=true&h=index,shard,prirep,state,node,unassigned.reason&s=state

使用Index recovery API檢視當前正在進行或已經完成的備份

curl -X GET "localhost:9200/_recovery?pretty"
curl -X GET "localhost:9200/my-index/_recovery?pretty"

STAGE欄位可以顯示當前的恢復階段:

  • INIT

Recovery has not started.

  • INDEX

Reading index metadata and copying bytes from source to destination.

  • VERIFY_INDEX

Verifying the integrity of the index.

  • TRANSLOG

Replaying transaction log.

  • FINALIZE

Cleanup.

  • DONE

Complete.

清理陳舊的資料

snapshot倉庫中可能會包含不被當前snapshot引用的陳舊資料,使用clean up API可以清除掉這些資料:

POST /_snapshot/my_repository/_cleanup
恢復到其他叢集

snapshot與叢集名稱無關,因此可以在一個叢集中建立snapshot,然後恢復到另一個相容的叢集中

網路診斷

elasticsearch的節點通訊和客戶端通訊時都會使用一條或多條TCP通道,每條TCP通道都屬於節點的某個transport_worker執行緒。每個transport_worker執行緒只負責其所有的通道的資料傳送和接收。此外,elasticsearch會將每個http和transport的服務端socket分配給某個transport_worker執行緒,然後由它來接收到服務端socket的連線。

如果elasticsearch的某個執行緒需要在特定的通道上傳送資料,它會將資料傳遞給其所屬的transport_worker執行緒。通常transport_worker執行緒不會完成處理其接收到的訊息,相反,它會做一些預處理,然後將訊息分發給不同的執行緒池來完成剩餘的工作。如bulk訊息會被分發到write執行緒池,searches會被分發到search執行緒池等。但有些情況下,訊息的處理很快,此時會在transport_worker中完成所有的工作,而不會再進行訊息分發。

預設一個CPU一個transport_worker執行緒,但可能存在上千條TCP通道。如果從TCP通道中接收到資料,但其所在的transport_worker又處於繁忙狀態,此時需要等待執行緒結束前面的工作才能處理資料。類似地,在transport_worker執行緒空閒時才能處理資料的傳送。

使用hot threads API 可以看到一個空閒的執行緒如下:

"elasticsearch[instance-0000000004][transport_worker][T#1]" #32 daemon prio=5 os_prio=0 cpu=9645.94ms elapsed=501.63s tid=0x00007fb83b6307f0 nid=0x1c4 runnable  [0x00007fb7b8ffe000]
   java.lang.Thread.State: RUNNABLE
	at sun.nio.ch.EPoll.wait(java.base@17.0.2/Native Method)
	at sun.nio.ch.EPollSelectorImpl.doSelect(java.base@17.0.2/EPollSelectorImpl.java:118)
	at sun.nio.ch.SelectorImpl.lockAndDoSelect(java.base@17.0.2/SelectorImpl.java:129)
	- locked <0x00000000c443c518> (a sun.nio.ch.Util$2)
	- locked <0x00000000c38f7700> (a sun.nio.ch.EPollSelectorImpl)
	at sun.nio.ch.SelectorImpl.select(java.base@17.0.2/SelectorImpl.java:146)
	at io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:813)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:460)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at java.lang.Thread.run(java.base@17.0.2/Thread.java:833)

注意transport_worker執行緒的狀態應該總是RUNNABLE的,cpu=other=分別表示執行緒執行使用的CPU以及等待輸入使用的CPU。

監控

elasticsearch_exporter給出了Prometheus形式的指標,內部透過呼叫_nodes/stats來獲取節點狀態,透過呼叫/_all/_stats開獲取索引狀態。

Cluster performance
  • elasticsearch_cluster_health_status:檢視叢集狀態

  • elasticsearch_cluster_health_number_of_nodes:叢集中的總節點數

  • elasticsearch_cluster_health_unassigned_shards:為建立或分配的分片

  • elasticsearch_cluster_health_active_shards:叢集中active的shards的總數(含主分片和副本分片)

  • elasticsearch_cluster_health_relocating_shards:elasticsearch會給予balancing或當前資源使用情況來在節點之間移動分片,使用該指標可以檢視發生分片移動的時間。

Node
CPU
  • elasticsearch_process_cpu_percent:elasticsearch程式的CPU使用百分比
DISK

disk可能是磁碟也可能是pvc,根據特定的指標給出如下表達:

Important Metrics for Node Health
磁碟容量 Total disk capacity on the node’s host machine.
磁碟使用量 Total disk usage on the node’s host machine.
可用的磁碟量 Total disk space available.
已用磁碟百分比 Percentage of disk which is already used.
JVM
  • elasticsearch_jvm_memory_max_bytes: jvm記憶體總量,分為heap和noheap

  • elasticsearch_jvm_memory_used_bytes/elasticsearch_jvm_memory_max_bytes:展示了各個area的記憶體使用量。

    sum(avg_over_time(elasticsearch_jvm_memory_used_bytes{cluster_name="xx",area="heap"}[1m]))by(instance)/sum(avg_over_time(elasticsearch_jvm_memory_max_bytes{cluster_name="xx",area="heap"}[1m]))by(instance) 
    
Thread Pools

每個節點都會使用一些執行緒池來執行如查詢、索引、執行叢集狀態請求或節點發現等操作。thread Pools可以限制每種操作使用的資源。thread Pools的指標有三種:activequeuerejectedactive可以表示正在執行的操作,如active的search thread pool為10,表示當前正在處理的查詢數為10。queue表示等待被處理的操作,設定較大的佇列長度會存在請求丟失的風險(如果節點當機)。如果出現queued和rejected的執行緒不斷增加的情況,則需要降低請求速率,或增加節點的處理器數目,或增加叢集的節點數目。rejected表示被拒絕的操作,此時沒有可用的執行緒,且佇列已滿,通常是因為流量過大導致的。

每個節點維護了很多型別的thread pools,但最重要的是search、management和bulk(也被稱為write thread pool,包括write/update/delete),對應請求型別:search、merge和bulk/write操作。在佇列達到佇列長度上限時,請求會被拒絕。不同型別的thread pool有不同的佇列長度

  • search :對應傳送到es的count、search和suggest操作。如果出現大量rejected,則說明請求數目過多。
  • write:用於索引操作(document的增刪改以及bulks操作),出現rejected可能會導致資料丟失。bulk操作是一種一次性發生多個請求的高效方式。出現bulk rejected通常是因為在一個bulk請求中index了過多的documents。根據elasticsearch的文件,出現bulk rejected並不需要過多擔心,但最好實現一個線性或退避策略來處理bulk rejected。
  • management:用於叢集管理。通常只會使用一兩個執行緒,但該型別的執行緒池是可擴充套件的。
快取

Field Data Cache

用於fielddata,以支援在查詢的時候使用排序或聚合操作。該快取沒有限制,因此如果該快取使用過大,可能會導致記憶體問題,進而影響節點和叢集的健康。推薦為堆大小的20%

  • elasticsearch_indices_fielddata_memory_size_bytes

Node Query Cache

用於在filter context中快取查詢結果,用來增加重複查詢下的效能。預設為節點快取的10%。注意該指標只限於2.0以前的版本。

  • elasticsearch_indices_query_cache_cache_total
  • elasticsearch_indices_query_cache_cache_size
  • elasticsearch_indices_query_cache_count

Shard Request Cache

用於在分片級別快取hits, aggregations和 suggestions的總數,注意並不包含查詢結果。預設為節點最大堆記憶體的1%。

pending task

pending task由主分片節點處理,如果該值變大,說明主分片節點處於繁忙狀態。

  • elasticsearch_cluster_health_number_of_pending_tasks
Search performance

search=query+fetch

Metric description Name Metric type
query總數 elasticsearch_indices_search_query_total Work: Throughput
query花費的總時間 elasticsearch_indices_search_query_time_seconds Work: Performance
fetch總數 elasticsearch_indices_search_fetch_total Work: Throughput
fetches花費的總時間 elasticsearch_indices_search_fetch_time_seconds Work: Performance
  • Query loadelasticsearch_indices_search_query_total,出現異常的尖峰或下降說明底層可能出現了問題。
  • Query latencyelasticsearch_indices_search_query_time_seconds / elasticsearch_indices_search_query_total
  • Fetch latencysearch處理中的第二個階段,該值過大,可能是因為磁碟處理過慢、正在渲染查詢的documents或請求結果過多導致的。elasticsearch_indices_search_fetch_time_seconds / elasticsearch_indices_search_fetch_total
indexing performance
  • Indexing Refresh latencyelasticsearch_indices_refresh_time_seconds_total/elasticsearch_indices_refresh_total。如果該值上升,說明在同一時間嘗試index過多的documents。如果正在index很多documents,且不需要在第一時間查詢,則可以透過降低refresh頻率來最佳化index效能,在index結束之後設定回原來的值(預設1s)。

    curl -XPUT <nameofhost>:9200/<name_of_index>/_settings -d '{
         "index" : {
         "refresh_interval" : "-1"
         }
    }'
    
  • Flush latencyelasticsearch_indices_flush_time_seconds/elasticsearch_indices_flush_total將資料重新整理到磁碟。此處該值增加,說可能可能出現磁碟慢的問題,此時無法寫入index。可以透過降低translog flush設定中的index.translog.flush_threshold_size

  • Merge Times:新增、更新和刪除操作都會被匹配flush到磁碟,作為新的segment,es會自動將這些小的segment merge為一個大的segment。如果merge的時間和次數增加通常會降低索引的吞吐量。此時可以考慮滾動索引或重新考慮分片策略。

    • elasticsearch_indices_merges_total_throttled_time_seconds_total

    • elasticsearch_indices_merges_total

    • elasticsearch_indices_merges_total_time_seconds_total

  • index Saturation: elasticsearch_indices_store_throttle_time_seconds_total:elasticsearch index操作(input和output操作)被抑制的總時間

  • index rate: elasticsearch_indices_indexing_index_time_seconds_total/elasticsearch_indices_indexing_index_total間。

dashboardexporter dashboard

參考

How to(官方)

下面給出了部分方法,完整方法參見官方文件

提升indexing速度
  • 發起index請求時儘量使用buik,將對多個索引的請求合併到一個請求中。
  • elasticsearch預設每秒執行一次 refresh,以便能夠查詢新增的資料。適當增加該值(index.refresh_interval)可以提升索引速度
  • 增加檔案系統的快取,即增大記憶體
  • 當檢索一個document時,elasticsearch需要檢查相同分片上是否存在相同id的document。如果採用自動生成id方式,則可以讓elasticsearch跳過該步驟,加快檢索速度
  • 如果節點上需要執行大量檢索,需要確保每個執行大量檢索的分片的 indices.memory.index_buffer_size不能小於512MB,elasticsearch將其作為所有active分配的共享緩衝。預設為10%的堆大小,假如JVM的10GB,則index buffer會分配到1GB,可以支援2個分片執行大量檢索。
提升查詢速度
  • 避免使用基於指令碼的排序
  • 使用完整的data進行查詢。在data欄位中使用now時,通常無法進行快取。
  • force-merge只讀的索引。注意不能force-merge可寫的索引。
  • 使用index.store.preload預載入熱點索引檔案,預設該值為空。通常不會設定預載入所有檔案,一般設定為["nvd", "dvd", "tim", "doc", "dim"],但使用該功能會增加主機記憶體使用量,在merge之後需要丟棄檔案快取,此時會導致檢索和查詢變慢。
降低磁碟使用量
  • 不使用預設的dynamic string mappings。預設的dynamic string mappings會透過text和keyword來檢索string欄位,當只需要其中一個時會比較浪費。通常使用id欄位作為keyword,使用body欄位作為text

  • 禁用_source_source欄位儲存了document的原始JSON體,如果不需要訪問,則可以禁用。注意某些API需要使用_source才能正常執行,如update、highlight和reindex。

  • 較大的分片在儲存資料時更加有效,可以透過shrink API修改現有的索引。但較大的分片在恢復時也會花費較長時間。

TroubleShooting

磁碟高水位錯誤

Error: disk usage exceeded flood-stage watermark, index has read-only-allow-delete block

當一個節點達到高水位之後,elasticsearch會阻止寫入索引,並轉移高水位節點上的資料,直到低於高水位。此時建議增加磁碟容量。

可以透過提高高水位的值來臨時解決該問題。

circuit breaker錯誤

elasticsearch使用circuit breaker來防止JVM發生OutOfMemoryError錯誤 ,預設當記憶體使用量達到95%時會觸發circuit breaker。當出現該問題時,elasticsearch會返回429 HTTP狀態碼。

{
  'error': {
    'type': 'circuit_breaking_exception',
    'reason': '[parent] Data too large, data for [<http_request>] would be [123848638/118.1mb], which is larger than the limit of [123273216/117.5mb], real usage: [120182112/114.6mb], new bytes reserved: [3666526/3.4mb]',
    'bytes_wanted': 123848638,
    'bytes_limit': 123273216,
    'durability': 'TRANSIENT'
  },
  'status': 429
}

使用如下方式檢視節點的JVM使用情況:

GET _cat/nodes?v=true&h=name,node*,heap*

使用如下方式檢視breaker的狀態:

GET _nodes/stats/breaker
高CPU問題

elasticsearch使用執行緒池來管理並行操作的CPU資源,如果執行緒池枯竭,則elasticsearch會拒絕請求,並返回429狀態碼和TOO_MANY_REQUESTS錯誤,如當search執行緒池枯竭時,elasticsearch會拒絕查詢請求。

使用如下方式檢視各個節點的CPU使用情況:

GET _cat/nodes?v=true&s=cpu:desc

使用cat thread pool API檢視各個節點上的請求處理情況:

GET /_cat/thread_pool?v=true&h=id,node_name,name,active,rejected,completed

長時間執行的查詢會阻塞search執行緒池,使用如下方式檢視當前執行的查詢操作:

GET _tasks?actions=*search&detailed

使用如下方式檢視當前執行的task的資訊:

GET /_tasks?filter_path=nodes.*.tasks

description欄位包含查詢請求和請求內容,running_time_in_nanos給出了請求執行的時間:

{
  "nodes" : {
    "oTUltX4IQMOUUVeiohTt8A" : {
      "name" : "my-node",
      "transport_address" : "127.0.0.1:9300",
      "host" : "127.0.0.1",
      "ip" : "127.0.0.1:9300",
      "tasks" : {
        "oTUltX4IQMOUUVeiohTt8A:464" : {
          "node" : "oTUltX4IQMOUUVeiohTt8A",
          "id" : 464,
          "type" : "transport",
          "action" : "indices:data/read/search",
          "description" : "indices[my-index], search_type[QUERY_THEN_FETCH], source[{\"query\":...}]",
          "start_time_in_millis" : 4081771730000,
          "running_time_in_nanos" : 13991383,
          "cancellable" : true
        }
      }
    }
  }
}

使用如下方式來取消請求操作:

POST _tasks/oTUltX4IQMOUUVeiohTt8A:464/_cancel
高JVM記憶體

使用如下方式檢視各個節點的jvm記憶體壓力:

GET _nodes/stats?filter_path=nodes.*.jvm.mem.pools.old

記憶體壓力計算方式為:JVM Memory Pressure = used_in_bytes / max_in_bytes

  • 降低分片數目。大部分場景下,少量大分片使用的資源要少於大量小分片。參見調整分片大小
叢集狀態為red或yellow
  • 可能在節點維護的時候暫時禁用了分配功能,重新啟用即可:

    PUT _cluster/settings
    {
      "persistent" : {
        "cluster.routing.allocation.enable" : null
      }
    }
    
  • 在一個data節點離開叢集之後,elasticsearch預設會等待index.unassigned.node_left.delayed_timeout來延遲對副本分片的分片(主分片不收該配置影響)。如果在恢復一個節點時不需要等到延遲時間,可以使用如下命令觸發分配流程:

    POST _cluster/reroute?metric=none
    
  • 如果因為磁碟問題導致分配失敗,可以採用如下策略:

    • 增加磁碟空間
    • 為索引新增生命週期
    • 如果一個索引不再執行寫操作,可以使用force merge API合併segments
    • 如果一個索引只讀,可以使用 shrink index API 縮減主分片數
請求拒絕

造成請求拒絕的原因通常為:

使用如下命令檢查每個執行緒池的請求訪問情況,如果searchwrite執行緒池中出現了過多的rejected,說明elasticsearch正在有規律地拒絕請求:

GET /_cat/thread_pool?v=true&h=id,name,active,rejected,completed

檢視CPU和記憶體以及circuit breaker,針對性地解決問題。

Task 佇列積壓

task 佇列積壓可能會導致請求拒絕。

首先檢視佇列狀態,檢視節點上被拒絕的task比較多:

GET /_cat/thread_pool?v&s=t,n&h=type,name,node_name,active,queue,rejected,completed

檢視節點上的熱點執行緒:

GET /_nodes/hot_threads
GET /_nodes/<node_id>/hot_threads

長時間執行的task會佔用資源,可以使用如下命令檢視,取消長時間執行的task:

GET /_tasks?filter_path=nodes.*.tasks
POST _tasks/oTUltX4IQMOUUVeiohTt8A:464/_cancel

定位異常訊息

elasticsearch會校驗從磁碟上讀取的資料是否和寫入的資料相同,如果不同,則會報異常,如:

  • org.apache.lucene.index.CorruptIndexException
  • org.elasticsearch.gateway.CorruptStateException
  • org.elasticsearch.index.translog.TranslogCorruptedException

具體參見源文件。

discover異常
沒有選舉master

當一個node選舉為master之後,其日誌中會包含elected-as-master且所有節點的日誌中會包含master node changed。如果沒有選舉出master節點,所有節點的日誌中會包含org.elasticsearch.cluster.coordination.ClusterFormationFailureHelper,預設10s輸出一次。

master選舉只會涉及master-eligible節點, 因此這種情況下需要關注這類節點的日誌。

elasticsearch依賴仲裁機制來選舉出master,如果叢集中無法選舉出master,通常原因是缺少足夠的節點來形成仲裁。如果無法啟動足夠的節點來形成仲裁,則可以建立一個新的叢集,並從最近的snapshot中恢復資料。

節點無法發現或加入穩定的master

如果叢集中有一個穩定的master,但節點無法發現或加入其所在的叢集,則日誌中會包含ClusterFormationFailureHelper,觀察日誌資訊來進一步定位問題。

叢集不穩定

節點加入和離開叢集時,master 的日誌中會分別列印NodeJoinExecutorNodeLeftExecutor

disconnect

elasticsearch依賴穩定的網路,它會在節點之間建立大量TCP連線。其中master到其他節點的連線尤為重要,master不會主動斷開到其他節點的連線,類似地,在連線建立之後,節點也不會主動斷開其入站連線(除非節點關閉)。

透過如下配置可以獲取更詳細的網路資訊:

logger.org.elasticsearch.transport.TcpTransport: DEBUG
logger.org.elasticsearch.xpack.core.security.transport.netty4.SecurityNetty4Transport: DEBUG

lagging

elasticsearch需要每個節點都能夠快速apply叢集狀態。master會移除掉存在lagging的節點(預設2分鐘無法apply叢集狀態)。

使用如下配置可以獲取更詳細的資訊

logger.org.elasticsearch.cluster.coordination.LagDetector: DEBUG

follower check retry count exceeded

出現這種問題說明follower響應慢,導致follower響應慢的原因有很多,如:

  1. GC時間過長
  2. VM暫停
  3. 中間裝置導致延遲或丟包等

可以使用jstack匯出執行緒的profile資訊,也可以使用如下介面檢視熱點執行緒,支援type引數,可選blockcpuwait,預設是cpu

GET /_nodes/hot_threads
GET /_nodes/<node_id>/hot_threads

ShardLockObtainFailedException異常

如果一個節點離開並重新加入叢集后,elasticsearch通常會停止然後重新初始化其分片。如果無法快速停止分片,則elasticsearch可能會因為ShardLockObtainFailedException而無法重新初始化分片。,當啟用如下配置時,elasticsearch會在遇到ShardLockObtainFailedException時嘗試執行節點 hot threads API:

logger.org.elasticsearch.env.NodeEnvironment: DEBUG

輸出結果會被壓縮編碼和分塊,可以使用如下方式檢視:

cat shardlock.log | sed -e 's/.*://' | base64 --decode | gzip --decompress
查詢異常

確保data stream或索引包含資料

GET /my-index-000001/_count

檢視索引的欄位

GET /my-index-000001/_field_caps?fields=*

檢視最新的資料

GET my-index-000001/_search?sort=@timestamp:desc&size=1

校驗和explain查詢

當查詢返回非預期的結果時,可以使用如下方式定位:

  • 使用validate API 來校驗請求:

    GET /my-index-000001/_validate/query?rewrite=true
    {
      "query": {
        "match": {
          "user.id": {
            "query": "kimchy",
            "fuzziness": "auto"
          }
        }
      }
    }
    
  • 使用 explain API 來找出為什麼某些文件無法匹配查詢:

    GET /my-index-000001/_explain/0
    {
      "query" : {
        "match" : { "message" : "elasticsearch" }
      }
    }
    

檢視索引配置

GET /my-index-000001/_settings

查詢慢查詢

Slow logs可以幫助定位執行的慢查詢,audit logging 可以幫助確定查詢源。在elasticsearch.yml中配置如下引數來追蹤查詢,主義在Troubleshotting之後關閉該功能:

xpack.security.audit.enabled: true
xpack.security.audit.logfile.events.include: _all
xpack.security.audit.logfile.events.emit_request_body: true

How To(自總結)

停止所有的master節點會怎樣?

master節點用於變更叢集狀態,因此如果叢集中沒有master節點,將無法變更叢集狀態。叢集狀態後設資料包括:節點、索引、分片、分片分配、索引的mappings&setting等。

elasticsearch的每個節點的資料目錄都儲存了叢集狀態資訊(master節點的path.data中儲存了最新的叢集狀態資訊),且會在記憶體中維護叢集狀態。因此如果叢集中沒有master節點,仍然能進行不會影響叢集狀態的操作,如從index中讀取document,但不能執行索引操作。

如何增刪叢集節點

如何發現並解決大型叢集狀態造成的問題

如何停止資料節點

使用如下方式排除掉不需要的資料節點,此時系統會停止在該節點上分配分片,並將該節點的分片轉移到其他節點,分片遷移過程中,叢集狀態是green。待分片轉移完成之後,就可以停止該節點。

curl -XPUT "http://localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "persistent" : {
    "cluster.routing.allocation.exclude._ip" : "1.1.1.1"
  }
}'

如何檢視叢集的master節點?

使用如下命令檢視,帶*號的就是當前的master節點:

# curl localhost:9200/_cat/nodes?v&h=id,ip,port,v,m
id   ip            port version m
pLSN 192.168.56.30 9300 2.2.0   m
k0zy 192.168.56.10 9300 2.2.0   m
6Tyi 192.168.56.20 9300 2.2.0   *

如果無法透過POSTPUT修改叢集狀態,說明master節點出現了問題,可以透過檢視master的server日誌來檢視問題原因,也可以透過重啟當前master節點來觸發master選舉,以此嘗試解決問題。

如何檢視節點上生效的配置

有時候需要確認elasticsearch.yml中的配置是否生效,可以使用node info API檢視:

GET /_nodes
GET /_nodes/_all/
GET /_nodes/<node_id>/_all/
如何檢視參與選舉投票的節點
GET /_cluster/state?filter_path=metadata.cluster_coordination.last_committed_config

索引按大小排序

GET /_cat/indices/?pretty&s=store.size:desc

分片按大小排序

curl -X GET "localhost:9200/_cat/shards?v&s=store"

一般解決思路

  1. elasticsearch叢集狀態與分片的分配息息相關,首先確保所有節點版本一直,並使用GET /_cluster/settings來檢查叢集是否啟用了分片分配功能,如果沒有則啟用該功能:

    PUT _cluster/settings
    {
        "transient" : {
            "cluster.routing.allocation.enable": true
        }
    }
    
  2. 首先檢視叢集狀態

     curl localhost:9200/_cluster/health
    
  3. 檢視叢集中節點分片分配狀態

    curl localhost:9200/_cat/allocation?v=true
    
  4. 如果發現有unassigned的分片,可以透過GET _cat/shards?h=index,shard,prirep,state,node,unassigned.reason找出unassigned的分片,同時藉助GET _cluster/allocation/explain可以檢視更細節的內容。

大部分命令都很卡

有可能是某臺資料節點CPU過高導致的,首先透過下面兩個命令分別找出CPU過高的節點和熱點執行緒

curl -XGET 'http://localhost:9200/_cat/nodes?v=true&s=cpu:desc'
curl -XGET 'http://localhost:9200/_nodes/hot_threads'

然後再透過如下命令檢視是哪個查詢型別的task佔用率過高:

curl -XGET 'http://localhost:9200/_tasks?actions=*search&detailed'

使用如下命令可以取消一個task:

POST _tasks/oTUltX4IQMOUUVeiohTt8A:464/_cancel

執行PUT或POST命令卡住,但GET沒有問題

只能透過master節點執行修改叢集狀態的PUT或POST命令,如果此類命令卡主,說明master節點可能出現了問題,首先透過curl -XGET http://localhost:9200/_cat/nodes命令找到master節點,然後檢視節點的CPU、記憶體、thread_pool和tasks。

也可以透過重啟master節點的方法讓master角色漂移到其他master eligible節點。

無法刪除或遷移系統索引

.geoip_databases這樣的系統索引在主分片狀態為unassigned時是無法透過 Delete /my-index,介面直接刪除的,可以透過停用然後啟用相應功能的方式來讓系統重新分配索引

PUT _cluster/settings
{
  "persistent": {
    "ingest.geoip.downloader.enabled": false
  }
}

PUT _cluster/settings
{
  "persistent": {
    "ingest.geoip.downloader.enabled": true
  }
}

如何刪除特殊符號的索引

使用url編碼。假如一個索引名稱為<my-index-{now/d}-000001>,可以將其轉化為%3Cmy-index-%7Bnow%2Fd%7D-000001%3E

Data stream

如何刪除所有unsigned的shards(非data-stream)?

獲取unsigned的shards(注意下面結果中也會包含data-stream的shards):

curl -XGET 'http://localhost:9200/_cat/shards?h=index,shards,state,prirep,unassigned.reason' | grep UNASSIGNED 
  1. 刪除unsigned的shards:

    curl -XGET http://localhost:9200/_cat/shards | grep UNASSIGNED | awk {'print $1'} | xargs -i curl -XDELETE "http://localhost:9200/{}"
    
  2. 修改索引副本數,適用於分片數目大於節點數目的場景:

    PUT /my-index/_settings
    {
        "index" : {
            "number_of_replicas" : 0
        }
    }
    
如何刪除data-stream中unsigned的索引?

當一個索引為data-stream中的write index時是不能被刪除的,需要透過rollover來建立新的write index,然後就可以刪除老的索引:

POST my-data-stream/_rollover

DELETE /my-index
修改datastream的Lifecycle policy

lifecycle的rollover只對新資料(write index)有效,對老的索引無效。修改datastream的Lifecycle policy時,首先需要在Index Management-->index template中修改index patterns和index lifecycle的對應關係,注意需要在Logistics步驟中開啟Create data stream選項並修改Priority。此外還需要修改data stream中的write index的lifecycle policy,只需刪除現有的lifecycle並新增新的lifecycle即可(lifecycle policy是有版本號的,因此在修改lifecycle policy之後,需要重新apply到index中)。

如何修改(刪除或更新)現有data stream的mapping欄位

每個data stream都有一個template,該template中的mappings和index settings會應用到data stream的後端索引上。下面介紹如何修改一個data stream的mappings或settings。

直接修改保留欄位

在elasticsearch中除一些保留mapping欄位支援直接修改外,不能對其他欄位直接進行修改。下面例子中將ignore_malformed修改為true:

PUT /_index_template/my-data-stream-template
{
  "index_patterns": [ "my-data-stream*" ],
  "data_stream": { },
  "priority": 500,
  "template": {
    "mappings": {
      "properties": {
        "host": {
          "properties": {
            "ip": {
              "type": "ip",
              "ignore_malformed": true            
            }
          }
        }
      }
    }
  }
}

然後使用update mapping API 應用到特定的data stream上。使用write_index_only=true引數可以將修改僅應用到write index上:

PUT /my-data-stream/_mapping
{
  "properties": {
    "host": {
      "properties": {
        "ip": {
          "type": "ip",
          "ignore_malformed": true
        }
      }
    }
  }
}

更多參見官方文件

使用reindex修改
  1. 使用resolve API檢查叢集中是否存已存在選擇的data stream名稱,如果存在則重新選擇一個名稱:

    GET /_resolve/index/new-data-stream*
    
  2. 建立或更新index template,如果只是在現有的template中新增或修改很少的欄位,建議建立新的template:

    PUT /_index_template/new-data-stream-template
    {
      "index_patterns": [ "new-data-stream*" ],
      "data_stream": { },
      "priority": 500,
      "template": {
        "mappings": {
          "properties": {
            "@timestamp": {
              "type": "date_nanos"       //修改timestamp欄位型別              
            }
          }
        },
        "settings": {
          "sort.field": [ "@timestamp"], //新增sort.field 設定         
          "sort.order": [ "desc"]        //新增sort.order 設定         
        }
      }
    }
    
  3. 建立新的data stream(不要使用自動建立方式)

    PUT /_data_stream/new-data-stream
    
  4. 獲取老data stream 的後端索引資訊

    GET /_data_stream/my-data-stream
    

    使用reindex API將老data stream 的索引複製到新的data stream中:

    POST /_reindex
    {
      "conflicts": "proceed",
      "source": {
        "index": [".ds-my-data-stream-2099.03.07-000001", ".ds-my-data-stream-2099.03.07-000002"]
      },
      "dest": {
        "index": "new-data-stream",
        "op_type": "create"
      }
    }
    

    使用"conflicts": "proceed"來防止因為資料型別無法轉換導致reindex中斷。開啟debug日誌:

    PUT /_cluster/settings
    {
    "transient": {
    	"logger.org.elasticsearch.action.bulk.TransportShardBulkAction":"DEBUG"
    }
    }
    

    修復完之後記得恢復配置:

    PUT /_cluster/settings
    {
    "transient": {
    	"logger.org.elasticsearch.action.bulk.TransportShardBulkAction":NULL
    }
    }
    

    也可以將特定時間範圍的資料複製到新的data stream中:

    POST /_reindex
    {
      "source": {
        "index": "my-data-stream",
        "query": {
          "range": {
            "@timestamp": {
              "gte": "now-7d/d",
              "lte": "now/d"
            }
          }
        }
      },
      "dest": {
        "index": "new-data-stream",
        "op_type": "create"
      }
    }
    
  5. 可以使用如下方式檢視reindex的進度
    curl -XGET http://localhost:9200/_tasks?actions=*reindex&wait_for_completion=false?detailed
    
  6. 刪除老的data stream

    DELETE /_data_stream/my-data-stream
    

這種方式也需要同時修改上游資料寫入端(如logstash)指定的data-stream名稱,來讓新的資料寫入到新的data stream中。可以先reindex非write index的資料,然後讓上游系統寫入新的data stream,然後將老data stream的write index的資料reindex 到新的data stream,防止因中斷而丟失資料。

另外一種防止資料丟失的方式是使用aliases來管理data stream

提升reindex效能

reindex可能會遇到幾種問題:

  • reindex的過程可能會很長,且可能會消耗大量硬體資源,導致Elasticsearch的效能下降。可以設定如下Target索引配置:

    • refresh_interval = -1:使用該設定時,遷移過程中只會寫入Trans log,而不會在Lucene上花費磁碟IO
      image
    • number_of_replicas = 0:降低額外的資料複製
  • 在reindex過程中,客戶端寫入原索引的資訊會被丟棄。如下圖中,客戶端寫入Origin索引的資訊並不會被複制到Target索引中。
    image

    可以使用如下方式

    • _reindex的型別設定external
    • 在別名切換之後再進行一次_reindex。注意在第二次_reindex之前,客戶端已經切換到向Target索引寫入資料。
    image

過程如下:

  1. 建立目標索引
  2. 更新目標索引配置(refresh_interval = -1number_of_replicas = 0)
  3. _reindex型別為external
  4. 將別名從原始索引切換到目標索引
  5. 使用external型別重新執行_reindex
  6. 更新目標索引配置(refresh_interval = nullnumber_of_replicas = null)

如何在重啟data節點時避免大量分片分配

一種方式是透過禁用副本分片分配來降低IO(注意在節點啟動之後恢復該配置)

PUT _cluster/settings
{
  "persistent": {
    "cluster.routing.allocation.enable": "primaries"
  }
}

PUT _cluster/settings
{
  "persistent" : {
    "cluster.routing.allocation.enable" : "all"
  }
}

另一種可以透過增加index.unassigned.node_left.delayed_timeout(預設1分鐘)來防止分片分配,<INDEX_NAME>all表示應用到叢集中的所有索引:

PUT _all/_settings
{
  "settings": {
    "index.unassigned.node_left.delayed_timeout": "5m"
  }
}

在節點重啟之後使用如下方式檢視節點(含master重啟)狀態:

GET _cat/health
GET _cat/nodes

上述重啟方式實際上要求暫停資料生產端,並執行POST /_flush來重新整理資料。為避免資料丟失,安全的方式如下:

將需要重啟的節點上的shards轉移到其他節點:

PUT _cluster/settings
{
    "transient": {
        "cluster.routing.allocation.exclude._name": "<node_id>"
    }
}

呼叫如下命令檢視節點的shards數目以及unassigned的shards數目:

GET _cat/allocation/<node_id>

在節點重啟之後,恢復設定,elasticsearch會在節點之間重新均衡shards:

PUT _cluster/settings
{
    "transient": {
        "cluster.routing.allocation.exclude._name": ""
    }
}

如果主分片變為unassigned,且確定不再需要原始主分片的資料,如何遷移主分片

curl -XPOST "localhost:9200/_cluster/reroute?pretty" -H 'Content-Type: application/json' -d'
{
    "commands" : [
        {
          "allocate_empty_primary" : {
                "index" : "constant-updates", 
                "shard" : 0,
                "node" : "<NODE_NAME>", 
                "accept_data_loss" : "true"
          }
        }
    ]
}
'

解決使用wildcard方式查詢不到資料的問題

當檢索document時,elasticsearch會將字串轉換為小寫再進行分割,因此如果value中包含大寫字母且沒有忽略大小寫的話,會導致無法查詢預期的資料。參見: elasticsearch wild card query not working

{
  "query": {
    "wildcard": {
      "ActId": {
        "value": "integrationTestId_panda_2023*",
        "boost": 1,
        "rewrite": "constant_score",
        "case_insensitive": true
      }
    }
  }
}

kibana

如何修復因索引修改而失效的kibana dashboard

使用Export objects API匯出配置,在修改之後透過import objects API更新配置即可。

exporter API在匯出配置時,需要指定配置的型別,支援的型別為 visualizationdashboardsearchindex-patternconfiglens。如下分別匯出dashboardindex-patternlens的配置。

curl -X POST localhost:5601/api/saved_objects/_export -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d '
{
  "objects": [
    {
      "type": "dashboard",
      "id": "b7b04e30-4f7b-11ed-be4b-43f0b3e8e524"
    }
  ]
}'

curl -X POST localhost:5601/api/saved_objects/_export -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d '
{
  "type": "index-pattern"
}'


curl -X POST localhost:5601/api/saved_objects/_export -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d '
{
  "type": "lens"
}'

在修改完對應的配置之後,可以將其儲存在file.ndjson中,使用如下命令載入更新即可。除使用overwrite之外,還支援createNewCopies,用於生成一份要儲存的物件的複製,重新生成每個物件ID,並重置原來的物件,這種方式可以防止配置衝突。

curl -X POST localhost:5601/api/saved_objects/_import?overwrite=true -H "kbn-xsrf: true" --form file=@file.ndjson
如何使用snapshot只備份kibana dashboard

kibana dashboard等資訊儲存在以.kibana開頭的索引中,屬於系統索引,使用如下配置可以只備份kibana資訊。"indices": "-*"可以排除非系統index或系統data-stream以外的index和data-stream

    "indices": "-*",
    "include_global_state": true,
    "feature_states": [
      "kibana"
    ]

es分片數目限制

有兩個引數可以限制es的分片數目:cluster.routing.allocation.total_shards_per_nodecluster.max_shards_per_node區別如下:

  • cluster.routing.allocation.total_shards_per_node:限制了單個節點上可以分片的分片上限。預設不限制。
  • cluster.max_shards_per_node:限制了叢集中分片的總數(叢集中分片上限為:number_of_data_nodes * cluster.max_shards_per_node ),並不關心單個節點上的分片數。預設1000。

如何定位kibana無資料的問題

fluentbit-->kafka-->logstash-->elasticsearch-->kibana架構下,發現kibana上面看不到日誌,且es沒有建立data stream/index,此時說明es沒有接收到kibana的資料:

  1. 首先檢視logstash的grafana,看下進來的event和出去的event是否正常,以此判斷問題是出現在上游還是下游

  2. 然後在logstash上檢視是否有無法在es上建立index的錯誤,如:

    this action would add [6] shards, but this cluster currently has [2996]/[3000] maximum normal shards open;"}}}}
    

    說明es叢集的shards數目已經達到上限,es允許的shards數目為:cluster.max_shards_per_node * number of non-frozen data nodes,預設情況下,每個 non-frozen的data節點的shards數目為1000,如果有3個節點,es叢集中允許的shards數目為3000。檢視data節點的磁碟使用量,如果不大的話,可以適當提高每個節點允許的shards數目:

    PUT /_cluster/settings
    {
      "persistent" : {
        "cluster.max_shards_per_node" : "1500"
      }
    }
    
    
  3. 如果還沒有日誌,可以檢視fluentbit是否執行正常

TIPS

  • elasticsearch的效能主要跟磁碟有關係

  • elasticsearch的mapping有兩種:Dynamic mappingExplicit mapping,第一種由系統自動發現欄位並新增到mapping中,第二種是手動設定的,可以透過GET <my-index>/_mapping 檢視mapping(含自動和手動)。如果document欄位發生變化(如型別變化),可能會導致mapping衝突。

  • elasticsearch.ymlxpack.ml.enabledxpack.security.http.ssl.enabled是兩個單獨的配置,後者不依賴前者。

  • 使用curl時需要加引號,否則返值可能會導致引數失效:curl -XGET "http://localhost:9200/_cat/indices?v&health=yellow"

  • 如果一次性移除的節點超過voting configuration的一半會導致叢集無法正常運作,此時只需要重新啟動被移除的節點即可。

  • voting configuration中的master eligible節點可以在master丟失之後成為新的master

  • 檢視索引配置

    GET /my-index/_settings
    
  • elasticsearch的日誌配置

  • elasticsearch的索引操作預設基於document ID,可以自定義routing,但不建議這麼做,可能會導致分片不均衡。

  • elasticsearch不會將副本分片分配到和主分片相同的節點

  • elasticsearch叢集狀態的含義:

    • 紅色:至少一個主分片為unassigned;
    • 黃色:至少一個副本分片為unassigned;
    • 綠色:全部主&副本都分配成功。

ECK

logstash目前處於alpha階段,暫不採納。

相容性

ECK和kubernetes的版本相容

特性相容

ECK某些特性,如LDAP等需要付費才能使用。

ECK安裝

ECK升級

Troubleshooting

在更新Elasticsearch配置之後,需要觀察Elasticsearch資源是否正確應用配置。如果狀態閾值卡在ApplyingChanges,可能是因為叢集狀態不正常,導致無法繼續更新或排程等原因導致pod啟動失敗。

$ kubectl get es

NAME                  HEALTH   NODES    VERSION   PHASE            AGE
elasticsearch-sample  yellow   2        7.9.2     ApplyingChanges  36m

解除安裝ECK

刪除名稱空間內容

kubectl get namespaces --no-headers -o custom-columns=:metadata.name \
  | xargs -n1 kubectl delete elastic --all -n

清空CRD定義:

kubectl delete -f https://download.elastic.co/downloads/eck/2.10.0/operator.yaml
kubectl delete -f https://download.elastic.co/downloads/eck/2.10.0/crds.yaml

訪問ECK服務

管理kubernetes services

可以在http.service.spec.type中指定暴露的服務:

apiVersion: <kind>.k8s.elastic.co/v1
kind: <Kind>
metadata:
  name: hulk
spec:
  version: 8.11.1
  http:
    service:
      spec:
        type: LoadBalancer

Http TLS 證照

預設下,operator會為每個資源管理一個自簽證照和自定義CA。

> kubectl get secret | grep es-http
hulk-es-http-ca-internal         Opaque                                2      28m
hulk-es-http-certs-internal      Opaque                                2      28m
hulk-es-http-certs-public        Opaque                                1      28m

使用如下方式建立自定義證照:

  • ca.crt: CA 證照 (可選,當 tls.crt 由知名 CA頒發時).
  • tls.crt: 證照.
  • tls.key: 證照中的第一個證照的私鑰.
kubectl create secret generic my-cert --from-file=ca.crt --from-file=tls.crt --from-file=tls.key

在http中引用自定義的證照

spec:
  http:
    tls:
      certificate:
        secretName: my-cert

可以使用如下方式取消Kibana、APM Server、 Enterprise Search和Elasticsearch的HTTP TLS:

spec:
  http:
    tls:
      selfSignedCertificate:
        disabled: true

連線Elasticsearch後端

在kubernetes內部:

NAME=elasticsearch

kubectl get secret "$NAME-es-http-certs-public" -o go-template='{{index .data "tls.crt" | base64decode }}' > tls.crt
PW=$(kubectl get secret "$NAME-es-elastic-user" -o go-template='{{.data.elastic | base64decode }}')

curl --cacert tls.crt -u elastic:$PW https://$NAME-es-http:9200/

在kubernetes外部:

NAME=elasticsearch

kubectl get secret "$NAME-es-http-certs-public" -o go-template='{{index .data "tls.crt" | base64decode }}' > tls.crt
IP=$(kubectl get svc "$NAME-es-http" -o jsonpath='{.status.loadBalancer.ingress[].ip}')
PW=$(kubectl get secret "$NAME-es-elastic-user" -o go-template='{{.data.elastic | base64decode }}')

curl --cacert tls.crt -u elastic:$PW https://$IP:9200/

自定義Pods

透過podTemplate配置pod屬性:

apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: quickstart
spec:
  version: 8.11.1
  nodeSets:
  - name: default
    count: 1
    podTemplate:
      metadata:
        labels:
          my.custom.domain/label: "label-value"
        annotations:
          my.custom.domain/annotation: "annotation-value"
      spec:
        containers:
          - name: elasticsearch
            env:
              - name: ES_JAVA_OPTS
                value: "-Xms4g -Xmx4g"

指定容器映象倉庫

Validating webhooks

ECK預設會安裝一個ValidatingWebhookConfiguration:

  • 在建立和更新時驗證所有的Elastic自定義資源 (Elasticsearch, Kibana, APM Server, Enterprise Search, Beats, Elastic Agent, Elastic Maps Server 和 Logstash)
  • operator本身也是一個webhook服務,透過elastic-system名稱空間的elastic-webhook-server service暴露
  • operator會為webhook生成一個名為elastic-webhook-server-cert的secret,operator負責該證照的滾動跟新

Elasticsearch

elasticsearch資源有多個配置欄位。

ECK管理的欄位

ECK會自動管理如下欄位,且不支援自定義配置如下欄位:

cluster.name
discovery.seed_hosts
discovery.seed_providers
discovery.zen.minimum_master_nodes [7.0]Deprecated in 7.0.
cluster.initial_master_nodes [7.0]Added in 7.0.
network.host
network.publish_host
path.data
path.logs
xpack.security.authc.reserved_realm.enabled
xpack.security.enabled
xpack.security.http.ssl.certificate
xpack.security.http.ssl.enabled
xpack.security.http.ssl.key
xpack.security.transport.ssl.enabled
xpack.security.transport.ssl.verification_mode

elasticsearch生成的預設elasticsearch.yml預設配置如下:

azure:
    client:
        default:
            endpoint_suffix: core.chinacloudapi.cn
cluster:
    name: quickstart
    routing:
        allocation:
            awareness:
                attributes: k8s_node_name
discovery:
    seed_hosts: []
    seed_providers: file
http:
    publish_host: ${POD_NAME}.${HEADLESS_SERVICE_NAME}.${NAMESPACE}.svc
network:
    host: "0"
    publish_host: ${POD_IP}
node:
    attr:
        k8s_node_name: ${NODE_NAME}
    name: ${POD_NAME}
path:
    data: /usr/share/elasticsearch/data
    logs: /usr/share/elasticsearch/logs
xpack:
    license:
        upload:
            types:
                - trial
                - enterprise
    security:
        authc:
            realms:
                file:
                    file1:
                        order: -100
                native:
                    native1:
                        order: -99
            reserved_realm:
                enabled: "false"
        enabled: "true"
        http:
            ssl:
                certificate: /usr/share/elasticsearch/config/http-certs/tls.crt
                certificate_authorities: /usr/share/elasticsearch/config/http-certs/ca.crt
                enabled: true
                key: /usr/share/elasticsearch/config/http-certs/tls.key
        transport:
            ssl:
                certificate: /usr/share/elasticsearch/config/node-transport-cert/transport.tls.crt
                certificate_authorities:
                    - /usr/share/elasticsearch/config/transport-certs/ca.crt
                    - /usr/share/elasticsearch/config/transport-remote-certs/ca.crt
                enabled: "true"
                key: /usr/share/elasticsearch/config/node-transport-cert/transport.tls.key
                verification_mode: certificate

nodeSets

用於設定elasticsearch叢集的拓撲。每個nodeSets表示一組共享相同配置的elasticsearch nodes。可以在nodeSet中定義elasticsearch.yml配置檔案。

下面設定了master節點和data節點

apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: quickstart
spec:
  version: 8.11.1
  nodeSets:
  - name: master-nodes
    count: 3
    config:
      node.roles: ["master"]
    volumeClaimTemplates:
    - metadata:
        name: elasticsearch-data
      spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 10Gi
        storageClassName: standard
  - name: data-nodes
    count: 10
    config:
      node.roles: ["data"]
    volumeClaimTemplates:
    - metadata:
        name: elasticsearch-data
      spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 1000Gi
        storageClassName: standard
更新叢集配置

ECK可以平滑地升級叢集,只需要apply新的elasticsearch配置即可。例如修改節點數目、記憶體限制、節點roles、elasticsearch版本等。

ECK可以保證:

  • 在一個節點移除前,其資料會被遷移到其他節點

  • 當一個叢集拓撲變更新後,ECK會自動調整如下elasticsearch配置:

    discovery.seed_hosts
    cluster.initial_master_nodes
    discovery.zen.minimum_master_nodes
    _cluster/voting_config_exclusions
    
  • 在可能的情況下,滾動升級可以安全地複用已有的PersistentVolumes

滾動升級的高階配置

Statefulset

ECK會將每個NodeSet轉化為一個statefulset。

StatefulSet的名稱來自 Elasticsearch 資源名稱和 NodeSet 名稱。使用 StatefulSet 名稱加上pod序號字尾來生成一個pod名稱。Elasticsearch 節點的名稱與它們所執行的 Pod 相同。

當一個pod重建時,statefulset controller會確保PVC附加到新的pod上。

叢集升級模式
  • 新增一個NodeSet

    ECK會建立出對應的Statefulset,並建立出TLS證照和elasticsearch配置檔案對應的secret和configmap

  • 增加已有NodeSet的節點數

    ECK會增加對應Statefulset的副本數

  • 降低已有NodeSet的節點數

    ECK會首先遷移該節點資料,然後再降低對應Statefulset的副本數,與該節點對應的PVC也會被自動移除

  • 移除已有的NodeSet

    ECK會遷移該NodeSet的資料,然後移除底層Statefulset

  • 更新已有的NodeSet,例如更新elasticsearch配置或PodTemplate欄位

    ECK會對對應的elasticsearch節點執行滾動更新,並在更新時保證Elasticsearch叢集的可用性。大部分情況下會逐個重啟elasticsearch節點。

  • 重新命名已有的NodeSet

    ECK會建立一個新名稱的NodeSet,並將資料從舊的NodeSet轉移過來,然後刪除舊的NodeSet。

滾動升級可以確保升級過程中Elasticsearch叢集的狀態是green,但如果索引只有一個副本,則可以在叢集狀態為yellow的情況下執行滾動更新。

如下情況中,會忽略叢集健康狀態:

  • 如果一個NodeSet的所有elasticsearch節點都是unavailable(可能是因為配置錯誤導致),此時operator會忽略叢集健康並更新該NodeSet的節點
  • 如果待更新的elasticsearch節點不健康,且不是elasticsearch叢集的一部分,此時operator會忽略叢集健康並更新該節點

注意,減少節點資料需要人工干預,此時可能會出現副本數大於節點數的情況,可以透過 index settings 來調整索引的副本數,或使用auto_expand_replicas 來自適應叢集的資料節點數。

排程

可以使用affinity、topologySpreadConstraints等方式指定pod排程方式。

Volume claim templates

注意volume claim必須為elasticsearch-data,與 data.path 有關,預設為/usr/share/elasticsearch/data

spec:
  nodeSets:
  - name: default
    count: 3
    volumeClaimTemplates:
    - metadata:
        name: elasticsearch-data # Do not change this name unless you set up a volume mount for the data path.
      spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 5Gi
        storageClassName: standard
Volume claim的刪除

可以透過volumeClaimDeletePolicy欄位配置刪除elasticsearch叢集時ECK的動作。可選值為DeleteOnScaledownAndClusterDeletionDeleteOnScaledownOnly,預設為DeleteOnScaledownAndClusterDeletion,即在刪除elasticsearch叢集時,同時也會刪除PVC。而DeleteOnScaledownOnly則會在刪除elasticsearch叢集的時候保留PVC。如果使用相同的名稱和node sets配置重新建立了被刪除的叢集,則新叢集會採用已有的PVC(但無法使用原來的資料):

apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: es
spec:
  version: 8.11.1
  volumeClaimDeletePolicy: DeleteOnScaledownOnly
  nodeSets:
  - name: default
    count: 3

Storageclass的volumeBindingMode必須WaitForFirstConsumer。建議將reclaimPolicy設定為Delete,表示在PVC刪除的時候刪除掉PV。ECK會在叢集縮容或刪除的時候自動清理PVC,但不會清理PV。鑑於不同叢集無法使用相同的資料,因此建議將reclaimPolicy設定為Delete

更新Volume claim配置

如果Storageclass支援卷擴容,則可以在volumeClaimTemplates中增加Storage request大小,ECK會更新對應的PVC,並重新建立Statefulset。如果volume 驅動支援ExpandInUsePersistentVolumes,則可以線上修改檔案系統,無需重啟Elasticsearch程式。

除此之後不能修改volumeClaimTemplates。如果必須要修改volumeClaimTemplates,則可以使用不同的配置建立一個新的nodeSet,然後移除已有的nodeSet。

transport

transport必須使用https

用於設定內部節點之間以及和外部叢集之間的通訊。在spec.transport.service欄位中可以修改暴露transport模組的Service:

spec:
  transport:
    service:
      metadata:
        labels:
          my-custom: label
      spec:
        type: LoadBalancer

ECK預設會使用自籤CA為叢集中的每個節點頒發一個證照,可以透過如下方式在生成的證照中新增額外的IP地址或DNS名稱:

apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: quickstart
spec:
  version: 8.11.1
  transport:
    tls:
      subjectAltNames:
      - ip: 1.2.3.4
      - dns: hulk.example.com
  nodeSets:
  - name: default
    count: 3
使用自籤CA

CA證照必須儲存在secret的ca.crt欄位中,key必須儲存在ca.key中:

spec:
  transport:
    tls:
      certificate:
        secretName: custom-ca

設定vm.max_map_count

支援透過手動、initcontainer或daemonset的方式設定vm核心引數,推薦使用initcontainer方式。下面是initcontainer方式:

cat <<EOF | kubectl apply -f -
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: quickstart
spec:
  version: 8.11.1
  nodeSets:
  - name: default
    count: 3
    podTemplate:
      spec:
        initContainers:
        - name: sysctl
          securityContext:
            privileged: true
            runAsUser: 0
          command: ['sh', '-c', 'sysctl -w vm.max_map_count=262144']
EOF

Secure settings

可以使用kubernetes的secrets來管理 secure settings(如Azure repository plugin),這些secret都必須是key-value組合。ECK會在elasticsearch啟動前將其注入到每個elasticsearch節點的keystore中。ECK operator會持續watch secrets,並在其變更時更新elasticsearch keystore。

spec:
  secureSettings:
  - secretName: one-secure-settings-secret
  - secretName: two-secure-settings-secret

自定義配置檔案和外掛管理

可以使用自定義容器映象或init container的方式來安裝外掛和配置檔案

spec:
  nodeSets:
  - name: default
    count: 3
    podTemplate:
      spec:
        initContainers:
        - name: install-plugins
          command:
          - sh
          - -c
          - |
            bin/elasticsearch-plugin install --batch repository-azure

更新策略

可以使用與deployment相同的策略來更新elasticsearch pod。maxSurge表示可以額外建立的新pod數目,maxUnavailable表示unavailable的pod數目。

spec:
  updateStrategy:
    changeBudget:
      maxSurge: 3
      maxUnavailable: 1

預設行為如下,即會立即建立新的pods,且unavailable的pod數目不能超過1:

spec:
  updateStrategy:r
    changeBudget:e
      maxSurge: -1 #負值表示不作限制
      maxUnavailable: 1

Pod disruption budget

elasticsearch可以指定PDB,保證在rescheduled情況下服務的穩定性:

apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: quickstart
spec:
  version: 8.11.1
  nodeSets:
  - name: default
    count: 3
  podDisruptionBudget:
    spec:
      minAvailable: 2
      selector:
        matchLabels:
          elasticsearch.k8s.elastic.co/cluster-name: quickstart

Readiness probe

透過READINESS_PROBE_TIMEOUT來修改readiness probe的超時時間,預設3s,下面設定為10s。

spec:
  version: 8.11.1
  nodeSets:
    - name: default
      count: 1
      podTemplate:
        spec:
          containers:
          - name: elasticsearch
            readinessProbe:
              exec:
                command:
                - bash
                - -c
                - /mnt/elastic-internal/scripts/readiness-probe-script.sh
              failureThreshold: 3
              initialDelaySeconds: 10
              periodSeconds: 12
              successThreshold: 1
              timeoutSeconds: 12
            env:
            - name: READINESS_PROBE_TIMEOUT
              value: "10"

Pod PreStop hook

使用PRE_STOP_ADDITIONAL_WAIT_SECONDS可以設定preStop hook,防止出現Elasticsearch連線錯誤(kube-proxy識別pod的預設間隔為30s)。預設值為50:

spec:
  version: 8.11.1
  nodeSets:
    - name: default
      count: 1
      podTemplate:
        spec:
          containers:
          - name: elasticsearch
            env:
            - name: PRE_STOP_ADDITIONAL_WAIT_SECONDS
              value: "5"

Security Context

elasticseaarch 8.8.0版本中,預設的security context如下:

securityContext:
  allowPrivilegeEscalation: false
  capabilities:
    drop:
    - ALL
  privileged: false
  readOnlyRootFilesystem: true 

users and roles

當建立elasticsearch資源時,會預設建立一個elastic使用者,並賦予superuser角色。也可以自定義role

ECK需要付費才能使用LDAP,目前只能使用簡單的基於檔案的使用者名稱認證方式。

設定計算資源

從elasticsearch 7.11開始,會根據節點的roles和可用記憶體(resources.limits.memory)來自動計算JVM的堆大小:

apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: quickstart
spec:
  version: 8.11.1
  nodeSets:
  - name: default
    count: 1
    podTemplate:
      spec:
        containers:
        - name: elasticsearch
          resources:
            requests:
              memory: 4Gi
              cpu: 8
            limits:
              memory: 4Gi

各個元件的預設配置

Kibana

連線Elasticsearch

當kibana和Elasticsearch在同一個ECK叢集中時,ECK會將所需的Secret從Elasticsearch所在的名稱空間複製到kibana所在的名稱空間,因此這種情況下,kibana無需單獨配置和Elasticsearch的使用者名稱密碼。

但如果連線到叢集外部的Elasticsearch,則需要單獨指定連線資訊。

新增配置

spec.config中可以新增Kibana配置。

apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
  name: kibana-sample
spec:
  version: 8.11.1
  count: 1
  elasticsearchRef:
    name: "elasticsearch-sample"
  config:
     elasticsearch.requestHeadersWhitelist:
     - authorization

Http配置

HTTP配置和Elasticsearch[類似](#Http TLS 證照)

benchmark

rally

Tips

  • 如何檢視elasticsearch叢集的狀態?
    透過kubectl get elasticsearch命令可以直接檢視叢集的狀態(HEALTH)和當前階段(ApplyingChanges,表示正在更改配置,如果是Ready,表示叢集正常)

    NAME            HEALTH   NODES   VERSION   PHASE             AGE
    elasticsearch   green    7       8.11.1    ApplyingChanges   8d
    
  • Volume擴容對叢集的影響?

    如果storageclass支援卷擴容(即sc中包含allowVolumeExpansion: true),則ECK會更新對應的PVC,並重新建立Statefulset。如果卷驅動支援ExpandInUsePersistentVolumes,則檔案系統會線上變更大小,無需重啟Elasticsearch程式。

    除此之外不支援在volumeClaimTemplates中的其他變更操作,如變更storageclass或減少卷大小。

  • 如果kibana的對外域名沒有配置tls,則Dev Tools中的copy as cCurl灰色

  • 配置logstash和kibana的許可權?

    elasticsearch 中提供了很多內建的roles,用於分配Cluster privilegesindics privileges,可以滿足大部分場景。

    • logstash的role如下:

      kind: Secret
      apiVersion: v1
      metadata:
        name: {{ .Values.logstash.role }}
        namespace: {{ .Release.Namespace }}
        labels:
          {{- include "eck-operator.labels" $ | nindent 4 }}
      stringData:
        roles.yml: |-
          {{ .Values.logstash.role }}:
              cluster: ["monitor", "manage_ilm", "read_ilm", "manage_logstash_pipelines", "manage_index_templates", "cluster:admin/ingest/pipeline/get",]
              indices:
              - names: [ '*' ]
                privileges: ["manage", "write", "create_index", "read", "view_index_metadata"]
      
    • kibana admin可以使用kibana_admin,viewer role。

      • kibana中有很多space,對於guest使用者來說只需要檢視個別space即可,建議根據官方文件配置kibana的role,user和space。

      • 需要注意的是,像data view(即index pattern)和dashboard是按照space隔離的,因此如果多個使用者需要檢視相同的data view和dashboard,則需要使用相同的space。kibana中有一個預設space,名為default,大部分情況下不需要建立單獨的space,在使用者的role中直接引用該space,並指定部分space許可權即可。

      • guest一般需要有檢視Discover和Dashboard空間的許可權,此時需要有create data view許可權,在Stack Management->Roles->guest(假設建立的role名為guest)->kibana中授予Management->Data View Management All許可權即可。

    • Elasticsearch-exporter可以設定如下role,注意role的名稱是exporter_admins,而不是metadata.name

      kind: Secret
      apiVersion: v1
      metadata:
        name: {{ .Values.exporter.role }}
        namespace: {{ .Release.Namespace }}
        labels:
          {{- include "eck-operator.labels" $ | nindent 4 }}
      stringData:
        roles.yml: |-
          exporter_admins: #定義的role名稱
            cluster: [ 'monitor' ]
            indices:
            - names: [ '*' ]
              privileges: [ 'monitor' ]
      

    自定義的user會被掛載到容器中的/usr/share/elasticsearch/config/users檔案中,而role會被掛載到/usr/share/elasticsearch/config/roles檔案中,user和role的對映關係可以檢視檔案/usr/share/elasticsearch/config/users_roles

    需要注意的是:如果使用file realm新增的user和role,是不能透過kibana的/Stack Management/Roles/選單或api檢視的

相關文章