記一次ElasticSearch集群災難恢復
1. 背景
生產es集群共12台伺服器,5個索引數據總量為2億,每個索引都有設置replicas=1-3不等。正常情況下12台伺服器down掉一兩台甚至是依次?掛掉過半伺服器都不會有問題。
伺服器配置為8-12核 48-96G內存,由於利用率不高,因此決定下線6台伺服器,在實際操作過程中由於內部溝通問題導致集群數據丟失、索引損壞等一系列問題。
本文對本次災難如何發生、怎樣恢復做下回顧,以期警示大家在對生產環境做操作一定要細緻謹慎、做好備份、避免此類問題發生。以及給萬一出現此類問題的朋友們提供下應對處理此類問題的一種思路。
如有更好的解決方案,歡迎提出,共同探討。
2. 災難發生時間軸
2017-6-09 周五下午——運營將6台伺服器拉出集群?,此時通過域名訪問不到這6台伺服器,但是實際上這6台伺服器仍然處於es集群中,看起來一切正常。
2017-6-12 周一上午9點——經過周末幾天的觀察沒發現問題,運營將6台伺服器下線重裝系統。
2017-6-12 周一上午10點——負責從db更新數據到es的job應用大量報錯(見下圖),開發收到相關報錯信息報警。
整個過程很簡單,暴露的問題也很明顯,如果溝通清楚的話在6台伺服器下線重裝前,開發將該6台伺服器es實例依次停掉,就不會有問題,不過這一切都只是後知後覺了~3.災難分析
3.1. 檢查集群健康狀態?:
3.2. 以索引a_index為例,查看shards情況:
可以看到集群共有12個未分配shards,a_index的0號shard主副共計2個全部丟失,因此3個分片只有2個可用,很確定的是已有數據丟失。查看集群數據總量,2億數據現僅存1.2億~
3.3. 錯誤日誌分析
class java.net.SocketTimeoutExceptionRead timed outnulljava.net.SocketInputStream.socketRead0:-2java.net.SocketInputStream.socketRead:116java.net.SocketInputStream.read:170java.net.SocketInputStream.read:141java.io.BufferedInputStream.fill:246java.io.BufferedInputStream.read1:286java.io.BufferedInputStream.read:345sun.net.www.http.HttpClient.parseHTTPHeader:704sun.net.www.http.HttpClient.parseHTTP:647sun.net.www.protocol.http.HttpURLConnection.getInputStream0:1535sun.net.www.protocol.http.HttpURLConnection.getInputStream:1440java.net.HttpURLConnection.getResponseCode:480com.xxx.xx.search.utils.EsSearcher.bulk:166
通過日誌結合集群狀態很容易得出結論——job通過bulk方式向es集群push數據,試圖訪問失聯的shard(為了表述方便,實際上應該是該shard所在的node),導致timed out~
4. 災難恢復
現在面臨如下幾個問題:
- 數據丟失——既定的事實,好在還可以通過job同步丟失的數據來彌補
- 集群狀態red,索引損壞
- 部分shards失聯導致job同步數據部分失敗大量報錯
因此,如何恢復集群以及索引狀態成為解決這些問題的關鍵,怎樣對客戶端查詢影響最小是難點。
4.1 嘗試恢復方法一——重啟es
寄希望於es會不會有很牛逼的恢復機制,原本倒是也沒有抱太大希望,6台伺服器依次重啟之後果然是沒啥用。
4.2 嘗試恢復方法二——轉移分片
把失聯的shards分配到其它可用的nodes,夢想總是有的,結果卻依舊失敗,分片都沒了談何轉移~
# curl -XPOST "http://ESnode:9200/_cluster/reroute" -d "{ "commands" : [ { "move" : { "index" : "xxx", "shard" : 1, "from_node" : "es_node_one", "to_node" : "es_node_two" } }] }"
4.3 嘗試恢復方法三——索引刪除重建
僅僅是個想法,不可行,2億的數據,刪掉重建要花費大量時間,各客戶端會哭~領導會瘋掉~
無論如何倖存的1.2億數據要保住~
4.4 複製索引之後通過添加別名替換原索引——可行!
感謝es提供的reindex api,詳見:Reindex API | Elasticsearch Reference [5.2] | Elastic
詳細step如下,以a_index為例:
- 創建a_index_copy索引
curl -XPUT 『http://xxxx:9200/a_index_copy/『 -d 『{ 「settings」:{ 「index」:{ 「number_of_shards」:3, 「number_of_replicas」:2 } }}
- 通過reindex api將a_index數據copy到a_index_copy
POST _reindex{ "source": { "index": "a_index" }, "dest": { "index": "a_index_copy", "op_type": "create" }}
- 刪除a_index索引,這個必須要先做,否則別名無法添加
curl -XDELETE "http://xxxx:9200/a_index"
- 給a_index_copy添加別名a_index
curl -XPOST "http://xxxx:9200/_aliases" -d " { "actions": [ {"add": {"index": "a_index_copy", "alias": "a_index"}} ] }"
5. Notes
?. 集群有伺服器down掉,丟失的shard會通過其replicas shard複製並重新分配到其它可用節點。強調「依次」,是因為如果不是大量伺服器同時down掉的情況(嚴謹點來說應該這樣表述:索引的某個shard的所有主副shard均損壞/丟失),es災備機制完全可以應付。
?. 軟負載,客戶端通過域名+軟負載伺服器訪問集群中各伺服器。
?. es集群健康狀態
- green 最健康得狀態,說明所有的分片包括備份都可用。
- yellow 主分片(primary)可用,但是副本分片(replicas)不可用(或者是沒有副本)。
- red 部分的分片可用,表明分片有一部分損壞,有的分片所有主副分片損壞/丟失的情況。此時執行查詢部分數據仍然可以查到。
6. 名詞釋義
- node——節點,伺服器運行的一個es實例,一般情況下一個es集群會有多個node。
- shard——分片,一個shard就是一個Lucene實例,是一個完整的搜索引擎。一個索引可以只包含一個shard,只是一般情況下會用多個分片,可以拆分索引到不同的node上,分擔索引壓力。
- replicas——副本數量,e.g.如果索引Aindex設置shard=2 replicas=2則表示其分片總數=2*(2+1)=6,每條數據都會有3條相同記錄。
推薦閱讀: