天天看點

ES 大規模資料遷移

背景

對于生産環境中産生的資料,可能會存在于不同的ES叢集,同時随着業務疊代、資料規劃改變等各種原因,可能會需要對現存ES中的資料進行遷移。

遷移方式分類

資料的遷移從操作方面可以分為以下一些主流的方式

分類 方式 ES版本 人工幹預 使用門檻 備注
自動遷移 設定ILM配合nodeattr 7.x以上 可能會存在license問題
半自動遷移 不同節點分片/副本移動 不限 需要規劃allocate順序
手動遷移 elasticsearch-dump 不限 github位址
手動遷移 snapshot+restore 6.x以上 需要所有節點都注冊共享存儲
手動遷移 自研腳本 不限(注意版本相容) 自由度高開發成本高

遷移注意事項

  1. 遷出叢集和遷入叢集的版本相容問題
    1. 不同版本的ES可能會存在索引結構不相容
      1. 如2.x中支援的多type,在6.x裡面被廢棄,到7裡會建議隻使用

        _doc

    2. 不同版本間的叢集、索引、字段配置不相容
      1. 如2.x中不索引的方式是

        "index":"not_analyze"

        ,到了6.x裡變成了

        "index":false

    3. 資料字段種類不相容的情況
      1. 如2.x中的

        string

        在6.x以上被分為了

        text

        keyword

    4. snapshot + restore方式隻能從低版本的叢集往高版本的叢集裡遷移
    5. 不同版本的ES插件會指明适用ES版本,如果版本之間差異過大會出現不相容
    6. 對于不同版本的用戶端
      1. 低版本的用戶端中有些接口在高版本中被廢棄
      2. 高版本用戶端會預設在query裡拼上一些低版本不支援的參數
  2. 遷出索引自身配置及配套配置
    1. 一般索引會包含

      settings

      mappings

      等,在遷移的時候要注意上面提到的版本相容的問題
    2. 除了索引自身的配置之外,還要考慮在建立索引的時候是否預設了包括 pipeline (

      GET http://ip:port/_ingest/pipeline

      )和 template (

      GET http://ip:9200/_template

      )在内的資料預處理配置
    3. 普通的 pipeline 和 template 可能會包含通配符對包括資料字段、index名稱等進行條件篩選,是以有可能會存在 pipeline 互相引用和 template 互相覆寫(繼承)的問題
  3. 資料是否需要雙寫
    1. 資料挎叢集雙寫甚至多寫的時候需要考慮是否存在資料異構的問題
      1. 比如雙寫2.x和6.x的叢集,2.x中的多type在6.x裡是不支援的
      2. 有些資料配置、甚至之前使用的client可能不相容,需要差別處理
    2. 資料雙寫中的資料一緻性問題
    3. 資料雙寫帶來的額外的網絡、磁盤等開銷
    4. 資料雙寫中 consumer/sinker 間的資料競争
  4. 資料是否需要挎叢集讀取
    1. 存量資料是否需要應對檢索
    2. 增量資料是否需要應對檢索
    3. 當同一條資料同時出現在源/目标索引中,是否需要進行去重
  5. 大量資料遷移的時候會對叢集産生負載,需要優先保證搜尋的穩定性還是優先保證資料導出/導入的速度

遷移手順

