前置き
ある事業Aのマイクロサービス化に伴い、CI/CDパイプラインも他の領域から独立したエンドツーエンドパイプラインになったサービスプロダクトを想定してください。
問題提起
そして、このサービスプロダクトを「他の事業Bでも再利用したい」となったとき、このサービスプロダクトは、事業Aと事業Bで共有する形になります。
この時、このサービスプロダクト用のパイプラインは変えなくていいのでしょうか?
独立したエンドツーエンドパイプラインの構造のままでいいと言えますか?
先に結論
実はパイプラインはそのままではいけません。大幅に変更する必要があります。
単一の事業(シングルテナント)専用のエンドツーエンドパイプラインは、「事業A」のことだけを考えれば良い、非常にシンプルなものでした。
しかし、それを「事業B」と 共有(マルチテナント) する瞬間に、そのサービスプロダクトとCI/CDパイプラインが負うべき「責務」が根本から変わってしまいます。
パイプラインは、「テナントAを壊さずに、テナントBの要求に応える」 という、非常に複雑な責務を新たに負うことになるのです。
なぜパイプラインを変更しなくてはならないのか
単一テナントのパイプラインは、「この変更は、事業Aにとって正しいか?」だけを検証すればOKでした。
ですが、マルチテナントのパイプラインは、
「この変更は、事業Aにとっても、事業Bにとっても、両方にとって安全か?」を検証し、「どのように両者に展開するか?」
を制御しなければなりません。
この 「両方にとって」 という要件が、パイプラインアーキテクチャのあらゆるステップに、以下のような重大な変更を要求します。
1. CI(ビルド・テスト)フェーズの変更
課題
事業Bは、事業Aとは異なる設定(例: 異なる機能フラグ、異なる認証方式)を必要とするかもしれません。
パイプラインの変更
「マルチテナント・テスト」のステージが新たに追加されます。
既存のユニットテストや結合テストが、「テナントA用の設定」と「テナントB用の設定」の両方のパターンで実行される必要があります。
「この変更はAでは動くが、Bの設定ではクラッシュする」といった問題を、CIの段階で検知しなければなりません。
2. CD(デプロイ)フェーズの変更
これが最も大きな変更点です。デプロイ戦略が根本から変わってしまいます。
課題
事業Bのための新機能(v1.1)をデプロイする際、
それが意図せず事業Aの既存機能(v1.0)を破壊するリスクをどう防ぐか?
パイプラインの変更
単純な「エンドツーエンドデプロイ」は廃止され、「プログレッシブ・デリバリー(段階的展開)」 が必須
になります。
(a) カナリアリリース戦略
パイプラインは、v1.1をデプロイした後、「事業Bのトラフィックだけ10%流す」 といった、テナント単位での高度なトラフィック制御を行います。
このとき、事業Aのトラフィックは100% v1.0に流れたままです。
(b) ブルー/グリーン戦略
あるいは、事業Aと事業Bで 物理的に異なる環境(Pod群) にデプロイします。
パイプラインは、「A環境のデプロイ」と「B環境のデプロイ」を個別に実行する
「ファンアウト(分岐)」 構造に変わる必要があります。
3. 検証・適応度関数の変更
課題
デプロイ成功の「定義」が変わります。「Podが起動した」だけでは不十分です。
「事業Aも事業Bも正常である」ことを確認しなければなりません。
パイプラインの変更
デプロイ後の「マルチテナント・スモークテスト」が必須 になります。
パイプラインはデプロイ後、自動的に以下のテストを実行します。
①. 事業Aの認証情報でAPIを実行し、正常に応答することを確認。
②. 事業Bの認証情報でAPIを実行し、正常に応答することを確認。
③. (ここが最重要) 事業Aの認証情報で、事業Bのデータにアクセスしようとし、「失敗(Forbidden)」することを確認(=テナント間のデータ分離が壊れていないか検証)。
これらのテスト(適応度関数)がすべてパスして初めて、パイプラインは「成功」と見なされます。
ここのまとめ
サービスをマルチテナント化するという決定は、アプリケーションアーキテクチャだけでなく、CI/CDパイプラインアーキテクチャの根本的な見直しを強制します。
独立したE2Eパイプラインは、テスト、デプロイ戦略、検証のすべてにおいて、「テナント」という新しい概念を管理するための、より高度で複雑なアーキテクチャへと進化しなければなりません。
マルチテナント化に伴うパイプライン全体の設計上の注意
さて、上記のエンドツーエンドパイプラインの状態から、マルチテナント化に伴うパイプラインアーキテクチャの変更は、ある意味、パイプライン全体というマクロな世界での
単一責任状態(エンドツーエンドパイプライン)から、変更容易性よりも再利用性を重視した多目的な単一責任原則に意図的に反した状態への変化
と言えないでしょうか?
この際に物理的にはパイプライン全体は、事業A&B用という、多目的になってしまいます。
多目的なので、何も考えずにパイプラインの設計・運用をしてしまっては、パイプライン全体の保守性を容易に低下してしまうことは容易に想像がつくと思います。
このようなトラブルを回避するにはどうしたらいいでしょうか?
実はここでも、SOLID原則が思想が生きてきます。
SRPから「多目的」へ(再利用性のための意図的違反)
Before (E2Eパイプライン)
・責務
「事業Aのプロダクトをデプロイする」という単一の責務(SRP)。
・凝集性
非常に高い。変更理由は「事業Aの要件変更」のみ。
After (マルチテナント・パイプライン)
・責務
「事業Aと事業Bのプロダクトを、互いに影響を与えずにデプロイする」という多目的な責務。
・凝集性
意図的に下げられる。
変更理由は「事業Aの要件変更」「事業Bの要件変更」「テナント共通のインフラ変更」の3つに増えます。
これは「2つの別々のパイプラインを作る」というコストを避け、共通のビルド・デプロイロジックを 「再利用」 するために、あえてSRPに違反しています。
なぜISPが「解毒剤」になるのか
このSRP違反の結果、パイプラインは
「ファット・パイプライン」(OOPでいう「ファット・インターフェイス」)
になる危険性をはらみます。
Fatパイプラインの脅威
「ファット・パイプライン」とは、事業Aのための変更(例: A用のテスト追加)が、
無関係な事業Bのデプロイプロセスにまで影響を与えてしまう
(例: Bのパイプラインが遅くなる、不安定になる)状態です。
ISP適用で脅威への対策
そこで、インターフェイス分離の原則(ISP) の思想に従い、この「ファット・パイプライン」を内部で論理的に分割し、この問題を解決します。
ISPは「クライアント(利用者)は、利用しないインターフェイスに依存すべきではない」という思想です。
このパイプラインにおいて、
「事業A」と「事業B」は、それぞれがパイプラインの「クライアント」 です。
ISPを適用したパイプラインアーキテクチャ
共通インターフェイス(全テナントが依存)
・Build(ビルド)
・Security Scan(共通セキュリティスキャン)
・Deploy(デプロイの“仕組み”自体)
事業A専用インターフェイス(事業Aのみ依存)
・Test Suite (Tenant A)
・Fitness Functions (Tenant A)
・Smoke Test (Tenant A)
事業B専用インターフェイス(事業Bのみ依存)
・Test Suite (Tenant B)
・Fitness Functions (Tenant B)
・Smoke Test (Tenant B)
パイプラインアーキテクチャは、これらを論理的に明確に分離します。
例えば、
Test Suite (Tenant B)の修正が、Test Suite (Tenant A)のコードや実行に一切影響を与えないように、ファイルやジョブ定義をカプセル化して分離します。
ここのまとめ
マルチテナント化とは、
パイプラインの単一責任(SRP)を意図的に犠牲にして、再利用性を取る
アーキテクチャ上のトレードオフです。
そして、そのトレードオフによって生じる、保守性の低下という「副作用」を抑え込むために、インターフェイス分離(ISP)の設計思想を適用し、
「テナントAにしか関係ないもの」と「テナントBにしか関係ないもの」をパイプライン内部で論理的に厳格に分離することが、このアーキテクチャを成功させるための鍵となります。
