高負荷状態に陥った Amazon Elasticsearch Service を安定させる方法

Posted on

Amazon ES で運用しているログ検索基盤の Elasticsearch が高負荷状態に陥ってしまい、なんとかして安定させる必要がありました。具体的には CPU 使用率がほぼ 100% に張り付いてしまい、クライアントが接続できないという状況です。いろいろ試行錯誤してノウハウが得られたのでまとめておきます。

Elasticsearch のバージョンは 6.2.3 で、このクラスターの特徴は次のとおりです。

  • Fluentd からさまざまなログを集めて Kibana で検索する
  • 日次でインデックスが作られ、古いインデックスは Curator で削除している
  • Fluentd から Amazon ES へ直接送っていて、クライアント側のサーバ台数はそこそこ多い
    • VPC の中に起動しているので Kinesis Data Firehose は使えない

ちなみに、Amazon ES 前提の内容になっているので、自前でクラスター運用している場合は当てはまらないこともあります。

専用マスターノードを有効にする

Amazon ES には専用マスターノードという機能があります。公式ドキュメントでは次のように説明されています。

Amazon Elasticsearch Service では、クラスターの安定性を向上するために専用マスターノードを使用します。専用マスターノードではクラスター管理タスクを実行しますが、データは保持せず、データのアップロードリクエストにも応答しません。このように、クラスター管理タスクをオフロードすると、ドメインの安定性が向上します。

専用マスターノードを有効にすることで、クラスターを管理するノードとデータを管理するノードを分けることができます。本番環境では有効にしておくことが推奨されています。

当初はコストを抑えるために有効にしていなかったのですが、トラブル対応の手間を考えると最初から有効にしておくべきだったと感じています。

専用マスターノードは、管理するノードの数によって推奨されるインスタンスタイプが異なります。詳しくはドキュメントを参照してください。

ノードを増やす、インスタンスタイプを変更する

次はノードを増やしてスケールアウトするか、インスタンスタイプを変更してスケールアップします。どちらの戦略を取るかはケースバイケースですが、Elasticsearch の場合はスペックの低いノードを並べても効果が薄いです。なので、まずはスケールアップしてノードのスペックを上げてからスケールアウトした方が効率的だと思います。

このとき Amazon ES では Blue/Green デプロイでノードが入れ替えられます。詳しくはひとつ前に書いた記事をご覧ください。

Blue/Green デプロイが行われると一時的に管理対象のノードが倍になるためクラスターの負荷が上がります。専用マスターノードを有効化せずにやると、負荷を下げるどころか高負荷状態のクラスターにトドメを刺すことになります(実際にトドメを刺しました…)。まずは専用マスターノードを有効にしてから変更することをおすすめします。

また Blue/Green デプロイ中は新しいノードにデータをコピーするため IOPS が跳ねます。ノードの変更はオンラインでできますが、IOPS が落ち着いている時間帯に実施した方がいいです。

ログの送信間隔を下げる

Fluentd でログを送る場合、flush_interval で Elasticsearch への送信間隔を調整できます。ログを送ってくるクライアントが多い環境だと、この flush_interval を伸ばすことで Elasticsearch の負荷を下げることができます。

ただし、送信間隔を伸ばすと次のような弊害もあります。

  • buffer_typefile の場合はストレージを圧迫する
  • サーバが急に落ちた場合にログを欠損する可能性がある

1 つ目については buffer_chunk_limitbuffer_queue_limit を設定すればコントロールできます。バッファの最大サイズは buffer_chunk_limit * buffer_queue_limit で決まります。

なお、バッファチャンクが buffer_chunk_limit に達した場合は flush_interval の時間に関係なく flush されるので注意が必要です。

2 つ目はそのログの特性次第ですが、今回は Elasticsearch 以外に S3 にマスターログを保存してあるので割り切ることにしました(Elasticsearch に入っていなくても他に調べる手段があるということ)。

不要なフィールドのインデックスを作らない

Elasticsearch はドキュメント内に JSON データがあると自動的にインデックスを作ってくれます。ですが、インデックスを作る必要がない、もしくは作りたくないフィールドもあります。

そういう場合は Mapping で enabled: false にすれば、インデックスは作られずに JSON のままフィールドが保存されます。

PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "user_id": {
          "type": "keyword"
        },
        "last_updated": {
          "type": "date"
        },
        "session_data": {
          "enabled": false
        }
      }
    }
  }
}

フィールドが一定でない場合は Dynamic templates で同じことができます。

インデックスの作成にはそれなりの負荷がかかるようで(ログの形式や量によるので一概には言えませんが)、今回はこの対応で CPU 使用率がガクンと下がりました。

まとめ

冷静にひとつずつ対応していけば難しいことではないのですが、すでに高負荷状態に陥っていると焦ってしまい順序立ててトラブルシュートできなかったのが反省点です。

引き続き、Elasticsearch と Amazon ES のノウハウをストックしていきたいと思います。

Popular Entries

Recent Entries