【AWS】Amplifyで構築したOpenSearchのシングルノード復旧手順 - スナップショットからのリストア方法

## 1. はじめに
AWSのAmplifyでOpenSearchを使用する際に、開発用のコストをケチってシングルノードにした結果、データが消えてしまったため、自動バックアップからの復旧手順について解説します。

- 想定読者：Amplify + OpenSearchを使用して、開発環境のOpenSearchがシングルノード構成の方
- この記事で解決できる問題：シングルノードでのデータ消失時の復旧（14日以内）

※3年前にまとめていたのですが、最近また、この手順を参照することがあったので、記事にします。

### 1-1. 前提条件
今回の記事は、Amplifyで構築した環境となり、DynamoDBからDynamoストリームでOpenSearchへ連携する構成となっております。
Amplifyで構築しているため、OpenSearchの自動バックアップで14日分のスナップショットが保持されています。

- 14日を過ぎている場合は、スナップショットからの復元はできません
- 全てのデータ障害で、復旧できるわけではございません。実際スナップショットから復旧できないケースもございました
  - 上記のようなケースの場合は、DynamoDBのデータを使って復旧しましょう

## 2. 背景
### 2-1. OpenSearchを使う理由
Amplifyで爆速に開発したいと思って取り組んだものの、実際のプロジェクトでは、以下のような理由でAmazon OpenSearchの利用が必要になることがあります

- DynamoDBの設計が難しさ
  - 特にRDBの経験者にとって、DynamoDBの設計思想への適応が課題
  - シングルテーブル設計などの新しい概念の習得が必要
- 高度な検索要件への対応
  - 管理画面での全項目検索など、柔軟な検索機能の実装が必要
  - DynamoDBだけでは実現が困難なケースが存在

### 2-2. コストの課題
OpenSearchの本番ワークロードでは、データノード3台、マスターノード3台が推奨構成となり、コストが課題になります。
そのため、本番以外の環境ではOpenSearchをシングルノード構成で運用することがあると思います。
そこで出てくるのが、「ノード障害時のデータ消失」のリスクになります。

## 3. 復旧手順
### 3-1. 事前準備
今回はCloudShellを使って作業を行なっていきます。

#### 3-1-1. CloudShellのIPアドレス確認
AWSのマネコンからCloudShellを起動し、下記コマンドでIPアドレスを確認してください。
```
curl https://ifconfig.io

123.45.67.89  // 例
```
IPアドレスが返されますので、控えましょう。

#### 3-1-2. OpenSearchのアクセスポリシー修正
Amazon OpenSearch Serviceの対象ドメインへの接続の許可設定をします。

1. 対象のドメインの詳細へ移動
2. アクション＞セキュリティ設定の編集へ移動
3. ポリシーを更新して、特定のIPから接続ができる設定をしましょう。IPはCloudShellのIPを利用します
```
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": "es:*",
      "Resource": "arn:aws:es:ap-northeast-1:XXXXXXXXXXXX:domain/amplify-opense-XXXXXXXXXXXX/*",
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": "123.45.67.89"
        }
      }
    }
  ]
}
```

### 3-2. リストア作業
まずは、スナップショットの確認をしていきます。
スナップショットがない場合は、データの復旧はできません。

以降は、CloudShell上でコマンドを実行していきます。
```
// スナップショットの確認
$ curl -XGET 'https://XXXXXXXXXXXXXXXXX.ap-northeast-1.es.amazonaws.com/_snapshot?pretty'

{
  "cs-automated" : {
    "type" : "s3"
  }
}
```
cs-automatedは、自動バックアップで作成されたバックアップになります。

