Amazon Web Services ブログ

Amazon Elastic Kubernetes Service で Kiosk にソフトマルチテナンシーをセットアップする



はじめに

現在、同じ Kubernetes クラスターで実行されている複数のテナント間を完全に分離することは不可能です。その理由は、Kubernetes がクラスターごとに 1 つのコントロールプレーンを持ち、クラスターで実行されているすべてのテナントが同じコントロールプレーンを共有するように設計されているためです。1 つのクラスターで複数のテナントをホストすると、いくつかの利点がもたらされます。主な利点には、効率的なリソースの利用と共有、コストの削減、設定のオーバーヘッドの削減があります。

ただし、マルチテナントの Kubernetes セットアップでは、リソースの共有とセキュリティに関して特殊な課題が生じます。これについての理解を深めましょう。共有クラスターの目標の 1 つは、各テナントが利用可能なリソースを公平に共有し、その要件を満たすことです。この場合に緩和する必要があると考えられる副作用に、ノイズの多い隣接効果があります。これは、テナント間で適切なレベルのリソース分離を保証することにより対処します。2 番目の課題にして主な課題は、セキュリティです。悪意のあるテナントが他のテナントを危険にさらすことを避けるには、テナント間の分離が必須です。分離メカニズムによって実装されるセキュリティレベルに応じて、業界では共有テナンシーモデルをハードとソフトのマルチテナンシーに分割しています。

マルチテナンシー

ハードマルチテナンシーは、テナント間の信頼がないことを意味し、1 つのテナントは他のテナントの何にもアクセスできません。このアプローチは、たとえば、互いに知られていない複数のテナントをホストするサービスプロバイダーに適しています。このセットアップの主な焦点は、テナント間でビジネスを完全に分離することです。オープンソースコミュニティでは、この課題を解決するための作業が進行中ですが、このアプローチはまだ本番ワークロード全体では広く使用されていません。

ハードマルチテナンシーの対極にあるのが、ソフトマルチテナンシーです。これは、同じ組織またはチームの一部である可能性のあるテナント間に信頼関係があることを表します。このアプローチの主な焦点は、セキュリティの分離ではなく、テナント間のリソースの公平な利用です。

オープンソースコミュニティには、ソフトマルチテナンシーを実装するための取り組みがいくつかあり、その 1 つが Kiosk です。Kiosk は、Kubernetes クラスターにソフトマルチテナンシーを実装するためのオープンソースフレームワークです。この記事では、Amazon Elastic Kubernetes Service (Amazon EKS) クラスターに実装するためのステップバイステップガイドを示します。

初期設定

セットアップを続行する前に、次の前提条件を満たしていることを確認してください。

  • AWS アカウントにログインします。
  • AWS マネジメントコンソールで Amazon EKS クラスターを作成します。
  • ローカルマシンから Amazon EKS クラスターに接続します。
  • ローカルマシンに kubectl をインストールします。これは、Kubernetes クラスターを制御するためのコマンドラインツールです。
  • ローカルマシンに helm バージョン 3 をインストールします。これは Kubernetes のパッケージマネージャーです。
  • Kiosk には、Kubernetes バージョン 1.14 以降が必要です。

チュートリアル

Amazon EKS で Kiosk をセットアップする方法を示すために、次のアーキテクチャを展開します。これは、Node.js アプリケーションと Redis データストアの 2 つのテナント間で共有される 1 つの Kubernetes クラスターを示しています。

セットアップを始める前に、Kiosk の基本的な構成要素をいくつか示します。

  • クラスター管理者 – クラスター全体で任意の操作を実行する管理者権限を持っています。
  • アカウント – テナントに関連付けられたリソース。これは、クラスター管理者が定義および管理します。
  • アカウントユーザー – Kubernetes ユーザー、グループ、またはサービスアカウントである可能性があります。これは Cluster Admin によって管理され、複数のアカウントに関連付けることができます。
  • スペース – 通常の Kubernetes 名前空間の仮想表現であり、単一のアカウントに属する可能性があります。
  • アカウントクォータ – アカウントのクラスター全体の集約的な制限を定義します。
  • テンプレート – 一連の Kubernetes リソースでスペースを初期化するために使用します。テンプレートは、アカウント設定を通じて適用されます。これは、クラスター管理者が定義および管理します。
  • テンプレートインスタンス – スペースに適用されたときのテンプレートの実際のインスタンスです。これには、インスタンス化に使用されるテンプレートとパラメータに関する情報が含まれています。

