好きな技術を布教したい 😗

〜 沼のまわりに餌をまく 〜

【ArgoCD🐙️】KubernetesのマルチテナントパターンとArgoCDの実践テナント設計


この記事から得られる知識

この記事を読むと、以下を "完全に理解" できます✌️

  • Kubernetesのマルチテナントパターンの種類
  • マルチテナントパターンをArgoCDで実践する場合にオススメのパターン (★)
  • ArgoCDのNamespacedスコープモードとClusterスコープモード
  • ArgoCDのテナントが防いでくれる誤った操作の具体例

記事のざっくりした内容は、以下のスライドからキャッチアップできちゃいます!



01. はじめに


どうも、熟成アルトバイエルンです。

argocd_community-board
画像引用元:Argo Project


さて最近の業務で、全プロダクトの技術基盤開発チームに携わっており、全プロダクト共有のArgoCD🐙のマルチテナント化を担当しました。

プロダクトが稼働するKubernetes Clusterが数十個あり、Clusterによっては複数のチームが合計100個以上のマイクロサービスを動かしています。

このような大規模なマイクロサービスシステムがいくつもある状況下で、ArgoCDのマルチテナント設計の知見を深められたため、記事で解説しました。

書きたいことを全部書いたところ、情報量がエグいことになってしまったため、気になる章だけでも拾って帰っていただけるとハッピーです🙏

それでは、もりもり布教していきます😗

記事中のこのボックスは、補足情報を記載しています。

飛ばしていただいても大丈夫ですが、読んでもらえるとより理解が深まるはずです👍


02. なぜマルチテナントが必要なのか

シングルテナントの場合

そもそも、なぜArgoCDにマルチテナントが必要なのでしょうか。

例えば、マニフェストのデプロイ先となるプロダクト用Cluster (例:foobarbaz) があると仮定します。

ArgoCDをシングルテナントにする場合、各プロダクトチームの操作するApplicationを同じテナントに共存させることになります。

この場合、単一のargocd-server (ダッシュボード) から全てのApplicationを操作できて便利です。

しかし、プロダクト用Cluster数が増えていくにつれて、問題が起こり始めます。

例えば、いずれかのプロダクトチームが誤ったApplicationを操作し、結果的に誤ったプロダクト用Clusterにマニフェストをデプロイしてしまう可能性があります。

もちろん、システムでインシデントを起こしてやろうという悪意を持った人が、誤ったプロダクト用Clusterを意図的に選ぶ可能性もあります😈

argocd_single-tenant


マルチテナントの場合

その一方で、いい感じのマルチテナントにしたとします。

プロダクトチームは、認可されたテナントに所属するApplicationにのみを操作でき、反対に無認可のテナントのApplicationは操作できません。

これにより、誤ったプロダクト用Clusterにマニフェストをデプロイすることを防げます。

argocd_multi-tenant


03. Kubernetesのマルチテナントパターン

マルチテナントパターンの一覧

ArgoCDのテナント設計を実践する前に、Kubernetesにはどんなマルチテナントパターンがあるのでしょうか。

Kubernetesのマルチテナントパターンは、以下に大別できます。

Clusters
as-a-Service
Control Planes
as-a-Service
Namespaces
as-a-Service
カスタムリソース
テナント
テナント単位 実Cluster 仮想Cluster Namespace ツール固有の論理空間
テナント間でKubernetesリソースを分離できるか Cluster
スコープ
リソース
ツールによる
Namespaced
スコープ
リソース
ツールによる
ツール
  • AWS EKS
  • GCP GKE
  • Azure AKE
  • Kubeadm
など
  • Kcp
  • tensile-kube
  • vcluster
  • VirtualCluster
など
Namespaceを増やすだけなので特別なツール不要
  • ArgoCDのAppProject
  • CapsuleのTenant
  • kioskのAccount
  • KubeZooのTenant
など


▶ 他のマルチテナントの分類方法について

本記事では言及しませんが、"ソフトマルチテナンシー""ハードマルチテナンシー" といった分類方法もあります。

この分類方法では、テナント間の分離度の観点で各マルチテナントを種別します。

ソフトマルチテナンシーは、互いに信頼できる前提の上で、テナント間を弱く分離します。

その一方で、ハードマルチテナンシーは、互いに信頼できない前提の上でテナント間を強く分離します。

分離度がソフトとハードのいずれであるかに客観的な指標がなく、やや曖昧な種別になってしまうため、本記事の X as-a-Service の方が個人的には好みです♡♡♡


Clusters as-a-Service

Clusters as-a-Serviceは、テナントごとに独立したClusterを提供します。

ツールとして、AWS EKS、GCP GKE、Azure AKE、Kubeadm、などがあります。

