スナップショットとリストアをやってみる

RDBと同じようにデータのバックアップとリストアをやってみました。

次のdocker-compose.ymlを使って環境構築しています。

version: '3.9'
services:
  es01:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.0.0
    ports:
      - 9200:9200
      - 9300:9300
    environment:
      - discovery.type=single-node
      - action.destructive_requires_name=true
      - xpack.security.enabled=false
      - path.repo=/usr/share/elasticsearch/backup
    networks:
      - elastic
    volumes:
      - backup:/usr/share/elasticsearch/backup
    deploy:
      resources:
        limits:
          memory: 2G
  ki01:
    image: docker.elastic.co/kibana/kibana:8.0.0
    environment:
      ELASTICSEARCH_HOSTS: "http://es01:9200"
    ports:
      - 5601:5601
    networks:
      - elastic
    deploy:
      resources:
        limits:
          memory: 2G
volumes:
  backup:
    driver: local
networks:
  elastic:
    driver: bridge

elasticsearchが8.0.0になりましたが、デフォルトでセキュリティの機能が有効化されているので、OFFにしています。ONにしてあると色々と設定してあげないとkibanaが接続できませんでした。ローカルの開発以外では必ずONにするべきだとは思います。

バックアップ

バックアップ先に指定できるものはいくつか種類があるようです。ここでは共有ファイルシステムでやってみます。

https://www.elastic.co/guide/en/elasticsearch/reference/current/snapshots-filesystem-repository.html

dockerで動かしているので、volumeを作成/アタッチしバックアップ先のディレクトリとして利用します。

elasticsearchのコンテナの環境変数 path.repo に、バックアップ先のディレクトリとしてこのvolumeを指定します。この設定がないと、バックアップ先として設定できないので注意が必要です。

次のリクエストでリポジトリを作り、バックアップ先として登録します。

PUT _snapshot/repository_1 <-- ①
{
  "type": "fs",
  "settings": {
    "location": "/usr/share/elasticsearch/backup", <-- ②
    "compress": true
  }
}

① repository_1 はリポジトリの名前なので、自由に決められます。

② バックアップ先に利用するパスを指定します。path.repo に指定がないディレクトリは使えません。

バックアップする対象を作成したいので、一件ドキュメントを保存します。

PUT demo-index

POST demo-index/_doc
{
  "name": "bookstore"
}

次のリクエストでスナップショットを作成できます。

PUT _snapshot/repository_1/snapshot_1?wait_for_completion=true
{
  "indices": "demo-index" <-- ①
}

① スナップショットに含めるインデックスを指定します。 * にすると、全インデックスのスナップショットを作成できます。

レスポンスは次の通りです。

{
  "snapshot" : {
    "snapshot" : "snapshot_1",
    "uuid" : "R-S8paawT0mUdt2f3UjvPg",
    "repository" : "repository_1",
    "version_id" : 8000099,
    "version" : "8.0.0",
    "indices" : [ <-- ①
      ".kibana_8.0.0_001",
      "demo-index",
      ".apm-custom-link",
      ".fleet-policies-7",
      ".kibana_task_manager_8.0.0_001",
      ".apm-agent-configuration",
      ".geoip_databases"
    ],
    "data_streams" : [ ],
    "include_global_state" : true,
    "state" : "SUCCESS",
    "start_time" : "2022-02-20T11:33:20.796Z",
    "start_time_in_millis" : 1645356800796,
    "end_time" : "2022-02-20T11:33:21.997Z",
    "end_time_in_millis" : 1645356801997,
    "duration_in_millis" : 1201,
    "failures" : [ ],
    "shards" : {
      "total" : 7,
      "failed" : 0,
      "successful" : 7
    },
    "feature_states" : [
      {
        "feature_name" : "geoip",
        "indices" : [
          ".geoip_databases"
        ]
      },
      {
        "feature_name" : "fleet",
        "indices" : [
          ".fleet-policies-7"
        ]
      },
      {
        "feature_name" : "kibana",
        "indices" : [
          ".kibana_8.0.0_001",
          ".apm-custom-link",
          ".apm-agent-configuration",
          ".kibana_task_manager_8.0.0_001"
        ]
      }
    ]
  }
}

① スナップショットに含まれるインデックス。指定したもの以外も入ってますが、この辺りはまだよくわかってません。

リストア

バックアップができたところで、リストアを試してみたいと思います。

https://www.elastic.co/guide/en/elasticsearch/reference/current/snapshots-restore-snapshot.html

リストアは、作成したスナップショットを元にインデックスを作成する操作になります。

なので、バックアップ時にやっていたように、リポジトリやスナップショットがelasticsearchに設定されていなければなりません。

今回のケースではそのまま同じDockerコンテナでリストアを試そうと思うので、リポジトリとスナップショットはバックアップ時に作成したものをそのまま使用できます。もし、別のコンテナやインスタンスにリストアをする場合には、バックアップデータを元にリポジトリを設定します。

リポジトリやスナップショットの確認は次のように行います。

// リポジトリの確認
GET _snapshot/repository_1

// スナップショットの確認
GET _snapshot/repository_1/snapshot_1

ワイルドカードを使って検索を行うこともできます。

GET _snapshot/*/*

リストアは、次のように行います。

POST _snapshot/repository_1/snapshot_1/_restore
{
  "indices": "demo-index"
}

URLは _snapshot/<リポジトリ名>/<スナップショット名>/_restore になります。

ボディではリストアするインデックスを指定します。

ただ、この場合だと同じ名前でインデックスをリストアしようとするために、同名のインデックスが既に存在するとエラーとなります。

{
  "error" : {
    "root_cause" : [
      {
        "type" : "snapshot_restore_exception",
        "reason" : "[repository_1:snapshot_1/TUBO3UFDTwWbi199smmt6A] cannot restore index [demo-index] because an open index with same name already exists in the cluster. Either close or delete the existing index or restore the index under a different name by providing a rename pattern and replacement name"
      }
    ],
    "type" : "snapshot_restore_exception",
    "reason" : "[repository_1:snapshot_1/TUBO3UFDTwWbi199smmt6A] cannot restore index [demo-index] because an open index with same name already exists in the cluster. Either close or delete the existing index or restore the index under a different name by providing a rename pattern and replacement name"
  },
  "status" : 500
}

リストアする時にインデックス名を変更するには、次のように、元のインデックス名から文字を一部分切り抜き、新しいインデックス名と連結させます。

https://www.elastic.co/guide/en/elasticsearch/reference/current/snapshots-restore-snapshot.html#rename-on-restore

POST _snapshot/repository_1/snapshot_1/_restore
{
  "indices": "demo-index",
  "rename_pattern": "(.+)",
  "rename_replacement": "restored_index_$1"
}

この場合、次のように、リストアが行われました。

GET restored_index_demo-index/_search
{
  "query": {
    "match_all": {}
  }
}

// レスポンス
{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "restored_index_demo-index",
        "_id" : "ZdevNH8BGshAomOEMFkj",
        "_score" : 1.0,
        "_source" : {
          "name" : "bookstore"
        }
      }
    ]
  }
}