アカウント、スペース、アカウントクォータ、テンプレート、およびテンプレートインスタンスは、Kiosk チャートのインストール時にクラスターに作成されるカスタムリソースです。きめ細かなアクセス許可をこれらのリソースに追加できるため、テナントの分離が可能になります。

Kiosk のインストール

1.EKS クラスターのノードグループのワーカーノードを表示できることを確認します。このガイドで使用する EKS クラスターは、3 x m5.large (2 vCPU および 8 GiB) インスタンスで構成されています。

$kubectl get nodes
NAME                              STATUS   ROLES    AGE   VERSION
ip-192-168-xxx-xxx.ec2.internal   Ready    <none>   48m   v1.16.8-eks-e16311
ip-192-168-xxx-xxx.ec2.internal   Ready    <none>   48m   v1.16.8-eks-e16311
ip-192-168-xxx-xxx.ec2.internal   Ready    <none>   48m   v1.16.8-eks-e16311

2.専用の名前空間を作成し、helm を使用して Kiosk をインストールします。Helm は Kubernetes のパッケージマネージャーです。

$kubectl create namespace kiosk
$helm install kiosk --repo https://charts.devspace.sh/ kiosk --namespace kiosk --atomic

この手順では、kiosk 名前空間にポッドを作成します。

ユーザーの作成

2 つの IAM (Identity and Access Management) ユーザー devdba を作成し、それぞれが個別のテナントを管理します。EKS は、AWS IAM Authentication for Kubernetes を介した Kubernetes RBAC (ロールベースのアクセス制御) と IAM サービスの統合をサポートしているため、次のステップは、2 人のユーザーに RBAC アクセスを追加することです。

1.次の手順に従って、ユーザー devdba を作成します。IAM サービスは認証のみに使用されるため、ユーザーの作成中にアクセス許可を付与する必要はありません。Kubernetes クラスター内の各ユーザーのアクセス許可は、以降の手順で RBAC メカニズムを介して付与されます。

初期設定フェーズで EKS クラスターを作成した IAM ユーザーには、クラスターの管理者権限が自動的に付与されるため、このガイドではクラスター管理者として使用します。クラスター管理者用の IAM アクセスキーがまだ作成されていない場合は、次の手順に従って作成し、こちらで説明されているように、認証ファイル中の kube-cluster-adminと名前がつけられたプロファイルに含めます。

[kube-cluster-admin] 
aws_access_key_id=<cluster-admin-access-key>
aws_secret_access_key=<cluster-admin-secret-key>

: このガイドのすべてのコマンドは、kubectl コマンドで明示的に指定されていない限り、クラスター管理者として実行されます。クラスター管理 IAM 認証情報を使用するには、AWS_PROFILE 環境変数をオーバーライドします。

Linux または macOS の場合

$export AWS_PROFILE=kube-cluster-admin

Windows の場合

C:\> setx AWS_PROFILE kube-cluster-admin

2.aws-auth ConfigMap を更新して、2 人のユーザーに RBAC アクセスを追加します。

$kubectl edit configmap aws-auth -n kube-system

3.data.mapUsers の下に 2 人のユーザーを追加します。ユーザー ARN は IAM コンソールからコピーできます。

apiVersion: v1
data:
  mapRoles: |
    - groups:
      - system:bootstrappers
      - system:nodes
      rolearn: arn:aws:iam::11122223333:role/EKS-Worker-NodeInstanceRole-UAYZGJSHZK2K
      username: system:node:{{EC2PrivateDNSName}}
  mapUsers: |
    - userarn: arn:aws:iam::11122223333:user/dev
      username: dev
    - userarn: arn:aws:iam::11122223333:user/dba
      username: dba
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system

