Elasticsearch系列---生產(chǎn)集群部署(下)

概要

本篇繼續(xù)講解Elasticsearch集群部署的細節(jié)問題

集群重啟問題

如果我們的Elasticsearch集群做了一些離線的維護操作時,如擴容磁盤,升級版本等,需要對集群進行啟動,節(jié)點數(shù)較多時,從第一個節(jié)點開始啟動,到最后一個節(jié)點啟動完成,耗時可能較長,有時候還可能出現(xiàn)某幾個節(jié)點因故障無法啟動,排查問題、修復(fù)故障后才能加入到集群中,此時集群會干什么呢?

假設(shè)10個節(jié)點的集群,每個節(jié)點有1個shard,升級后重啟節(jié)點,結(jié)果有3臺節(jié)點因故障未能啟動,需要耗費時間排查故障,如下圖所示:

image

整個過程步驟如下:

  1. 集群已完成master選舉(node6),master發(fā)現(xiàn)未加入集群的node1、node2、node3包含的shard丟失,便立即發(fā)出shard恢復(fù)的指令。

  2. 在線的7臺node,將其中一個replica shard升級為primary shard,并且進行為這些primary shard復(fù)制足夠的replica shard。

  3. 執(zhí)行shard rebalance操作。

  4. 故障的3臺節(jié)點已排除,啟動成功后加入集群。

  5. 這3臺節(jié)點發(fā)現(xiàn)自己的shard已經(jīng)在集群中的其他節(jié)點上了,便刪除本地的shard數(shù)據(jù)。

  6. master發(fā)現(xiàn)新的3臺node沒有shard數(shù)據(jù),重新執(zhí)行一次shard rebalance操作。

這個過程可以發(fā)現(xiàn),多做了四次IO操作,shard復(fù)制,shard首次移動,shard本地刪除,shard再次移動,這樣憑空造成大量的IO壓力,如果數(shù)據(jù)量是TB級別的,那費時費力不討好。

出現(xiàn)此類問題的原因是節(jié)點啟動的間隔時間不能確定,并且節(jié)點越多,這個問題越容易出現(xiàn),如果可以設(shè)置集群等待多少個節(jié)點啟動后,再決定是否對shard進行移動,這樣IO壓力就能小很多。

針對這個問題,我們有下面幾個參數(shù):

  • gateway.recover_after_nodes:集群必須要有多少個節(jié)點時,才開始做shard恢復(fù)操作。
  • gateway.expected_nodes: 集群應(yīng)該有多少個節(jié)點
  • gateway.recover_after_time: 集群啟動后等待的shard恢復(fù)時間

如上面的案例,我們可以這樣設(shè)置:

gateway.recover_after_nodes: 8
gateway.expected_nodes: 10
gateway.recover_after_time: 5m

這三個參數(shù)的含義:集群總共有10個節(jié)點,必須要有8個節(jié)點加入集群時,才允許執(zhí)行shard恢復(fù)操作,如果10個節(jié)點未全部啟動成功,最長的等待時間為5分鐘。

這幾個參數(shù)的值可以根據(jù)實際的集群規(guī)模來設(shè)置,并且只能在elasticsearch.yml文件里設(shè)置,沒有動態(tài)修改的入口。

上面的參數(shù)設(shè)置合理的情況,集群啟動是沒有shard移動的現(xiàn)象,這樣集群啟動的時候就可以由之前的幾小時,變成幾秒鐘。

JVM和Thread Pool設(shè)置

一提到JVM的調(diào)優(yōu),大家都有手癢的感覺,好幾百個JVM參數(shù),說不定開啟了正確的按鈕,從此ES踏上高性能、高吞吐量的道路。現(xiàn)實情況可能是我們想多了,ES的大部分參數(shù)是經(jīng)過反復(fù)論證的,基本上不用咱們太操心。

JVM GC

Elasticsearch默認使用的垃圾回收器是CMS。

## GC configuration
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=75
-XX:+UseCMSInitiatingOccupancyOnly

CMS回收器是并發(fā)式的回收器,能夠跟應(yīng)用程序工作線程并發(fā)工作,最大程度減少垃圾回收時的服務(wù)停頓時間。

CMS還是會有兩個停頓階段,同時在回收特別大的heap時也會有一些問題。盡管有一些缺點,但是CMS對于要求低延時請求響應(yīng)的軟件來說,還是最佳的垃圾回收器,因此官方的推薦就是使用CMS垃圾回收器。

有一種最新的垃圾回收器叫做G1。G1回收器可以比CMS提供更少的回收停頓時間,而且能夠這對大heap有更好的回收表現(xiàn)。它會將heap劃分為多個region,然后自動預(yù)測哪個region會有最多可以回收的空間。通過回收那些region,就可以最小化停頓時長,而且可以針對大heap進行回收。

聽起來還挺好的,只是G1還是比較年輕的一種垃圾回收器,而且經(jīng)常會發(fā)現(xiàn)一些新的bug,這些bug可能會導(dǎo)致jvm掛掉。穩(wěn)定起見,暫時不用G1,等G1成熟后ES官方推薦后再用不遲。

線程池

