Gunosy Tech Blog

Gunosy Tech Blogは株式会社Gunosyのエンジニアが知見を共有する技術ブログです。

Elasticsearch5系で、シャードunassignedによるstatus redを強引に修復する方法

はじめまして、Gunosyでサーバーサイドエンジニアをしているmanoと申します。 最近ハマっていることは、Huluで安室奈美恵さんの特番をみることです。引退するなんて・・・本当に惜しいですね。

私は現在ニュースパスというアプリのサーバーサイド開発を担当しています。
ニュースパスでは、記事検索機能等でElasticsearchを使っています。運用周りではcuratorを使っています。 最近そのあたりでハマってしまったことがあったので、今回はそれについて書こうと思います。

起こったこと

開発環境にて、Elasticsearchのノードインスタンスをシャットダウンしたときに、いくつかのシャードがunassignedになりステータスもredとなってしまうということがありました。
それでも検索機能は生きてはいたのですが、それによりレスポンスタイムが大幅に増加してしまっていました。
下の画像は当時のElasticsearch clientのELB Latencyです。平均値で20秒近くと、通常の200倍程度になっていました。 f:id:jumpeim37:20171110173510p:plain とりあえず応急処置を施すことにしました。
※ニュースパスでは、Elasticsearchからデータがロストしても後から復旧可能なため、こちらの応急処置は「unassignedになっているシャードは、いったんはデータ復旧しなくていい」というときの応急処置です。

現状確認

まずはクラスタのステータスを確認

$ curl -XGET host:9200/_cluster/health

{
  "cluster_name": "クラスタ名",
  "status": "red",
  "timed_out": false,
  "number_of_nodes": 13,
  "number_of_data_nodes": 9,
  "active_primary_shards": 170,
  "active_shards": 340,
  "relocating_shards": 0,
  "initializing_shards": 0,
  "unassigned_shards": 112,
  "delayed_unassigned_shards": 0,
  "number_of_pending_tasks": 0,
  "number_of_in_flight_fetch": 0,
  "task_max_waiting_in_queue_millis": 0,
  "active_shards_percent_as_number": 75.22123893805309
}

うーん、"status": "red"です。そしてunassigned_shardsはあるけどdelayed_unassigned_shardsはないから、この後自助作用で復旧することはなさそう。
次に、実際にunassignedとなっているシャードの情報を取得します。

$ curl -XGET host:9200/_cat/shards?h=index,shard,prirep,state,unassigned.reason

index-1   2 p STARTED 
index-1   2 r STARTED 
index-1   3 r UNASSIGNED NODE_LEFT
index-1   3 p UNASSIGNED NODE_LEFT
index-1   1 r STARTED 
index-1   1 p STARTED 
index-1   5 p UNASSIGNED NODE_LEFT 
index-1   5 r UNASSIGNED NODE_LEFT 
index-1   4 p STARTED 
index-1   4 r STARTED 

なるほど・・・index-1のシャード3・5が、primaryもreplicaもないと。
ここでバックアップデータからの復旧などができればそれをすればいいかと思いますが、今回はそういうのもなくて「とにかく犠牲を払ってでも復旧を!」というときの手段を書いていきます。(実行される方は自己責任でお願いします。。)

対処法

概要: 空のプライマリシャードをノードを指定してセットしていく

強引です(笑)

手順1 シャードを入れるノードのnode_idを取得

node_idがわかっているならスキップしても問題ありません。 サーバーIPアドレス等しかわからないとき、下記のように調べていきます。

$ curl -XGET host:9200/_nodes/(nodeサーバーのIPアドレスなど)

設定によっては1サーバー内に複数のノードがあるためです。
(後述のnode.max_local_storage_nodesが2以上で設定されているとき)

手順2 空のプライマリシャードをセットしていく

こちらを参考に、rerouteコマンドにて空のプライマリシャードを突っ込んでいきます。

curl -XPOST 'http://localhost:9200/_cluster/reroute' -d '{
    "commands": [{
        "allocate_empty_primary": {
            "index": "index-1",
            "shard": 3,
            "node": "node_id",
            "accept_data_loss": true
        }
    }]
}'

これで、指定したノードに、指定したindex・shardの空プライマリデータがassignされます。
環境にもよるかとは思いますが数秒でassignされます。 このように欠損してしまった全てのシャードの空データをセットしていけば、unassignedシャードがなくなった時点でstatus greenに復旧します。

そもそも

なおそもそも、このようなシャードの欠損を起こりにくくするには、下記のパラメータを適切に設定すると効果があるかと思います。

  • node.max_local_storage_nodesを1(=デフォルト)にしておく
    本番環境ではまずしないかと思いますが、こちらが2以上になっていると、同一のサーバーインスタンスに同一シャードのprimary・replicaが保存されてしまう可能性があり、その状態でそのインスタンスが落ちるとallocateが正常にできずstatus redになります。
  • delayed_timeoutを設定する
    ノードのネットワークが瞬断したときなどのnode_left発生時、即座にallocateが動いてしまうと過大な負荷がかかってしまうため、allocateが動くまでの時間を遅延させるパラメータです。 (デフォルト: 1分)

最後に

Gunosyではニュースパス等自社サービスのサーバーサイドエンジニアを募集中です!

hrmos.co