GKEを用いたマルチテナントSaaSの構築

アイキャッチ

はじめに

こんにちは、プラットフォーム部の近藤です。

2018年に初期リリースしたチャット機能は、システムの拡張性と安定性の向上が課題となっていました。そこで、これらの課題を解決し、より快適なサービスを提供するために、2025年3月に「TUNAGチャット」をリリースしました。本記事では、このリプレースにおいて、マルチテナント型の SaaS アプリケーションをどのように構成したのかをご紹介します。

prtimes.jp

図1にアーキテクチャ全体の構成を示しました。Google Kubernetes Engine (GKE) を中心に構成しています。実際にはもっと多くのコンポーネントを用いていますが、この記事で取り上げたい内容に限定して図にしました。弊社ではこれまで、Amazon Elastic Container Service (Amazon ECS) を採用することがほとんどで、GKE の導入は今回が初めてです。

図1 全体の構成図
図1 全体の構成図

マルチテナントを構築する上で考慮すべきトピックは多岐にわたります。本記事では、インフラストラクチャの共有・占有という主にテナント分離の観点に焦点を当てて説明させていただきます。

ドメイン

図1で示したとおり、アプリケーションはテナントごとに分離されています。このようなサイロモデルにおいては、テナントルーティングが必要です。テナントルーティングの戦略として、(1)ドメイン駆動型ルーティング、(2)データ駆動型ルーティングの2つが考えられます*1。図2に、それぞれの比較を簡単にまとめました。詳しくは、Amazon Web Services ブログなどもご参照ください*2

図2 ルーティング戦略の比較
図2 テナントルーティング戦略の比較

今回は認証をアプリケーション側で実装する必要があったため、ドメイン駆動型ルーティングを選択しました。具体的には、各テナントに固有のサブドメインを作成し、テナントを識別しています。そしてサブドメインには、意味のない文字列ではなく、テナントごとにパーソナライズされたバニティサブドメインを使用しています。例えば、株式会社スタメンの場合、「stmn.example.com」のようなサブドメインとなります。バニティサブドメインはシステム側ではなく顧客が決定するため、運用や開発にコストが発生しますが、ユーザーエクスペリエンスを重視し、この方式を採用しました*3

ロードバランサー

ロードバランサーは、全テナント共有のインフラストラクチャとなっています。インフラストラクチャを共有する理由は、主にコストの最適化のためです。Cloud Load Balancing は、受信・送信データ量に加えて、「転送ルール数 × 利用時間」に対する従量課金のため*4、テナント間で共有することでコストは抑えられます。

Kubernetesクラスター内で実行されているサービスへの外部アクセスには、一般的に Ingress が使用されることが多いかもしれません。今回は図1に示すとおり、各テナントは Namespace で分離されています。Ingress は Namespace を跨いで利用することが単純にはできないため、このような場合には Gateway の使用が選択肢として考えられます*5。Gateway は Ingress のスーパーセットとして提供されている点*6も、採用理由の一つです。ただし、GKE Gateway では Cloud CDN をサポートしていない点*7には留意が必要です。

アプリケーション

今回のアプリケーションの要件として、テナントごとに Pod を用意して、サーバーを立てる必要がありました。Pod を分離した主目的ではありませんが、テナントごとに最適なコンピューティングリソースが適用できたり、ノイジーネイバーの対策にも繋がっています。

また、各テナントの Pod は Namspace によって分離しています*8。テナント専用 Namespace によるサイロ化には、いくつかのメリットがありました。

第一に、分離された環境により、セキュリティを強化できます。テナント間のデータ漏洩は、SaaS において非常に大きなビジネスリスクの一つです。

第二、モニタリングでの利便性です。Google Cloud のコンソール上で、Namespace 単位で各種メトリクスが確認でき、より柔軟かつ素早く分析することが可能です。

第三に、Kubernetes オブジェクトの名前の衝突を意識する必要がなくなるなど、オブジェクトの管理が容易になります。今回のアプリケーションでは、新規にテナントを追加する場合は、テナント専用の設定ファイルを追加してワークフローを実行します。解約に伴いテナントを削除する場合は、Namespace を削除することで、属するオブジェクトを一括で削除できます。こうした管理も、Namespace による分離が効果を発揮しています。

図3 ファイル管理のイメージ
図3 ファイル管理のイメージ

データベース

マルチテナントにおけるデータベースの分離パターンはいくつか考えられます。(1)単一インスタンス共通データベース方式、(2)単一インスタンス個別データベース方式、(3)インスタンス分離方式などです*9。また、RDB にとらわれず、マネージドなサービスも選択肢になり得るかもしれません*10。今回は、アプリケーションの特性として、RDB の利用が前提となっていました。