argocd_multi-tenant_k8s_clusters-as-a-service


Control Planes as-a-Service

Control Planes as-a-Serviceは、テナントごとに独立したコントロールプレーン (言い換えば仮想Cluster) を提供します。

ツールとして、Kcptensile-kubevclusterVirtualCluster、などがあります。

argocd_multi-tenant_k8s_control-planes-as-a-service


Namespaces as-a-Service

Namespaces as-a-Serviceは、テナントごとに独立したNamespaceを提供します。

Namespaceを増やすだけなため、ツールは不要です。

argocd_multi-tenant_k8s_namespaces-as-a-service


カスタムリソーステナント

カスタムリソーステナントは、テナントごとにツール固有の論理空間 (例:ArgoCDのAppProject、CapsuleのTenant、kioskのAccount、KubeZooのTenant、など) を提供します。

ツールによっては、X as-a-Service も兼ねている場合があります。

今回紹介するAppProjectはNamespaceテナントを兼ねており、カスタムリソーステナント で解説しています。

argocd_multi-tenant_k8s_tool


04. ArgoCDでのテナントパターン実践一覧

お待たせしました。

ここからは、KubernetesのマルチテナントパターンをArgoCDで具体的に実践し、おすすめのパターン実践を解説していきます。

なお、オススメするものを ★ としています。

実Cluster
テナント
仮想Cluster
テナント
Namespace
テナント
AppProject
テナント
CLモード
AppProject
テナント
NSモード
対応するテナントパターン Clusters
as-a-Service
Control Planes
as-a-Service
Namespaces
as-a-Service
カスタムリソース
テナント
ArgoCDがテナント間で占有 / 共有 占有 占有 占有 共有 占有
テナント間でKubernetesリソースを分離できるか Namespaced
スコープ
リソース
Cluster
スコープ
リソース
オススメ ★★


以降の図の凡例です。

ArgoCDの各コンポーネント (application-controller、argocd-server、dex-server、repo-server) と各リソース (Application、AppProject) を区別しています。

argocd_multi-tenant_legend


04-02. Clusters as-a-Service 実践

実Clusterテナント

実Clusterテナントは、Clusters as-a-Serviceなテナントの実践であり、実際のClusterをテナントの単位とします。

後述の仮想Clusterと対比させるために、"実Cluster" と呼ぶことにします。

各プロダクトチームは、実Clusterテナント内のApplicationを操作し、正しいプロダクト用Clusterにマニフェストをデプロイします。

argocd_multi-tenant_physical_cluster


オススメしない理由

実Clusterテナントには、以下のメリデメがあります。

デメリットの回避策も考慮して、独断と偏見でオススメしませんでした。

アーキテクチャ
特性
メリット ⭕️ デメリット × デメリットの回避策
拡張性 - テナントを増やすために実Clusterを用意する必要があり、作業量が多い。 ➡︎ IaCツールで実Clusterを用意するようにすれば作業量を減らせるが、やっぱりとてもつらい😭
安全性
(セキュリティ)
ClusterからClusterへの名前解決を不可能にすれば、他のテナントからの通信を遮断できる。 - ➡︎ -
保守性 ClusterスコープまたはNamespacedスコープなKubernetesリソースを他のテナントから分離できる。
これらのKubernetesリソース (特にCRD) の変更が他のテナントに影響しない。
各テナントが、個別に実Clusterを保守しないといけない。(例:アップグレード、機能修正、など) ➡︎ 回避できず、とてもつらい😭
性能 Clusterのハードウェアリソースを他のテナントと奪い合うことなく、これを独占できる。 - ➡︎ -
信頼性 テナントごとに実Clusterが独立しており、他の実Clusterから障害の影響を受けない。 - ➡︎ -


04-03. Control Planes as-a-Service 実践

仮想Clusterテナント - ★

仮想Clusterテナントは、Control Planes as-a-Serviceなテナントの実践であり、仮想Clusterをテナントの単位とします。

各プロダクトチームは、仮想Clusterテナント内のApplicationを操作し、正しいプロダクト用Clusterにマニフェストをデプロイします。

argocd_multi-tenant_virtual_cluster


オススメした理由

仮想Clusterテナントには、以下のメリデメがあります。

デメリットの回避策も考慮して、独断と偏見で オススメ しました。

