魔窟と化した全文検索サーバーとふっかつのじゅもん

サイボウズのクラウド黎明期から運用し続けていたSolrサーバーを Elasticsearchに置き換えるプロジェクトが先日完了しました。

プロジェクト完了報告もかねてプロジェクトのあらましを公開したいと思います。

はじめに

このプロジェクトの主軸は『魔窟と化したレガシー技術をどう捌くか?』になります。 このプロジェクトの報告をする前に、いくつかエクスキューズをさせていただきます。

  • クラウド黎明期を支えてくれたSolrには畏敬の念に近い感謝をもっています
  • レガシーな技術に対してマウントやディスリスペクトの意図はありません
  • 魔窟にかかわることになってしまった人に対して負の感情は一切ありません
  • 今回の採用している構成はElasticsearchのあるべきアーキテクチャではありません
    • 今後、Neco 環境への移行を通して継続的に改善していきます

サイボウズでのSolrの使い方と用語説明

サイボウズのクラウドサービスでは、サービス開始当初から長年にわたり全文検索サーバーとしてSolrを採用していました。弊社では、次のようにSolrを使っています。

  • アプリケーションサーバーやデータベースなど、プロダクトを運用するために必要なコンポーネントごとにVMを配置しています。SolrサーバーもVMに配置しています
  • サイボウズのクラウドサービスはマルチテナント型のサービスとなっています。お客様(テナント)ごとに独立した環境を提供しています。1サーバーですべてのテナントを賄うのではなく、一定数のテナントごとにSolrやDBサーバーを用意しています

cybozu.comのクラウド概要図
cybozu.comのクラウド概要図

  • お客様別にデータを分離するため、プロダクトごと、テナントごとにSolrの「インデックス」を作成しています。つまり、各Solrサーバーには多数のインデックスが格納されています

ここで、インデックスとは「Solrの検索データをためる箱」のことです。Solrの用語としては「コア」と呼ぶべき物ですが、一般にインデックスと呼ばれることが多いことやElasticsearchの用語でもインデックスと呼ぶことから、この文書では一貫してインデックスと記載します。

他に、この文章では次のように用語を使います。

  • ユーザーの作成したデータをインデックスに検索データとして保存することをインデクシングと記載します
  • バッチプログラムでインデックスを再作成することを再インデクシングと記載します
  • 全文検索サービスを利用可能な製品を指してプロダクトと記載します
    • Solrを利用しているのはkintone、ガルーン、メールワイズの3プロダクトです

魔窟に化けるまでの歴史

それでは、どのように全文検索システムが魔窟と化していったのか、歴史を紐解いてみたいと思います。

魔窟に化けるまでの歴史1 Solrのバージョンアップができなくなる

Solrのバージョンアップに伴い、社内で利用していたトークナイザの互換性がなくなる問題が発生しました。

トークナイザとは、全文検索を実現するための技術要素の一つで、索引付けをするために文章データを単語単位や一定長などの断片に分割する機構です。 全文検索エンジンは元の文章を指すIDと、分割された文章の断片を紐づけて索引情報を作成しています。 そして、検索時は検索キーワードをトークナイザで分割し、分割された語句が索引情報にあるかを調べています。

このトークナイザがどのように文章を分割するかで検索の精度が変わります。 一般的に、トークナイザが更新され、賢くなることは検索精度向上につながり、嬉しいことが多いです。 しかしながら、トークナイザの互換性が失われる場合はインデックスデータの再作成が必要になり、 同じ検索キーワードでも検索結果が変わってしまうことに注意が必要です。

弊社プロダクトのkintoneでは、レコード絞り込み機能の一部に全文検索エンジンを使用しています。 そのため、トークナイザを変更すると、レコード絞り込み結果に大きく影響が出ることが分かりました。

これが致命的でした。当時はこれを解決する手段が思いつかず、一時的にバージョンアップを見送る対応としました。

