generated at
Elasticsearchを毎月式年遷宮している

こんにちは。最近は毎月Elasticsearchサーバーのクラスタ環境を作り直しています。shokaiですshokai
ほぼ毎月式年遷宮に相当する作業を行っているという事です
この記事では、実際にどうやってクラスタ環境の移行をしているのか、解説します

これはHelpfeel Advent Calendar 2022の12日目の記事です
昨日はデザイナーのTakeruさんの記事でした
Figmaを使ってモブプログラミングのデザイン版をやっています


ScrapboxでのElasticsearch活用

2021年にCosenseの全文検索サーバーにElasticsearchを採用しました
サーバーはElastic Cloudにホスティングしてもらっている
fully managed serviceのElasticsearch環境
productionと同じ構成のサーバークラスタをもう1セット作るのも簡単

最初は、ページの本文を検索するだけの普通の検索でした

1年半かけて、少しずつ機能を追加していった
検索結果を下にスクロールして、検索対象を「自分が参加している全project」に広げる機能
さらに下にスクロールして、検索対象を「最近見たpublic project」に広げる機能
検索結果のソート方法に、被リンク数などを使って計算したスコアを採用した
NFDとNFCへの対応
ページタイトルだけ検索
本文を検索対象から除外
data nodeを複数のzoneに分けて負荷分散、耐障害性もアップ
app server起動時にESのクラスタ構成をチェックし、今後投入されるデータ量を予測して、いい感じにnumber_of_shardsを調整する
ファイル検索
Scrapboxにアップロードされたファイルの中に書かれているテキストを検索する機能
ファイル検索の対象を「画像だけ」「PDFだけ」など絞り込む機能
などなど
合計13回、Elasticsearchのindexやshardの構成を変更しました

Elasticsearchのindexへのデータ投入
ScrapboxではメインのDBとしてMongoDBを採用している
全文検索にはElasticsearchを使っている
Elasticsearchで検索するには
MongoDBからElasticsearchに、検索に使うテキストデータをコピーしてやる必要がある
めんどくさいけどやるしかないshokai
データを投入する前に、Elasticsearchにはデータをどんな風に持ってもらうか指示する必要がある
index-templateとかshard分割とか

13回Elasticsearchを作り直した理由
検索機能を追加・変更した時にindex-templateの構造を変更する必要がある
新しいfieldを増やしたり
tokenizerを変えたり
mappingを変えたり
pluginをインストールしたり
これらの変更を行うと、MongoDBからElasticsearchへのデータ投入をやりなおさなければならない
がんばれば運用中のサーバーに対してコマンドを送って実現できる変更もあった
しかしやらなかった
オンプレ版Scrapboxなど、自分たちが直接管理していないサーバーも存在する為
新しいコードをデプロイするだけで、いい感じに新しいindexに切り替わるようにしたかった
メインDBであるMongoDB以外は全てImmutable Infrastructureで扱いたい
オンプレ版Scrapboxの管理者の視点では、docker pullするだけで全てうまくいくようにしたかった
実現しましたshokai



検索サービスのダウンタイムが無いindex入れ替えのしくみ

新旧2つのElasticsearch indexを同時に更新しつつ、準備できた部分から新しいindexに乗り換える
project毎にES index versionという数値を持たせる
今、最新のES index versionは13です
アクティブに誰かが使っているprojectは全て13
12のprojectも存在する
11のprojectも存在する
ES index versionが空の場合
そのprojectはまだElasticsearchにデータが投入されていない
検索の準備ができていないという事
新規作成したばかりのprojectはこれshokai
検索時の挙動
バージョン13のprojectで全文検索したら
バージョン13のindexで検索する
バージョン12のprojectで全文検索したら
バージョン12のindexで検索しつつ
バックグラウンドでバージョン13のindexの作成を開始する
バージョン11のprojectで全文検索したら
「検索インデックスを作成中なので後でもう一度試してください」というメッセージを表示する
誰かがそのprojectを使おうとした時、新しいindexへの移行が自動的に開始される
ページの新規作成・編集・削除は、新旧両方のindexに反映される

新しいindexがあれば、もちろんそっちを使う
新しいindexが用意できていないprojectは、とりあえず古いindexで検索する

新旧2つのindexを1つのESサーバー群に持たせる事もできるし、別々のサーバー群に持たせる事もできる
使用するESサーバー群を ES_URI という環境変数で指定した場合
新旧のindexが1つのESサーバー群に投入される
ES_URI_V13 ES_URI_V12 の様に、後ろにバージョン番号を付けて指定した場合
新旧のindexが別々のESサーバー群に投入される
新旧のindexを別々のESサーバー群に分ける事で、キャパシティプランニングしやすくなった
もし1つのESサーバー群に同居させたら?
productionの規模だと、ピークタイムのread/write数がさらに激しくなってしまう
保持するindexのサイズも一時的に2倍近くになってしまう
Elastic Cloudなら同じ構成のサーバーを簡単に作れる
簡単に3 zoneでcoordinate nodeありのクラスタを作ったり捨てたりできて、本当にすごいshokai
なお、負荷の低い環境では、1つのESサーバー群に複数バージョンのindexを同居させています
セミオンプレ版Scrapboxやstaging環境、ローカル開発環境など


まとめ

すごく便利になった
サーバーのコストを削減できた
使われていないprojectのデータをElasticsearchに持たせる必要がなくなった為
ほぼダウンタイムが無い
新しいindexに移行できていないprojectも、とりあえず古いindexで検索して結果を返せる
2つ以上前のES index versionのまま、しばらく使われていなかったprojectだけは「今index作ってるのでまた後で試してください」を見る事になる
productionで元気良くチャレンジできる
新しいindexの実装に問題があった時も、コードをrevertすればすぐ古いindexに戻せる
今もElasticsearch初心者ですshokai
色々と自由度が高くて何をどうすれば良いのか手探りでやっています
どんどんデプロイして試してダメだったら戻せるしくみを真っ先に用意した事で、学習効率が高まった
Elasticsearchにどういう設定項目が存在するのかわかってきた
Elastic cloudの管理コンソールで1からサーバーを構築する作業を13回繰り返したおかげ


この実装はGyazoを参考にしました
Scrapboxでは、最初はMongoDBからElasticsearchへのデータ投入をbatch処理でやっていた
70時間ぐらいかかっていた
困って社内で相談したらGyazoのエンジニアのhiroshiが教えてくれました
当時、Gyazoでもindexの移行方法を試行錯誤していた
ユーザーの検索操作をトリガーとして新しいindexに移行する、などを試していた
そのアイディアを元に実装した
現在は、Gyazoにも今回の記事で紹介しているような方式が実装済みらしいですshokai


余談
ScrapboxではElasticsearchの他に、WebWorkerを使ったクライアントサイドでのインクリメンタルサーチも使用しています
QuickSearchと呼んでいる
これを参考にしつつ、もう少し洗練させたものがHelpfeelで使われていたりと、社内では技術交流がさかんに行われています
Scrapboxに技術調査をまとめていると、いつでも誰かが突撃してくる
隔週で行われているエンジニアお茶会
今出川FMNota Tech Confなどの社外向けメディア・イベントが話のきっかけになる
株式会社Helpfeelでは、社内の他のプロダクトのエンジニアと意見交換して高めあえる、研究的な態度を持った人を募集しています

次回のHelpfeelアドベントカレンダーは、12/19(月)にpastakが書いてくれる予定です

12/23(金)の回でbalarがScrapboxのファイル検索について書いてくれました