概要
info.snapmart.jp
私が運用しているサービスの一つであるSnapmartで障害が発生しました。
原因はAmazon Elasticsearch Service(以下AES)のデータノードが消失しStatusがRedになったためです。
Elasticsearchのデータノードが消失しているので、検索周りで多大な影響が出てしまいました。
この記事では上記の障害を振り返り、類似の問題が発生した場合のトラブルシューティングの一助としたいと思います。
Elasticsearchの基本概念
まずはざっくりElasticsearchの構成で出てくる用語を整理しましょう!
今回の障害の登場人物は以下の通りです。
- クラスタ
- ノード
- インデックス
- ドキュメント
- シャード&レプリカ
詳しい解説は本家ドキュメントとクラスメソッドさんの解説に譲ります。
www.elastic.co
dev.classmethod.jp
障害内容
タイトルの通りAESのデータノードが消失しStatusがRedになったのですが、具体的には以下の問題が発生しました。
日本時間3月8日10時20分:
- 特定のノードに問題が発生。このノードは検索システムで使用しているインデックスのプライマリシャードが割り当てられていた。
- 1のノード(プライマリシャード)とそのレプリカがノードに割り当てられず、クラスターヘルスがRedに
検索システムのプライマリシャードに問題が発生してしまったため、以下のような不具合が発生しました。
- (1/ノード数)%の確率で読み込み/書き込みが失敗
- (1/ノード数)%のデータが消失
対応内容
プライマリシャードが障害により消失してしまったため、復旧しなければなりません。
また、StatusがRedになった場合は、まずは問題を取り除き、ステータスGreenに戻す必要があります。
無闇にクラスタに変更を加えてProcessing状態にすると復旧不可になる可能性があります。
ドメインを再設定する前にAmazon ES、赤のクラスター状態を解決することです。赤のクラスター状態のドメインを再設定すると、問題が複雑化し、状態を解決するまで、設定状態が [Processing (処理中)] のままドメインがスタックする可能性があります。
StatusがRedのインデックスを特定
該当クラスタにアクセスできる環境から以下のコマンドを実行し、インデックスの状態を確認します。
$ curl -X GET http://<endpoint>/_cat/indices?v health status index uuid pri rep docs.count docs.deleted store.size pri.store.size green open index1 p2mhaaaCA4uId7GEZzN-AQ 5 1 12345 0 1.2mb 3.4kb red open index2 GLwW836AOm61FOQp6jIgre 5 1 67890 0 6.7mb 5.6kb
出力結果を確認し、healthがredになっているものが問題が発生しているインデックスです。
スナップショットの確認
消えてしまったデータはスナップショットから復元するしかありません。
以下のドキュメントを参考に復元をしていきます。
スナップショットの情報は以下のコマンドで確認します。
まずはリポジトリから確認します。
$ curl -XGET 'elasticsearch-domain-endpoint/_snapshot?pretty' { "cs-automated" : { "type" : "s3" } }
今回問題となったクラスタでは自動でスナップショットを取る設定をしていました。
そのため1時間ごとに新たなスナップショットが作成されていきます。
1時間ごとのスナップショットの情報を確認します。
$ curl -XGET 'elasticsearch-domain-endpoint/_snapshot/{スナップショットの一覧が見たいリポジトリ名}/_all?pretty' ... , { "snapshot" : "スナップショット名-001", ... "indices" : [ "インデックス一覧" ], "state" : "SUCCESS", "start_time" : "2021-03-08T00:52:06.288Z", "start_time_in_millis" : 1615164726288, "end_time" : "2021-03-08T00:52:39.672Z", "end_time_in_millis" : 1615164759672, "duration_in_millis" : 33384, "failures" : [ ], "shards" : { "total" : 36, "failed" : 0, "successful" : 36 } }, { "snapshot" : "スナップショット名-002", ... "indices" : [ "インデックス一覧" ], "state" : "FAILED", "reason" : "Indices don't have primary shards [問題のインデックス名]", "start_time" : "2021-03-08T01:51:56.473Z", "start_time_in_millis" : 1615168316473, "end_time" : "2021-03-08T01:51:57.678Z", "end_time_in_millis" : 1615168317678, "duration_in_millis" : 1205, "failures" : [ { "index" : "問題のインデックス名", "index_uuid" : "問題のインデックスのuuid", "shard_id" : 1, "reason" : "primary shard is not allocated", "status" : "INTERNAL_SERVER_ERROR" } ], "shards" : { "total" : 36, "failed" : 1, "successful" : 35 } }, ...
上記コマンドで、以下のことがわかります。
- 問題のあったインデックス名とその原因
- 復元対象のスナップショット名
- スナップショットから復元後、どの時点からドキュメントを復旧するか
スナップショットから復元
スナップショットから復元するにはまず、既存の問題のあるインデックスを削除する必要があります(なぜなら、リストアするコマンドを実行すると同一のインデックスを作成しようとしてしまうため、インデックス名が重複してエラーになるためです)。
特定のインデックスのみ削除するコマンドは以下の通りです。
$ curl -XDELETE 'elasticsearch-domain-endpoint/{削除対象のインデックス名}'
続いて、スナップショットを指定して、インデックスを復元します。
$ curl -XPOST 'elasticsearch-domain-endpoint/_snapshot/{スナップショットのリポジトリ名}/{スナップショット名}/_restore' -d '{"indices": "問題となったインデックス名"}' -H 'Content-Type: application/json'
これで2021-03-08T00:52:06.288Zまでのデータが復元できます。 2021-03-08T00:52:06.288Zから先のデータについては、自前で復旧する必要があります。
また、AESでは追加料金なしで自動でスナップショットを取得します。自動スナップショットは1時間ごとにスナップショットを取得し、14日間保持されます。 そのため、インデックスが壊れたとしても、14日までに復元できれば被害を少なくすることができます。 14日間以上保持したい場合や、クラスター間でのデータの移行は手動でスナップショットを取得する必要があります。 今回は自動スナップショットに助けられました...。
注意点
ドキュメントを参考にした一連の流れは、インデックス削除~スナップショット復元まで、検索機能が失われます(インデックスに登録されているドキュメントが消えるため)。
そのため、巨大な検索データの場合は、インデックス削除もスナップショット復元も時間がかかると思うので、スナップショットから復元時に別名で復元し、それをアプリケーションから参照するようにすると良いと思います。
まとめ
Elasticsearchのプライマリシャードがいきなり消失したので非常に驚きました。
今回は「データが消えた場合の対応」であるため、基本に忠実に以下を行います。
- 問題となったインデックスの特定
- スナップショットの取得状況の確認
- 問題が発生前のスナップショットの特定
- スナップショットからデータ復元
データ復旧でのポイントは2点です。「スナップショットを取得しているか、復元できるスナップショットは存在するか」です。
今回がAESの自動スナップショット機能に助けられました。
バックアップは重要ですね...!