魔窟に化けるまでの歴史2 データ規模の巨大化

これは喜ばしいことですが、クラウドサービスのリリースから数年でデータの規模が大きくなりすぎました。 結果、サービス開始初期に開発された再インデクシング用ツールが使い物にならなくなる問題に直面していました。

当時は全文検索の索引情報を再作成するためのツールをインフラチームが持っていました。 そのため、トークナイザの互換性は置いておくにしても、 このツールを実行すれば再インデクシングができるはずでした。

しかし実際には、データ規模が巨大になるにつれて途中でエラーが起きたり、 オペレーションミスで再インデクシングが途中で停止する事故が起きる状態になってしまいました。

ツールの設計上、再インデクシングが停止した場合は最初からやり直しが必要で、 非常に苦痛をともなうオペレーションとなっていました。 ツールを改修しようにも、各テナント・プロダクトの再インデクシングの実施状況や進捗などの状態管理を 一手に引き受ける非常に複雑なツールとなっており、改修が困難でした。

こうして、再インデクシングは触れてはいけないオペレーションになっていきました。

魔窟に化けるまでの歴史3 矢面に立ってくれていたコアメンバーの退職

そうこうしているうちに再インデクシングのオペレーションを矢面に立ってやってくれていたコアメンバーが 相次いで退職してしまいます。今振り返ると彼らの意思の強さに強く依存していました。

彼らの退職のタイミングあたりから再インデクシングが話題に上がったことは私の記憶ではありません。 彼らがこの記事を見ているかわかりませんが、矢面に立って作業してくれたことに感謝しかありません。

完全なる魔窟に化ける

こういったことが重なりSolrの長期間の塩漬けが決定的になりました。

一方、検索データは日々肥大化し、毎年1TB増加するテナントも存在しました。 大きなテナントでは2018年末の時点で1.8TBに達しました。 インデックスが肥大化するにつれて検索スピードは劣化していきます。

特にガルーンは顕著に検索スピードが劣化し検索リクエストのタイムアウトが頻発するようになります。 さまざまな調査を試みましたが、解決にいたらず時間だけが過ぎていく状態でした。

Elasticsearch置き換えプロジェクト 挫折編

時を同じくしてElasticsearchが登場し、日本国内でも流行の兆しを見せていました。 Elasticsearchの高度な検索機能やクラスタリングなど魅力的な機能は多かったため、サイボウズでも置き換え調査をしていました。 しかし、再インデクシングの問題やトークナイザの互換性問題は未解決のままでした。

当時社内で構想していたアーキテクチャではクラスタ構成のElasticsearchを多数運用するのはインフラの管理ルーツの変更や運用コスト的にきびしかったため、 巨大クラスタにすべてのテナントのインデックス入れて運用する構想でした。

しかし、調査を進めていると、多数のインデックスの扱いに難があることが判明します。

Elasticsearch の巨大クラスタを複数用意し、すべてのインデックスを運用することはインフラ側の変更が大きく、 この構成では構築・運用不可能という判断になり、調査そのものが突然停止することになります。

最後にすがったElasticsearchへの望みも蜘蛛の糸のように切れてしまったのでした。

Elasticsearch置き換えプロジェクト ビバーク編

そこに追い打ちをかけるように利用しているSolrのバージョンがEOLになることが分かりました。

看過できる状況ではなかったので、Solrの置き換えを強行するプロジェクトを起案します。 プロジェクト名をアウトドア用語で緊急的なエスケープを意味する『ビバーク』としました。

プロジェクトを成功させるための技術的な制約を明確化し、古いSolrからの脱却に集中しています。

Solrから脱却してElasticsearchを採用した理由ですが

  • 次世代のkintoneアーキテクチャ構想にてElasticsearchが採用されていたため技術スタックを合わせました
  • Solrアーカイブ形式が変わったため、弊社で独自カスタマイズしていた全文検索サーバーを改修して対応させるには時間がかかる
  • 独自カスタマイズしたサーバーをメンテナンスし続けるよりもElasticsearchを運用する方が維持コストは低そう

