Legalscape の現在のシステム構成とこれまでの歩み
Legalscape アドベントカレンダー 2021 のエントリ #3 です。
昨日の 城戸によるエントリ では、Legalscape を支える技術スタックとその採用理由についてご紹介しました。本日のエントリでは、それらの技術をどのように組み合わせて Legalscape のシステムを構成しているのかを、β版提供開始から現在に至るまでの変遷を振り返りながらご紹介します。
β版提供開始時点のシステム構成
Legalscape (リーガルスケープ) は 2019 年 9 月にβ版の提供を開始しました。この時点ではプロダクトの成熟度的にまだ仮説検証の段階であったこと、また開発リソースや時間、コストなど諸々の制約から、Google Cloud Platform を利用しつつも以下のような Compute Engine インスタンス一台構成のモノリシックなシステム構成としていました。
現在の Legalscape を構成する主要なコンポーネントはこのときから大きくは変化しておらず、JavaScript や CSS などの各種静的ファイルで構成される フロントエンド、フロントエンドから呼び出され、各種ミドルウェアとの連携を仲介する API を提供している API サーバ、Google や Microsoft の Azure AD などの認証プロバイダーと連携してユーザの認証を執り行う 認証サービス、そして法文書の全文検索機能を実現する 検索サーバ (Elasticsearch クラスタ) で構成されています。
このβ版提供開始の時点では、フロントエンドの静的ファイルは nuxt-ts のプロセス (nuxt-ts start) でビルドしてそのままホストされ、また API サーバと認証サービスはそれぞれ個別の ts-node (w/ express.js) のプロセスでサーブされていました。全文検索には当初から Elasticsearch を利用していましたが、この時点ではまだクラスタ構成をとっておらずシングルノード構成で稼動していました。インターネット側からの HTTPS リクエストはリバースプロキシとして機能する Nginx が受け付けて SSL 終端し、フロントエンド、API サーバ、認証サービスそれぞれのサーバプロセスに HTTP リクエストとして転送する構成となっていました。
なお、システムデザインの経験がある方ならこのシステム構成をみてすぐにお気づきになるかと思いますが、この当時のシステム構成はモノリシックな構成がゆえに障害耐性に難があり、安定的なサービス提供を実現するためにはクラウドプラットフォームが提供するマネージドサービスへの移行や冗長化によって障害耐性を高める必要がありました。
認証サービスのマネージドサービス移行
まずはじめに取り組んだのが、認証サービスのマネージドサービス移行です。
当初の認証サービスの実装では、認証処理に passport1 を利用していました。この認証処理を Firebase Authentication に移行し、認証サービスのプロセス自体も App Engine で稼働させるようにすることで最初のマネージドサービス化を達成しました。
検索サーバのクラスタ化
β版の提供開始から時間が経過するとともに徐々にユーザ数が増加し、また書籍を始めとして Legalscape 上で閲覧できる法律文書の数も増加していきました。すると、法律文書の全文検索を実現している検索サーバの性能問題が顕在化し始めました。
Elasticsearch のプロセスは、この時点ではまだモノリシックな Compute Engine インスタンスに同居していました。そこで検索性能の改善と同時に障害耐性を高めることを目的に、Elasticsearch のプロセスを別の Compute Engine インスタンスに切り出し、またシングルノード構成をやめてクラスタ構成に移行することにしました。
ロードバランサー導入
当初のモノリシック構成であった Compute Engine インスタンスから少しずつプロセスが引き剥がされていき、検索サーバのクラスタ化によってモノリシックな Compute Engine インスタンスにはフロントエンドと API サーバが残っている状態となりました。
このフロントエンドも API サーバもどちらもマネージドサービスに移行することで、より安定したサービス提供が見込めます。しかし Compute Engine インスタンス上で稼動する Nginx によって SSL 終端とバックエンドサービスへのルーティングが依然として行われている限りこの Compute Engine インスタンスが単一障害点であり続け、インスタンスの突然死などで Legalscape のサービス提供が全面的に停止してしまうリスクが存在し続けてしまいます。
ゆえにフロントエンドと API サーバのマネージドサービス移行を実施する前にロードバランサーを導入し、フロントエンドと API サーバのマネージドサービス移行とともに Nginx を完全に退役させる準備を進めることにしました。以下がロードバランサー導入時点のシステム構成図になります。
なおこの図にあるように、ロードバランサーは稼動系と待機系の 2 系統を用意しています。この 2 系統のロードバランサーは障害発生への備えを目的としたものではなく、ロードバランサーのルーティング (URL マップ) を書き換える際に数十秒ほどバックエンドサービスに疎通できなくなる問題2 を回避することがその目的になります。
したがってロードバランサーの URL マップを書き換える際は、Blue-green デプロイメントの要領で待機系の URL マップを先に書き換え、DNS の設定変更によって稼動系と待機系を入れ替えるようにしています。
フロントエンド/APIサーバのマネージドサービス移行
ロードバランサー導入により、フロントエンドと API サーバのマネージドサービス移行への下準備が整いました。
API サーバに関しては認証サービスと同様に App Engine をマネージドサービスとして利用するのが妥当であると判断しましたが、フロントエンドのホスティングについては以下のように選択肢が複数存在しました。
- その 1: App Engine で
nuxt-ts start
のプロセスを稼動させる - その 2:
nuxt-ts build
で生成したアセット (JavaScript や CSS などのファイル) を Cloud Storage でホスティングする - その 3:
nuxt-ts build
で生成したアセットを Firebase Hosting でホスティングする
結果的に Firebase Hosting を選択することにしましたが、この決断に至った理由としてはコスト面で App Engine よりも有利であったこと、また SPA (single page application) 特有の URL の問題に対して有効な解決策 (URL パスのリライト機能) を有していたことが挙げられます。
現在の構成
β版提供開始当初からの逐次的な改善により、当初のゴールとしていた安定的なサービス提供が実現できるシステム構成に進化させることができました。その後も RDBMS の導入やデータ集計・分析基盤の整備により、現在のシステム構成はより進化したものになっています。
なおシステム構成の進化はこれで終わりかというとそうでもなく、API サーバ全体もしくは CPU/memory-intensive な処理だけを App Engine から Cloud Run に移行するなどしてコストの最適化を試みる計画も進んでいます。
また今回ご紹介したシステム構成は主にエンドユーザーフェーシングな部分にフォーカスしたものであり、Legalscape に掲載する法律情報を自動的に収集・変換するシステムや、リーガル・ウェブを実現するためのアノテーションツールなど、Legalscape のプロダクトを支えているシステムすべてのご紹介には至っておりません。
そういうわけで、今回ご紹介できなかった Legalscape のまだ見知らぬシステムにご興味を抱いた方、または Legalscape を支えるシステム構成をより進化させてみたいという気概をお持ちの方、ちょうど SWE や SRE のポジションを現在募集中 でございますのでどうぞ覗いていってみてください!
終わりに
最後までお読みいただきありがとうございました。Legalscape アドベントカレンダー 2021 の次回のエントリは久本から 12/6 (月)3 に公開となります。お楽しみに!
-
express.js に統合可能な認証ミドルウェア ↩
-
IssueTracker には 似たような事例 が報告されており、コンフィギュレーション伝搬の遅延がその問題の原因であるようですが、Google 的には「ダウンタイムではない」ということらしいので利用者側で対策を講じるしかなさそうです ↩
-
弊社のアドベントカレンダーは業務の一環としてエントリを書いてるので、土曜・日曜のエントリ公開はございません ↩