Snapshot方案

  1. 在機器上挂載nfs盤(其他共享存儲請參考官網文檔)
    1. 安裝nfs相關軟體
      1. apt update && apt install nfs-common

        => ubuntu
      2. yum install -y nfs-common

        => centos
    2. 建立挂載點檔案夾
      mkdir /mnt/disk1
                 
    3. 修改挂載配置
      vi /etc/fstab
                 
    4. 挂載磁盤
      mount -a
                 
    5. (可選)在挂載磁盤中建立子目錄
      mkdir /mnt/disk1/logs
                 
  2. 把對應的共享路徑配置進ES裡(如果要使用子路徑,需要把子路徑也注冊在config檔案裡)
    echo 'path.repo: ["/mnt/disk1"]' >> /$ES_HOME/config/elasticsearch.yml
               
  3. 在ES叢集中進行 rolling restart 使配置生效
    1. (可選)關閉部分不需要的索引
      curl -X POST http://ip:port/$index/_close
                 
    2. (可選)把索引分片挪出待重新開機節點,把副本關掉
      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
                 
    3. 檢視分片狀态
      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
                 
    4. 沒有分片在待遷移節點上之後,開始重新開機
      1. 檢查一下目标程序
        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
                   
      2. 殺程序重新開機(opt1)
        ps -ef | grep elasticsearch | grep -v grep | awk '{ print $2 }' | xargs kill -15
                   
        ./$ES_HOME/bin/elasticsearch -d
                   
      3. 服務重新開機(opt2)
        sudo systemctl restart elasticsearch
                   
  4. 把共享存儲路徑注冊為snapshot倉庫
    1. 注冊存儲倉庫(如果這裡的location需要被注冊在config檔案裡)
      curl -X PUT -H 'content-type: application/json;charset=UTF-8' -d '{
          "type": "fs",
          "settings": {
              "location": "/mnt/disk1"
          }
      }' http://ip:port/_snapshot/es_backup
                 
      ⚠️ location:剛才我們注冊的共享存儲的路徑,需要叢集中所有節點都能通路到的共享存儲位置`
    2. 檢查是否成功,正常的傳回是
      {
          "acknowledged" : true
      }
                 
      通過指令檢視注冊狀态,正常結果應該和我們設定(PUT)進去的一緻
      curl -X GET /_snapshot/es_backup
                 
      結果為:
      {
          "project" : {
              "type" : "fs",
              "settings" : {
                  "location" : "/mnt/disk1"
              }
          }
      }
                 
  5. 建立存儲
    1. 執行指令:
      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的一些說明,不會影響資料
    2. 校驗一下資料制作結果
      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:部分完成
      • 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:成功的分片數
  6. 導出所需pipeline
    1. 找出需要遷移的pipeline
      curl -X GET http://ip:port/_ingest/pipeline
                 
      ⚠️ 主要找和索引有直接關系的pipeline,比如帶

      date_index_name

      set

      user_agent

      之類的pipeline
  7. 導出所需索引模闆
    1. 找出需要遷移的索引模闆,主要關注

      index_patterns

      裡面能比對到目标索引的模闆
      curl -X GET http://ip:port/_template
                 
      ⚠️ 索引模闆會根據

      order

      從小到大的順序執行,是以多個

      index_pattern

      同時比對一個索引時會出現彼此覆寫(或者叫繼承)的問題,需要将相關的 所有 索引模闆都備份出來
  8. 複制(同版本目标叢集)/建立(不同版本目标叢集)目前叢集中所用自定義插件
    1. 插件目錄為

      $ES_HOME/plugins

    2. 裡面的目錄理論上都是自定義插件,都得做遷移
  9. 準備snapshot遷移目标叢集環境,操作同導出叢集設定
    1. 注冊共享存儲
    2. 加載snapshot配置
    3. 把剛才備份出來的自定義插件放置在新的節點的

      $ES_HOME/plugins

      目錄裡
    4. (重新開機)使配置生效
    5. 建立snapshot倉庫

      ⚠️ 這裡的倉庫路徑需要和導出的目錄一緻,確定這個叢集的所有節點都能通路到之前導出的資料檔案

      ⚠️ 自定義插件描述檔案中的ES版本号需與目前ES版本一緻

      less $ES_HOME/plugins/${plugin-name}/plugin-descriptor.properties
       ...
       elasticsearch.version=$ES_VERSION
       ...
                 
  10. 導入剛才備份出來的

    pipeline

    index template

    1. pipeline
      curl -X PUT -H 'content-type: application/json;charset=UTF-8' -d '{
          "description" : "pipeline description",
          "processors" : [
              ...
          ]
      }' http://ip:port/_ingest/pipeline/${pipeline_name}
                 
    2. 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}
                 
  11. 恢複(導入)剛才建立的snapshot
    1. 運作指令
      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

        接口
    2. 檢查索引是否恢複完成
      1. 運作指令

繼續閱讀