我們開發(fā)Java應(yīng)用系統(tǒng)時,對系統(tǒng)調(diào)優(yōu)的一個常見手段就是調(diào)整線程池,但在ES中,默認的threadpool設(shè)置是非常合理的,對于所有的threadpool來說,除了搜索的線程池,都是線程數(shù)量設(shè)置的跟cpu core一樣多的。如果我們有8個cpu core,那么就可以并行運行8個線程。那么對于大部分的線程池來說,分配8個線程就是最合理的數(shù)量。

搜索會有一個更加大的threadpool,線程數(shù)量一般被配置為:cpu core * 3 / 2 + 1。

Elasticsearch的線程池分成兩種:接受請求的線程和處理磁盤IO操作的線程,前面那種由ES管理,后一種由Lucene管理,它們之間會進行協(xié)作,ES的線程不會因為IO操作而block住,所以ES的線程設(shè)置跟CPU核數(shù)一樣或略大于CPU核數(shù)即可。

服務(wù)器的計算能力是非常有限的,線程池的數(shù)量過大會導(dǎo)致上下文頻繁切換,更費資源,如果threadpool大小設(shè)置為50,100,甚至500,會導(dǎo)致CPU資源利用率很低,性能反而下降。

只需要記?。河媚J的線程池,如果真要修改,以CPU核數(shù)為準。

heap內(nèi)存設(shè)置最佳實踐

Elasticsearch默認的jvm heap內(nèi)存大小是2G,如果是研發(fā)環(huán)境,我會改成512MB,但在生產(chǎn)環(huán)境2GB有點少。

在config/jvm.options文件下,可以看到heap的設(shè)置:

# Xms represents the initial size of total heap space
# Xmx represents the maximum size of total heap space

-Xms2g
-Xmx2g

分配規(guī)則

Elasticsearch使用內(nèi)存主要有兩個大戶:jvm heap和lucene,前者ES用來存放很多數(shù)據(jù)結(jié)構(gòu)來提供更快的操作性能,后者使用os cache緩存索引文件,包括倒排索引、正排索引,os cache內(nèi)存是否充足,直接影響查詢檢索的性能。

一般的分配規(guī)則是:jvm heap占用小于一半的內(nèi)存,剩下的全歸lucene使用。

如果單臺機器總內(nèi)存64GB,那么heap頂格內(nèi)存分配為32GB,因為32GB內(nèi)存以下,jvm會使用compressed oops來解決object pointer耗費過大空間的問題,超過32GB后,jvm的compressed oops功能關(guān)閉,這樣就只能使用64位的object pointer,會耗費更多的空間,過大的object pointer還會在cpu,main memory和LLC、L1等多級緩存間移動數(shù)據(jù)的時候,吃掉更多的帶寬。最終的結(jié)果可能是50GB內(nèi)存的效果和32GB一樣,白白浪費了十幾GB內(nèi)存。

這里涉及到j(luò)vm的object pointer指針壓縮技術(shù),有興趣可以單獨了解一下。

如果單臺機器總內(nèi)存小于64GB,一般heap分配為總內(nèi)存的一半即可,具體要看預(yù)估的數(shù)據(jù)量是多少。

如果使用超級機器,1TB內(nèi)存的那種,官網(wǎng)不建議上那么牛逼的機器,建議分配4-32GB內(nèi)存給heap,其他的全部用來做os cache,這樣數(shù)據(jù)量全部緩存在內(nèi)存中,不落盤查詢,性能杠杠滴。

最佳實踐建議

  1. 將heap的最小值和最大值設(shè)置為一樣大。
  2. elasticsearch jvm heap設(shè)置得越大,就有越多的內(nèi)存用來進行緩存,但是過大的jvm heap可能會導(dǎo)致長時間的gc停頓。
  3. jvm heap size的最大值不要超過物理內(nèi)存的50%,才能給lucene的file system cache留下足夠的內(nèi)存。
  4. jvm heap size設(shè)置不要超過32GB,否則jvm無法啟用compressed oops,將對象指針進行壓縮,確認日志里有[node-1] heap size [1007.3mb], compressed ordinary object pointers [true] 字樣出現(xiàn)。
  5. 最佳實踐數(shù)據(jù):heap size設(shè)置的小于zero-based compressed ooops,也就是26GB,但是有時也可以是30GB。通過-XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode開啟對應(yīng),確認有heap address: 0x00000000e0000000, size: 27648 MB, Compressed Oops mode: 32-bit字樣,而不是heap address: 0x00000000f4000000, size: 28672 MB, Compressed Oops with base: 0x00000000f3ff0000字樣。

swapping問題

部署Elasticsearch的服務(wù)盡可能關(guān)閉到swap,如果內(nèi)存緩存到磁盤上,那查詢效率會由微秒級降到毫秒級,會造成性能急劇下降的隱患。

關(guān)閉辦法:

  1. Linux系統(tǒng)執(zhí)行 swapoff -a 關(guān)閉swap,或在/etc/fstab文件中配置。

  2. elasticsearch.yml中可以設(shè)置:bootstrap.mlockall: true 鎖住自己的內(nèi)存不被swap到磁盤上。