Elasticsearchに移行するために解決すべき要件は次の3点のみに絞りました。

  • Elasticsearch不安定化問題
  • トークナイザの互換性問題
  • インデックスの再作成問題

この問題を解決するためには一部を除いて難しい技術はほぼ使っていません。

タイトルを『ふっかつのじゅもん』としているのは全体として魔法のようにうまくいっているように見えますが 巧妙に制約を回避・リスクを許容範囲に収まるように手を打ち、今回の移行にこぎつけているためです。

このプロジェクトのビバークという名前にふさわしく、今の状況をとにかくしのぎ切るために移行計画を構想しています。 K.U.F.Uを方針としています。わりと普通にいうと工夫というやつです。

ふっかつのじゅもん1 インデックス数の制限を回避する

多数のインデックス作成時にElasticsearchが不安定化する問題は、Issue などを調査した結果からクラスタに所属するElasticsearchノードの通信が原因で不安定化することが分かりました。 また、コミュニティのQ&A からインデックスの作成で顕著に性能が劣化するがインデクシング、検索は問題なく行えることがわかってきました。 実際に社内で実験したところ、シンプルなクラスタの運用であれば2000程度のインデックスがあっても十分安定稼働すると確認できました。

性能検証としてシンプルなElasticsearchクラスタを構築し、レガシーSolrで1.8TBのインデックスになっていた環境と同データを持つ環境を再現して、検索検証を行いました。 次のような結論が得られました。

  • ガルーンは10倍程度の性能改善を期待できる
  • kintone、メールワイズは性能劣化が発生しない
  • レガシーSolrとElasticsearch 6との比較で40%のインデックス容量の圧縮効果を期待できる

SolrとElasticsearchは内部ではどちらもApache Lucene を使っているため、大きな性能の違いは発生しないだろうという推測をしていました。 Luceneの進化は素晴らしいものがあり、推測はいい意味で裏切られることが検証を通してわかりました。 ガルーンは全文データの構造を改善しているために大きな改善が得られています。

先述の通り、サイボウズのクラウドサービスでは一定数のテナントごとに独立したSolrサーバーを立てる運用をしています。このSolrサーバー同様にElasticsearchを多数運用する形であれば、 インデックス数の増大で不安定化する問題を回避できます。

さらに、Solrサーバーのデプロイと同じ要領でElasticsearchをデプロイできるので、 巨大クラスタを構築するのに比べインフラ側の修正も少なくなり、 短期間での基盤更新に向いていることから ビバークではこの構成を採用しました。

ふっかつのじゅもん2 トークナイザの互換性を確保する

Elasticsearchではトークナイザを指してアナライザ(Analyzer)と表記することが多いですが、このドキュメントでは便宜上このままトークナイザとします。

Elasticsearchを調査したところ、トークナイザはプラグイン機構で拡張できることが分かりました。 Elasticsearchが提供しているアジア圏の言語向けのトークナイザの icu-analysis-plugin の実装を調べたところ、数百行のシンプルなコードでした。

加えて、プラグインはElasticsearchに付属するコマンドラインツールで簡単にインストールできるようになっていました。 Elasticsearchにパッチをあてて再ビルドする作業は要求されません。

そこでSolrで廃止されたトークナイザのソースコードを移植してプラグインとして再実装することを思いつきます。 プロトタイプの作成を通して、プラグインのメンテナンスを続けてもコストは許容できると考えるようになりました。

そして、後方互換性の破壊に関する議論で時間を溶かすよりはプラグインとして移植するほうがトータルコストは安く上がるという結論にいたりました。

ふっかつのじゅもん3 インデックスの再作成問題をなんとかする

