kakakakakku blog

Weekly Tech Blog: Keep on Learning!

Amazon Elasticsearch Service で「手動スナップショット」を取得する

自動スナップショットと手動スナップショット

Amazon Elasticsearch Service でクラスタのスナップショットを取得する場合,「自動スナップショット」と「手動スナップショット」の2種類がある.

AWS 側で自動的に日次取得してくれるものを「自動スナップショット」と呼ぶ.14世代保管される.ただし「自動スナップショット」のリストアは自分たちではできず,AWS サポートに依頼して実施してもらう必要がある.よって,リストアのタイミングが AWS サポート側に依存することになり,運用的には自由度が低いものとなる.

逆に「手動スナップショット」は,Elasticsearch 自体のスナップショット機能を使って,任意のタイミングでスナップショットの取得とリストアができるため,自由度が高いものとなる.また EC2 上で運用している Elasticsearch クラスタのスナップショットを取得して,Amazon Elasticsearch Service にリストアすることで,移行としても使える.

詳しくはドキュメントに記載されているが,日本語訳が変で「スナップショットを取る」と「スナップショットを取得する」と訳すべきところが「スナップショットを撮る」と「スナップショットを撮影する」になっていて,どうしても読みながら気になってしまった.フィードバックしたら修正してもらえるのかな?

docs.aws.amazon.com

www.elastic.co

手動スナップショットを試してみた

最近 Amazon Elasticsearch Service で運用しているクラスタが落ちたことがあって,AWS サポートに依頼して「自動スナップショット」からのリストアをお願いした.そのときに「手動スナップショットはしてますか?」や「手動スナップショットがあるとリストアも素早くできますよ」的なコミュニケーションがあって,AWS 的にも自由度が高い「手動スナップショット」を推奨しているのかなという雰囲気を感じて,一度試してみようと思った.

前提

  • Amazon Elasticsearch Service ドメイン(クラスタ)が構築済であること
  • エンドポイントは全て ${amazon_es_endpoint} で記載している

事前準備(ドキュメントを登録する)

事前にサンプルドキュメントを1件登録する.

$ curl -s -XPUT ${amazon_es_endpoint}/blog/articles/1 -d '
quote> {
quote>   "title": "xxx",
quote>   "body": "yyy",
quote>   "tags": ["z1", "z2"]
quote> }
quote> '
{"_index":"blog","_type":"articles","_id":"1","_version":1,"_shards":{"total":2,"successful":1,"failed":0},"created":true}%

$ curl -s ${amazon_es_endpoint}/blog/articles/1 | jq .
{
  "_index": "blog",
  "_type": "articles",
  "_id": "1",
  "_version": 1,
  "found": true,
  "_source": {
    "title": "xxx",
    "body": "yyy",
    "tags": [
      "z1",
      "z2"
    ]
  }
}

IAM 設定をする

スナップショットを S3 に保存するため,IAM で少し複雑な設定をする必要がある.詳しくは上に載せたドキュメントに書いてあるので,ここでは箇条書きに留めておく.

  • スナップショットを保存する S3 に対するアクセス許可をした IAM ポリシーを作成する
  • ロールタイプを EC2 にした IAM ロールを作成し,Amazon Elasticsearch Service に対する権限と作成した IAM ポリシーをアタッチする

リポジトリ登録をする

Amazon Elasticsearch Service で手動スナップショットを取得する場合,S3 に保存することになるが,事前に S3 をスナップショットのリポジトリとして登録する必要がある.さらに登録するときに AWS のリクエスト署名が必要になるため,curl から API を叩くことができず,SDK を経由する必要がある.今回はドキュメントに書いてある Python (boto) のサンプルコードを使った.これは面倒すぎる...!

実際に実行するときは以下の項目を書き換える必要がある.

  • region
  • host
  • aws_access_key_id
  • aws_secret_access_key
  • path
  • bucket
  • role_arn
from boto.connection import AWSAuthConnection

class ESConnection(AWSAuthConnection):

    def __init__(self, region, **kwargs):
        super(ESConnection, self).__init__(**kwargs)
        self._set_auth_region_name(region)
        self._set_auth_service_name("es")

    def _required_auth_capability(self):
        return ['hmac-v4']

if __name__ == "__main__":

    client = ESConnection(
            region='us-east-1',
            host='search-weblogs-etrt4mbbu254nsfupy6oiytuz4.us-east-1.es.a9.com',
            aws_access_key_id='my-access-key-id',
            aws_secret_access_key='my-access-key', is_secure=False)

    print 'Registering Snapshot Repository'
    resp = client.make_request(method='POST',
            path='/_snapshot/weblogs-index-backups',
            data='{"type": "s3","settings": { "bucket": "es-index-backups","region": "us-east-1","role_arn": "arn:aws:iam::123456789012:role/MyElasticsearchRole"}}')
    body = resp.read()
    print body

