背景
對于生産環境中産生的資料,可能會存在于不同的ES叢集,同時随着業務疊代、資料規劃改變等各種原因,可能會需要對現存ES中的資料進行遷移。
遷移方式分類
資料的遷移從操作方面可以分為以下一些主流的方式
分類 | 方式 | ES版本 | 人工幹預 | 使用門檻 | 備注 |
---|---|---|---|---|---|
自動遷移 | 設定ILM配合nodeattr | 7.x以上 | 否 | 低 | 可能會存在license問題 |
半自動遷移 | 不同節點分片/副本移動 | 不限 | 是 | 中 | 需要規劃allocate順序 |
手動遷移 | elasticsearch-dump | 不限 | 是 | 中 | github位址 |
手動遷移 | snapshot+restore | 6.x以上 | 是 | 中 | 需要所有節點都注冊共享存儲 |
手動遷移 | 自研腳本 | 不限(注意版本相容) | 是 | 高 | 自由度高開發成本高 |
遷移注意事項
- 遷出叢集和遷入叢集的版本相容問題
- 不同版本的ES可能會存在索引結構不相容
- 如2.x中支援的多type,在6.x裡面被廢棄,到7裡會建議隻使用
_doc
- 如2.x中支援的多type,在6.x裡面被廢棄,到7裡會建議隻使用
- 不同版本間的叢集、索引、字段配置不相容
- 如2.x中不索引的方式是
,到了6.x裡變成了"index":"not_analyze"
"index":false
- 如2.x中不索引的方式是
- 資料字段種類不相容的情況
- 如2.x中的
在6.x以上被分為了string
和text
keyword
- 如2.x中的
- snapshot + restore方式隻能從低版本的叢集往高版本的叢集裡遷移
- 不同版本的ES插件會指明适用ES版本,如果版本之間差異過大會出現不相容
- 對于不同版本的用戶端
- 低版本的用戶端中有些接口在高版本中被廢棄
- 高版本用戶端會預設在query裡拼上一些低版本不支援的參數
- 不同版本的ES可能會存在索引結構不相容
- 遷出索引自身配置及配套配置
- 一般索引會包含
、settings
等,在遷移的時候要注意上面提到的版本相容的問題mappings
- 除了索引自身的配置之外,還要考慮在建立索引的時候是否預設了包括 pipeline (
)和 template (GET http://ip:port/_ingest/pipeline
)在内的資料預處理配置GET http://ip:9200/_template
- 普通的 pipeline 和 template 可能會包含通配符對包括資料字段、index名稱等進行條件篩選,是以有可能會存在 pipeline 互相引用和 template 互相覆寫(繼承)的問題
- 一般索引會包含
- 資料是否需要雙寫
- 資料挎叢集雙寫甚至多寫的時候需要考慮是否存在資料異構的問題
- 比如雙寫2.x和6.x的叢集,2.x中的多type在6.x裡是不支援的
- 有些資料配置、甚至之前使用的client可能不相容,需要差別處理
- 資料雙寫中的資料一緻性問題
- 資料雙寫帶來的額外的網絡、磁盤等開銷
- 資料雙寫中 consumer/sinker 間的資料競争
- 資料挎叢集雙寫甚至多寫的時候需要考慮是否存在資料異構的問題
- 資料是否需要挎叢集讀取
- 存量資料是否需要應對檢索
- 增量資料是否需要應對檢索
- 當同一條資料同時出現在源/目标索引中,是否需要進行去重
- 大量資料遷移的時候會對叢集産生負載,需要優先保證搜尋的穩定性還是優先保證資料導出/導入的速度
遷移手順
Snapshot方案
- 在機器上挂載nfs盤(其他共享存儲請參考官網文檔)
- 安裝nfs相關軟體
-
=> ubuntuapt update && apt install nfs-common
-
=> centosyum install -y nfs-common
- …
-
- 建立挂載點檔案夾
mkdir /mnt/disk1
- 修改挂載配置
vi /etc/fstab
- 挂載磁盤
mount -a
- (可選)在挂載磁盤中建立子目錄
mkdir /mnt/disk1/logs
- 安裝nfs相關軟體
- 把對應的共享路徑配置進ES裡(如果要使用子路徑,需要把子路徑也注冊在config檔案裡)
echo 'path.repo: ["/mnt/disk1"]' >> /$ES_HOME/config/elasticsearch.yml
- 在ES叢集中進行 rolling restart 使配置生效
- (可選)關閉部分不需要的索引
curl -X POST http://ip:port/$index/_close
- (可選)把索引分片挪出待重新開機節點,把副本關掉
curl -X PUT -H 'content-type: application/json;charset=UTF-8' -d '{ "settings":{ "index.routing.allocation.exclude._name": "$node_name", "index.routing.allocation.exclude._ip_": "$node_ip", "index.number_of_replicas": 0 } }' http://ip:port/$index/_settings
- 檢視分片狀态
⬆️正在遷移的分片顯示為curl -X GET http://ip:port/_cat/shards
$old_index_name $shard_num $shard_type RELOCATING $doc_num $index_size $old_node_ip $old_node_name -> $new_node_ip $new_index_id $new_node_name ==> index1 6 p RELOCATING 3929901 3gb 192.168.100.1 node-121 -> 192.168.100.2 fjfF3dYOQgGMNHL1g08Few node-103
- 沒有分片在待遷移節點上之後,開始重新開機
- 檢查一下目标程序
ps -ef | grep elasticsearch | grep -v grep dev 16043 1 19 2019 ? 68-12:37:06 /home/javen/jdk1.8.0_171/bin/java -Xms16g -Xmx16g -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -Des.networkaddress.cache.ttl=60 -Des.networkaddress.cache.negative.ttl=10 -XX:+AlwaysPreTouch -Xss1m -Djava.awt.headless=true -Dfile.encoding=UTF-8 -Djna.nosys=true -XX:-OmitStackTraceInFastThrow -Dio.netty.noUnsafe=true -Dio.netty.noKeySetOptimization=true -Dio.netty.recycler.maxCapacityPerThread=0 -Dlog4j.shutdownHookEnabled=false -Dlog4j2.disable.jmx=true -Djava.io.tmpdir=/tmp/elasticsearch-946212112948700879 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=data -XX:ErrorFile=logs/hs_err_pid%p.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -Xloggc:logs/gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=32 -XX:GCLogFileSize=64m -Des.path.home=/$ES_HOME -Des.path.conf=/$ES_HOME/config -Des.distribution.flavor=default -Des.distribution.type=tar -cp /$ES_HOME/lib/* org.elasticsearch.bootstrap.Elasticsearch -d dev 16069 16043 0 2019 ? 00:00:00 /$ES_HOME/modules/x-pack-ml/platform/linux-x86_64/bin/controller
- 殺程序重新開機(opt1)
ps -ef | grep elasticsearch | grep -v grep | awk '{ print $2 }' | xargs kill -15
./$ES_HOME/bin/elasticsearch -d
- 服務重新開機(opt2)
sudo systemctl restart elasticsearch
- 檢查一下目标程序
- (可選)關閉部分不需要的索引
- 把共享存儲路徑注冊為snapshot倉庫
- 注冊存儲倉庫(如果這裡的location需要被注冊在config檔案裡)
⚠️ location:剛才我們注冊的共享存儲的路徑,需要叢集中所有節點都能通路到的共享存儲位置`curl -X PUT -H 'content-type: application/json;charset=UTF-8' -d '{ "type": "fs", "settings": { "location": "/mnt/disk1" } }' http://ip:port/_snapshot/es_backup
- 檢查是否成功,正常的傳回是
通過指令檢視注冊狀态,正常結果應該和我們設定(PUT)進去的一緻{ "acknowledged" : true }
結果為:curl -X GET /_snapshot/es_backup
{ "project" : { "type" : "fs", "settings" : { "location" : "/mnt/disk1" } } }
- 注冊存儲倉庫(如果這裡的location需要被注冊在config檔案裡)
- 建立存儲
- 執行指令:
curl -X PUT -H 'content-type: application/json;charset=UTF-8' -d '{ "indices": "index_1,index_2,index_tag_*", "ignore_unavailable": true, "include_global_state": false, "metadata": { "taken_by": "steven", "taken_because": "backup before migration" } }' http://ip:port/_snapshot/es_backup/my_backup_20200820
- indices:需要遷移的索引名稱,可以支援通配符
- ignore_unavailable:忽略不可用索引
- include_global_state:包含全局配置(包括索引模闆之類的,但是這裡需要考慮,如果新叢集中索引模闆需要修改建議這裡設為false)
- metadata:制作這個snapshot的一些說明,不會影響資料
- 校驗一下資料制作結果
curl -X GET http://ip:port/_snapshot/es_backup/my_backup_20200820 { "snapshots" : [ { "snapshot" : "my_backup_20200820", "uuid" : "m-uZwk7zTOiPzT0FO-hVLw", "version_id" : 6080099, "version" : "6.8.0", "indices" : [ "index_1", "index_2", "index_tag_1", "index_tag_2" ], "include_global_state" : false, "state" : "IN_PROGRESS", "start_time" : "2020-08-20T03:14:08.517Z", "start_time_in_millis" : 1597893248517, "end_time" : "1970-01-01T00:00:00.000Z", "end_time_in_millis" : 0, "duration_in_millis" : -1597893248517, "failures" : [ ], "shards" : { "total" : 0, "failed" : 0, "successful" : 0 } } ] }
- indices:這個snapshot裡包含的索引,需要檢查裡面是不是包含了你想要的所有索引(
)GET _cat/indices
- state:snapshot建立狀态
- IN_PROGRESS:正在進行
- 可以通過添加
來等他進行完wait_for_completion=true
-
PUT /_snapshot/es_backup/my_backup_20200820?wait_for_completion=true
- 可以通過添加
- PARTIAL:部分完成
- IN_PROGRESS:正在進行
- failures:snapshot建立失敗的清單
{ "index" : "index1", "index_uuid" : "index1", "shard_id" : 4, "reason" : "IndexShardSnapshotFailedException[Failed to snapshot]; nested: ElasticsearchException[failed to create blob container]; nested: AccessDeniedException[/mnt/disk1/logs/indices/0szV2p6oTOCzjNi-r1QnNg/4]; ", "node_id" : "fjfF3dYOQgGMNHL1g08Few", "status" : "INTERNAL_SERVER_ERROR" }
- index:索引名稱
- shard_id:分片編号
- reason:失敗原因
- node_id:叢集節點id
- status:錯誤種類
- start_time/end_time/duration_in_millis:建立的開始、結束、持續時間
- shards:
- total:總共處理的分片數
- failed:失敗的分片數
- successful:成功的分片數
- indices:這個snapshot裡包含的索引,需要檢查裡面是不是包含了你想要的所有索引(
- 執行指令:
- 導出所需pipeline
- 找出需要遷移的pipeline
⚠️ 主要找和索引有直接關系的pipeline,比如帶curl -X GET http://ip:port/_ingest/pipeline
,date_index_name
,set
之類的pipelineuser_agent
- 找出需要遷移的pipeline
- 導出所需索引模闆
- 找出需要遷移的索引模闆,主要關注
裡面能比對到目标索引的模闆index_patterns
⚠️ 索引模闆會根據curl -X GET http://ip:port/_template
從小到大的順序執行,是以多個order
同時比對一個索引時會出現彼此覆寫(或者叫繼承)的問題,需要将相關的 所有 索引模闆都備份出來index_pattern
- 找出需要遷移的索引模闆,主要關注
- 複制(同版本目标叢集)/建立(不同版本目标叢集)目前叢集中所用自定義插件
- 插件目錄為
$ES_HOME/plugins
- 裡面的目錄理論上都是自定義插件,都得做遷移
- 插件目錄為
- 準備snapshot遷移目标叢集環境,操作同導出叢集設定
- 注冊共享存儲
- 加載snapshot配置
- 把剛才備份出來的自定義插件放置在新的節點的
目錄裡$ES_HOME/plugins
- (重新開機)使配置生效
-
建立snapshot倉庫
⚠️ 這裡的倉庫路徑需要和導出的目錄一緻,確定這個叢集的所有節點都能通路到之前導出的資料檔案
⚠️ 自定義插件描述檔案中的ES版本号需與目前ES版本一緻
less $ES_HOME/plugins/${plugin-name}/plugin-descriptor.properties ... elasticsearch.version=$ES_VERSION ...
- 導入剛才備份出來的
和pipeline
index template
- pipeline
curl -X PUT -H 'content-type: application/json;charset=UTF-8' -d '{ "description" : "pipeline description", "processors" : [ ... ] }' http://ip:port/_ingest/pipeline/${pipeline_name}
- index template
curl -X PUT -H 'content-type: application/json;charset=UTF-8' -d '{ "order": 0, "index_patterns":[*], "settings": {}, "mappings": {}, "aliaes": {} }' http://ip:port/_template${template_name}
- pipeline
- 恢複(導入)剛才建立的snapshot
- 運作指令
curl -X POST -H 'content-type: application/json;charset=UTF-8' -d '{ "indices": "index_1", "ignore_unavailable": true, "include_global_state": false, "rename_pattern": "index_(.+)", "rename_replacement": "restored_index_$1", "include_aliases": false, "index_settings": { "index.number_of_replicas": 0 }, "ignore_index_settings": [ "index.refresh_interval" ] }' /_snapshot/es_backup/my_backup_20200820/_restore
- indices:指定需要恢複的索引名稱,支援通配符
- ignore_unavailable:忽略不可用索引,如目前叢集已存在索引之類的
- include_global_state:包含全局設定,作用見導出
- rename_pattern:如果需要對導入索引進行重命名,則通過這裡的正則進行元素擷取
- rename_replacement:配合
使用,将其比對出來的元素組合成新的索引名稱rename_pattern
- include_aliases:是否包含索引别名
- ignore_index_settings:忽略原有索引中的配置
-
index_settings:将裡面的設定覆寫掉原有的索引設定
⚠️ 這裡不支援修改分片數,如果要修改需要通過
接口shrink
- 檢查索引是否恢複完成
- 運作指令
- 運作指令