このプロジェクトでの技術的難所はインデックスの再作成です。 過去のインフラチームのツールの問題をふりかえって『カプセル化が不適切な結果、プロダクトが持つべき責務をインフラチームが過剰に背負ってしまっている』と解釈しました。

インフラチームの背負っていたテナント・プロダクト内部の細かい進捗状態管理の責務をプロダクトに移し、 詳細の内部実装をプロダクト内に隠蔽するアプローチを取りました。 次の図はプロダクトが内部的に隠蔽している状態遷移図です。インフラからは各プロダクトが公開するWeb APIを通じて 再インデクシングの開始・停止や進捗の確認ができますが、この状態遷移をインフラ側で保持・管理する必要がなくなりました。

プロダクト内部に隠蔽されている再インデクシングの状態遷移図
プロダクト内部に隠蔽されている再インデクシングの状態遷移図

結果、インフラからはどのテナントをいつ再インデクシングするかだけを制御すればよくなりました。

各プロダクトには、データを順次読み取り再インデクシングするバッチ処理や、進捗状況の管理が 増えることになります。しかし、これにより、プロダクトのデータの持ち方に合わせた効率の良い 再インデクシングがやりやすくなるなど、プロダクトが独自に再インデクシング処理を変更・改善しやすくなります。 また、各プロダクトはアプリケーションサーバが再起動することも考慮してバッチ処理を 実装するため、必然的に進捗状況はデータベースサーバに保存することになります。これを活かし、 再インデクシングが何らかの原因で停止しても、原因を解消後に停止したところからの再開が可能になりました。

この結果、数か月におよぶ再インデクシングが可能になりました。

ふっかつのじゅもん4 ストラングラーパターン

ストラングラーパターンとは?

ストラングラーパターン はレガシーになったサービスを新しいサービスに置き換えるための移行パターンです。 レガシーなサービスと新サービスを平行運用しながらレガシーなサービスへの依存を徐々に減らしていきます。

下図はストラングラーパターンを使ってSolrへの依存を減らしていく様子を示しています。

ストラングラーパターンを使った移行の様子
ストラングラーパターンを使った移行の様子

  • Fig1. kintoneがElasticsearch切り替え後にSolrへの切り戻しに備えてインデックスを更新し続けています
  • Fig2. kintoneがElasticsearchに移行完了状態です
    • ガルーンがElasticsearch切り替え後にSolrへの切り戻しに備えてインデックスを更新し続けています
  • Fig1、Fig2ともにメールワイズはSolrを利用し続けています

カナリアリリースに近しいものがありますが、特徴の一つとして平行運用期間は新旧両方のデータを更新し続けます。 バグや性能問題があった場合は古いサービスに切り戻して一時的に問題を回避しながら移行を進めていくことになります。

今回の移行プロジェクトでは再インデクシングを行うWeb APIの実装が完了したプロダクトから再インデクシングを開始し 再インデクシングが完了したテナントは Elasticsearchに切り替えていきました。 Elasticsearchの性能特性が理解できていないリスクを考慮して、データ量の少ないテナントから移行を実施していきました。 仕様差異が発生する検索処理の切り替えは、定期メンテナンスに合わせることで事前の告知作業などにも配慮して作業を進めています。

Elasticsearchへ切り替え後、不具合に備えてSolrへの切り戻しをサポートする並行運用期間を2か月間設けました。 こうして、徐々に運用環境で開始し、徐々に全文検索のバックエンドを切り替えてSolrへの依存を減らす作業を続けていきました。

そしてついに移行完了

そして、2021年6月にすべてのSolrサーバーへの切り替えサポート期間が終了しElasticsearch移行が完了しました。

ちなみに、2018年末でSolrサーバーで1.8TBのインデックスがあったテナントは、最終的に4TBのインデックスとなっていました。 このテナントはElasticsearchへの再インデクシングに2か月もの時間を要しました。

移行振り返りその1 性能はどうでしたか?