実行したところ,以下のような結果が返ってきた.

$ python snapshot.py
Registering Snapshot Repository
{"acknowledged":true}

手動スナップショットを取得する

これで,やっと準備が整ったので,手動スナップショットの取得ができる.取得自体は簡単で,以下の API をコールする.今回は2回スナップショットを取得してみた.

$ curl -XPUT ${amazon_es_endpoint}/_snapshot/backups/snapshot_1
{"accepted":true}%

$ curl -XPUT ${amazon_es_endpoint}/_snapshot/backups/snapshot_2
{"accepted":true}%

取得後にスナップショットの一覧を確認することもできる.

$ curl -s ${amazon_es_endpoint}/_snapshot/backups/_all | jq .
{
  "snapshots": [
    {
      "snapshot": "snapshot_1",
      "version_id": 2030299,
      "version": "2.3.2",
      "indices": [
        "blog"
      ],
      "state": "SUCCESS",
      "start_time": "2016-12-17T08:03:25.067Z",
      "start_time_in_millis": 1481961805067,
      "end_time": "2016-12-17T08:03:26.174Z",
      "end_time_in_millis": 1481961806174,
      "duration_in_millis": 1107,
      "failures": [],
      "shards": {
        "total": 1,
        "failed": 0,
        "successful": 1
      }
    },
    {
      "snapshot": "snapshot_2",
      "version_id": 2030299,
      "version": "2.3.2",
      "indices": [
        "blog"
      ],
      "state": "SUCCESS",
      "start_time": "2016-12-17T08:07:18.741Z",
      "start_time_in_millis": 1481962038741,
      "end_time": "2016-12-17T08:07:19.207Z",
      "end_time_in_millis": 1481962039207,
      "duration_in_millis": 466,
      "failures": [],
      "shards": {
        "total": 1,
        "failed": 0,
        "successful": 1
      }
    }
  ]
}

S3 でもスナップショットが取得されていることを確認できた(S3 は新 UI に変更済).

f:id:kakku22:20161217180035p:plain

手動スナップショットをリストアする

次にリストアを試す.1点注意点があって,Amazon Elasticsearch Service は _close API をサポートしていないため,リストアするインデックスを事前に削除した状態でないとリストアできないという制約がある.まず最初に,インデックスが存在する状態でリストアをしてみたところ,以下のように 500 が返ってきた.

$ curl -s -XPOST ${amazon_es_endpoint}/_snapshot/backups/snapshot_1/_restore | jq .
{
  "error": {
    "root_cause": [
      {
        "type": "snapshot_restore_exception",
        "reason": "[backups:snapshot_1] cannot restore index [blog] because it's open"
      }
    ],
    "type": "snapshot_restore_exception",
    "reason": "[backups:snapshot_1] cannot restore index [blog] because it's open"
  },
  "status": 500
}

よって,インデックスを削除して,ドキュメントがクエリできないことを確認した.

$ curl -XDELETE ${amazon_es_endpoint}/_all
{"acknowledged":true}%

$ curl -s ${amazon_es_endpoint}/blog/articles/1 | jq .
{
  "error": {
    "root_cause": [
      {
        "type": "index_not_found_exception",
        "reason": "no such index",
        "resource.type": "index_expression",
        "resource.id": "blog",
        "index": "blog"
      }
    ],
    "type": "index_not_found_exception",
    "reason": "no such index",
    "resource.type": "index_expression",
    "resource.id": "blog",
    "index": "blog"
  },
  "status": 404
}

改めてリストアすると,成功して,ドキュメントもクエリできた.

$ curl -s -XPOST ${amazon_es_endpoint}/_snapshot/backups/snapshot_1/_restore | jq .
{"accepted": true}

$ curl -s ${amazon_es_endpoint}/blog/articles/1 | jq .
{
  "_index": "blog",
  "_type": "articles",
  "_id": "1",
  "_version": 1,
  "found": true,
  "_source": {
    "title": "xxx",
    "body": "yyy",
    "tags": [
      "z1",
      "z2"
    ]
  }
}

まとめ

クラスタの運用を考えると Amazon Elasticsearch Service の「手動スナップショット」は重要で,今回一通りの手順を試してみた.

感想としては「面倒な部分が多いな...」という印象で,管理コンソールと API 経由でもっと簡単にスナップショットを管理したいし,S3 のリポジトリ登録も管理コンソールと API 経由で行いたいし,もっと言うと「自動スナップショット」を AWS サポートに依頼せずともリストアできたら便利なんだけどなーとも思う.今後のアップデートに期待する!