: クラスターを作成する IAM エンティティには、クラスターの RBAC 設定で system:masters アクセス許可が自動的に付与されます。ユーザー devdba は、どのグループにも追加されていないため、デフォルトで読み取り専用アクセス許可があります。

偽装ユーザー

Kubernetes では、コマンドラインから user impersonation を介して kubectl コマンドを実行するときに、ユーザーが別のユーザーとして行動することができます。これを行うには、偽装ユーザーに、偽装する属性のタイプ (この場合は user) に対して impersonate アクションを実行する権限が必要です。クラスター管理者はデフォルトで system:masters 権限を持っているため、ユーザー devdba に偽装することができます。ユーザーに偽装するには、kubectl コマンドの -as=<username> フラグを使用します。

各テナントに Kiosk アカウントを作成する

1.Node.js アプリケーションのアカウントの定義ファイルを作成します。

node-account.yml 
apiVersion: tenancy.kiosk.sh/v1alpha1
kind: Account
metadata:
  name: node-account
spec:
  subjects:
  - kind: User
    name: dev
    apiGroup: rbac.authorization.k8s.io

アカウントは、アカウントにアクセスできるアカウントユーザーであるサブジェクトを定義します。アカウントユーザーは、Kubernetes ユーザー、グループ、またはサービスアカウントである可能性があります。この場合のアカウントユーザーは dev です。これは以前に aws-auth ConfigMap に追加されています。

2.アカウントを作成する

$kubectl apply -f node-account.yml

3.Redis アプリケーションに対してこの手順を繰り返します。metadata.nameredis-account に、spec.subjects[0].namedba に更新します。

4.作成したアカウントをクラスター管理者として表示します。

$kubectl get accounts

5.作成したアカウントをユーザー dev として表示します。このユーザーに関連付けられているアカウントのみを表示するアクセス権があります。

$kubectl get accounts --as=dev

アカウントごとに Kiosk スペースを作成する

1.デフォルトでは、クラスター管理者のみがスペースを作成できます。アカウントユーザーがスペースを作成できるようにするには、Kubernetes RBAC ClusterRoleBinding を作成します。ユーザー devdba がアカウントにスペースを作成できるようにします。

 cluster-role-binding.yml 
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: kiosk-role-binding
subjects:
- kind: User
  name: dev
  apiGroup: rbac.authorization.k8s.io
- kind: User
  name: dba
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: kiosk-edit
  apiGroup: rbac.authorization.k8s.io

: kiosk-edit は、Kiosk のグラフがクラスターにインストールされたときに作成された ClusterRole です。これは、ClusterRoleBinding 設定に含まれるサブジェクトによるスペースリソースの作成、更新、削除アクションを許可します。kiosk-edit ロールの完全な設定は、次のコマンドを実行して確認できます。

$kubectl get clusterroles kiosk-edit -n kiosk -o yaml

2.クラスター管理者として ClusterRoleBinding を作成します。

$kubectl apply -f cluster-role-binding.yml

3.Node.js アプリケーション用のスペースを作成します。まず、定義ファイルを作成します。

node-space.yml

apiVersion: tenancy.kiosk.sh/v1alpha1
kind: Space
metadata:
  name: node-space
spec:
  account: node-account

4.ユーザー dev に偽装し、node-account 用のスペースを作成します。

$kubectl apply -f node-space.yml --as=dev

5.Redis アプリケーションに対してこの手順を繰り返します。metadata.nameredis-space に、spec.accountredis-account に更新します。

6.ユーザー dev として redis-account にスペースを作成しようとすると、エラーが発生することに注意してください。

Error from server (Forbidden): error when creating "redis-space.yml": space.tenancy.kiosk.sh "redis-space" is forbidden: User "dev" cannot create resource "spaces" in API group "tenancy.kiosk.sh" at the cluster scope