詳細を確認していきましょう。
```
// スナップショットの確認
$ curl -XGET 'https://XXXXXXXXXXXXXXXXX.ap-northeast-1.es.amazonaws.com/_snapshot/cs-automated/_all?pretty'

{
  "snapshots" : [ {
  ・・・ 
  }, {
    "snapshot" : "2024-12-06t22-11-45.2621d2fe-1b71-46aa-ac97-XXXXXXXXXX",
    "uuid" : "CRd6BbxJR5qoHq********",
    "version_id" : 6020399,
    "version" : "6.2.3",
    "indices" : [ "todo", "comment" ],
    "include_global_state" : true,
    "state" : "SUCCESS",
    "start_time" : "2024-12-06T22:11:45.620Z",
    "start_time_in_millis" : 1733490705620,
    "end_time" : "2024-12-06T22:11:51.844Z",
    "end_time_in_millis" : 1733490711844,
    "duration_in_millis" : 6224,
    "failures" : [ ],
    "shards" : {
      "total" : 15,
      "failed" : 0,
      "successful" : 15
    }
  }, {
    "snapshot" : "2024-12-06t23-11-45.2621d2fe-1b71-46aa-ac97-XXXXXXXXXX",
    "uuid" : "CRd6BbxJR5qoHq********",
    "version_id" : 6020399,
    "version" : "6.2.3",
    "indices" : [ ],
    "include_global_state" : true,
    "state" : "SUCCESS",
    "start_time" : "2024-12-06T23:11:45.620Z",
    "start_time_in_millis" : 1733494305620,
    "end_time" : "2024-12-06T23:11:51.844Z",
    "end_time_in_millis" : 1733494311844,
    "duration_in_millis" : 6224,
    "failures" : [ ],
    "shards" : {
      "total" : 15,
      "failed" : 0,
      "successful" : 15
    }
  } ]
}
// データはダミーです
```
1時間毎、14日分のスナップショットが確認できます。
ざっとみていくと、`indices`が空になるタイミングで障害が起きたことがわかります。

その直前のスナップショットで復元していきましょう。
すでにインデックスを復旧させてしまっている場合は、同名での復元はできませんので、削除します。
```
問題のあるインデックスを削除(※同名で復元しようとするため)
$ curl -XDELETE 'https://XXXXXXXXXXXXXXXXX.ap-northeast-1.es.amazonaws.com/{削除対象のインデックス名}'

// 例
$ curl -XDELETE 'https://XXXXXXXXXXXXXXXXX.ap-northeast-1.es.amazonaws.com/todo'
$ curl -XDELETE 'https://XXXXXXXXXXXXXXXXX.ap-northeast-1.es.amazonaws.com/comment'
```
同名のインデックスがないことを確認したら、復元していきます。

```
// スナップショットからの復元
$ curl -XPOST 'https://XXXXXXXXXXXXXXXXX.ap-northeast-1.es.amazonaws.com/_snapshot/{スナップショットのリポジトリ名}/{スナップショット名}/_restore' -d '{"indices": "問題となったインデックス名"}' -H 'Content-Type: application/json'

// 例
$ curl -XPOST 'https://XXXXXXXXXXXXXXXXX.ap-northeast-1.es.amazonaws.com/_snapshot/cs-automated/2024-12-06t22-11-45.2621d2fe-1b71-46aa-ac97-XXXXXXXXXX/_restore' -d '{"indices": [ "todo", "comment" ]}' -H 'Content-Type: application/json'
```
一気に全てのインデックスを復元しようとすると、タイムアウトになり復元が途中で止まってしまう場合がございます。
その場合は、再度インデックスを削除して、1つずつ復元することをお試しください。

### 3-3. 動作確認
リストア後は、データが復元されていることを確認しましょう。

確認後、「3-1-2. OpenSearchのアクセスポリシー修正」で許可した、CloudShellからのアクセスは塞いでおきましょう。


## 4. まとめ
今回は、シングルノードのOpenSearchのリストア手順を紹介しました。
この構成は以下の点に注意して利用することをお勧めします

- シングルノード構成は、あくまでも開発環境などコスト制約のある環境での利用を想定
- 本番ワークロードでは、データ損失のリスクが大きいため、推奨構成（マルチノード）を採用すべき
- 自動スナップショットの保持期間は14日間のため、早期発見が重要

データのバックアップは重要な安全策ですが、「スナップショットがあるから安心」という考えで満足せず、実際の復旧手順を理解し、定期的に確認することが重要です。障害は予期せぬタイミングで発生するものです。その時に慌てることなく対応できるよう、今のうちから準備しておきましょう。