はじめに
Kubernetesで複数のチームやサービスを1つのクラスターに同居させる「マルチテナント」は、コスト効率と運用負荷の観点から多くの組織が採用するアーキテクチャだ。しかしその実装は「Namespaceを分ければいい」という単純な話ではない。RBAC(Role-Based Access Control)とNetworkPolicyを組み合わせて初めて、テナント間のアクセス分離と通信分離が実現できる。
本記事ではNamespaceベースのマルチテナントに絞り、設計パターンと各コンポーネントの役割を体系的に解説する。
対象読者
Kubernetesをすでに運用しており、マルチテナント設計を整理・強化したい中上級エンジニア
マルチテナントの分離レイヤーを整理する
Kubernetesにおけるテナント分離には複数のレイヤーがある。まず全体像を把握しておこう。

本記事では①②を中心に扱う。③④は別途検討が必要な領域だ。
Namespaceだけでは何も分離されない
まず重要な前提として、Namespaceを作るだけでは分離は実現しない。
Kubernetesのデフォルト挙動は以下の通り
| 分離したいもの | デフォルト | 追加設定が必要 |
|---|---|---|
| APIアクセス | 分離されない(全Namespaceにアクセス可能) | RBAC |
| ネットワーク通信 | 分離されない(全Pod間通信が可能) | NetworkPolicy |
| リソース使用量 | 分離されない(上限なし) | ResourceQuota |
つまり「Namespaceベースのマルチテナント」とは、これらを組み合わせて設計することを指す。
3. RBAC設計パターン
3-1. RBACの基本構造
RBACは4つのリソースで構成される。
ClusterRole / Role → 権限の定義(何ができるか)
ClusterRoleBinding / RoleBinding → 権限の付与(誰に与えるか)
スコープの違いを整理すると:
| リソース | スコープ | 用途 |
|---|---|---|
Role | Namespace内 | テナントへの権限付与 |
ClusterRole | クラスター全体 | プラットフォームチームの管理権限 |
RoleBinding | Namespace内 | Role / ClusterRoleをNamespace内ユーザーに付与 |
ClusterRoleBinding | クラスター全体 | ClusterRoleをクラスター全体で付与 |
マルチテナントで重要なのは、ClusterRoleをRoleBindingで参照できるという点だ。テナントに共通の権限セットをClusterRoleとして定義し、各Namespaceのみ有効なRoleBindingで付与するパターンが多用される。
3-2. 典型的なテナント権限設計
以下の3ロールを定義するのが一般的なパターンだ。
① developer ロール(Namespace内の通常操作)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: tenant-developer
rules:
- apiGroups: ["", "apps", "batch"]
resources: ["pods", "deployments", "services", "configmaps", "jobs"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list"] # Secretはread-onlyに絞る
- apiGroups: [""]
resources: ["pods/log", "pods/exec"]
verbs: ["get", "create"]
② viewer ロール(読み取り専用)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: tenant-viewer
rules:
- apiGroups: ["", "apps"]
resources: ["pods", "deployments", "services", "configmaps"]
verbs: ["get", "list", "watch"]
③ Namespaceへの付与(RoleBinding)
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: team-a-developer
namespace: team-a # このNamespaceにのみ有効
subjects:
- kind: Group
name: team-a-engineers # OIDCやSSO経由のグループ名
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole # ClusterRoleを参照
name: tenant-developer
apiGroup: rbac.authorization.k8s.io
3-3. よくある設計ミスと対策
ミス①: wildcardの使用
# NG: 将来追加されるリソースにも権限が及ぶ
verbs: ["*"]
resources: ["*"]
# OK: 明示的に列挙する
verbs: ["get", "list", "watch"]
ミス②: ClusterRoleBindingで付与してしまう
# NG: クラスター全体にdeveloper権限が付与される
kind: ClusterRoleBinding # ← 危険
subjects:
- kind: Group
name: team-a-engineers
# OK: Namespace内のRoleBindingで付与する
kind: RoleBinding # ← Namespace内に限定
metadata:
namespace: team-a
ミス③: ServiceAccountの放置
デフォルトのServiceAccountは最小権限で運用し、必要なワークロードには専用のServiceAccountを発行する。
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-sa
namespace: team-a
automountServiceAccountToken: false # 不要なら自動マウントを無効化
4. NetworkPolicy設計パターン
4-1. NetworkPolicyが機能するための前提
NetworkPolicyはCNIプラグインが対応していないと機能しない。 デフォルトのKubernetesクラスター(特にkindやminikubeのデフォルト設定)では通信制御が適用されないため、本番環境では必ずCilium、Calico、Antrea などNetworkPolicyをサポートするCNIを使用すること。
4-2. デフォルト拒否ポリシー(Deny-All)
マルチテナントのベースラインとして、各Namespaceにデフォルト拒否ポリシーを設定するのが基本だ。
# Ingress(受信)のデフォルト拒否
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: team-a
spec:
podSelector: {} # Namespace内のすべてのPodに適用
policyTypes:
- Ingress
---
# Egress(送信)のデフォルト拒否
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
namespace: team-a
spec:
podSelector: {}
policyTypes:
- Egress
これにより、明示的に許可したトラフィック以外はすべてドロップされる。
4-3. 必要な通信を許可するポリシー
デフォルト拒否の上で、必要な通信を追加していく。
① 同一Namespace内の通信を許可
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-same-namespace
namespace: team-a
spec:
podSelector: {}
ingress:
- from:
- podSelector: {} # 同一Namespace内のPodからの通信を許可
egress:
- to:
- podSelector: {}
policyTypes:
- Ingress
- Egress
② DNS解決を許可(必須)
デフォルト拒否にするとDNSも止まるため、CoreDNSへのEgressを忘れずに許可する。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: team-a
spec:
podSelector: {}
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
policyTypes:
- Egress
③ 共有サービス(monitoring, ingress)からのアクセスを許可
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-monitoring-scrape
namespace: team-a
spec:
podSelector:
matchLabels:
app: my-app
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: monitoring
podSelector:
matchLabels:
app: prometheus
ports:
- port: 8080
policyTypes:
- Ingress
4-4. テナント間通信の制御パターン
パターンA: 完全分離(テナント間通信を禁止)
# Namespaceのラベルを活用した分離
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-cross-tenant
namespace: team-a
spec:
podSelector: {}
ingress:
- from:
- namespaceSelector:
matchLabels:
tenant: team-a # 自テナントNamespaceからのみ許可
policyTypes:
- Ingress
Namespaceに適切なラベルを付与しておくことが前提になる。
kubectl label namespace team-a tenant=team-a
kubectl label namespace team-b tenant=team-b
パターンB: 特定テナント間の通信のみ許可
# team-aからteam-bの特定サービスへのアクセスのみ許可
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-team-a
namespace: team-b
spec:
podSelector:
matchLabels:
app: shared-api
ingress:
- from:
- namespaceSelector:
matchLabels:
tenant: team-a
ports:
- port: 8080
policyTypes:
- Ingress
5. RBAC × NetworkPolicy の組み合わせで実現する分離モデル
ここまでの要素をまとめると、Namespaceベースのマルチテナントは以下の二軸で分離を実現する。

6. 運用上の注意点
Namespace作成をセルフサービス化しない
テナントが自由にNamespaceを作れると、NetworkPolicyやResourceQuotaが付与されない素のNamespaceが増殖する。Namespace作成はプラットフォームチームが管理し、テンプレート化したマニフェストセット(Namespace + RBAC + NetworkPolicy + ResourceQuota)を一括適用する仕組みを整えるべきだ。
NetworkPolicyの検証を怠らない
NetworkPolicyは「設定したつもりが効いていない」という事態が起きやすい。kubectl execでcurlを実行するか、netshootなどのデバッグコンテナを使って実際の通信可否を確認する習慣をつけよう。
# デバッグ用Podでの疎通確認
kubectl run netshoot --rm -it --image=nicolaka/netshoot \
-n team-a -- curl http://service.team-b.svc.cluster.local:8080
RBACの棚卸しを定期的に行う
不要なRoleBindingが蓄積すると権限が形骸化する。kubectl auth can-i --listやrakkessなどのツールで定期的に権限の棚卸しを行うことを推奨する。
まとめ
Namespaceベースのマルチテナントを機能させるには:
- RBAC: ClusterRoleで権限を定義し、RoleBindingで各Namespaceにスコープを限定する
- NetworkPolicy: デフォルト拒否をベースラインに置き、必要な通信を明示的に追加する
- 運用: Namespace作成のテンプレート化・NetworkPolicyの疎通確認・RBAC棚卸しをプロセスに組み込む
この3つが揃って初めて「テナントが互いに影響を与えない」状態が実現できる。次のステップとしては、vclusterを使ったクラスターレベル分離や、OPA/GatekeeperによるAdmission Controlの強化が視野に入ってくる。