1. 程式人生 > >Elasticsearch系列---生產叢集部署(下)

Elasticsearch系列---生產叢集部署(下)

### 概要 本篇繼續講解Elasticsearch叢集部署的細節問題 ### 叢集重啟問題 如果我們的Elasticsearch叢集做了一些離線的維護操作時,如擴容磁碟,升級版本等,需要對叢集進行啟動,節點數較多時,從第一個節點開始啟動,到最後一個節點啟動完成,耗時可能較長,有時候還可能出現某幾個節點因故障無法啟動,排查問題、修復故障後才能加入到叢集中,此時叢集會幹什麼呢? 假設10個節點的叢集,每個節點有1個shard,升級後重啟節點,結果有3臺節點因故障未能啟動,需要耗費時間排查故障,如下圖所示: ![](https://imgkr.cn-bj.ufileos.com/db1333d5-6722-4730-b3f1-e6da4cd9118e.png) 整個過程步驟如下: 1. 叢集已完成master選舉(node6),master發現未加入叢集的node1、node2、node3包含的shard丟失,便立即發出shard恢復的指令。 2. 線上的7臺node,將其中一個replica shard升級為primary shard,並且進行為這些primary shard複製足夠的replica shard。 3. 執行shard rebalance操作。 4. 故障的3臺節點已排除,啟動成功後加入叢集。 5. 這3臺節點發現自己的shard已經在叢集中的其他節點上了,便刪除本地的shard資料。 6. master發現新的3臺node沒有shard資料,重新執行一次shard rebalance操作。 這個過程可以發現,多做了四次IO操作,shard複製,shard首次移動,shard本地刪除,shard再次移動,這樣憑空造成大量的IO壓力,如果資料量是TB級別的,那費時費力不討好。 出現此類問題的原因是節點啟動的間隔時間不能確定,並且節點越多,這個問題越容易出現,如果可以設定叢集等待多少個節點啟動後,再決定是否對shard進行移動,這樣IO壓力就能小很多。 針對這個問題,我們有下面幾個引數: - gateway.recover_after_nodes:叢集必須要有多少個節點時,才開始做shard恢復操作。 - gateway.expected_nodes: 叢集應該有多少個節點 - gateway.recover_after_time: 叢集啟動後等待的shard恢復時間 如上面的案例,我們可以這樣設定: ```java gateway.recover_after_nodes: 8 gateway.expected_nodes: 10 gateway.recover_after_time: 5m ``` 這三個引數的含義:叢集總共有10個節點,必須要有8個節點加入叢集時,才允許執行shard恢復操作,如果10個節點未全部啟動成功,最長的等待時間為5分鐘。 這幾個引數的值可以根據實際的叢集規模來設定,並且只能在`elasticsearch.yml`檔案裡設定,沒有動態修改的入口。 上面的引數設定合理的情況,叢集啟動是沒有shard移動的現象,這樣叢集啟動的時候就可以由之前的幾小時,變成幾秒鐘。 ### JVM和Thread Pool設定 一提到JVM的調優,大家都有手癢的感覺,好幾百個JVM引數,說不定開啟了正確的按鈕,從此ES踏上高效能、高吞吐量的道路。現實情況可能是我們想多了,ES的大部分引數是經過反覆論證的,基本上不用咱們太操心。 #### JVM GC Elasticsearch預設使用的垃圾回收器是CMS。 ```java ## GC configuration -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly ``` CMS回收器是併發式的回收器,能夠跟應用程式工作執行緒併發工作,最大程度減少垃圾回收時的服務停頓時間。 CMS還是會有兩個停頓階段,同時在回收特別大的heap時也會有一些問題。儘管有一些缺點,但是CMS對於要求低延時請求響應的軟體來說,還是最佳的垃圾回收器,因此官方的推薦就是使用CMS垃圾回收器。 有一種最新的垃圾回收器叫做G1。G1回收器可以比CMS提供更少的回收停頓時間,而且能夠這對大heap有更好的回收表現。它會將heap劃分為多個region,然後自動預測哪個region會有最多可以回收的空間。通過回收那些region,就可以最小化停頓時長,而且可以針對大heap進行回收。 聽起來還挺好的,只是G1還是比較年輕的一種垃圾回收器,而且經常會發現一些新的bug,這些bug可能會導致jvm掛掉。穩定起見,暫時不用G1,等G1成熟後ES官方推薦後再用不遲。 #### 執行緒池 我們開發Java應用系統時,對系統調優的一個常見手段就是調整執行緒池,但在ES中,預設的threadpool設定是非常合理的,對於所有的threadpool來說,除了搜尋的執行緒池,都是執行緒數量設定的跟cpu core一樣多的。如果我們有8個cpu core,那麼就可以並行執行8個執行緒。那麼對於大部分的執行緒池來說,分配8個執行緒就是最合理的數量。 搜尋會有一個更加大的threadpool,執行緒數量一般被配置為:cpu core * 3 / 2 + 1。 Elasticsearch的執行緒池分成兩種:接受請求的執行緒和處理磁碟IO操作的執行緒,前面那種由ES管理,後一種由Lucene管理,它們之間會進行協作,ES的執行緒不會因為IO操作而block住,所以ES的執行緒設定跟CPU核數一樣或略大於CPU核數即可。 伺服器的計算能力是非常有限的,執行緒池的數量過大會導致上下文頻繁切換,更費資源,如果threadpool大小設定為50,100,甚至500,會導致CPU資源利用率很低,效能反而下降。 只需要記住:用預設的執行緒池,如果真要修改,以CPU核數為準。 ### heap記憶體設定最佳實踐 Elasticsearch預設的jvm heap記憶體大小是2G,如果是研發環境,我會改成512MB,但在生產環境2GB有點少。 在config/jvm.options檔案下,可以看到heap的設定: ```java # Xms represents the initial size of total heap space # Xmx represents the maximum size of total heap space -Xms2g -Xmx2g ``` #### 分配規則 Elasticsearch使用記憶體主要有兩個大戶:jvm heap和lucene,前者ES用來存放很多資料結構來提供更快的操作效能,後者使用os cache快取索引檔案,包括倒排索引、正排索引,os cache記憶體是否充足,直接影響查詢檢索的效能。 一般的分配規則是:jvm heap佔用小於一半的記憶體,剩下的全歸lucene使用。 如果單臺機器總記憶體64GB,那麼heap頂格記憶體分配為32GB,因為32GB記憶體以下,jvm會使用compressed oops來解決object pointer耗費過大空間的問題,超過32GB後,jvm的compressed oops功能關閉,這樣就只能使用64位的object pointer,會耗費更多的空間,過大的object pointer還會在cpu,main memory和LLC、L1等多級快取間移動資料的時候,吃掉更多的頻寬。最終的結果可能是50GB記憶體的效果和32GB一樣,白白浪費了十幾GB記憶體。 這裡涉及到jvm的object pointer指標壓縮技術,有興趣可以單獨瞭解一下。 如果單臺機器總記憶體小於64GB,一般heap分配為總記憶體的一半即可,具體要看預估的資料量是多少。 如果使用超級機器,1TB記憶體的那種,官網不建議上那麼牛逼的機器,建議分配4-32GB記憶體給heap,其他的全部用來做os cache,這樣資料量全部快取在記憶體中,不落盤查詢,效能槓槓滴。 #### 最佳實踐建議 1. 將heap的最小值和最大值設定為一樣大。 2. elasticsearch jvm heap設定得越大,就有越多的記憶體用來進行快取,但是過大的jvm heap可能會導致長時間的gc停頓。 3. jvm heap size的最大值不要超過實體記憶體的50%,才能給lucene的file system cache留下足夠的記憶體。 4. jvm heap size設定不要超過32GB,否則jvm無法啟用compressed oops,將物件指標進行壓縮,確認日誌裡有`[node-1] heap size [1007.3mb], compressed ordinary object pointers [true]` 字樣出現。 5. 最佳實踐資料:heap size設定的小於zero-based compressed ooops,也就是26GB,但是有時也可以是30GB。通過-XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode開啟對應,確認有`heap address: 0x00000000e0000000, size: 27648 MB, Compressed Oops mode: 32-bit`字樣,而不是`heap address: 0x00000000f4000000, size: 28672 MB, Compressed Oops with base: 0x00000000f3ff0000`字樣。 #### swapping問題 部署Elasticsearch的服務儘可能關閉到swap,如果記憶體快取到磁碟上,那查詢效率會由微秒級降到毫秒級,會造成效能急劇下降的隱患。 關閉辦法: 1. Linux系統執行 `swapoff -a` 關閉swap,或在/etc/fstab檔案中配置。 2. elasticsearch.yml中可以設定:`bootstrap.mlockall: true` 鎖住自己的記憶體不被swap到磁碟上。 使用命令 `GET _nodes?filter_path=**.mlockall` 可以檢視是否開啟mlockall 響應資訊: ```java { "nodes": { "A1s1uus7TpuDSiT4xFLOoQ": { "process": { "mlockall": true } } } } ``` ### Elasticsearch啟動的幾個問題 1. root使用者啟動例項的問題 如果你用root使用者啟動Elasticsearch的例項,將得到如下的錯誤提示: ```java 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,相應的系統目錄和資料儲存目錄都賦予esuser賬戶為歸屬者。 2. 啟動時提示elasticsearch process is too low,並且無法啟動成功 完整的提示資訊: ```java 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 ``` 解決辦法:設定系統引數,命令列中的esuser為建立的Linux使用者。 ```java [root@elasticsearch01 bin]# vi /etc/security/limits.conf # 在檔案最後新增 esuser hard nofile 65536 esuser soft nofile 65536 esuser soft memlock unlimited esuser hard memlock unlimited ``` 設定完成後,可以通過命令檢視結果: ```java # 請求命令 GET _nodes/stats/process?filter_path=**.max_file_descriptors # 響應結果 { "nodes": { "A1s1uus7TpuDSiT4xFLOoQ": { "process": { "max_file_descriptors": 65536 } } } } ``` 3. 提示vm.max_map_count [65530] is too low錯誤,無法啟動例項 完整的提示資訊: ```java max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144] ``` 解決辦法:新增`vm.max_map_count`配置項 臨時設定:`sysctl -w vm.max_map_count=262144` 永久修改:修改`vim /etc/sysctl.conf`檔案,新增`vm.max_map_count`設定 ```java [root@elasticsearch01 bin]# vim /etc/sysctl.conf # 在檔案最後新增 vm.max_map_count=262144 # 執行命令 [root@elasticsearch01 bin]# sysctl -p ``` ### Elasticsearch例項啟停 例項一般使用後臺啟動的方式,在ES的bin目錄下執行命令: ```java [esuser@elasticsearch01 bin]$ nohup ./elasticsearch & [1] 15544 [esuser@elasticsearch01 bin]$ nohup: 忽略輸入並把輸出追加到"nohup.out" ``` 這個elasticsearch沒有stop引數,停止時使用`kill pid`命令。 ```java [esuser@elasticsearch01 bin]$ jps | grep Elasticsearch 15544 Elasticsearch [esuser@elasticsearch01 bin]$ kill -SIGTERM 15544 ``` 傳送一個SIGTERM訊號給elasticsearch程序,可以優雅的關閉例項。 ### 小結 本篇接著上篇的內容,講解了叢集重啟時要注意的問題,JVM Heap設定的最佳實踐,以及Elasticsearch例項啟動時常見的問題解決辦法,最後是Elasticsearch優雅關閉的命令。 專注Java高併發、分散式架構,更多技術乾貨分享與心得,請關注公眾號:Java架構社群 可以掃左邊二維碼新增好友,邀請你加入Java架構社群微信群共同探討技術 ![Java架構社群](https://img2020.cnblogs.com/blog/1834889/202003/1834889-20200303074927076-17248626