7.現在のスペースをクラスター管理者として表示します。node-spaceredis-space の両方が表示されます。

$kubectl get spaces

8.現在のスペースをユーザー dev として表示します。ユーザー dev が所有するスペースのみにアクセスできることに注意してください。この場合、これは node-space で、node-account に属しています。

$kubectl get spaces --as=dev

9.スペースは Kubernetes 名前空間の仮想表現であるため、コマンドラインで同じ構文を使用できます。たとえば、スペース内のすべてのポッドを一覧表示するには:

$kubectl get pods -n redis-space 

Kiosk アカウントに制限を適用する

アカウントごとのスペース数を制限する

1.アカウントに関連付けることができるスペースの数を制限します。定義ファイル node-account.yml を更新して、スペース制限を追加してみましょう。

node-account.yml

apiVersion: tenancy.kiosk.sh/v1alpha1
kind: Account
metadata:
  name: node-account
spec:
  space:
    limit: 2
  subjects:
  - kind: User
    name: dev
    apiGroup: rbac.authorization.k8s.io

2.変更をクラスター管理者として node-account に適用します。

$kubectl apply -f node-account.yml

現在、node-account は 2 つのスペースのみを持つことができます。3 番目のスペースを作成しようとすると、エラーがスローされます。

Error from server (Forbidden): [...]: space limit of 2 reached for account node-account

3.定義ファイル redis-account.yml を更新して、2 番目のアカウントに同じ制限を適用します。

既存のアカウントにアカウントクォータを適用する

1.アカウントクォータを定義して、アカウントのコンピューティングリソースの制限を定義します。クォータ定義ファイルを作成します。

node-quota.yml

apiVersion: config.kiosk.sh/v1alpha1
kind: AccountQuota
metadata:
  name: default-user-limits
spec:
  account: node-account
  quota:
    hard:
      pods: "2"
      limits.cpu: "4"

2.クラスター管理者としてアカウントクォータを作成します。

$kubectl apply -f node-quota.yml

AccountQuotas は、同じリソースを制限する点で Kubernetes リソースクォータと非常に似ていますが、単一の名前空間に適用されるリソースクォータとは異なり、アカウントのすべてのスペースに制限が適用されるという利点があります。

3.AccountQuota は、クラスター管理者のみが作成できます。アカウントユーザーとしてアカウントクォータを作成しようとすると、エラーが発生します。

$kubectl apply -f node-quota.yml –-as=dev
User "dev" cannot get resource "accountquotas" in API group "config.kiosk.sh" at the cluster scope

4.クラスター管理者として、クラスター全体のアカウントクォータを表示します。

$kubectl get accountquotas

スペース用のテンプレートを作成する

1.Kiosk のテンプレートは、スペースの設計図として機能します。テンプレートは、デフォルトでクラスター管理者が定義および管理します。

2.このスペースにデプロイされたすべてのコンテナに 500 milli CPU の CPU リクエストと 1 CPU の CPU 制限が割り当てられるように制限するテンプレートを作成してみましょう。

template-definition.yml 
apiVersion: config.kiosk.sh/v1alpha1
kind: Template
metadata:
  name: space-template
resources:
  manifests:
  - apiVersion: v1
    kind: LimitRange
    metadata:
      name: container-limit-range
    spec:
      limits:
      - default:
          cpu: 1
        defaultRequest:
          cpu: 0.5
        type: Container

3.テンプレートをクラスター管理者として作成します。

$kubectl apply -f template-definition.yml

4.デフォルトでは、テンプレートはオプションです。テンプレートルールに従うようにスペースの作成を強制するには、これをアカウント設定に追加する必要があります。アカウント内にスペースが作成されたときにテンプレートに従うように redis-account を更新しましょう。

redis-account.yml 
apiVersion: tenancy.kiosk.sh/v1alpha1
kind: Account
metadata:
  name: redis-account
spec:
  space:
    limit: 2
    templateInstances:
    - spec:
        template: space-template
  subjects:
  - kind: User
    name: dba
    apiGroup: rbac.authorization.k8s.io

