Elasticsearch 2.x から 7.xへの道

こんにちは！Rettyの李晟圭（ケイ）です。この記事は[Elastic Stack Advent Calendar 2019](https://qiita.com/advent-calendar/2019/elasticsearch)の17個目つまり12月17日のものになります。
昨日は吉岡さんの[Elastic Stackを導入することでRDB検索を高速化する](http://acro-engineer.hatenablog.com/entry/2019/12/16/120000)でした。

それでは始めていきますー

### 注意
- 下記の内容の変更点は、7.xで初めて変更があったものではなく、2.xから変わっているもの、を前提としています。
- この記事では、技術的説明やコマンドの例題などは扱いません。関連公式資料へのリンクを貼ります。

# 2.xをまだ使ってるところはまだまだ多い？
勘違いであってほしいですが、まだまだElasticsearch2.x系を使い続けている方や組織がちらほら。。。
実は私が努めている会社でもまだまだ2.x系がメインとして使われてます。クラスターは複数あるんですが、メインとして使ってきたのはなかなかバージョンアップできず。うーん
で、周りからも割と話を聞くのです。うちまだ2.xですよーと。

## なんでバージョンアップできないんだろう？
### 2.xの次に上げるバージョンがよくわからない
この変化に乗れなかった瞬間、あっという間に置いてかれてしまった、というのはあります。
５→6への変化も早く、5系自身のアップデートもものすごく早かった。
どれにすればいいの？という気持ちになるのは今も同じで、7系にバージョンアップの話をしている今、来年春には8が待ち構えている、との噂が。。。
プロダクトにこのElasticsearchを密接にくっつけて使っているところでは、そのバージョンアップはそう簡単なことではないので、目まぐるしく変わるバージョンに追いつくのは大変でしょう。
そして、2系でも十分動いてはいるのと、2系を導入したとしたらもう3,4年前のことで、k8sどころか、Dockerなどの仮想化による環境構築もあんまり普及されてなかったのではないでしょうか。
それで今まで運用している、としたら、そう簡単にどんどんバージョンアップに対応できるものではないと思われます。

### ログ分析ではなく検索基盤として使うところはバージョンアップしにくいのでは？
上にも少し言及しましたが、プロダクトで使っているかどうかはかなりバージョンアップの難易度が変わります。
Elastic Stackの場合、今はSIEMやAPMなども導入されてきてますが、2系の当時は「ログ分析」「検索」の2軸のプロダクトというのがありまして、
ログ分析用にElastic Stackを使ってるところと、検索基盤としてElastic Stackを使っているところではこのバージョンアップの障壁が全然違います。
#### ログ分析の場合、バージョンアップするには
- Clusteringの再適用
- Mappingの再作成
ぐらいが必要かと思われます。もちろん、データストアとしてElasticsearchを使って、他のBIツールとかで参照している場合は違うと思いますが、ElasticsearchをデータストアとしているのであればほとんどはKibanaを使うと思うので、データさえ入ってしまえばkibanaでさばける、なので区切って切り替えるのは簡単でしょう。

#### 検索基盤の場合、バージョンアップするには
- Clusteringの再適用
- Mappingの再作成
- クエリの再構築
- レスポンスを活用するクライアント（アプリケーション含め）すべてを修正
ざっと並べてもこれぐらいは必要で、特にElasticsearchからのレスポンスフォーマットに変化がある、というのはものすごく高い障壁です。
技術的な障壁ではなく、日々事業の成長のためにサービス運用に取り組んでいる中で、
なんでやるべきなのか明確にその理由を伝えることができないバージョンアップのタスクは、投資的な位置づけになりやすく後回しにされがちではないでしょうか。
そういった意味で、クエリとレスポンスに変更を加えなければいけない場合はバージョンアップの難易度がかなり上がる、と思います。

# それでもやるのだ
とはいえ、公式サポートも切れてるし、プラグインのメンテも終わってるし、バグを見つけてもissueを立てることもなんか恥ずかしいし、自分で直してもcommitもできず（恥ずかしくて）、discussで質問しようとしても気が引ける。
そもそもノウハウが生きていて、今が情報が旬であるプロダクトではない、つまり新しい情報が出ないプロダクトを使い続けるということはかなり危険なんですよね。
テレビでよく見る、「20年前の型番xxx-1990の製品をお持ちの方はご連絡ください」的な活動に近いイメージまで持たされます。

新しい機能が出てもそうですか。うちには遠い未来の話ですね。となってモチベーションも下がるし、ノウハウが停滞するので関連エンジニアとしての競争力も低下、そしてその古いプロダクトを使い続けるサービスの価値も下がってしまう悪循環に陥ることまで想像できます。（言い過ぎ？）
なので、我々は何が何でもバージョンアップをしなければいけないのです。
Elastic Stackへの愛がそうさせてるのではなく、必要だからやるのです！（と自分にも言い聞かせながらやります）

# いざ、バージョンアップへ
## やらなければいけないこと
### Clusteringで変わったこと
#### initial_master_nodeってなんだよ
これです。[modules-discovery-bootstrap-cluster](https://www.elastic.co/guide/en/elasticsearch/reference/master/modules-discovery-bootstrap-cluster.html)
2.xでは、zen discoveryでnodeを書き込みとか、EC2 discovery = aws cloud plugin などクラウドサポートしてくれるpluginを使って、ec2の中でこの子達が兄弟だよ、仲良くしてねと教えていたら勝手にmaster node electionが始まってmaster nodeが決まる形だったと思います。
それが、7.xではclusteringの最初bootstrap時に、どのnodeをmasterの候補にするかを指定するようになりました。
やり方は[こちら](https://www.elastic.co/guide/en/elasticsearch/reference/master/discovery-settings.html#initial_master_nodes)を参考にしてください。

これが分からず、最初めっちゃ時間かかりました。なんでmasterが選定できないんだ？！
single-nodeでやっていてもわからないことなので、だいたい検証用としてsingle-nodeで立ち上げていざ本番用としてclusteringやろうかーと~~古い~~2.xの考えのまま突入したので、これの存在を知るのに時間を使ってしまいました。
個人的に、あんまり知らされてないけど要注意な変更点でした。

### Mapping, Queryで変わったこと
#### typeがなくなりました
これが一番有名ではないでしょうか。
今までの`Cluster-Index-Type-Document`の構造から、
変わって`Cluster-Index-Document`の構造になりました。
？？？と思うかもしれませんが、もともとElasticsearchは同一index内ではtypeはタグ付けのようなイメージで、
よく言われてきたRDBSの構造と比較として以下のようなのは崩れてます。（そもそもこれで理解したらいけないものでした）
```Cluster : Database
Index : Schema
Type : Table
Document : Record
```
*※上記は間違った比較です* 

なんでこれが正しくないのか？というと、そもそもtypeはdocumentの属性を表すtag的なイメージであるので、
1個のindexの中で入っているdocumentというのは変わらないためです。
これを表す例として、typeが違っても、同じindexの中では同一field名で異なるfield data typeの設定ができない、という制約がありましたね。
もともとindexの中のdocumentはみんなindex配下。type配下で区分された存在ではなかった、ということですね。

それで、このtypeがなくなったことによって、以下のような作業が発生します。

- 1 index, n typesで構築していた場合、すべてのtypeをマージしなければいけない
  - typeがなくなったので当たり前ですね。。。
- 親子関係の再指定
  - [ここ](https://www.elastic.co/guide/en/elasticsearch/reference/current/parent-join.html)でその方法が見れます。
今まであんまり意識せずに指定してきた親子関係ですが、真剣にnestedやobject, arrayを併用していく検討が必要ですね。
- Queryの修正
  - type指定で作っていたクエリがほとんどだと思うので、その対応が必要です。
もちろん、親子関係で`has_child` `has_parent`など使ってるクエリはすべて[joining query](https://www.elastic.co/guide/en/elasticsearch/reference//current/joining-queries.html)に変えなければいけません。
- Responseを使うところの修正
  - こちらも、typeがなくなったためすべて変わってます。json parsingをtypeが来る前提でやっていたのであれば、全て対応しなければいけません。

#### stringは2個になった
[keyword](https://www.elastic.co/guide/en/elasticsearch/reference/current/keyword.html)と[text](https://www.elastic.co/guide/en/elasticsearch/reference/current/text.html)というdata typeがあります。
2.xの `not_analyzed` がkeywordですね。analyzer適用しないで、そのまま一つの単語として扱ってね、ということになります。

## Queryで変わったこと
### ScriptがGroovyじゃなくPainlessになってる
scriptを使ってるところがあるなら、対応が必要かもしれません。[Painless](https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting-painless.html)の文法に合わせましょう。

## やってみてもいいこと
### I heard you authenticate Elasticsearch
最近見た映画、Irishmanの原作、I heard you paint houseからモジリました。
せっかく認証機能を無料公開してくれたので、使わない手はないです！
[Secure a Cluster](https://www.elastic.co/guide/en/elasticsearch/reference/current/secure-cluster.html)を参考に、設定しましょう。
もちろんですが、設定したらkibanaもlogstashもユーザとroleが必要になります。すべてkibanaで設定可能なので泣けます。

## おまけ
時代が変わったのでDockerでクラスタリングするのが普通だと思うので、その場合は[この案内](https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html#docker-prod-prerequisites)に従ってください。

- vm.max_map_countの設定
- データ永続化のためのディレクトリーの権限設定
- ulimitの指定（docker run時に指定可能）

# 終わりに
長いようでそうでもない道のりではありますが、皆さんの7系生活にちょっとでも参考になれたら幸いです。

ありがとうございました！