Techtouch Developers Blog

テックタッチ株式会社の開発チームによるテックブログです

Elasticsearch を使っていくために最低限知っておきたいこと

f:id:techtouch:20201130172506p:plain

この記事はテックタッチアドベントカレンダー5日目の記事です。4日目は terunuma によるプロダクト開発と事業へ成果を出せる SET になるために読んだ書籍・スライドでした。品質は開発速度に関わる部分なので何でもお手伝いして、安定してリリースし続けることを一緒に頑張っていけたらと思っています。

この記事について

Elasticsearch を使っていくためにデータ構造周りで最低限知っておきたいことをまとめておきました。
対象は Elasticsearch 初心者です。この記事を読んで Elasticsearch ってこういう構造で動いているんだなと思い出せればいいなと思っています。
API や内部構造については、6 系を対象に書いています。

背景

弊社では Elasticsearch を使っているのですが、あるとき開発環境で Elasticsearch が倒れてしまいました。
原因は、t2.small.elasticsearch x2 で動かしていたのですが安定稼働をさせるにはシャード数 40 が目安とされているのに対して数千のシャードができてしまっていたことした。
Elasticsearch がデータを shard という単位で分割しているのは知っていましたが、それをどう管理していくかそこまで意識していなかったので復習しておきたいなと思います。
商用環境では横にデータノード並べたり、スケールアップで対応は可能ですが、開発や検証環境では節約のために低スペックなものでまかなえるようにもしたいです。

構造

f:id:techtouch:20201206125418p:plain

Elasticsearch のデータノードに注目して図におこしました。今回はマスターノード*1については触れません。データノードについて見ていきます。

データノードの中には、データのまとまりである Index がシャードと呼ばれる単位で分割されて保存されています。
Index は、RDB でいうテーブルだと考えてもらえると理解しやすいです。

今回、Node-01 に入っているのはプライマリシャードと呼ばれるもので、Node-01 が動き続ける限り書き込みリクエストはこちらに送信されます。
また、データノードを複数並べているときは、レプリカの設定をしているとき他のノードにデータを複製するようになっています。
図でいうと、Node-02 にシャードのコピーが作られるとしています。
レプリカは、読み込みリクエストの際に参照データとして使われる他、Node-01 が倒れたときに Node-02 がプライマリとして昇格するスタンバイとしても使われます。
逆に Node-02 が倒れたときには、Node-01 からデータの同期ができなくなるので、クラスタの状態として異常になります。

また、図にある Lucense*2 Index はシャードと同様で、これらは Segment と呼ばれる小さなファイルに分割されています。
検索実行時に Lucense は散りばめられた Segment を順次検索していきます。
Segment は書き込みの度に作られるので、Elasticsearch の用語でいうデータの 1単位のドキュメントと近い(というか同じ?)と考えてよさそうです。(RDB でいうレコード)
そのため、書き込みのたびに Segment が増えて行きますが、Lucense は Segment を大きな単位でマージしてくれたりするようです。Elasticsearch の API でも操作が可能です。
前述したとおり、Lucense は Segment を順次検索するので、Segment はなるべくまとまっていたほうが検索スループットは上がるようです。
しかし、マージにはマシンリソースを多く使うことが想像できるのでタイミングには注意が必要に思えます。

データ構造についてはこの辺にして、次は運用の際の設定値について見ていこうと思います。

運用の際に最低限見ておくべきポイント

Elasticsearch, kibana は 6.9.8 の oss version を使っています。
Elasticsearch は 1 Node です。

Index のメタ情報を確認する

まずは kibana で index を作成してみます

PUT index-01

すると、こんな注釈が出ました。

#! Deprecation: the default number of shards will change from [5] to [1] in 7.0.0; 
if you wish to continue using the default of [5] shards, you must manage this on the create index request or with an index template

7 系からは、デフォのシャード数が 5 から 1 になるようですね。6 系ではデフォが 5 なので、商用環境以外ではシャード数節約のため次の index template などを使っていきたいと思います。
index 作成時の他のデフォ値は、レプリカ 1 も設定されています。 確認してみます。

GET _cat/shards?v&h=index,shard,prirep,state,unassigned.reason

index                   shard prirep state      unassigned.reason
index-01                1     p      STARTED    
index-01                1     r      UNASSIGNED INDEX_CREATED
index-01                3     p      STARTED    
index-01                3     r      UNASSIGNED INDEX_CREATED
index-01                4     p      STARTED    
index-01                4     r      UNASSIGNED INDEX_CREATED
index-01                2     p      STARTED    
index-01                2     r      UNASSIGNED INDEX_CREATED
index-01                0     p      STARTED    
index-01                0     r      UNASSIGNED INDEX_CREATED

shard が 0 ~ 4 の計 5 つ、さらにそれぞれについてレプリカが作られようとしています。prirep 列の r , p がそれにあたります。
しかし、今回は 1 Node での環境なのでレプリカが配置できないため、レプリカのステータスは UNASSIGNED INDEX_CREATED となっています。

index 作成時にこれらの値を設定するには以下のようにします。

PUT index-01

{
    "settings" : {
        "number_of_shards" : 1,
        "number_of_replicas" : 0
    }
}

Index template

index 作成時にシャードやレプリカ数を設定する他に、index の名前に応じてそれらの値を設定させておくことができます。
シャード数は後から変更できないので、開発環境ではすべてのインデックス作成に対してシャード数を 1 にしておいてよいかと思います。

PUT _template/dev_template
{
    "index_patterns" : ["*"],
    "settings" : {
        "number_of_shards" : 1,
        "number_of_replicas" : 0
    }
}

Index Lifecycle

シャード数を最低限に絞っていても、インデックスがたくさん作られてしまうとパフォーマンス劣化や、Elasticsearch が倒れてしまうことがあります。
古いインデックスや不要なものは自動で削除したいところです。そういった設定を簡単にできるのが Index Lifecycle です。
ただし、OSS の機能には搭載されていないので AWS の場合は、7 系に上げてから Index State Management を利用するとよさそうです。早く 7 系にバージョンアップしたいと思います。

マシンスペックに対するシャード数目安

そもそも、1 シャードは 20GB ~ 40GB に抑えとくとうまく運用できるようです。故に、該当インデックスのサイズがその範囲で足りるならシャードの数は 1 つでよいことになります。
そして、データノードが持てるシャード数は、1GB のヒープ領域に対して 20 未満にしとくのがよいとのこと。
t2.small.elasticsearch をベースに考えると、開発環境で 1 シャードが 30GB 以内に収まるなら 20 を目安にインデックスの削除を行ったほうがよいことになります。
日次のデータだったら量にもよるとは思いますがすぐパンパンになってしまいますね。2GB を積んだデータノード 1 台で運用したいなと思いました。

まとめ

今回は、Elasticsearch とデータ構造側と設定周りから仲良くなるための方法を書いていきました。
商用環境ではデータのローテーションはできないので、その辺りを便利にする API や設定を調べたりしようと思います。 7 系に上げて Index Lifecycle Management を設定していきたいところです。 また、Elasticsearch のバージョンがまだ6系のためリリースノートを把握して、バージョンアップ計画を立てて行こうと思います。(その前にインデックスの整理もしないと)

参考

明日は、BHO (Browser Helper Object) をめぐる冒険 です。時空のねじれで内容を先に知ってしまっているのですが、冒頭の元ネタがなにか自分はわかりませんでした。。w

*1:インデックスの作成や削除、どのノードにどのシャードを割り当てるかの決定を行うクラスタ全体の指示役

*2:Elasticsearh が使う検索エンジンの名称。ApacheFoundation の OSS