データベースの分離パターン
データベースの分離パターン

単一インスタンス共通データベース方式は、インフラ費用や運用コストにメリットがあります。このパターンの場合、データ分離は PostgreSQL の Row Level Security を利用したり、アプリケーションレイヤーで対応することになります。これまで弊社のRailsアプリケーションでは、主にこのパターンを採用してきました。

今回は、アプリケーションレイヤーでデータ分離を対応することが困難だったため、単一インスタンス個別データベース方式を採用しました。テナントごとにデータベースユーザを作成し、各アプリケーションはそのユーザを利用して、データベースにアクセスします。各テナントは、別テナントのデータベースを参照することは、権限の制約によりできません。一方で、インスタンスを共有しているため、ノイジーネイバーなどはリスクとなり得ます。インスタンス分離方式は、インフラ費用や運用コストが高いため採用は現実的ではありませんでした。

図5 分離されたアクセスのイメージ
図5 分離されたアクセスのイメージ

単一インスタンス共通データベース方式を採用している弊社の Rails アプリケーションでは、activerecord-multi-tenant*11 という Gem を用いて、アプリケーションレイヤーでデータ分離を実現しています。ただし、Gem による制約の適用を確実にするため、弊社では独自のテストコードを実装するといった様々な工夫が必要になっており、その対応は簡単ではありません。

単一インスタンス個別データベース方式にも課題はあり、1つのインスタンス内のデータベース数とテーブル数がスケールしたことで、マイグレーションにかかる時間の増加などの課題が生じた事例をSmartHR社が紹介しています*12。今回はそこまでのスケールは不確実であり、懸念には考えていません。また、アプリケーションの特性として、テナントは明確に分離されており、今後は複数インスタンス構成も比較的容易に導入できると考えています。

図6 複数インスタンス構成
図6 複数インスタンス構成

最後に

私にはGoogle Cloud と Kubernetes の経験がほとんどなかったのですが、さまざまな選択肢の Pros & Cons やトレードオフを整理して構築を進めていくことは非常に良い経験になりました。

9年目を迎えた弊社の TUNAG では、ありがたいことに導入企業数やユーザー数が着実に増えています。このようなスケールに伴い、今後の中長期的なスケーラブルなアーキテクチャのための取り組みを積極的に行っています。また、マルチプロダクト戦略にも本格的に取り組んでおり、0 → 1でアプリケーションを構築する機会も今後ますます増えていくはずです。

クラウドを中心としたアーキテクチャ構築に興味を持っているエンジニアにとっては、非常にやりがいのある環境だと感じています。少しでも興味を持っていただけたら、ぜひまずはカジュアル面談でお話しできればと思います。

herp.careers

参考

Golding Tod. (2024). Building Multi-Tenant Saas Architectures: Principles, Practices, and Patterns Using AWS. California: O'Reilly Media. (ゴールディング・トッド. 河原 哲也・櫻谷 広人(訳)(2025).マルチテナントSaaSアーキテクチャの構築―原則、ベストプラクティス、AWSアーキテクチャパターン)

*1:AWS における SaaS アプリケーションのテナントルーティング戦略

*2:SaaS におけるテナントリソースへのリクエストルーティングを JWT を用いて実現する

*3:バニティサブドメインは、SlackやZoomなど広く採用されている。今回のアプリケーションの仕様では、ログイン時にサブドメインを入力するため、記憶しやすいバニティサブドメインを用いた方がユーザーエクスペリエンスは向上する。SmartHR社のスマートフォン向けアプリのログインにサブドメインの入力が必要になりました(03/21更新)のような体験に近い。

*4:All networking pricing

*5:Cross namespace communication in GKE ingress

*6:About the Gateway API Comparison of Ingress and Gateway

*7:Deploying Gateways Restrictions and limitations

*8:Google Kubernetes Engine 上で SaaS プラットフォームを作成する

*9:Windows Azureエンタープライズアプリケーション開発技法 マルチテナント・アーキテクチャを参考にした。"データベース"という単語が PostgreSQL で用いられる"データベース"との混同につながる。よって説明の便宜上、参考記事の語彙を、「データベース → インスタンス」、「スキーマ → データベース」に置き換えて本記事では表現をさせていただいた。

*10:RDBを使わない究極のマルチテナント

*11:activerecord-multi-tenant

*12:SmartHR が定期メンテナンスを始めた理由とやめる理由