ガルーンで特に性能劣化が顕著だったテナントでの検索にかかった時間のグラフです。 先述の通りガルーンは全文データの構造を改善しているため、Elasticsearch切り替え後は検索結果が高速化されていることがわかります。

インデックス容量はレガシーなSolrと比較して40%圧縮される効果は検証通りの結果が得られました。

ガルーンの検索時間の改善グラフ
ガルーンの検索時間の改善グラフ

移行振り返りその2 トークナイザどうでしたか?

検索エンジンが切り替わっても検索仕様は維持できているため、仕様変更に気付かれることは稀でした。 「稀」としているのはプロダクトによっては移行のタイミングで細かい仕様変更や脆弱性改修などを行っているためです。 微妙に仕様変更が発生するケースに気付いたお問い合わせが数件ありました。

コミュニケーションコスト、メンテナンスコスト、リスク面への配慮など総合するとペイしているという判断です。

移行振り返りその3 クローラーAPIどうでしたか?

プロダクト・インフラチームに新しい十字架を背負わせてしまうので罪悪感はありました。 数か月におよぶ再インデクシングを乗り越えてSolrから乗り換えれたのでご容赦いただきたい...。

移行振り返りその4 ストラングラーパターンどうでしたか?

ストラングラーパターンが特に役に立った例として、Elasticsearch内部のサーキットブレイカーが発動して100%検索が失敗する障害がありました。 障害発生後、問題の起きたテナントではElasticsearchでの運用を短期的に諦めてSolrへの切り戻しAPIを実行して一時凌ぎをしています。 その後、調査・改修を行い、ふたたびElasticsearchへの切り替えと安定稼働に成功しています。

ストラングラーパターンはバグや性能問題が見つかったときの対応として非常に役に立ちました。 問題が起きても策を講じて粛々対応、粘り強く移行を最後まで遂行できました。

私の知る限りでは社内では初採用のパターンでした。このパターンを使った感想は、 レガシーソフトウェアと折り合いをつける方法として有用性が高く、強力な一手という感想です。

そしてNecoへ

今のプロジェクトの状況としては命からがら緊急的なエスケープから一時しのぎに成功し小康状態にこぎつけた状態です。

この構成に長期間とどまり続けるのは危険なので、引き続き改善していく必要があります。 現在、サイボウズではKubernetesで構築された新インフラ環境 (Neco) への移行作業を進めています。ElasticsearchもNeco環境へ移行する必要があるので、今はそれに合わせて最適なElasticsearchの構成にするための改善作業・移行の準備中です。

今回のSolrからElasticsearchへの移行では、移行の調査と移行計画の構想がほぼ成功し、全体として驚くほどうまく移行できました。 しかしながら、移行の中で想定外のことや考慮が足りなかった点もあります。この辺りの改善点は次のNeco移行完了時にお話できればと思います。

最後に

貴重な時間を割いてここまで読んでいただいたみなさまの気分転換にすこしでもなれば幸いです。

Solr時代含め魔窟と戦い続けてくれたすべての人に感謝の意を示します。そしてこのプロジェクトに直接的、間接的にかかわったみなさまに感謝します。

記事執筆およびプロジェクト構想は@yokotaso が担当しました。 プロジェクトにかかわってくれたメンバーの協力、最後まで躊躇せずアクセルを踏み続けたインフラチーム責任者の理解、 意思決定をする人たちのリスクを判断したうえでの承認など、どれひとつ欠けても、全文検索すら提供できなっていた可能性があります。

最後に感謝の意と、プロジェクトメンバー(自分含む)が圧倒的昇給を勝ち取ることを願いつつこのプロジェクトの完了報告としたいと思います。

いつ壊れてもおかしくない状況で最後まで動き続けてくれたSolrにありがとう。そして、さようなら。

参考

修正履歴

  • 2021/6/21 わかりにくい・誤解を招きうる表現を修正