Amazon Elasticsearch Serviceを使ってみての所感

この記事はLivesense AdvendCalendar 5日目の記事です。

## 背景・機運
転職会議では、口コミや求人データを扱う際にElasticsearchを使っています。
その際に、[ElasticCloud](https://www.elastic.co/jp/cloud/)というElastic社が提供するマネージドサービスを利用してきました。 
Kibanaなどの周辺サービスも充実しており導入当初は良かったのですが、課題が徐々に露呈してきました  

### 1. ネットワークがボトルネック
外部サービスのためVPC外にリクエストを投げることになり、場合によってはレスポンスが悪くなる。特に大量のリクエストを投げると顕著。
メルマガ配信時も使っており、配信時にリクエストのタイムアウトが頻発。

### 2. コスト・運用面
料金に応じたCPUやメモリのスペックが確保されるため運用用途にあった料金を設定できず、AWS NATの転送量による支払いもあり、総合してみると多額の支払いが発生。
移行すれば「1年でプリウスくらいは買えるくらい浮きそう」という結果に
また、転職会議ではDatadogでサービス・ミドルウェアの監視・通知を行っているのですが、ElasticCloudだけマネージドされた監視ツールを使っていたため独立しており、雑多な状態になっていました。

### 3. カスタムプラグインが特定のプラン以上でしか使えない
ElasticCloudでは料金プランがいくつか用意されていて、用途に合わせて契約することが可能です。 
転職会議では最小プランのスタンダードを利用していましたが、カスタムプラグインが更に上のプランでしか利用できず、ユーザ辞書を追加して検索の精度改善を実施することができない状態でした。
プランを変更してもよいのですがコスト面が見合わず、しばらく使わない状態で運用していました。

### 4. パフォーマンス
辞書が使えなかったのでtermクエリを使うと想定外のドキュメントが取れてしまうので、それっぽいことをするためにAPIで取得する際にquery_stringで大量のドキュメントを取得し、そこからアプリ側でゴリッと色々とやっていました。。。。
結果APIのレイテンシーが徐々に悪化。ときにリクエストがタイムアウトを起こすこともありました。
またSLO改善の一環で、アプリ改修を含めこちらにも手を入れる必要がありました。

これらの課題を解決するためにElasticCloudを脱却することを決定。
当初はEKS上に自前でElasticsearchを立てる動きでした。

## 4月、AWSからのアナウンス
今年のはじめから少しずつ移行対応の検討と対応を行ってきましたが、なんとAmazon Elasticserch Service（以下 Amazon ES）側で[カスタム辞書をサポート](https://aws.amazon.com/jp/about-aws/whats-new/2020/04/custom-dictionary-files-now-supported-on-amazon-elasticsearch-service/) 
Amazon ESではkuromojiしかサポートしておらず、kuromojiの辞書は更新がほとんどされてないため、想定とは異なる言語処理結果になってしまうことが多いです。これはかなり嬉しいアナウンスでした。 
他にも調査してみると、EKSで自前で立てるのと彩色無く利用できそうだったのと、スロークエリログをCloudWatch Logsに流せるなど、他のAWSのサービスと親和性が高い。
また費用面でも自前よりは劣るが、それでも安くなるので急遽利用することに。

## やったこと
  1. Amazon ES側にインデックスを作成、ElasticCloudと並行して差分インデクシングさせる
    1. アプリ側からもドキュメントの更新を行う処理があるので、差分が出ないように先に実施しておく
  1. バッチでAmazon ES側に作成し、aliasを書き換える
  1. アプリやサービスで参照しているエンドポイントの切り替え
  1. ElasticCloudを脱却

大きなことはやっておらず、負荷も考慮しながら少しずつ移行対応していきました。

## 運用してみて
移行してしばらく経ちますが問題なく動いています。辞書も使えてaggregationによるキャッシュも効かせられ、レイテンシーも改善。（オレンジの線の前後でレイテンシーが変わっていることが分かります。嬉しい、、、！）
![スクリーンショット_2020-12-01_16_41_31.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57325/db3c38c7-59a5-6ab1-28b6-f85e8b404210.jpeg)
しかしAmazon ES特有の問題（癖というべきか、、）があり、かゆいところに手が届かない感じです。。。。

### 1. カスタム辞書の関連付けがterraform管理できない
Amazon ESで辞書を使うには、S3に指定のフォーマットでアップロードしてそれをパッケージとして紐付けます。
紐付けた際に発行されたリファレンスパスをuser_dictionaryとして追記し、インデックスを作り直す必要があります。

```json
    "analysis": {
      "tokenizer" : {
        "sample_kuromoji_tokenizer" : {
          "type":"kuromoji_tokenizer",
          "user_dictionary": "analyzers/F123456789"
        }
      }
```
上記のanalyzers/F123456789が発行されたリファレンスパスに当たります。  
AWSのリソースをterraformを使って管理してるのでまとめたかったのですが、aws_elasticsearch_domainリソースでは提供されてないようです。  
頻繁に辞書を更新する場合はスクリプトで管理するか、7.4以降からはuser_dictionary_rulesという、synonymと同様に直接辞書を追加できる設定があるのでそちらを使うと良いかもしれません。  

### 2. 一部のAPIが利用できない
OSSで提供されているElasticsearchとは異なり、許可されていないAPI（rerouteなど）が存在します。
自前で立てた場合と同じ想定で運用すると思いもよらない落とし穴にハマるかもしれません。
利用用途に合うか[リファレンス](https://docs.aws.amazon.com/ja_jp/elasticsearch-service/latest/developerguide/aes-supported-es-operations.html)を読んでから利用することをおすすめします。

### 3. ElasticCloud独自の機能やサポートが利用できなくなる
これは当たり前なのですが、管理画面からボタン一つでローリングアップグレードやマイナーバージョンアップグレード、スケールアップが容易にできたのはありがたかったですね。

## まとめ
移行してよかったと思う一方で、やはりサービスで提供されてるって良いなぁと思うときもあります。
通常運用する分には申し分ないかと思うので、自前で運用を考えてるならこちらも検討材料に入れてはいかがでしょうか。