はじめに
情報システム部の横山です。今回はElasticsearchを利用した機能を本番リリースする直前に発生した、表題のエラーについてご紹介します。このエラーが出るのは一部のアプリケーションだけだとは思いますが、同じエラーが出た人の一助となれば幸いです。
背景
Elasticsearchを利用した全文検索機能のMR(マージリクエスト)をdevelopブランチにマージし、社内で使っているStaging環境でも無事動作確認ができ、別のテスト環境にもデプロイしました。それではお客様にも確認して頂き、それがOKだったら本番にもリリースしよう……という時に、テスト環境からSlackにエラーが通知されました。
エラーの概要
表題にもしたこのエラーをGoogle翻訳すると、「このアクションは[10]の合計シャードを追加しますが、このクラスターは現在[1992]/[2000]の最大シャードを開いています」。最初に見た時は何のことやら?と思いましたが、つまり、「シャード数の上限が2000に設定されている状態で、現在は1992のシャードがあり、そこに10個のシャードを新しく追加しようとしているけどこれを追加したら2002になって上限超えるからエラーだよ」と言っているようです。ではシャードとは何でしょうか?
シャード
シャードについては、Elastic社の公式ページに説明が書かれています。
Elasticsearchのデータは、複数のインデックスに整理されます。各インデックスは1つ以上のシャードで構成されています。各シャードはLuceneインデックスのインスタンスです。(How many shards should I have in my Elasticsearch cluster? | Elastic Blogより引用)
どうも各インデックスは1つ以上のシャードで構成されているようです。
デフォルトでは、Elasticsearchの各インデックスは5つのプライマリシャードと1つのレプリカを割り当てられます。これは、クラスタに少なくとも2つのノードがある場合、インデックスは、インデックスにつき合計10個のシャードのうち、5つのプライマリシャードと5つのレプリカシャード(完全なレプリカ1つ)を持つということです。(基本概念 | Elasticsearchリファレンス [5.4] | Elasticより引用)
そしてそのシャード数のデフォルト値は5で、それぞれにレプリカがあるので、つまりシャード数のチューニングをせずにインデックスを作ると、以下の画像のようになることが分かります。
Elasticsearchが内部で利用しているLuceneという全文検索ライブラリが存在するのですが、そのLuceneのインデックスを作成するために、シャードは存在します。このシャードを単位としてElasticsearchのインデックスからLuceneのインデックスに変換するので、小さいシャードが増えすぎてしまったり、逆にシャードの中身が大きくなりすぎたりするとElasticsearchのパフォーマンスに影響が出てしまうようでした。
では、シャードの中身はどれくらいの大きさであれば良いのでしょうか? 公式には以下のように書かれていました。
シャードサイズに関する決まった上限はありませんが、よく言われているシャードサイズの上限は50GBです。これがさまざまなユースケースで実用可能な大きさと考えられています。(How many shards should I have in my Elasticsearch cluster? | Elastic Blogより引用)
エラーが出た時の構成はどうなっていたか
エラーが起きた時、シャード数はデフォルト設定のまま、つまり1インデックスにつき10個のシャードが作られる状態でした。そして4つのModel(テーブル)に対して個別にインデックスを作成したため、これでシャード数は10 × 4 = 40。更に、このアプリケーションはテナントによってデータベースを分けるように設計されていて、この当時、エラーが起きたテスト環境のテナント数は60程度あり……40 × 60 = 2400。よってElasticsearchのシャード数の上限は2000を超え、エラーが起きてしまいました。
kibanaのコンソールから GET /_cat/shards?v
して確認を行うと、確かに1インデックスあたり10のシャードができていました。
どう対応したか
テナント別にデータベースが分けられているので、それぞれのテナントのレコード数はそう多くありませんでした。各インデックスのサイズを調査したところ、大きいものでも200MB程度だったため、プライマリシャードとレプリカシャードを1つずつの構成に変更しました。
searchkickというgemを使っていたため、設定は簡単でした。 以下のように設定できます。
class Product < ApplicationRecord searchkick settings: { number_of_shards: 1 } end
そして設定を変更した後、全てのインデックスを作り直し、無事このエラーは解消しました。
おわりに
AWSブログでもシングルシャードを恐れるなと記載されています。
シングルシャードを恐れるな!
もし、あなたのインデックスのサイズが30GB以下であるのであれば、一つのシャードのみを使うべきです。”more is better”というガッツフィーリングをお持ちの方もいらっしゃいますが、誘惑を断ち切りましょう!(Amazon Elasticsearch Service をはじめよう: シャード数の算出方法 | Amazon Web Services ブログより引用)
シャード数は適切にメンテナンスし、Elasticsearchを使っていきましょう。