アーキテクチャ
特性
メリット ⭕️ デメリット × デメリットの回避策
拡張性 テナントを増やすためにマニフェストで定義した仮想Clusterを用意するだけでよく、実Clusterを用意することと比べて作業量が少ない。 - ➡︎ -
安全性
(セキュリティ)
仮想ClusterからホストClusterへの名前解決を不可能にすれば、他のテナントからの通信を遮断できる。 - ➡︎ -
保守性 ClusterスコープまたはNamespacedスコープなKubernetesリソースを他のテナントから分離できる。
これらのKubernetesリソース (特にCRD) の変更が他のテナントに影響しない。
各テナントが、個別に仮想Clusterを保守しないといけない。(例:アップグレード、機能修正、など) ➡︎ 仮想Clusterに関する知見を持つ組織であれば、各テナントで保守できる。
性能 - Clusterのハードウェアリソースを他のテナントと奪い合うことになる。 ➡︎ 多くの利用者が同時並行的にArgoCDを操作する状況になりにくければ、奪い合いも起こらない。
信頼性 テナントごとに仮想Clusterが独立しており、他の仮想Clusterから障害の影響を受けない。 - ➡︎ -


04-04. Namespaces as-a-Service 実践

Namespaceテナントは、Namespaces as-a-Serviceなテナントの実践であり、Namespaceをテナントの単位とします。

後述の AppProjectテナント は二重のテナントを持ち、Namespaceテナントも兼ねています。

そのため、ここではNamespaceテナントの解説は省略します。


04-05. カスタムリソーステナントの実践

AppProjectテナント

argocd_multi-tenant_appproject

AppProjectテナントは、カスタムリソーステナントの実践であり、NamespaceとAppProjectをテナントの単位とします。

AppProjectテナントは、二重のテナント (第一テナントにNamespace第二テナントに複数のAppProject) を持ち、"あらゆる面から" マニフェストのデプロイを制限します。

特に、AppProjectはNamespaceスコープなカスタムリソースであり、自身に所属するApplicationを一括して制限します。

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: foo-tenant
  namespace: foo
  # 自身に所属するApplicationを制限する
spec: ...
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: infra-application
  namespace: foo
spec:
  # foo-tenantに所属する
  project: foo-tenant
  ...


▶ カスタムリソースの仕様について

カスタムリソースのCRDの実装から、ドキュメントに未記載の仕様を読み取れることがあります。

例えば、本記事ではAppProjectに自由にNamespaceを設定していますが、ArgoCDのドキュメントには任意のNamespaceを設定できることが明記されていません。

しかし、AppProjectのCRDの.spec.scopeキーからも分かる通り、AppProjectはNamespacedスコープなカスタムリソースであり、任意のNamespaceを設定できます👍
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  labels:
    app.kubernetes.io/name: appprojects.argoproj.io
    app.kubernetes.io/part-of: argocd
  name: appprojects.argoproj.io
spec:
  group: argoproj.io
  names:
    kind: AppProject

    ...

  # Namespacedスコープなカスタムリソースであるとわかる
  scope: Namespaced

...

CLモード vs. NSモード

ArgoCDには、ClusterスコープモードNamespacedスコープモード (以降、"CLモード""NSモード") があります。

スコープモードに応じて、AppProjectテナントの設計方法が異なります。

本章では、CLモードとNSモードの両方でAppProjectテナントを解説していきます。


05. CLモードなArgoCD

CLモードなArgoCDとは

CLモードなArgoCDの場合、各テナント間で共有のArgoCDを管理します

例えば、AppProjectテナントとして、プロダクト別のNamespace (foobarbaz) とAppProject (foobarbaz) を用意します。

別途、ArgoCD専用のNamespace (argocd) を用意し、ここに関連するKubernetesリソース (例:ConfigMap) を配置します。

各プロダクトチームは、AppProjectテナント内のApplicationを操作し、正しいプロダクト用Clusterにマニフェストをデプロイします。

argocd_multi-tenant_appproject_cluster-scope

AppProject

NSモードと同様にして、AppProjectに所属するApplicationによるマニフェストのデプロイを制限できます。

例えば、以下のような実装になります。

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: foo-tenant
  namespace: foo
spec:
  destinations:
    # ArgoCD用Clusterに関する認可を設定する
    # App Of Appsパターンの場合に使用する
    - namespace: foo
      server: "https://kubernetes.default.svc"
    # プロダクト用Clusterに関する認可を設定する
    - namespace: "*"
      server: https://foo-cluster.gr7.ap-northeast-1.eks.amazonaws.com
  # CLモードでは設定が必要である
  sourceNamespaces:
    - foo

Applicationを操作するログインユーザーが、無認可のNamespaceやClusterをデプロイ先に指定できないように、.spec.destinationキーで制限しています。

一方で後述のNSモードとは異なり、CLモードなArgoCDは任意のNamespaceのApplicationにアクセスできます。

