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

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

### 概要 本篇開始介紹Elasticsearch生產叢集的搭建及相關引數的配置。 ### ES叢集的硬體特性 我們從開始程式設計就接觸過各種各樣的元件,而每種功能的元件,對硬體要求的特性都不太相同,有的需要很強的CPU計算能力,有的對記憶體需求量大,有的對網絡卡要求高等待,下面我們討論一下ES叢集對幾種硬體的特性需求。 #### CPU ES叢集對CPU的要求相對低一些,畢竟純計算的比重要小一些,選用主流的CPU,2核到8核的都可以。 如果有兩種CPU可以挑選,一種是主頻高但核數少的CPU,另一種是主頻一般核數多的CPU,肯定選後一種,因為多核的CPU可以提供更多的併發處理能力,遠比單核高效能帶來的效益要高。 #### 記憶體 ES叢集對記憶體的要求很高,部署ES叢集時,要把大部分資源投入到記憶體當中。記憶體分配主要有兩部分,JVM heap記憶體(堆記憶體)和OS Cache記憶體。 JVM heap記憶體用得不多,主要是OS Cache,我們知道,ES建立的倒排索引,正排索引,過濾器快取,都是優先放在記憶體當中的,OS Cache的大小直接決定搜尋的效能,如果OS Cache不夠,ES搜尋等操作只有被迫讀硬碟,延時就會從毫秒級升到秒級。 OS Cache具體在多大才算夠,取決於資料量,如果是百萬級別的資料,16GB左右應該可以接受,如果是億級,一般單節點都是64GB記憶體。生產環境最低要求記憶體應不低於8GB。 #### 硬碟 硬碟成本本身比較便宜,能用SSD就用SSD,訪問速度肯定比機械硬碟快,預估好資料量後就儘可能多規劃一些容量。 另外儘量使用本地儲存,網路儲存還依賴於網路傳輸,這個容易造成一些延遲。 #### 網路 對ES叢集這種分散式系統來說,快速並且可靠的網路還是比較重要的,shard的分配和rebalance都需要佔用大量的頻寬,叢集最好部署在同一個區域網內,異地容災等跨資料中心的部署方案,要考慮到網路故障帶來的影響。 #### JVM選擇 使用ES官網推薦的JDK版本,服務端和客戶端儘量使用同一個版本的JDK。 涉及到ES服務端的JVM調優設定,保持原樣不要輕易改動,畢竟ES已經花了大量人力物力驗證過的,隨意調整jvm引數可能適得其反。 #### 容量規劃 規劃叢集裡,要規劃好投入幾臺伺服器,資料量上限是多少,業務模型資料讀寫的比例是多少,歷史資料的遷移方案等,一般來說,百萬到10億內的資料量,使用ES叢集還是能夠支撐下來的,ES節點數建議不要超過100個。 舉個例子:資料量10億以內,部署5臺伺服器,8核64GB記憶體,是能夠支撐的。 ### 生產案例模擬 #### Linux作業系統搭建 我們使用Linux虛擬機器來演示一個生產ES叢集的搭建。我們建立4臺虛擬機器,每臺2核CPU,4GB記憶體,作業系統為CentOS 7 64bit。 虛擬機器我用的是VMware workstation,有用virtual box也行,CentOS 7、JDK的安裝不贅述。記得把CentOS的防火牆關了。 修改每臺機器的hostname資訊,命令 `vi /etc/hostname`,修改檔案,儲存即可,建議修改成elasticsearch01,elasticsearch02,elasticsearch03,elasticsearch04。 假定我們4臺虛擬機器的域名和IP是這樣分配的: ```java 192.168.17.138 elasticsearch01 192.168.17.137 elasticsearch02 192.168.17.132 elasticsearch03 192.168.17.139 elasticsearch04 ``` 把這段配置放在 `/etc/hosts`檔案末尾,4臺機器做相同的配置。 這4臺機器之間,可以配置免密登入,如在elasticsearch01機器上,我們執行以下操作: 1. 生成公鑰檔案,命令: ```java ssh-keygen -t rsa ``` 一直輸入回車,不要設定密碼預設會將公鑰放在/root/.ssh目錄下生成id_rsa.pub和id_rsa兩個檔案 2. 拷貝公鑰檔案 ```java cp id_rsa.pub authorized_keys ``` 3. 將公鑰檔案拷貝到另外三臺機器 ```java ssh-copy-id -i elasticsearch02 ssh-copy-id -i elasticsearch03 ssh-copy-id -i elasticsearch03 ``` 拷貝完成後,可以在目標機器上`/root/.ssh/`目錄下看到多了一個authorized_keys檔案。 4. 嘗試免密登入,在elasticsearch01機器上輸入`ssh elasticsearch02`,如果不需要輸入密碼就能登入到elasticsearch02,說明配置成功,其他機器類似。 這4臺機器也可以相互做ssh免密設定。 這裡補充一點免密登入的方向性問題,上面的案例是在elasticsearch01機器生成的公鑰,並且傳送給了elasticsearch02等三臺機器,那麼我從elasticsearch01跳到elasticsearch02是不需要密碼的,反過來從elasticsearch02登入到elasticsearch01,還是需要密碼的。 最後補充幾個常用檢查命令: - 檢查NetManager的狀態:systemctl status NetworkManager.service - 檢查NetManager管理的網路介面:nmcli dev status - 檢查NetManager管理的網路連線:nmcli connection show #### Elasticsearch服務端 這裡選用的JDK版本為1.8.0_211,Elasticsearch版本為6.3.1,自行安裝不贅述。 ES解壓後的目錄結構: ```java # 用 "tree -L 1" 命令得到的樹狀結構 . ├── bin ├── config ├── lib ├── LICENSE.txt ├── logs ├── modules ├── NOTICE.txt ├── plugins └── README.textile ``` - bin:存放es的一些可執行指令碼,比如用於啟動程序的elasticsearch命令,以及用於安裝外掛的elasticsearch-plugin外掛 - config:用於存放es的配置檔案,比如elasticsearch.yml - logs:用於存放es的日誌檔案 - plugins:用於存放es的外掛 - data:用於存放es的資料檔案的預設目錄,就是每個索引的shard的資料檔案,一般會另外指定一個目錄。 ### Elasticsearch引數設定 在config目錄下的檔案,包含了ES的基本配置資訊: ```java . ├── elasticsearch.yml ├── jvm.options ├── log4j2.properties ├── role_mapping.yml ├── roles.yml ├── users └── users_roles ``` #### 預設引數 Elasticsearch的配置項比較豐富並且預設配置已經非常優秀了,基本上我們需要改動的是跟伺服器環境相關的配置,如IP地址,叢集名稱,資料儲存位置,日誌儲存位置等外圍引數,涉及到內部機制及JVM引數的,一般不干預,不恰當的JVM引數調整反而會導致叢集出現效能故障,如果沒有充足的理由或資料驗證結果,不要輕易嘗試修改。 #### 叢集和節點名稱 在elasticsearch.yml檔案裡這項配置表示叢集名稱,配置項預設是註釋掉的,叢集名稱預設為elasticsearch。 `#cluster.name: my-application` 這個配置項強烈建議開啟,用專案約定的命名規範進行重新命名,並且將研發環境、測試環境、STG準生產環境、生產環境分別命名,如elasticsearch_music_app_dev表示研發環境,elasticsearch_music_app_sit表示測試環境,elasticsearch_music_app_pro表示生產環境等。避免開發測試環境連錯環境,無意中加入叢集導致資料問題。 `cluster.name: elasticsearch_music_app_pro` 節點名稱的配置項 `#node.name: node-1` 預設也是註釋掉的,ES啟動時會分配一個隨機的名稱,建議還是自行分配一個名稱,這樣容易記住是哪臺機器,如 `node.name: es_node_001_data` #### 檔案路徑 涉及到檔案路徑的幾個引數,主要有資料、日誌、外掛等,預設這幾個地址都是在Elasticsearch安裝的根目錄下,但Elasticsearch升級時,有些目錄可能會有影響,安全起見,可以單獨設定目錄。 ```java # # ----------------------------------- Paths ------------------------------------ # # Path to directory where to store the data (separate multiple locations by comma): # #path.data: /path/to/data # # Path to log files: # #path.logs: /path/to/logs # ``` 例如我們可以在`/var`目錄下建立相應的資料夾,並且賦予相應的讀寫許可權,如: ```java path.data: /var/es/data path.logs: /var/es/logs ``` #### 日誌檔案配置 log4j2.properties檔案,ES日誌框架選用的是log4j2,也就是log4j的進化版本,對Java技術棧熟悉的童鞋,看到這個配置檔案會非常熟悉,預設的日誌輸入配置、格式均能滿足日常的故障定位和分析,也不需要什麼改動。 預設是一天生成一個日期檔案,如果ES承載的資料量特別大,可以調整日誌檔案產生頻率和每個日誌檔案的大小,以及ES最多儲存日誌的大小、數量。 ### Elasticsearch叢集發現機制 #### 配置引數 Zen Discovery是Elasticsearch叢集發現機制的預設實現,底層通訊依賴transport元件,我們完成Elasticsearch叢集的配置主要有下面幾個引數: - cluster.name 指定叢集的名稱。 - node.name 節點名稱。 - network.host 節點繫結的IP。 - node.master 可選值為true/false,決定該節點型別為master eligible或data node。 - discovery.zen.ping.unicast.hosts gossip路由服務的IP地址,即叢集發現協議通訊的公共節點,可以寫多個,有節點啟動時會向裡面的IP傳送訊息,獲取叢集其他節點的資訊,最後加入叢集。 Elasticsearch叢集是點對點(P2P)的分散式系統架構,資料索引、搜尋操作是node之間直接通訊的,沒有中心式的master節點,但Elasticsearch叢集內的節點也分成master node和data node兩種角色。 正常情況下,Elasticsearch叢集只有一個master節點,它負責維護整個叢集的狀態資訊,叢集的元資料資訊,有新的node加入或叢集內node宕機下線時,重新分配shard,並同步node的狀態資訊給所有的node節點,這樣所有的node節點都有一份完整的cluster state資訊。 叢集發現的一般步驟如下: 1. 節點配置network.host繫結內網地址,配置各自的node.name資訊,cluster.name設定為相同的值。 2. discovery.zen.ping.unicast.hosts配置了幾個gossip路由的node。 3. 所有node都可以傳送ping訊息到路由node,再從路由node獲取cluster state回來。 4. 所有node執行master選舉。 5. 所有node都會跟master進行通訊,然後加入master的叢集。 #### master選舉 node.master設定為true的,將成為master eligible node,也叫master候選節點,只有master eligible node才能被選舉成master node。如果是個小叢集,那麼所有節點都可以是master eligible node,10個節點以上的叢集,可以考慮拆分master node和data node,一般建議master eligible node給3個即可。 master選舉過程是自動完成的,有幾個引數可以影響選舉的過程: - discovery.zen.ping_timeout: 選舉超時時間,預設3秒,網路狀況不好時可以增加超時時間。 - discovery.zen.join_timeout: 有新的node加入叢集時,會發送一個join request到master node,同樣因為網路原因可以調大,如果一次超時,預設最多重試20次。 - discovery.zen.master_election.ignore_non_master_pings:如果master node意外宕機了,叢集進行重新選舉,如果此值為true,那麼只有master eligible node才有資格被選為master。 - discovery.zen.minimum_master_nodes: 新選舉master時,要求必須有多少個 master eligible node去連線那個新選舉的master。而且還用於設定一個叢集中必須擁有的master eligible node。如果這些要求沒有被滿足,那麼master node就會被停止,然後會重新選舉一個新的master。這個引數必須設定為我們的master eligible node的quorum數量。一般避免說只有兩個master eligible node,因為2的quorum還是2。如果在那個情況下,任何一個master候選節點宕機了,叢集就無法正常運作了。 #### 叢集故障探查 有兩種叢集故障探查機制 1. master主動對叢集中所有的其他node發起ping命令,判斷它們是否是存活著的。 2. 每個node向master node傳送ping請求,判斷master node是否存活,否則就會發起一個選舉過程。 有下面三個引數用來配置叢集故障的探查過程: - ping_interval:ping一次node的間隔時間,預設是1s - ping_timeout:每次ping的timeout等待時長,預設是30s - ping_retries:對node的ping請求失敗了,重試次數,預設3次。 #### 叢集狀態更新 master node是叢集中唯一可以對cluster state進行更新的node。更新的步驟如下: 1. master node收到更新事件,如shard移動,可能會有多條事件,但master node一次只處理一個叢集狀態的更新事件。 2. master node將事件更新到本地,併發布publish message到叢集所有的node上。 3. node接收publish message後,對這個message返回ack響應,但是不會立即更新。 4. 如果master沒有在指定的時間內(discovery.zen.commit_timeout配置項,預設是30s),從至少N個節點(discovery.zen.minimum_master_nodes配置項)獲取ack響應,那麼這次cluster state change事件就會被reject,最終不會被提交。 5. 如果在指定時間內,指定數量的node都返回了ack訊息,那麼cluster state就會被commit,然後master node把 commit message傳送給所有的node。所有的node接收到那個commit message之後,接著才會將之前接收到的叢集狀態應用到自己本地的狀態副本中去。 6. master會等待所有node的commit message 的ack訊息,在一個等待超時時長內,如果接收到了響應,表示狀態更新成功,master node繼續處理記憶體queue中儲存的下一個更新事件。 discovery.zen.publish_timeout預設是30s,這個超時等待時長是從plublish cluster state開始計算的。 我們可以參照此圖: ![](https://imgkr.cn-bj.ufileos.com/b296199f-a391-4304-a2b4-2ed4ea09680b.png) #### master node宕機問題 Elasticsearch叢集中,master node扮演著非常重要的角色,如果master node宕機了,那豈不是群龍無首了?雖然有master選舉,但這個也是要時間的,沒有master node那段空檔期叢集該怎麼辦? 說了一半,基本上是完了,但我們也可以設定,群龍無首時哪些操作可以做,哪些操作不能做。 discovery.zen.no_master_block配置項可以控制在群龍無首時的策略: - all: 一旦master宕機,那麼所有的操作都會被拒絕。 - write:預設的選項,所有寫操作都會被拒絕,但是讀操作是被允許的。 #### split-brain(腦分裂問題) 在Elasticsearch叢集中,master node非常重要,並且只有一個,相當於整個叢集的大腦,控制將整個叢集狀態的更新,如果Elasticsearch叢集節點之間出現區域性的網路中斷,比如10個節點的Elasticsearch叢集,4臺node部署在機房A區,6臺node部署在機房B區,如果A區與B區的交換機故障,導致兩個區隔離開來了,那麼沒有master node的那個區,會觸發master選舉,如果選舉了新的master,那麼整個叢集就會出現兩個master node,這種現象叫做腦分裂。 ![](https://imgkr.cn-bj.ufileos.com/d4baf564-2f42-4b25-ba96-51d98edac4b3.png) 這樣現象很嚴重,會破壞叢集的資料,該如何避免呢? 回到我們前面提到的`discovery.zen.minimum_master_nodes`引數,這個值的正確設定,可以避免上述的腦分裂問題。 `discovery.zen.minimum_master_nodes`引數表示至少需要多少個master eligible node,才可以成功地選舉出master,否則不進行選舉。 足夠的master eligible node計算公式: `quorum = master_eligible_nodes / 2 + 1` 如上圖我們10個node的叢集,如果全部是master eligible node,那麼quorum = 10/2 + 1 = 6。 如果我們有3個master eligible node,7個data node,那麼quorum = 3/2 + 1 = 2。 如果叢集只有2個節點,並且全是master eligible node,那麼quorum = 2/2 + 1 = 2,問題就來了,如果隨便一個node宕機,在只剩下一個node情況下,無法滿足quorum的值,master永遠選舉不成功,叢集就徹底無法寫入了,所以只能設定成1,後果是隻要這兩個node之間網路斷了,就會發生腦分裂的現象。 所以一個Elasticsearch叢集至少得有3個node,全部為master eligible node的話,quorum = 3/2 + 1 = 2。如果我們設定minimum_master_nodes=2,分析一下會不會出現腦分裂的問題。 場景一:A區一個node,為master,B區兩個node,為master eligible node ![](https://imgkr.cn-bj.ufileos.com/80cdc4b1-07f3-4627-8d29-7fa4df226ac9.png) A區因為只剩下一個node,無法滿足quorum的條件,此時master取消當前的master角色,且無法選舉成功。 B區兩個master eligible node,滿足quorum條件,成功選舉出master。 此時叢集還是隻有一個master,待網路故障恢復後,叢集資料正常。 場景二:A區一個node,為master eligible node,B區2個node,其中一個是master ![](https://imgkr.cn-bj.ufileos.com/e217630d-6cd7-451d-a46d-fc77d684f202.png) A區只有一個master eligible node,不滿足quorum的條件,無法進行選舉。 B區原本的master存在,不需要進行選舉,並且滿quorum的條件,master角色可以保留。 此時叢集還是一個master,正常。 綜上所述:3個節點的叢集,全部為master eligible node,配置discovery.zen.minimum_master_nodes: 2,就可以避免腦裂問題的產生。 ##### minimum_master_nodes動態修改 因為叢集是可以動態增加和下線節點的,quorum的值也會跟著改變。minimum_master_nodes引數值需要通過api隨時修改的,特別是在節點上線和下線的時候,都需要作出對應的修改。而且一旦修改過後,這個配置就會持久化儲存下來。 修改api請求如下: ```java PUT /_cluster/settings { "persistent" : { "discovery.zen.minimum_master_nodes" : 2 } } ``` 響應報文: ```java { "acknowledged": true, "persistent": { "discovery": { "zen": { "minimum_master_nodes": "2" } } }, "transient": {} } ``` 也可以通過命令查詢當前的配置: `GET /_cluster/settings` 響應結果如下: ```java { "persistent": { "discovery": { "zen": { "minimum_master_nodes": "1" } } }, "transient": {} } ``` ##### 留一個問題 上圖10個節點的叢集,假設全是master eligible node,按照上述的網路故障,會不會出現腦分裂現象 ?配置項minimum_master_nodes最低要配置成多少,才不會出現腦分裂的問題? ### 小結 本篇主要介紹了Elasticsearch叢集的部署和引數設定等知識,大部分都不需要人工干預,預設值已經是最優選,叢集發現機制和master選舉機制瞭解一下就OK。 專注Java高併發、分散式架構,更多技術乾貨分享與心得,請關注公眾號:Java架構社群 可以掃左邊二維碼新增好友,邀請你加入Java架構社群微信群共同探討技術 ![Java架構社群](https://img2020.cnblogs.com/blog/1834889/202003/1834889-20200303074927076-17248626