5.クラスター管理者としてアカウントに変更を適用します。

$kubectl apply -f redis-account.yml

6.redis-account 内にスペースを作成して、これをテストしてみましょう。

redis-mandatory-space.yml 
apiVersion: tenancy.kiosk.sh/v1alpha1
kind: Space
metadata:
  name: redis-mandatory-space
spec:
  account: redis-account

7.クラスター管理者としてスペースを作成します。

$kubectl apply -f redis-mandatory-space.yml

8.スペースが作成されると、LimitRange リソースが作成されたことを確認できます。

$kubectl get limitrange -n redis-mandatory-space

9.テンプレートから作成されたスペースごとに、テンプレートインスタンスが作成されます。テンプレートインスタンスを使用して、テンプレートから作成されたリソースを追跡できます。新しいスペースのインスタンスを表示します。

$kubectl get templateinstances -n redis-mandatory-space

10.テンプレートが適用されていることをテストするには、新しいスペースにテストポッドをデプロイし、制限範囲が適用されているかどうかを確認します。

$kubectl run nginx --image nginx -n redis-mandatory-space --restart=Never

11.ポッド設定を確認し、適用されたリソース制限を確認します。

$kubectl describe pod nginx -n redis-mandatory-space

12.ポッドを削除してセットアップを続行します。

$kubectl delete pod nginx -n redis-mandatory-space

2 つのアカウントでアプリケーションをデプロイする

1.node-account のアカウントクォータが作成されているため、必要なコンピューティングリソースをデプロイの定義ファイルで指定する必要があります。

node-deployment.yml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nodejs
  labels:
    app: nodejs
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nodejs
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: nodejs
    spec:
      containers:
      - image: brentley/ecsdemo-nodejs:latest
        imagePullPolicy: Always
        name: nodejs
        ports:
        - containerPort: 3000
          protocol: TCP
        resources:        
          requests:
            cpu: "1"
          limits:
            cpu: "2"
$kubectl apply -f node-deployment.yml -n node-space --as=dev

2.Redis データストアをユーザー dba として 2 番目のアカウントにデプロイします。

$kubectl create deploy redis --image redis -n redis-space --as=dba

アカウントの分離を確認する

1.アカウントユーザー dev がアクセスできるリソースを確認します。

$kubectl get all -n node-space --as=dev

2.アカウントユーザー devredis-space のリソースにアクセスできるかどうかを確認します。多くのエラーが発生するでしょう。

$kubectl get all -n redis-space --as=dev

テナント間のアクセスを確認する

1.node-account のポッドを確認します。ポッドの名前をメモします。

$kubectl get pods -n node-space --as=dev

2.redis-account のポッドを確認します。ポッドの IP アドレスをメモします。

$kubectl get pods -n redis-space -o wide --as=dba

3.2 つのポッド間の接続をテストします。

$kubectl exec -n node-space <pod-name> --as=dev -- ping <ip-address>

テナントはスペースを越えてアクセスできることがわかります。より厳密なセキュリティ制御のために、ネイティブの Kubernetes Network PoliciesPod Security Policies を利用できます。

クリーンアップ

Amazon Elastic Kubernetes Service クラスターを削除して、コストが余計にかかるのを回避しましょう。

まとめ

プラットフォームの進化とこの機能を実装する複雑さのため、最近では、Kubernetes のマルチテナンシーがオープンソースコミュニティのホットトピックになっています。最新情報を取得するには、kubernetes-sigs/multi-tenancy で Kubernetes マルチテナンシー Special Interest Group コミュニティをフォローできます。

この記事では、Kiosk の 1 つの Kubernetes クラスターでソフトマルチテナンシーを簡単に設定できることと、ネイティブの Kubernetes 機能にはない利点があることについて説明しました。アカウントクォータを通じてクラスター全体でリソースを分離し、アカウント、アカウントユーザー、スペースなどのプリミティブを通じてセキュリティ境界を実装しました。