そのため、.spec.sourceNamespacesキーで、特定のNamespaceのApplicationがこのAppProjectに所属できないように、ApplicationのNamespaceを制限しています。

ArgoCDコンポーネント用ConfigMap (argocd-cmd-params-cm)

NSモードと同様にして、argocd-cmd-params-cmでは、ArgoCDの各コンポーネントのコンテナの引数を設定できます。

例えば、以下のような実装になります。

apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cmd-params-cm
  # 専用のNamespaceを設定する
  namespace: argocd
data:
  # CLモードでは設定が必要である
  # 全てのNamespaceを指定したい場合は、ワイルドカードを設定する
  application.namespaces: "*"

.application.namespacesキーは、argocd-serverとapplication-controllerの--application-namespacesオプションに相当します。

一方での後述のNSモードとは異なり、CLモードなArgoCDは任意のNamespaceのApplicationにアクセスできます。

--application-namespacesオプションで、任意のNamespaceにアクセスするための認可を設定できます。

--application-namespacesオプションの設定方法について

argocd-cmd-params-cmの代わりに、例えば以下のようにPodに引数を直接渡しても良いです🙆🏻‍

例えば、以下のような実装になります。
apiVersion: v1
kind: Pod
metadata:
  name: argocd-server
  namespace: argocd
spec:
  containers:
    - name: argocd-server
      image: quay.io/argoproj/argocd:latest
      args:
        - /usr/local/bin/argocd-server
        # コンテナ起動時の引数として
        - --application-namespaces="*"

  ...
apiVersion: v1
kind: Pod
metadata:
  name: argocd-application-controller
  namespace: argocd
spec:
  containers:
    - name: argocd-application-controller
      image: quay.io/argoproj/argocd:latest
      args:
        - /usr/local/bin/argocd-application-controller
        # コンテナ起動時の引数として
        - --application-namespaces="*"

  ...

ログインユーザー用ConfigMap (argocd-rbac-cm)

NSモードと同様にして、argocd-rbac-cmでは、Applicationを操作するログインユーザーが、無認可のAppProjectやNamespaceに所属するApplicationを操作できないように制限します。

例えば、以下のような実装になります。

apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-rbac-cm
  # 専用のNamespaceを設定する
  namespace: argocd
