ELK運維文檔

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([email protected]/Native Method)
	at sun.nio.ch.EPollSelectorImpl.doSelect([email protected]/EPollSelectorImpl.java:118)
	at sun.nio.ch.SelectorImpl.lockAndDoSelect([email protected]/SelectorImpl.java:129)
	- locked <0x00000000c443c518> (a sun.nio.ch.Util$2)
	- locked <0x00000000c38f7700> (a sun.nio.ch.EPollSelectorImpl)
	at sun.nio.ch.SelectorImpl.select([email protected]/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([email protected]/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 [email protected]
如何使用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查看的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章