ElasticSearch(0.19)で無理やりデータ復旧させた話 | サイバーエージェント 公式エンジニアブログ

こんにちは。Amebaの基幹系インフラ担当している鳥垣です。

 

ユーザーのサービス用途でElasticSearch(0.19.10)を使用しているのですが、先日ElasticSearchの障害で一部のShardが読めなくなってしまいまして、それを力技で無理やり読めるように復旧させたのでその時の奮闘記を記載したいと思います。

 

運用情報

  • 台数:30台
  • CPU:24コア
  • Heap:8GB
  • インデックス数:3
  • 総データ容量:約300GB
  • Shard数:128
  • レプリカ数:2
  • バージョン:0.19.10

※OpenStackの仮想サーバ

 

ホスト障害発生

  • OpenStackのホストサーバがダウンし、ElasticSearchのノードが1台ダウン。
  • Shardの再配置処理が走り、ダウンしたノードが持っていたShardは他ノードに分散される。
  • この時点ではElasticSearchのクラスタステータスはグリーンだった(Headプラグインで確認)。
  • ElasticSearchと連携しているAPIサーバ側からのElasticSearchヘルスチェックをしているのだが、そのチェックがfalseを返していた。

 

ホスト復旧

  • ホストサーバが復旧し、ダウンしていたElasticSearchノードサーバも起動したことを確認。
  • 他のホストサーバにマイグレーションする(またいつホストダウンするかわからないため)。

 

ノード復旧

  • マイグレーション完了後、ElasticSearchのプロセスを起動しクラスタに入れる。
  • Shardの再配置処理が走り、クラスタに無事Joinできたことを確認。
  • クラスタステータスもグリーンになっていることを確認。
  • しかし、APIサーバ側からのElasticSearchヘルスチェックがfalseを返していた。。
  • 以前別環境で運用しているElasticSearchでもAPIサーバ側のヘルスチェックでfalseを返していたときに全ノードを再起動して復旧させたことがあったため、今回も全ノードの再起動を実施する。

 

※これが悲劇の始まりでした。。

 

全台再起動

  • 1号機から順番に再起動を実施。ログファイルにてステータス確認しながら順次再起動していく。
  • 20台目までを再起動したあたりからAPIサーバ側にて遅延発生。
  • 再起動オペレーションを一旦中断。
  • HeadプラグインでElasticSeachのステータスが見れなくなる。。

※Shardの再配置処理が走りまくったせいでクラスタが不安定になりました。。。

  • 再び1号機から順番に全台再起動を実施。
  • Headプラグインでステータスが見れるようになる。
  • APIサーバ側の遅延解消し、ヘルスチェックもtrueを返すようになる。

 

しかし、ここで12個のShardがクラスタに戻らない事象が発生

その時のShard状態のキャプチャがこちらです。

 

 

※プライマリShardまでクラスタから外れてしまい、クラスタに戻ってくれない状態になってしまいました。。

※APIサーバ自体は遅延もおさまり運用できるようになりましたが、データが一部ロストしている状態になってしまいました。。

 

復旧検証

allocateコマンドを使ってunassignedになっているShardの移動を検討。

以下のようなコマンドで移動をやってみました。

curl -XPOST 'http://nodeIP:9200/_cluster/reroute' -d '{
  "commands": [{
  "allocate": {
  "index": "index name",
  "shard": 53,
  "node": "node host name",
  "allow_primary": false
  }
  }]
}'

結果:失敗 

エラーになり移動できませんでした。

 

Shardファイル確認

unassignedになってしまっているShardのファイルがどのように保存されているか確認してみました。

※ここでは3つのインデックスを仮にA,B,Cと呼称。

Aインデックス

Shard番号:53

状態:

  • 3,10,22,29号機にディレクトリを確認
  • ただしファイルがあるのは22号機のみ
  • 3,10,29号機のディレクトリは空状態

ステータスファイル:「A/53/_state/state-93」ファイルの中身

{
"version" : 93,
"primary" : false
}

※レプリカとして認識されていると予測。

 

Bインデックス

Shard番号:51

状態:

  • 27,28号機にディレクトリを確認
  • 両方にデータがあるがファイル内容が異なっている

ステータスファイル:「B/51/_state/state-108」ファイルの中身

{
"version" : 108,
"primary" : false
}

※レプリカとして認識されていると予測 。

※27号機にはステータスファイルはありませんでした。

 

Cインデックス

Shard番号:2,4

状態:

  • 27号機にのみ2番のディレクトリを確認
  • 25号機にのみ4番のディレクトリを確認

ステータスファイル:

「C/2/_state/state-427」ファイルの中身

{
"version" : 427,
"primary" : true
}

「C/4/_state/state-417」ファイルの中身

{
"version" : 417,
"primary" : true
}

※両方ともプライマリとして認識されていると予測

 

無理やり復旧させてみる

上記の状態を踏まえて、それぞれのShardを以下方法で無理やり復旧させてみました。

AインデックスのShard復旧手順

ファイルコピー

※対象ノードのElasticSerchプロセスを最初に停止しておく。

  • 22号機にのみファイルが残っているので、そのファイルをディレクトリのみ残っている3,10号機にコピーする。
  • 29号機のディレクトリは放置しておく(放置しておいても自然に削除されることを検証済み)。

ステータスファイルの書き換え

  • 22号機のみ「   "primary"   : false 」の箇所を「true」に変更する。
  • 3,10号機は「false」のままにする。

※上記作業を完了後、ElasticSeachプロセスを起動する。

※HeadプラグインでShardがクラスタに戻っていることを確認する。

 

BインデックスのShard復旧手順

このサイトを参考にlucene-coreを使ったインデックスチェックを実施してみる。

27号機のほうは壊れており、28号機のほうは正常であることを確認。

ファイルコピー

※対象ノードのElasticSerchプロセスを最初に停止しておく。

  • 27号機のファイルは壊れているので削除する。
  • 28号機のファイルを27号機と他ノードにコピーする。

ステータスファイルの書き換え

  • 28号機のみ「   "primary"   : false 」の箇所を「true」に変更する。
  • 他ノードは「false」のままにする。

※上記作業を完了後、ElasticSeachプロセスを起動する。

※HeadプラグインでShardがクラスタに戻っていることを確認する。

 

CインデックスのShard復旧手順

ファイルコピー

※対象ノードのElasticSerchプロセスを最初に停止しておく。

  • 2番Shardは27号機にのみファイルが残っているので、そのファイルを他の2ノードにコピーする。
  • 4番Shardは25号機にのみファイルが残っているので、そのファイルを他の2ノードにコピーする。

ステータスファイルの書き換え

  • 27,25号機のみ「   "primary"   : true 」のままにする。
  • コピーした他ノードは「false」に変更する。

※上記作業を完了後、ElasticSeachプロセスを起動する。

※HeadプラグインでShardがクラスタに戻っていることを確認する。

 

今回の反省

  • 再起動するときは自動再配置をOFFにしてから作業するべきでした。
  • 1ノードの再起動後はログファイルの確認だけでなく、再配置処理が完全に終了するのを見届けてから次のノードの再起動をするべきでした。

今後について

バージョンアップします!

Cassandraの面倒を見るのが大変だったので今まで大きな障害もなく放置していましたが、もうこんな古いバージョン使いつづけるのはやめます。。

 

今回の記事が、Shardが戻らなくなって困ったという悩みを抱えてらっしゃる方(あまりいらっしゃらないと思いますが。。。)のお役に立てれば幸いです。