こんにちは。金融ソリューション事業部の水野です。
本記事は電通国際情報サービス Advent Calendar 2023の12月25日の記事です。
今回は、BtoB向けSaaSをマルチテナントで実装した際の検討事項、採用した戦略について記載します。
Kubernetesでクラスタを構築しており、AzureのPaaSであるAKS を利用しました。
テナントとは?
Kubernetesでは、テナントの定義は一つではないと説明されています。
https://kubernetes.io/docs/concepts/security/multi-tenancy/#tenants
テナントが「チーム」を指すか「顧客(カスタマー)」を指すかで定義は異なります。
- テナントがチームを表しているケース
- 各チームはサービスの複雑さに応じてスケーリングする少数のワークロードをデプロイ
- チームは大きな部門であったり、小さなチーム(開発チームなど)に細分されることも
- テナントがカスタマーを表しているケース
- カスタマーごとに専用のワークロードを展開する場合、複数顧客モデルとなる
- テナントは会社全体と大きい場合もあれば、会社の1部署と小さいこともある
概念的には、以下の図のようになります。
- multi-team tenancy
- multi-customer tenancy
今回説明するテナントの定義は、上記 multi-customer tenancyにおけるテナントです。
設計方法論
AzureではなくAWSなのですが・・・ デザインパターンのようなモデル化された3つの手法が提示されています。
まずはこの3つのパターンを整理し、ビジネス上の要求に最もフィットするモデルを選択することにしました。3つの設計モデルの概要及び、大まかなPros./Consは以下です。
モデル | 特徴 | Pros. | Cons. |
---|---|---|---|
サイロ | ・すべてのリソースはテナントごとに専有する ・一つのクラスタはシングルテナントになる |
・テナント境界が強固なためセキュリティ向上 ・リソースの上限に到達する可能性が比較的低い ・アプリケーションの実装難易度が他のモデルより下がる |
・クラウドランニング費用が増大 |
ブリッジ | ・コンピューティングリソースは共有/データストアは専有 ・アカウント/インスタンス/スキーマなど、データストアによって分離方法は異なる |
・サイロよりはインフラ構築難易度が低い ・リスク影響度の高いデータに関するリスクを一定下げられる |
・もっともインフラ的に複雑になりうるDB周りが依然複雑なため、開発・運用コストが増大 |
プール | ・すべてのリソースを各テナントで共有する ・レコードで分離するため、データの取得時はアプリケーションでテナント識別子を指定する必要がある |
・ランニングコストが低い ・構成がシンプル |
・テナント間のデータ保護のほとんどをアプリケーションレイヤで担保 ・リソースのリミットに到達する可能性が比較的高い |
3つのモデルを更にブレイクダウンし、いくつかの軸で評価・検討したものは以下(主観あり)です。
凡例 ◎:最も良い ○:良い △:普通 ×:悪い
特筆すべき点です。
- セキュリティ
- サイロはN/W・H/W、リソースが完全に分離しており、テナント間の分離が容易に実現可能
- ブリッジ、プールは共有部分に依存してリスクは高まるが、設計でカバー可能なレベル
- スケーラビリティ(ビジネス面)
- サイロ→プールに行くにしたがってROIは高まる
- 実装難度・コスト
- サイロのアプリ実装はシングルテナントと同様で特に考慮不要
- ブリッジ→プールに行くほどテナント間のデータ境界を意識する必要があり、インフラ実装難度が上がる
- 可用性
- 全て分離している方が爆心半径が狭い
- パフォーマンス
- 専有リソースが多いほどシステムリソースに余裕がある(調整の余地が大きい)
- デプロイのアジリティ
- デプロイ対象が多いほど大変。プールならば一度のデプロイで全テナントに反映される
マルチテナントにおける要求
今回実装するSaaSにおけるビジネス要求は以下でした。
カテゴリ | 要求事項 | 適したモデル |
---|---|---|
セキュリティ | 極めてセンシティブな企業の機密情報を扱う | サイロ |
セキュリティ | 未許可のデータが他のテナントから参照出来てはならない (アプリケーションのバグやオペミス含め、絶対に別テナントに見せてはならない) |
サイロ |
分離レベル | ストレージアカウント・データベースアカウントはテナントごとに物理的に分離し、データの独立性を保つ | サイロ |
テナント数 | 一桁スタートで徐々に増加し、最大100想定 | 総合的に検討 |
ランニングコスト | 各テナントのランニングコストを可視化したい | サイロ |
ランニングコスト | ランニングコストは可能な限り下げたい | プール |
可用性 | あるテナントの障害を別のテナントの障害に波及させない | サイロ |
コンピューティング リソース |
テナントごとにワークロードが大きく異なり、テナント間でリソース干渉する事態は避けたい | サイロ |
デプロイ | デプロイ対象・回数は少ない方が良い | プール |
デプロイ | デプロイのアジリティを高めたい | プール |
物理的にリソースを分離する/ランニングコストをテナントごとに正確に把握するというビジネス要求から、サイロ寄りのブリッジモデルを選択しました。完全なサイロモデルの場合、テナント数からランニングコストが莫大になるという事情もありました。
AKSでの分離方針
分離方式は、クラスタとノードプールをどのように扱うかで、以下の3つが考えられます。
- 共有クラスタ/共有ノードプール
全てのテナントのアプリケーションを一つのクラスタ、一つのノードプールで共有する方式 - 共有クラスタ/専有ノードプール
全てのテナントのアプリケーションを一つのクラスタで共有するが、アプリケーションが稼働するノードプールはテナントごとに分離する方式 - 専有クラスタ
テナント共通アプリケーション、テナント固有アプリケーション双方を専用のクラスタに分離する方式
クラスタ
PodがデプロイされるKubernetesノードの集合
ノードプール
PodがデプロイされるKubernetesノードの集合
- systemノードプールとuserノードプールから成る
- 可用性の観点からZONE冗長構成を組むため、1ノードプールあたり3ノード必要=2ノードプールで6ノード必要
主要な検討項目ごとに、重みなしで点数を付けた結果が以下です。
項目 | 共有クラスタ/ 共有ノードプール |
共有クラスタ/ 専有ノードプール |
専有クラスタ |
---|---|---|---|
ランニングコスト | ◎ | 〇 | × |
コスト管理の容易性 | × | △ | ◎ |
N/Wの分離 | 〇 | 〇 | ◎ |
IDの分離(アプリケーション) | △ | △ | ◎ |
IDの分離(クラスタオペレーション) | 〇 | 〇 | 〇 |
コンピューティングリソースの分離 | △ | 〇 | ◎ |
運用性(クラスタ管理) | ◎ | ◎ | × |
実装・運用の難易度 | △ | 〇 | ◎ |
1テナントあたりの実装・運用コスト | ◎ | 〇 | × |
拡張性 | ◎ | △ | 〇 |
点数 | 19 | 21 | 19 |
凡例 ◎:3点 ○:2点 △:1点 ×:0点 |
3つの方式でほぼ同点、つまり各分離方針で大差はなく、どの方式でも最終的に実現される可用性、信頼性、セキュリティでは大きな違いはありません。
違いが出る&大きな要素に関わるのは以下の点のため、こちらに絞って検討しました。
- ランニングコスト
- 実装の難易度
- トラブルシューティングの容易性
- 保守運用業務の効率/生産性
- AKSの仕様(提供機能、制約)との要件適合性
検討の結果、共有クラスタ/共有ノードプール
を選択し、ノードプール内のPodは各テナントで占有としました。
決め手になったポイントです。
- 専有クラスタ はコストが割高
- 専有クラスタ は実装難易度は低いが、テナントの数が増えるに従って運用が煩雑になる
共有クラスタ
は、近い将来非推奨になる機能の書き換えが必要だが、実装時点でノックアウトはなく移行も容易- Pod Identity から Azure AD Workload Idenity への移行が必要となる(2023年12月現在既に移行済み)
- 共有クラスタ/専有ノードプール は共有ノードプールよりノードプール部分の実装難易度が低い
- ただし、最大ノードプール数100の上限があるため、100テナントを超えると2つめのクラスタが必要
共有クラスタ/共有ノードプール
は実装・運用の難易度が最も高いが、保守効率が高い共有クラスタ/共有ノードプール
は、ランニングコストが最も低い- テナントごとのコスト割合を厳密に算出することが難しいというデメリットはあるが、おおよそ正しい値を算出できるため許容する
概念図としては以下になります。
マルチテナントにおける要求に記載の通り、データベースアカウントとシークレットをテナント毎に保持する必要があるため、占有リソースとしています。なお、データベースはCosmosDBを採用しています。
テナント分離方針
テナントの分離はThree Tenancy Models For Kubernetesから選択しました。
Three Tenancy Models For Kubernetes
分離モデル | 特徴 |
---|---|
Namespaces as a Service | Namespaceで分離し、各テナントが個別のNamespaceを持つ |
Clusters as a Service | クラスタ単位で分離し、各テナントが個別のクラスタを持つ |
Control planes as a Service | クラスタ単位で分離し、各テナントが個別のコントロールプレーンを持つ |
広く採用されており、最もシンプルに構成できる Namespaces as a Service を採用しました。
Namespaces as a Service
Namespaceで必要な境界を設けます。
大方針は以下です。
- テナントごとにNamespaceを分離
- 通常のワークロードとデータ監査のワークロードを分離するため、同一テナント内で2つのNamespaceに分離
- テナントに依存しない認証などの共通機能は別のNamespaceで管理
分離に伴い、KubernetesやAzureのサービス/コンポーネントは以下を使用しました。
カテゴリ | 概要 | 構成要素 |
---|---|---|
Namespace | ・Kubernetesリソースの分離の基本要素 | Namespace |
N/W | ・テナントのルーティング ・トラフィック制御 |
Application Gateway Ingress Controller Network Policy |
RBAC | ・Kubernetesリソースに対するRBAC ・Azureリソース/外部サービスに対するRBAC |
ClusterRole / ClusterRoleBindings Role / RoleBindings AAD Pod Identity Azure Key Vault Provider for Secrets Store CSI Driver |
コンピューティングリソース | ・使用するリソースの上限 | Resource Quota |
デプロイ | ・運用 | Terraform Module Helm Chart |
上記を踏まえた、テナント毎に分離するアーキテクチャの概念図は以下となります。
N/W
- N/W空間は共有
- テナントのルーティング
- テナントごとにIngressリソースを作成し、テナントごとのPodへのルーティング
- テナントごとのアプリケーションはFrontendのみを公開
- Egressリソースで外部SaaS接続時のNAT IPは、全テナントで共通のIPを共用
- トラフィック制御
- Network Policyを使用してトラフィックを制御し、Pod間の通信を制限
- 自テナントかつ当該Podにアクセスする必要がある送信元からのInboundのみ許可
RBAC
- Kubernetesリソースに対するRBAC
- ClusterRole / ClusterRoleBindings および Role / RoleBindings を利用
- ロールをAzure ADグループに紐づけ、特定の人間が特定のテナントの管理オペレーションを行う
- Azureリソース/外部サービスに対するRBAC
- 各サービスに接続する際に必要なクレデンシャル情報は、テナント内のSecret経由でのみ参照可能
- テナントごとのクレデンシャル情報は自テナント専用のKey Vaultで管理し、Kubernetes Secretと同期させる
- テナントごとのIDでKey Vaultアクセスを認証し、他テナントのKey Vaultはアクセスできない
- 各サービスに接続する際に必要なクレデンシャル情報は、テナント内のSecret経由でのみ参照可能
コンピューティングリソースのクォータ
- テナントごとにvCPU、Memoryなどのコンピューティングリソースの上限をResource Quotaで設定
- ノイジーネイバー問題を防ぐ
- 性能テストの結果および本番トラフィックでのリソース使用量から上限値を決定
テナント追加のオペレーションコストの低減
- テナントごとのAzureリソース、Kubernetesリソースの管理を容易にするためにIaCで構築
- テナント固有の一連のAzureリソースを管理するためのTerraformモジュール
- テナント固有の一連のKubernetesリソースを管理するためのHelmチャート
その他
他にも、権限分掌やセキュリティについて、Azureリソースや外部サービス利用についていくつかのポイントはありますが、本題ではないので簡単に紹介するにとどめます。
- アクセスポリシーにより自テナントのKey Vaultにのみアクセスできるように制御
- CosmosDBのアクセスに必要なアクセスキーは自テナントのKubernetes Secret内に保持
- Kubernetes Secretへのアクセスを自テナントのアプリケーションに限定
- APIで呼びだす外部SaaSは、アクセスに必要なアクセスキー、APIトークンを自テナントのみ参照可能
- ユーザー認証は全てAzureADに委譲
おわりに
今回は、AKS上でSaaSをマルチテナント構成で構築した設計戦略・構築上の大きなポイントについて書かせていただきました。まだまだ改善できるポイントはきっとあるので、日々クラスタを運用しながら検討を続けていきたいと思います。
最後までご覧いただきありがとうございました。皆さま良いお年を!
私たちは一緒に働いてくれる仲間を募集しています!
募集職種一覧執筆:@mizuno.kazuhiro、レビュー:@sato.taichi
(Shodoで執筆されました)