使用命令 GET _nodes?filter_path=**.mlockall 可以查看是否開啟mlockall
響應(yīng)信息:

{
  "nodes": {
    "A1s1uus7TpuDSiT4xFLOoQ": {
      "process": {
        "mlockall": true
      }
    }
  }
}

Elasticsearch啟動的幾個問題

  1. root用戶啟動實例的問題
    如果你用root用戶啟動Elasticsearch的實例,將得到如下的錯誤提示:
org.elasticsearch.bootstrap.StartupException: java.lang.RuntimeException: can not run elasticsearch as root
    at org.elasticsearch.bootstrap.Elasticsearch.init(Elasticsearch.java:140) ~[elasticsearch-6.3.1.jar:6.3.1]
    at org.elasticsearch.bootstrap.Elasticsearch.execute(Elasticsearch.java:127) ~[elasticsearch-6.3.1.jar:6.3.1]
    at org.elasticsearch.cli.EnvironmentAwareCommand.execute(EnvironmentAwareCommand.java:86) ~[elasticsearch-6.3.1.jar:6.3.1]
    at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:124) ~[elasticsearch-cli-6.3.1.jar:6.3.1]
    at org.elasticsearch.cli.Command.main(Command.java:90) ~[elasticsearch-cli-6.3.1.jar:6.3.1]
    at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:93) ~[elasticsearch-6.3.1.jar:6.3.1]
    at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:86) ~[elasticsearch-6.3.1.jar:6.3.1]
Caused by: java.lang.RuntimeException: can not run elasticsearch as root
    at org.elasticsearch.bootstrap.Bootstrap.initializeNatives(Bootstrap.java:104) ~[elasticsearch-6.3.1.jar:6.3.1]
    at org.elasticsearch.bootstrap.Bootstrap.setup(Bootstrap.java:171) ~[elasticsearch-6.3.1.jar:6.3.1]
    at org.elasticsearch.bootstrap.Bootstrap.init(Bootstrap.java:326) ~[elasticsearch-6.3.1.jar:6.3.1]
    at org.elasticsearch.bootstrap.Elasticsearch.init(Elasticsearch.java:136) ~[elasticsearch-6.3.1.jar:6.3.1]
    ... 6 more

無它,建立一個用戶,專門用來啟動Elasticsearch的,如esuser,相應(yīng)的系統(tǒng)目錄和數(shù)據(jù)存儲目錄都賦予esuser賬戶為歸屬者。

  1. 啟動時提示elasticsearch process is too low,并且無法啟動成功

完整的提示信息:

max file descriptors [4096] for elasticsearch process is too low, increase to at least [65536]
memory locking requested for elasticsearch process but memory is not locked

解決辦法:設(shè)置系統(tǒng)參數(shù),命令行中的esuser為建立的Linux用戶。

[root@elasticsearch01 bin]# vi /etc/security/limits.conf

# 在文件最后添加
esuser hard nofile 65536
esuser soft nofile 65536
esuser soft memlock unlimited
esuser hard memlock unlimited

設(shè)置完成后,可以通過命令查看結(jié)果:

# 請求命令
GET _nodes/stats/process?filter_path=**.max_file_descriptors

# 響應(yīng)結(jié)果
{
  "nodes": {
    "A1s1uus7TpuDSiT4xFLOoQ": {
      "process": {
        "max_file_descriptors": 65536
      }
    }
  }
}
  1. 提示vm.max_map_count [65530] is too low錯誤,無法啟動實例

完整的提示信息:

max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

解決辦法:添加vm.max_map_count配置項

臨時設(shè)置:sysctl -w vm.max_map_count=262144

永久修改:修改vim /etc/sysctl.conf文件,添加vm.max_map_count設(shè)置

[root@elasticsearch01 bin]# vim /etc/sysctl.conf

# 在文件最后添加
vm.max_map_count=262144

# 執(zhí)行命令
[root@elasticsearch01 bin]# sysctl -p

Elasticsearch實例啟停

實例一般使用后臺啟動的方式,在ES的bin目錄下執(zhí)行命令:

[esuser@elasticsearch01 bin]$ nohup ./elasticsearch &
[1] 15544
[esuser@elasticsearch01 bin]$ nohup: 忽略輸入并把輸出追加到"nohup.out"

這個elasticsearch沒有stop參數(shù),停止時使用kill pid命令。

[esuser@elasticsearch01 bin]$ jps | grep Elasticsearch
15544 Elasticsearch
[esuser@elasticsearch01 bin]$ kill -SIGTERM 15544

發(fā)送一個SIGTERM信號給elasticsearch進程,可以優(yōu)雅的關(guān)閉實例。

小結(jié)

本篇接著上篇的內(nèi)容,講解了集群重啟時要注意的問題,JVM Heap設(shè)置的最佳實踐,以及Elasticsearch實例啟動時常見的問題解決辦法,最后是Elasticsearch優(yōu)雅關(guān)閉的命令。

專注Java高并發(fā)、分布式架構(gòu),更多技術(shù)干貨分享與心得,請關(guān)注公眾號:Java架構(gòu)社區(qū)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容