翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。
テナントの分離
マルチテナンシーについて考えた場合、共有インフラストラクチャで実行されている他のユーザーやアプリケーションからユーザーやアプリケーションを分離することがよくあります。
Kubernetes は単一テナントオーケストレーターです。つまり、コントロールプレーンの単一インスタンスがクラスター内のすべてのテナント間で共有されます。ただし、マルチテナンシーのアセンブリを作成するために使用できるさまざまな Kubernetes オブジェクトがあります。例えば、名前空間とロールベースのアクセスコントロール (RBAC) を実装して、テナントを相互に論理的に分離できます。同様に、クォータと制限範囲を使用して、各テナントが消費できるクラスターリソースの量を制御できます。それでも、クラスターは強力なセキュリティ境界を提供する唯一のコンストラクトです。これは、 を管理する攻撃者が、そのホストにマウントされたすべてのシークレット、ConfigMapsボリュームを取得できるためです。また、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 シナリオで特に役立ちます。
ポッドの優先度とプリエンプション
Pod の優先度とプリエンプションは、他の Pod よりも Pod を重視する場合に役立ちます。例えば、ポッドの優先度では、顧客 A のポッドを顧客 B よりも高い優先度で実行するように設定できます。使用可能な容量が不足している場合、スケジューラは顧客 A の優先度の高いポッドに対応するために、顧客 B の優先度の低いポッドを削除します。これは、顧客がプレミアムの支払いを希望する 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’s credentials to update the node object or modify the system setup to pass these labels into `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 名前空間宛てのポッドには、tentens-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 サーバーリクエストに適用され、テナント 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 つのミューテーションの結果、ポッドは目的のノードに引き付けられますが、同時に特定のノードテイントによって反発されることはありません。これを検証するには、2 つのkubectl
呼び出しからの出力のスニペットを表示して、 というラベルが付いたノードを取得しtenant=tenants-x
、tenants-x
名前空間のポッドを取得します。
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) では、〔ポリシー] オブジェクト継承を持つ名前空間間に親子関係を作成する方法と、テナント管理者がサブ名前空間を作成する機能について説明します。
仮想クラスター提案では、クラスター内の各テナント (Kubernetes on Kubernetes とも呼ばれます) に対して、API サーバー、コントローラーマネージャー、スケジューラなど、コントロールプレーンサービスの個別のインスタンスを作成するメカニズムについて説明します。
マルチテナンシーベンチマーク