data:
  # デフォルトのロール
  # @see https://github.com/argoproj/argo-cd/blob/master/assets/builtin-policy.csv#L9-L16
  policy.default: role:readonly
  policy.csv: |
    p, role:foo, *, *, foo/*/*, allow
    p, role:bar, *, *, bar/*/*, allow
    p, role:baz, *, *, baz/*/*, allow

    g, foo-team, role:foo
    g, bar-team, role:bar
    g, baz-team, role:baz
  scopes: "[groups]"

認証済みグループ (foo-team、bar-team、baz-team) に対して、無認可のAppProject (foobarbaz) に所属するApplicationを操作できないように、認可スコープを制限しています。


▶ AppProjectの認可定義の記法について

ConfigMap (argocd-rbac-cm) の認可スコープの定義には、 Casbin の記法を使用します。

今回の実装例で使用したp (パーミッション) とg (グループ) では、以下を記法を使用できます👍
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-rbac-cm
  namespace: argocd
data:
  policy.default: role:readonly
  policy.csv: |
    # ロールとArgoCD系カスタムリソースの認可スコープを定義する
    p, role:<ロール名>, <Kubernetesリソースの種類>, <アクション名>, <AppProject名>/<ApplicationのNamespace名>/<Application名>, <許否>

    # 認証済みグループにロールを紐付ける
    g, <グループ名>, role:<ロール名>
  scopes: "[groups]"



オススメしない理由

CLモードなArgoCDのAppProjectテナントには、以下のメリデメがあります。

デメリットの回避策も考慮して、独断と偏見でオススメしませんでした。

アーキテクチャ
特性
メリット ⭕️ デメリット × デメリットの回避策
拡張性 テナントを増やすためにNamespaceとAppProjectを用意するだけでよく、作業量が少ない。 - ➡︎ -
安全性
(セキュリティ)
NetworkPolicyでNamespace間の名前解決を不可能にすれば、他のNamespaceからの通信を遮断できる。 - ➡︎ -
保守性 ArgoCD用Clusterの管理者が単一のClusterを保守すればよい。(例:アップグレード、機能修正、など) AppProjectはNamespacedスコープなカスタムリソースのため、ClusterスコープなKubernetesリソースを他のテナントと共有しないといけない。
そのため、ClusterスコープなKubernetesリソース (特にCRD) の変更は全てのテナントに影響する。
➡︎ ArgoCDのアップグレード時 (CRDの変更時) は、ついでにKubernetesもアップグレードしたい。
新しいClusterを別に作成し、そこで新ArgoCDを作成すれば一石二鳥である。
性能 - Clusterのハードウェアリソースを他のテナントと奪い合うことになる。 ➡︎ 多くの利用者が同時並行的にArgoCDを操作する状況になりにくければ、奪い合いも起こらない。
信頼性 - ClusterまたはArgoCDで障害が起こると、これは全てのテナントに影響する。 ➡︎ 代わりにNodeやArgoCDを十分に冗長化して可用性を高めれば、影響を緩和できる。
ただ、そもそもの影響範囲が大きすぎる😭


05-02. NSモードなArgoCD - ★★

NSモードなArgoCDとは

NSモードなArgoCDの場合、前述のCLモードとは異なり、各AppProjectテナント間でArgoCDを占有します。

例えば、AppProjectテナントとして、プロダクト別のNamespace (foobarbaz) とAppProject (foobarbaz) を用意します。

各AppProjectテナントに、ArgoCDと関連するKubernetesリソース (例:ConfigMap) を配置します。

各プロダクトチームは、AppProjectテナント内のApplicationを操作し、正しいプロダクト用Clusterにマニフェストをデプロイします。

argocd_multi-tenant_appproject_namespaced-scope

AppProject

CLモードと同様にして、AppProjectに所属するApplicationによるマニフェストのデプロイを制限できます。

例えば、以下のような実装になります。

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: foo-tenant
  namespace: foo
spec:
  destinations:
    # ArgoCD用Clusterに関する認可を設定する
    # App Of Appsパターンの場合に使用する
    - namespace: foo
      server: "https://kubernetes.default.svc"
    # プロダクト用Clusterに関する認可を設定する
    - namespace: "*"
      server: https://foo-cluster.gr7.ap-northeast-1.eks.amazonaws.com
# NSモードでは設定が不要である
# sourceNamespaces:
#   - foo

Applicationを操作するログインユーザーが、無認可のNamespaceやClusterをデプロイ先に指定できないように、.spec.destinationキーで制限しています。

前述のCLモードとは異なり、NSモードなArgoCDは自身が所属するNamespaceのApplicationのみにアクセスできます。

そのため、.spec.sourceNamespacesキーでマニフェストのデプロイを制限する必要はありません。

ArgoCDコンポーネント用ConfigMap (argocd-cmd-params-cm)

CLモードと同様にして、argocd-cmd-params-cmでは、ArgoCDの各コンポーネントのコンテナの引数を設定できます。

例えば、以下のような実装になります。

apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cmd-params-cm
  namespace: foo
data:
# NSモードでは設定が不要である
# application.namespaces: "*"

前述の通り、.application.namespacesキーは、argocd-serverとapplication-controllerの--application-namespacesオプションに相当します。

前述のCLモードとは異なり、NSモードなArgoCDは自身が所属するNamespaceのApplicationのみにアクセスできます

そのため、.application.namespacesキーでNamespaceに関する認可を設定する必要はありません

もちろん、Podのコンテナ引数にも設定は不要です。

ログインユーザー用ConfigMap (argocd-rbac-cm)

CLモードと同様にして、argocd-rbac-cmでは、Applicationを操作するログインユーザーが、無認可のAppProjectやNamespaceに所属するApplicationを操作できないように制限します。

例えば、以下のような実装になります。

apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-rbac-cm
  namespace: foo
data:
  # デフォルトのロール
  # @see https://github.com/argoproj/argo-cd/blob/master/assets/builtin-policy.csv#L9-L16
  policy.default: role:readonly
  policy.csv: |
    p, role:app, *, *, app/*/*, allow
    p, role:infra, *, *, infra/*/*, allow

    g, app-team, role:app
    g, infra-team, role:infra
  scopes: "[groups]"

認証済みグループ (app-team、infra-team) に対して、無認可のAppProject (appinfra) に所属するApplicationを操作できないように、認可スコープを制限しています。


特にオススメした理由

NSモードなArgoCDのAppProjectテナントには、以下のメリデメがあります。

デメリットの回避策も考慮して、独断と偏見で 特にオススメ しました。

アーキテクチャ
特性
メリット ⭕️ デメリット × デメリットの回避策
拡張性 テナントを増やすためにNamespaceとAppProjectを用意するだけでよく、作業量が少ない。 - ➡︎ -
安全性
(セキュリティ)
NetworkPolicyでNamespace間の名前解決を不可能にすれば、他のNamespaceからの通信を遮断できる。 - ➡︎ -
保守性 単一のClusterを保守すればよい。(例:アップグレード、機能修正、など) AppProjectはNamespacedスコープなカスタムリソースのため、ClusterスコープなKubernetesリソースを他のテナントと共有しないといけない。
そのため、ClusterスコープなKubernetesリソース (特にCRD) の変更は全てのテナントに影響する。
➡︎ ArgoCDのアップグレード時 (CRDの変更時) は、ついでにKubernetesもアップグレードしたい。
新しいClusterを別に作成し、そこで新ArgoCDを作成すれば一石二鳥である。
性能 - Clusterのハードウェアリソースを他のテナントと奪い合うことになる。 ➡︎ 多くの利用者が同時並行的にArgoCDを操作する状況になりにくければ、奪い合いも起こらない。
信頼性 テナントごとにArgoCDを占有しており、他のArgoCDから障害の影響を受けない。 Clusterで障害が起こると、これは全てのテナントに影響する。 ➡︎ 代わりに、Nodeを十分に冗長化して可用性を高める。
いずれかのインスタンスで障害が起こっても、正常なインスタンスでArgoCDが稼働できる。


AppProjectテナント例の一覧

NSモードなArgoCDを採用する場合、AppProjectテナント例を解説していきます。

前述の通り、AppProjectテナントが二重テナント (第一テナントにNamespace、第二テナントに複数のAppProject) を持つことに留意してください。

なお、オススメするものを ★ としています。

テナント例
(二重テナント)
オススメ
Namespace
(第一テナント)
AppProject
(第二テナント)
テナント例1 プロダクトの実行環境別 プロダクトの実行環境別
テナント例2 プロダクト別 プロダクトの実行環境別
テナント例3 プロダクト別 プロダクトのサブチーム別 ★★


▶ Namespaceの分割パターンについて

本記事では言及しませんが、Namespaceにはいくつかの分割パターンがあり、本記事ではこれも取り入れてAppProjectテナントを設計しています。

例えば、"管理チーム別" (今回でいうプロダクト別) というNamespaceの分割パターンは、様々な著名な書籍やブログで紹介されています👀


テナント例1

Namespace (プロダクトの実行環境別)、AppProject (プロダクトの実行環境別)

プロダクトの実行環境 (Dev環境、Tes環境) 別に管理されたClusterがいる状況と仮定します。

この場合に、プロダクトの実行環境別にNamespace (devtes) とAppProject (devtes) を用意します。

argocd_multi-tenant_appproject_namespaced-scope_pattern_1

オススメしなかった理由

テナント例1には、以下のメリデメがあります。

独断と偏見でオススメしませんでした。

アーキテクチャ
特性
メリット ⭕️ デメリット × デメリットの回避策
拡張性 - ArgoCDのPod数が多くなり、将来的にNode当たりのPodやIPアドレスの上限数にひっかかりやすい。
その時点で、AppProjectテナントの増やせなくなる。
➡︎ 例えばAWS EKSの場合、Node数を増やしたり、Nodeのスペックを上げる。
ただ、お金がかかる😭
安全性
(セキュリティ)
ログインユーザー用ConfigMap (argocd-rbac-cm) を使用すれば、無認可の実行環境別AppProjectに所属するApplicationを操作できないように制限できる。 - ➡︎ -
保守性 異なる実行環境に関するApplicationが共存しておらず、別のargocd-serverから操作することになるため、実行環境間の選択ミスが起こりにくい。 - ➡︎ -


テナント例2 - ★

Namespace (プロダクト別)、AppProject (プロダクトの実行環境別)

プロダクトの実行環境 (Dev環境、Tes環境) 別に管理されたClusterがいる状況と仮定します。

プロダクト別にNamespace (foobar) 、プロダクトの実行環境別にAppProject (devtes) を用意します。

argocd_multi-tenant_appproject_namespaced-scope_pattern_2

オススメした理由

テナント例2には、以下のメリデメがあります。

独断と偏見で オススメ しました。

アーキテクチャ
特性
メリット ⭕️ デメリット × デメリットの回避策
拡張性 ArgoCDのPod数が多くなり、将来的にNode当たりのPodやIPアドレスの上限数にひっかかりにくい。 - ➡︎ -
安全性
(セキュリティ)
ログインユーザー用ConfigMap (argocd-rbac-cm) を使用すれば、無認可の実行環境別AppProjectを操作できないように制限できる。 - ➡︎ -
保守性 - 異なる実行環境に関するApplicationが共存しており、同じargocd-server (ダッシュボード) から操作することになるため、実行環境間の選択ミスが起こりやすい。 ➡︎ ダッシュボードにはApplicationのフィルタリング機能があるため、選択ミスを回避できる。


テナント例3 - ★★

Namespace (プロダクト別)、AppProject (プロダクトのサブチーム別)

プロダクトの実行環境 (Dev環境、Tes環境) 別に管理されたClusterがいる状況と仮定します。

プロダクト別にNamespace (foobar) 、プロダクトのサブチーム別にAppProject (appinfra) を用意します。

argocd_multi-tenant_appproject_namespaced-scope_pattern_3

特にオススメした理由

テナント例3には、以下のメリデメがあります。

独断と偏見で 特にオススメ しました。

アーキテクチャ
特性
メリット ⭕️ デメリット × デメリットの回避策
拡張性 ArgoCDのPod数が多くなり、将来的にNode当たりのPodやIPアドレスの上限数にひっかかりにくい。 - ➡︎ -
安全性
(セキュリティ)
ログインユーザー用ConfigMap (argocd-rbac-cm) を使用すれば、無認可のサブチーム別AppProjectに所属するApplicationを操作できないように制限できる。 - ➡︎ -
保守性 - 異なる実行環境に関するApplicationが共存しており、同じargocd-server (ダッシュボード) から操作することになるため、実行環境間の選択ミスが起こりやすい。 ➡︎ ダッシュボードにはApplicationのフィルタリング機能があるため、選択ミスを回避できる。


06. どのような誤った操作を防いでくれるのか

そろそろ解説を読むのがしんどい方がいるのではないでしょうか。

『君がッ、泣くまで、解説をやめないッ!』

AppProjectテナントとNamespacedスコープモードがマニフェストのデプロイをどのように制限するのかについて、例を挙げて解説します。

ここでは、以下のAppProjectを作成したと仮定します。

AppProjectテナントが二重テナント (第一テナントにNamespace、第二テナントに複数のAppProject) を持つことに留意してください。

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  # appチーム
  name: app
  namespace: foo
spec:
  destinations:
    # ArgoCD用Clusterに関する認可を設定する
    # Namespace (foo) へのデプロイを許可する
    - namespace: foo
      server: "https://kubernetes.default.svc"
      # プロダクト用Clusterに関する認可を設定する
      # Namespace (app) へのデプロイを許可する
    - namespace: app
      server: https://foo-cluster.gr7.ap-northeast-1.eks.amazonaws.com
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  # infraチーム
  name: infra
  namespace: foo
spec:
  destinations:
    # ArgoCD用Clusterに関する認可を設定する
    # Namespace (foo) へのデプロイを許可する
    - namespace: foo
      server: "https://kubernetes.default.svc"
    # プロダクト用Clusterに関する認可を設定する
    # Namespace (infra) へのデプロイを許可する
    - namespace: infra
      server: https://foo-cluster.gr7.ap-northeast-1.eks.amazonaws.com


マニフェストのデプロイ制限

プロダクトの実行環境 (Dev環境、Tes環境) 別に管理されたClusterがいる状況と仮定します。

プロダクト別にNamespace (foo) 、プロダクトのサブチーム別にAppProject (appinfra) を用意します。

AppProjectテナントは、例えば 赤線 の方法で、マニフェストのデプロイを制限します。

argocd_multi-tenant_appproject_namespaced-scope_restrict-manifest-deploy

マニフェストをデプロイできる場合

マニフェストを正しくデプロイする場合、AppProjectテナントはこれを制限しません。

(1) argocd-serverは、argocd-cmd-params-cmからアクセスできるNamespaceを取得します。

apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cmd-params-cm
  namespace: foo
data:
# 設定しないことで、argocd-serverは同じNamespaceにしかアクセスできなくなる。
# application.namespaces: "*"

(2) fooプロダクトのinfraチームが、argocd-serverを操作します。

(3) argocd-serverは、argocd-rbac-cmからApplication操作に関する認可スコープを取得します

apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-rbac-cm
  namespace: foo
data:
  policy.default: role:readonly
  policy.csv: |
    p, role:app, *, *, app/*/*, allow
    p, role:infra, *, *, infra/*/*, allow

    g, app-team, role:app
    g, infra-team, role:infra
  scopes: "[groups]"

(4) infraチームは、認可されたAppProjectに所属するApplicationを操作します。

(5) infraチームは、Dev環境のfooプロダクト用ClusterのNamespace (infra) にマニフェストをデプロイできます。

(🚫制限例1) 無認可のNamespaceでApplicationを作成しようとした場合

例えば、fooプロダクトのinfraチームが無認可のNamespace (bar) でApplicationを作成しようとします。

すると、argocd-serverは以下のようなエラーを返却し、この操作を制限します。

namespace bar is not permitted in project 'infra-team'

無認可のNamespaceでApplicationを作れてしまうと、そのApplicationから無認可のプロダクト用Clusterにマニフェストをデプロイできてしまいます😈

(🚫制限例2) 無認可のAppProjectでApplicationを作成しようとした場合

例えば、fooプロダクトのinfraチームが、無認可のAppProject (app) でApplicationを作成しようとします。

すると、argocd-serverは以下のようなエラーを返却し、この操作を制限します。

Application referencing project 'app' which does not exist

任意のAppProjectでApplicationを作成できてしまうと、そのApplicationから無認可のプロダクト用Clusterにマニフェストをデプロイできてしまいます😈

(🚫制限例3) 無認可のClusterをデプロイ先に指定しようとした場合

例えば、fooプロダクトのinfraチームがApplicationを操作し、無認可のプロダクト用Cluster (bar-cluster) をデプロイ先として指定しようします。

すると、argocd-serverは以下のようなエラーを返却し、この操作を制限します。

application destination
{https://bar-cluster.gr7.ap-northeast-1.eks.amazonaws.com infra} is not permitted in project 'infra-team'

任意のClusterをデプロイ先に指定できてしまうと、Applicationから無認可のプロダクト用Clusterにマニフェストをデプロイできてしまいます😈

(🚫制限例4) 無認可のNamespaceをデプロイ先に指定しようとした場合

例えば、fooプロダクトのinfraチームがApplicationを操作し、無認可のNamespace (app) をデプロイ先に指定しようします。

すると、argocd-serverは以下のようなエラーを返却し、この操作を制限します。

application destination
{https://foo-cluster.gr7.ap-northeast-1.eks.amazonaws.com app} is not permitted in project 'infra-team'

任意のNamespaceをデプロイ先に指定できてしまうと、そのApplicationから無認可のNamespaceにマニフェストをデプロイできてしまいます😈


▶ AppProjectで設定できる認可の種類について

AppProjectテナントは、その他にも以下のような認可を設定できます。

  • argocd-serverとapplication-controllerでデプロイできるKubernetesリソースの種類 (.spec.clusterResourceWhitelistキー、.spec.namespaceResourceWhitelistキー、など)
  • repo-serverでポーリングできるリポジトリ (.spec.sourceReposキー)
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: foo-tenant
  namespace: foo
spec:
  clusterResourceWhitelist:
    - group: "*"
      kind: "*"
  namespaceResourceWhitelist:
    - group: "*"
      kind: "*"
  sourceRepos:
    - "*"

  ...

"AppProjectテナントによるマニフェストのデプロイ丸ごとの制限" という観点でテーマが異なるため、本記事では言及しませんでした🙇🏻‍


カスタムリソースのReconciliation制限

プロダクトの実行環境 (Dev環境、Tes環境) 別に管理されたClusterがいる状況と仮定します。

プロダクト別にNamespace (foo) 、プロダクトのサブチーム別にAppProject (appinfra) を用意します。

AppProjectテナントは、例えば 赤線 の方法で、ArgoCD系カスタムリソースに対するapplication-controllerのReconciliationを制限します。

argocd_multi-tenant_appproject_namespaced-scope_restrict-reconciliation

ArgoCD系カスタムリソースをReconciliationできる場合

正しいNamespaceに対してReconciliationを実行する場合、AppProjectテナントはこれを制限しません。

(1) application-controllerは、argocd-cmd-params-cmから自身がアクセスできるNamespaceを取得します。

apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cmd-params-cm
  namespace: foo
data:
# 設定しないことで、application-controllerは同じNamespaceにしかアクセスできなくなる。
# application.namespaces: "*"

(2) application-controllerは、同じNamespaceに所属するArgoCD系カスタムリソースに対して、Reconciliationを実行します。

(🚫制限例1) 無認可のNamespaceにReconciliationを実行しようとした場合

例えば、application-controllerがReconciliationの対象とするNamespaceを選ぼうとしているとします。

すると、application-controllerは内部で検証メソッドを実行し、無認可のNamespace (bar) は選ばないようにします。


07. おわりに

KubernetesのマルチテナントパターンとArgoCDでのパターン実践をもりもり布教しました。

あらゆる面からマニフェストのデプロイを制限してくれる、AppProjectテナントの素晴らしさが伝わりましたでしょうか。

KubernetesのマルチテナントパターンをArgoCDでどう実践するべきか、について困っている方の助けになれば幸いです👍


謝辞

本記事のタイトルは、私が崇拝しているドメイン駆動設計の書籍 "実践ドメイン駆動設計" から拝借しました🙏

また、ArgoCDでのパターン実践の収集にあたり、以下の方からの意見も参考にさせていただきました。

この場で感謝申し上げます🙇🏻‍


記事関連のおすすめ書籍