翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。
テナントの分離
マルチテナンシーについては、共有インフラストラクチャで実行されている他のユーザーやアプリケーションからユーザーやアプリケーションを分離したい場合があります。
Kubernetes は単一テナントオーケストレーターです。つまり、コントロールプレーンの単一インスタンスがクラスター内のすべてのテナント間で共有されます。ただし、マルチテナンシーの類似性を作成するために使用できるさまざまな Kubernetes オブジェクトがあります。例えば、名前空間とロールベースのアクセスコントロール (RBAC) を実装して、テナントを相互に論理的に分離できます。同様に、クォータと制限範囲を使用して、各テナントが消費できるクラスターリソースの量を制御できます。それでも、クラスターは強力なセキュリティ境界を提供する唯一のコンストラクトです。これは、 を管理してクラスター内のホストにアクセスする攻撃者が、そのホストにマウントされたすべての Secrets、ConfigMaps、および Volumes を取得できるためです。また、Kubelet を偽装して、ノードの属性を操作したり、クラスター内で横に移動したりすることもできます。
以下のセクションでは、Kubernetes などの単一のテナントオーケストレーターを使用するリスクを軽減しながら、テナント分離を実装する方法について説明します。
ソフトマルチテナンシー
ソフトマルチテナンシーでは、名前空間、ロールとロールのバインディング、ネットワークポリシーなどのネイティブ Kubernetes コンストラクトを使用して、テナント間に論理的な分離を作成します。例えば、RBAC はテナントが互いのリソースにアクセスしたり操作したりできないようにします。クォータと制限範囲は、各テナントが消費できるクラスターリソースの量を制御しますが、ネットワークポリシーは、異なる名前空間にデプロイされたアプリケーションが相互に通信するのを防ぐのに役立ちます。
ただし、これらのコントロールのいずれも、異なるテナントのポッドによるノードの共有を妨げません。より強力な分離が必要な場合は、ノードセレクタ、アンチアフィニティルール、テイントや許容値を使用して、異なるテナントのポッドを別々のノードに強制的にスケジュールできます。これは、多くの場合、唯一のテナントノードと呼ばれます。テナントが多い環境では、これはかなり複雑でコストがかかる可能性があります。
重要
名前空間で実装されたソフトマルチテナンシーでは、名前空間がグローバルスコープのタイプであるため、名前空間のフィルタリングされたリストをテナントに提供できません。テナントが特定の名前空間を表示できる場合は、クラスター内のすべての名前空間を表示できます。
警告
soft-multi-tenancyでは、テナントはデフォルトでクラスター内で実行されるすべてのサービスの CoreDNS をクエリする機能を保持します。攻撃者は、クラスター内の任意のポッド ..svc.cluster.local
から dig SRV を実行することで、これを悪用する可能性があります。クラスター内で実行されるサービスの DNS レコードへのアクセスを制限する必要がある場合は、 CoreDNS のファイアウォールまたはポリシープラグインの使用を検討してください。詳細については、https://github.com/coredns/policy#kubernetes-metadata-multi-tenancy-policy
Kiosk
-
共有 Kubernetes クラスター内のテナントを分離するためのアカウントとアカウントユーザー
-
アカウントユーザー向けのセルフサービス名前空間プロビジョニング
-
クラスターを共有するときにサービスの品質と公平性を確保するためのアカウント制限
-
安全なテナント分離とセルフサービス名前空間の初期化のための名前空間テンプレート
Loft
-
異なるクラスター内のスペースへのアクセスを許可するためのマルチクラスターアクセス
-
スリープモードは、非アクティブ状態のときにスペース内のデプロイをスケールダウンします。
-
GitHub などの OIDC 認証プロバイダーによるシングルサインオン
ソフトマルチテナンシーで対処できる主なユースケースは 3 つあります。
エンタープライズ設定
1 つ目はエンタープライズ設定で、「テナント」は従業員、請負業者、または組織によって承認されているという点で半信頼されています。通常、各テナントは部門やチームなどの管理部門と連携します。
このタイプの設定では、通常、クラスター管理者が名前空間の作成とポリシーの管理を担当します。また、特定の個人が名前空間を監督される委任管理モデルを実装し、デプロイ、サービス、ポッド、ジョブなどのポリシーに関連しないオブジェクトに対して CRUD オペレーションを実行できるようにする場合もあります。
コンテナランタイムによって提供される分離は、この設定内で許容される場合や、ポッドセキュリティの追加のコントロールで拡張する必要がある場合があります。また、より厳密な分離が必要な場合は、異なる名前空間内のサービス間の通信を制限する必要がある場合もあります。
Kubernetes as a Service
対照的に、Kubernetes as a Service (KaaS) を提供する設定では、ソフトマルチテナンシーを使用できます。KaaS では、アプリケーションは、一連の PaaS サービスを提供するコントローラーと CRDs のコレクションとともに、共有クラスターでホストされます。テナントは Kubernetes API サーバーと直接やり取りし、非ポリシーオブジェクトに対して CRUD オペレーションの実行が許可されます。には、テナントが独自の名前空間を作成および管理できるセルフサービスの要素もあります。このタイプの環境では、テナントは信頼できないコードを実行していると見なされます。
このタイプの環境でテナントを分離するには、多くの場合、厳格なネットワークポリシーとポッドサンドボックスを実装する必要があります。サンドボックス化は、Firecracker などのマイクロ VM 内またはユーザースペースカーネル内でポッドのコンテナを実行する場所です。現在、EKS Fargate を使用してサンドボックス化されたポッドを作成できます。
Software as a Service (SaaS)
ソフトマルチテナンシーの最終的なユースケースは、Software-as-a-Service (SaaS) 設定です。この環境では、各テナントはクラスター内で実行されているアプリケーションの特定のインスタンスに関連付けられます。多くの場合、各インスタンスには独自のデータがあり、通常は Kubernetes RBAC から独立した個別のアクセスコントロールを使用します。
他のユースケースとは異なり、SaaS 設定のテナントは Kubernetes API と直接インターフェイスしません。代わりに、SaaS アプリケーションは Kubernetes API とやり取りして、各テナントをサポートするために必要なオブジェクトを作成する責任があります。
Kubernetes コンストラクト
これらの各インスタンスでは、次のコンストラクトを使用してテナントを相互に分離します。
名前空間
名前空間は、ソフトマルチテナンシーを実装するための基本です。これにより、クラスターを論理パーティションに分割できます。マルチテナンシーの実装に必要なクォータ、ネットワークポリシー、サービスアカウント、およびその他のオブジェクトは、名前空間に限定されます。
ネットワークポリシー
デフォルトでは、Kubernetes クラスター内のすべてのポッドは相互に通信できます。この動作は、ネットワークポリシーを使用して変更できます。
ネットワークポリシーは、ラベルまたは IP アドレス範囲を使用してポッド間の通信を制限します。テナント間の厳密なネットワーク分離が必要なマルチテナント環境では、ポッド間の通信を拒否するデフォルトルールと、すべてのポッドが名前解決のために DNS サーバーにクエリを実行できるようにする別のルールから始めることをお勧めします。これにより、名前空間内での通信を可能にする、より寛容なルールの追加を開始できます。これは、必要に応じてさらに改良できます。
注記
Amazon VPC CNI では、Kubernetes ネットワークポリシーがサポートされるようになりました
重要
ネットワークポリシーは必要ですが、十分ではありません。ネットワークポリシーを適用するには、Calico や Cilium などのポリシーエンジンが必要です。
ロールベースのアクセスコントロール (RBAC)
ロールとロールバインディングは、Kubernetes でロールベースのアクセスコントロール (RBAC) を適用するために使用される Kubernetes オブジェクトです。ロールには、クラスター内のオブジェクトに対して実行できるアクションのリストが含まれます。ロールバインディングは、ロールが適用される個人またはグループを指定します。エンタープライズおよび KaaS 設定では、RBAC を使用して、選択したグループまたは個人によるオブジェクトの管理を許可できます。
クォータ
クォータは、クラスターでホストされるワークロードの制限を定義するために使用されます。クォータを使用すると、ポッドが消費できる CPU とメモリの最大量を指定したり、クラスターまたは名前空間に割り当てることができるリソースの数を制限したりできます。制限範囲を使用すると、各制限の最小値、最大値、デフォルト値を宣言できます。
共有クラスターでリソースを上書きすると、リソースを最大化できるため、多くの場合有益です。ただし、クラスターへの無制限のアクセスによりリソースが不足し、パフォーマンスが低下し、アプリケーションの可用性が失われる可能性があります。ポッドのリクエストの設定が低すぎて、実際のリソース使用率がノードの容量を超えると、ノードで CPU またはメモリの負荷が発生し始めます。この場合、ポッドが再起動されたり、ノードから削除されたりすることがあります。
これを防ぐには、マルチテナント環境の名前空間にクォータを課して、テナントがクラスターでポッドをスケジュールするときにリクエストと制限を指定するように強制する計画を立てる必要があります。また、ポッドが消費できるリソースの量を制限することで、サービス拒否の可能性を軽減します。
クォータを使用して、テナントの支出に合わせてクラスターのリソースを割り当てることもできます。これは、KaaS シナリオで特に役立ちます。
ポッドの優先度とプリエンプション
ポッドの優先度とプリエンプションは、他のポッドよりもポッドに重要度を与える場合に便利です。例えば、ポッドの優先度では、顧客 A のポッドを顧客 B よりも高い優先度で実行するように設定できます。使用可能な容量が不足している場合、スケジューラは顧客 B から優先度の低いポッドを削除して、顧客 A の優先度の高いポッドに対応します。これは、顧客がプレミアムを支払うことを優先する SaaS 環境で特に便利です。
重要
ポッドの優先度は、優先度の低い他のポッドに望ましくない影響を与える可能性があります。例えば、対象ポッドは適切に終了されますが、PodDisruptionBudget は保証されていないため、ポッドのクォーラムに依存する優先度の低いアプリケーションが破損する可能性があります。「 プリエンプションの制限
コントロールの軽減
マルチテナント環境の管理者としての最大の懸念は、攻撃者が基盤となるホストにアクセスできないようにすることです。このリスクを軽減するには、次のコントロールを検討する必要があります。
コンテナのサンドボックス実行環境
サンドボックス化は、各コンテナを個別の仮想マシンで実行する手法です。ポッドサンドボックスを実行するテクノロジーには、Firecracker
Firecracker を EKS でサポートされているランタイムにする作業の詳細については、https://threadreaderapp.com/thread/1238496944684597248.html
オープンポリシーエージェント (OPA) とゲートキーパー
Gatekeeper
CoreDNS の実験的な OPA プラグイン
キバーノ
Kyverno
Kyverno を使用して、名前空間の分離、ポッドセキュリティやその他のベストプラクティスの適用、ネットワークポリシーなどのデフォルト設定の生成を行うことができます。このプロジェクトの GitHub リポジトリ
テナントワークロードを特定のノードに分離する
テナントワークロードを特定のノードで実行するように制限することで、ソフトマルチテナンシーモデルの分離を強化できます。このアプローチでは、テナント固有のワークロードは、各テナント用にプロビジョニングされたノードでのみ実行されます。この分離を実現するために、ネイティブ Kubernetes プロパティ (ノードアフィニティ、テイントと許容範囲) を使用して、ポッドスケジューリングのために特定のノードをターゲットにし、他のテナントからのポッドがテナント固有のノードでスケジュールされないようにします。
パート 1 - ノードアフィニティ
Kubernetes ノードアフィニティrequiredDuringSchedulingIgnoredDuringExecution
ノードのアフィニティがそれぞれのポッドに適用されます。その結果、ポッドは次のキー/値でラベル付けされたノードをターゲットにします: node-restriction.kubernetes.io/tenant: tenants-x
。
...
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-restriction.kubernetes.io/tenant
operator: In
values:
- tenants-x
...
このノードアフィニティでは、ラベルはスケジューリング時に必要になりますが、実行時には必要ではありません。基になるノードのラベルが変更された場合、そのラベルが変更されてもポッドは削除されません。ただし、将来のスケジュールが影響を受ける可能性があります。
警告
のラベルプレフィックスnode-restriction.kubernetes.io/
は、Kubernetes で特別な意味を持ちます。EKS クラスターに対して有効になっている NodeRestrictionkubelet
を に禁止します。攻撃者は、 kubelet
の認証情報を使用してノードオブジェクトを更新したり、これらのラベルを変更できないkubelet
ため、これらのラベルを に渡すようにシステム設定を変更したりkubelet
することはできません。このプレフィックスをすべてのポッド間スケジューリングに使用すると、攻撃者がノードラベルを変更して別のワークロードセットをノードに引き付けるシナリオを防ぐことができます。
ノードアフィニティの代わりに、ノードセレクタ
パート 2 - テイントと許容範囲
ポッドをノードに引き付けることは、この 3 つの部分からなるアプローチの最初の部分にすぎません。このアプローチを機能させるには、ポッドが認可されていないノードへのポッドのスケジューリングを回避する必要があります。不要なポッドや不正なポッドを破棄するために、Kubernetes はノードテイントtenant: tenants-x
。
...
taints:
- key: tenant
value: tenants-x
effect: NoSchedule
...
上記のノード の場合taint
、テイントを許容するポッドのみをノードでスケジュールできます。承認されたポッドをノードにスケジュールできるようにするには、以下に示すように、それぞれのポッド仕様にテイントtoleration
への を含める必要があります。
...
tolerations:
- effect: NoSchedule
key: tenant
operator: Equal
value: tenants-x
...
上記の を持つポッドtoleration
は、少なくともその特定のテイントのために、ノードでのスケジューリングを停止されません。テイントは、ノードリソースのプレッシャーなど、特定の条件下でポッドスケジューリングを一時的に停止するために Kubernetes によっても使用されます。ノードのアフィニティ、テイント、許容度により、目的のポッドを特定のノードに効果的に引き付け、不要なポッドを反発できます。
重要
特定の Kubernetes ポッドは、すべてのノードで実行する必要があります。これらのポッドの例は、コンテナネットワークインターフェイス (CNI)
パート 3 - ノード選択のポリシーベースの管理
CICD パイプラインでのルールの適用など、ポッド仕様のノードアフィニティと許容範囲を管理するために使用できるツールがいくつかあります。ただし、分離の適用は Kubernetes クラスターレベルで行う必要があります。この目的のために、ポリシー管理ツールを使用して、リクエストペイロードに基づいてインバウンド Kubernetes API サーバーリクエストを変更し、上記のそれぞれのノードアフィニティルールと許容値を適用できます。
例えば、tentains-x 名前空間宛てのポッドには、tentains-x ノードでスケジューリングできるように、正しいノードアフィニティと許容値でスタンプを付けることができます。Kubernetes Mutating Admission Webhook
apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: Assign
metadata:
name: mutator-add-nodeaffinity-pod
annotations:
aws-eks-best-practices/description: >-
Adds Node affinity - https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity
spec:
applyTo:
- groups: [""]
kinds: ["Pod"]
versions: ["v1"]
match:
namespaces: ["tenants-x"]
location: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms"
parameters:
assign:
value:
- matchExpressions:
- key: "tenant"
operator: In
values:
- "tenants-x"
上記のポリシーは、Kubernetes API サーバーリクエストに適用され、pod を tenants-x 名前空間に適用します。このポリシーはrequiredDuringSchedulingIgnoredDuringExecution
ノードアフィニティルールを追加し、 tenant: tenants-x
ラベルを持つノードにポッドが引き付けられるようにします。
以下に示している 2 番目のポリシーでは、ターゲットの名前空間とグループ、種類、バージョンの同じ一致基準を使用して、同じポッド仕様に許容値を追加します。
apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: Assign
metadata:
name: mutator-add-toleration-pod
annotations:
aws-eks-best-practices/description: >-
Adds toleration - https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/
spec:
applyTo:
- groups: [""]
kinds: ["Pod"]
versions: ["v1"]
match:
namespaces: ["tenants-x"]
location: "spec.tolerations"
parameters:
assign:
value:
- key: "tenant"
operator: "Equal"
value: "tenants-x"
effect: "NoSchedule"
上記のポリシーはポッドに固有です。これは、ポリシーの 要素の変更されたlocation
要素へのパスが原因です。デプロイリソースやジョブリソースなど、ポッドを作成するリソースを処理するために追加のポリシーを記述できます。リストされたポリシーやその他の例は、このガイドの付随する GitHub プロジェクト
これらの 2 つのミューテーションの結果、ポッドは目的のノードに引き付けられ、同時に特定のノードテイントによってリピートされないことがわかります。これを検証するには、 というラベルの付いたノードを取得しtenant=tenants-x
、 tenants-x
名前空間のポッドを取得するための 2 つのkubectl
呼び出しからの出力のスニペットを確認できます。
kubectl get nodes -l tenant=tenants-x NAME ip-10-0-11-255... ip-10-0-28-81... ip-10-0-43-107... kubectl -n tenants-x get pods -owide NAME READY STATUS RESTARTS AGE IP NODE tenant-test-deploy-58b895ff87-2q7xw 1/1 Running 0 13s 10.0.42.143 ip-10-0-43-107... tenant-test-deploy-58b895ff87-9b6hg 1/1 Running 0 13s 10.0.18.145 ip-10-0-28-81... tenant-test-deploy-58b895ff87-nxvw5 1/1 Running 0 13s 10.0.30.117 ip-10-0-28-81... tenant-test-deploy-58b895ff87-vw796 1/1 Running 0 13s 10.0.3.113 ip-10-0-11-255... tenant-test-pod 1/1 Running 0 13s 10.0.35.83 ip-10-0-43-107...
上記の出力からわかるように、すべてのポッドは というラベルの付いたノードでスケジュールされますtenant=tenants-x
。簡単に言うと、ポッドは目的のノードでのみ実行され、他のポッド (必要なアフィニティと許容値なし) は実行されません。テナントワークロードは効果的に分離されます。
変更後のポッド仕様の例を以下に示します。
apiVersion: v1
kind: Pod
metadata:
name: tenant-test-pod
namespace: tenants-x
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: tenant
operator: In
values:
- tenants-x
...
tolerations:
- effect: NoSchedule
key: tenant
operator: Equal
value: tenants-x
...
重要
Kubernetes API サーバーリクエストフローに統合されているポリシー管理ツールは、変更と検証のアドミッションウェブフックを使用して、指定された期間内に API サーバーのリクエストに応答するように設計されています。これは通常 3 秒以下です。ウェブフック呼び出しが設定された時間内にレスポンスを返さない場合、インバウンド API サーバーリクエストのミューテーションや検証が発生する場合と発生しない場合があります。この動作は、アドミッションウェブフック設定が Fail Open または Fail Close
上記の例では、OPA/Gatekeeper 用に記述されたポリシーを使用しました。ただし、ノード選択のユースケースを処理する他のポリシー管理ツールもあります。例えば、この Kyverno ポリシー
注記
正しく動作している場合、ポリシーを変更すると、インバウンド API サーバーのリクエストペイロードに必要な変更が反映されます。ただし、変更が保持される前に、必要な変更が行われることを確認するために、検証ポリシーも含める必要があります。これは、tenant-to-node分離にこれらのポリシーを使用する場合に特に重要です。また、クラスターで不要な設定を定期的にチェックするための監査ポリシーを含めることをお勧めします。
リファレンス
-
k-rail
特定のポリシーを適用してマルチテナント環境を保護するように設計されています。
ハードマルチテナンシー
テナントごとに個別のクラスターをプロビジョニングすることで、ハードマルチテナンシーを実装できます。これによりテナント間の分離が非常に強力になりますが、いくつかの欠点があります。
まず、テナントが多い場合、このアプローチはすぐに高価になる可能性があります。クラスターごとにコントロールプレーンのコストを支払う必要があるだけでなく、クラスター間でコンピューティングリソースを共有することはできません。これにより、最終的に断片化が発生し、クラスターのサブセットが十分に活用されず、他のサブセットが過剰に活用されます。
次に、これらのすべてのクラスターを管理するために、特別なツールを購入または構築する必要があります。時間が経つと、数百または数千のクラスターを管理するのは、単純に扱いにくい状態になる可能性があります。
最後に、テナントごとのクラスターの作成は、名前空間の作成と比較して遅くなります。ただし、規制の厳しい業界や強力な分離が必要な SaaS 環境では、ハードテナンシーアプローチが必要になる場合があります。
今後の方向性
Kubernetes コミュニティは、ソフトマルチテナンシーの現在の欠点とハードマルチテナンシーの課題を認識しました。Multi-Tenancy Special Interest Group (SIG)
HNC 提案 (KEP) では、〔ポリシー] オブジェクト継承を使用して名前空間間に親子関係を作成する方法と、テナント管理者がサブ名前空間を作成する機能について説明します。
仮想クラスター提案では、クラスター内のテナントごとに、API サーバー、コントローラーマネージャー、スケジューラ (Kubernetes 上の Kubernetes とも呼ばれます) を含むコントロールプレーンサービスの個別のインスタンスを作成するメカニズムについて説明します。
マルチテナンシーベンチマーク