Knative serving 用のクロスプロジェクト マルチテナンシー

このガイドでは、別の Google Cloud プロジェクトの GKE クラスタで実行されているワークロードを 1 つ以上の Google Cloud プロジェクトで実行して管理できるように、Knative serving を構成する方法について説明します。

Knative serving でよく利用されている運用モデルは、アプリケーション デベロッパーがチームの Google Cloud プロジェクトから別のチームの Google Cloud プロジェクトの GKE クラスタにサービスをデプロイし、そのサービスを管理するモデルです。この機能(マルチテナンシー)により、プラットフォーム オペレーターは組織内のさまざまな環境(本番環境、ステージング環境など)で実行されているサービスに対する開発チームのアクセスを調整できます。

Knative serving は、特にエンタープライズ マルチテナンシーをサポートしています。このタイプのマルチテナンシーでは、クラスタの Google Cloud プロジェクトから GKE クラスタの特定のリソースにアクセスできるようになります。クラスタの Google Cloud プロジェクトへのアクセスが許可されている Google Cloud プロジェクトは、テナントの Google Cloud プロジェクトです。クラスタの Google Cloud プロジェクトのテナントは、Knative serving を使用して、アクセス権が付与されているサービスやリソースへのアクセス、運用、所有が可能です。

概念的には、Knative serving でエンタープライズ マルチテナンシーを構成するには、次の 4 つのステップがあります。

  1. Google グループと Identity and Access Management を使用して、クラスタの Google Cloud プロジェクトに対するテナント アクセスを構成します。
  2. 各テナント Google Cloud プロジェクトをクラスタの Google Cloud プロジェクトにマッピングします。
  3. ログバケットとシンクを使用して、クラスタの Google Cloud プロジェクトのログデータをテナントの Google Cloud プロジェクトに転送します。
  4. ロールベース アクセス制御を使用して、テナントのクラスタ権限を定義します。

始める前に

マルチテナンシーを構成するプラットフォーム オペレータは、次の要件を満たす必要があります。

ローカル環境変数を定義する

このプロセスで使用するコマンドを単純にするため、クラスタの Google Cloud プロジェクトとテナントの Google Cloud プロジェクトの両方にローカル環境変数を定義します。

  1. YOUR_CLUSTER_PROJECT_ID をクラスタ Google Cloud プロジェクトの ID に置き換えて、次のコマンドを実行します。

    export CLUSTER_PROJECT_ID=YOUR_CLUSTER_PROJECT_ID
    
  2. YOUR_TENANT_PROJECT_ID をテナント Google Cloud プロジェクトの ID に置き換えて、次のコマンドを実行します。

    export TENANT_PROJECT_ID=$YOUR_TENANT_PROJECT_ID
    
  3. 次のコマンドを実行して、ローカル環境変数を確認します。

    echo "cluster Google Cloud project is:" $CLUSTER_PROJECT_ID
    echo "tenant Google Cloud project is:" $TENANT_PROJECT_ID
    

これで、$CLUSTER_PROJECT_ID$TENANT_PROJECT_ID が指定されているすべてのコマンドで、クラスタの Google Cloud プロジェクト ID とテナントの Google Cloud プロジェクト ID が使用されます。

IAM 権限の確認

次の testIamPermissions コマンドを実行して、クラスタの Google Cloud プロジェクトとテナントの Google Cloud プロジェクトのリソースにアクセスするために必要な IAM 権限があることを確認します。

次のコマンドを実行して、クラスタの Google Cloud プロジェクトの権限を確認します。

curl -X POST \
  -H "Authorization: Bearer "$(gcloud auth application-default print-access-token) \
  --header "Content-Type: application/json" \
  --data '{"permissions":["logging.sinks.create", "logging.sinks.get", "resourcemanager.projects.setIamPolicy"]}' \
  https://cloudresourcemanager.googleapis.com/v1/projects/$CLUSTER_PROJECT_ID:testIamPermissions

クラスタの Google Cloud プロジェクトで想定される結果:

{
  "permissions": [
    "logging.sinks.create",
    "logging.sinks.get",
    "resourcemanager.projects.setIamPolicy"
  ]
}

次のコマンドを実行して、各テナントの Google Cloud プロジェクトの権限を確認します。

curl -X POST \
  -H "Authorization: Bearer "$(gcloud auth application-default print-access-token) \
  --header "Content-Type: application/json" \
  --data '{"permissions":["logging.buckets.create", "logging.buckets.get", "resourcemanager.projects.setIamPolicy", "resourcesettings.settingvalues.create", "serviceusage.services.enable"]}' \
  https://cloudresourcemanager.googleapis.com/v1/projects/$TENANT_PROJECT_ID:testIamPermissions

各テナントの Google Cloud プロジェクトに想定される結果:

{
  "permissions": [
    "logging.buckets.create",
    "logging.buckets.get",
    "resourcemanager.projects.setIamPolicy",
    "resourcesettings.settingvalues.create",
    "serviceusage.services.enable",
  ]
}

Google グループと Identity and Access Management を使用してテナント アクセスを構成する

Google グループを使用して、テナントに GKE クラスタへのアクセスを許可します。IAM 権限により、テナントには認証情報の取得が許可されますが、後のステップで Kubernetes ロールベース アクセス制御を構成するまで、クラスタ内では何もできません。

テナントの Google Cloud プロジェクトのすべてのユーザーを含む Google グループを作成する必要があります。セキュリティ グループの使用方法については、GKE 向け Google グループの使用をご覧ください。

Google グループに次のローカル環境変数を作成します。

export SECURITY_GROUP=gke-security-groups@company.com

Kubernetes クラスタ閲覧者

次のコマンドを実行して、テナントにクラスタの認証情報の取得を許可します。ただし、GKE クラスタ リソースの読み取りまたは操作は許可されません。

IAM リファレンス

gcloud projects add-iam-policy-binding $CLUSTER_PROJECT_ID \
   --member=group:$SECURITY_GROUP \
   --role='roles/container.clusterViewer' \
   --condition=None

特定のクラスタへのアクセスを制限するには、IAM 条件を使用します。

gcloud projects add-iam-policy-binding $CLUSTER_PROJECT_ID \
   --member=group:$SECURITY_GROUP \
   --role='roles/container.clusterViewer' \
   --condition="expression=resource.name == 'cluster-name',title=Restrict cluster access"

モニタリング閲覧者

次のコマンドを実行して、テナントにモニタリング指標の読み取りを許可します。

モニタリング ロールのリファレンス

gcloud projects add-iam-policy-binding $CLUSTER_PROJECT_ID \
   --member=group:$SECURITY_GROUP \
   --role='roles/monitoring.viewer' \
   --condition=None

各テナント Google Cloud プロジェクトをクラスタの Google Cloud プロジェクトにマッピングする

リソース設定値を使用して、テナントの Google Cloud プロジェクトをクラスタの Google Cloud プロジェクトにマッピングします。

リソース設定は、個々のテナントの Google Cloud プロジェクトに対して構成することも、フォルダ階層の任意のレベルで設定することもできます。単一テナント フォルダ レベルで設定するのが簡単ですが、テナント プロジェクト レベルごとに柔軟に設定することも可能です。設定後、テナントが Knative serving UI をブラウジングすると、クラスタの Google Cloud プロジェクトのサービスが表示されます。クラスタの Google Cloud プロジェクトまたは GKE クラスタの IAM 権限は変更されません。これは、テナント プロジェクト(またはフォルダ)からクラスタの Google Cloud プロジェクトへのマッピングにすぎません。

  1. テナントの Google Cloud プロジェクトで resourcesettings API を有効にします。

    gcloud services enable resourcesettings.googleapis.com \
      --project=$TENANT_PROJECT_ID
    
  2. 次のコマンドを実行して、ユーザー ID に組織管理者の権限(roles/resourcesettings.admin)を追加します。

    gcloud organizations add-iam-policy-binding YOUR_ORGANIZATION_ID \
      --member=YOUR_ADMIN_MEMBER_ID \
      --role='roles/resourcesettings.admin'
    

    YOUR_ORGANIZATION_ID組織の ID に、YOUR_ADMIN_MEMBER_ID はユーザー ID(user:my-email@my-domain.com など)に置き換えます。

  3. 次のいずれかの方法でマッピングを定義します。

    すべての子 Google Cloud プロジェクトと Google Cloud フォルダで同じ値を使用する場合は、親 Google Cloud フォルダにリソース設定値を設定できます。

テナント プロジェクト

テナントの Google Cloud プロジェクトごとにリソース設定値を設定します。

  1. テナントの Google Cloud プロジェクトの name を取得し、ローカル環境変数に設定します。
    export TENANT_PROJECT_NUMBER=$(gcloud projects describe $TENANT_PROJECT_ID --format="value(projectNumber)")
  2. リソース設定値ファイルを作成して、テナントの Google Cloud プロジェクトからクラスタの Google Cloud プロジェクトへのマッピングを定義します。このファイルで複数のクラスタの Google Cloud プロジェクト ID を定義し、単一テナントの Google Cloud プロジェクトに追加できます。
    cat > value-file.json << EOF
    {
    "name": "projects/$TENANT_PROJECT_NUMBER/settings/cloudrun-multiTenancy/value",
    "value": {
      "stringSetValue": {
        "values": [ "projects/$CLUSTER_PROJECT_ID" ]
      }
    }
    }
    EOF
  3. リソース設定をテナントの Google Cloud プロジェクトにデプロイします。
    gcloud resource-settings set-value cloudrun-multiTenancy --value-file value-file.json --project $TENANT_PROJECT_ID

テナント フォルダ

親テナント プロジェクトにリソース設定値を設定し、その値をすべての子テナントの Google Cloud プロジェクトとフォルダに設定します。

  1. テナント フォルダの number を取得し、ローカル環境変数に設定します。
    export TENANT_FOLDER_NUMBER=$TENANT_FOLDER_NUMBER
  2. リソース設定値ファイルを作成して、テナント フォルダからクラスタの Google Cloud プロジェクトへのマッピングを定義します。このファイルに複数のクラスタ Google Cloud プロジェクト ID を定義し、1 つのテナント フォルダに追加できます。
    cat > value-file.json << EOF
    {
    "name": "folders/$TENANT_FOLDER_NUMBER/settings/cloudrun-multiTenancy/value",
    "value": {
      "stringSetValue": {
        "values": [ "projects/$CLUSTER_PROJECT_ID" ]
      }
    }
    }
    EOF
  3. リソース設定をテナント フォルダにデプロイします。
    gcloud resource-settings set-value cloudrun-multiTenancy --value-file value-file.json --folder $TENANT_FOLDER_NUMBER

ログデータをルーティングするログバケットとシンクの設定

テナントごとにログバケットとシンクを作成します。また、クラスタの Google Cloud プロジェクトのログデータをテナントの Google Cloud プロジェクトに転送するための権限を作成します。次の手順では、クラスタの Google Cloud プロジェクトの Namespace にあるすべてのログがバケットに転送されます。共有するログを制限する方法については、以下の設定をご覧ください。

次のローカル環境変数を作成します。

  • テナントがアクセスする GKE クラスタの Namespace を指定します。
  • シンクの名前。この手順を簡略化するため、前に作成したクラスタの Google Cloud プロジェクトとテナントの Google Cloud プロジェクトのローカル環境変数の組み合わせを名前に使用します。この値は変更できます。
export NAMESPACE=$NAMESPACE
export SINK_NAME=$CLUSTER_PROJECT_ID-$TENANT_PROJECT_ID

次のコマンドを実行して、テナント プロジェクトにログバケットを作成します。ログバケット名は、クラスタの Google Cloud プロジェクトの ID にする必要があります。これは変更できません。

gcloud logging buckets \
   create $CLUSTER_PROJECT_ID \
   --location=global \
   --project=$TENANT_PROJECT_ID

次のコマンドを実行して、クラスタの Google Cloud プロジェクト内の指定した Namespace からテナントの Google Cloud プロジェクト バケットにシンクを作成します。ログの範囲を限定することもできます。たとえば、追加の log-filterを定義して、個々の GKE クラスタまたは特定の Knative serving リソースのみを共有できます。

gcloud logging sinks \
   create $SINK_NAME \
   logging.googleapis.com/projects/$TENANT_PROJECT_ID/locations/global/buckets/$CLUSTER_PROJECT_ID \
   --log-filter=resource.labels.namespace_name=$NAMESPACE \
   --project $CLUSTER_PROJECT_ID

次のコマンドを実行して、ログシンク サービス アカウントから作成したバケットに権限を追加します。

export SINK_SERVICE_ACCOUNT=$(gcloud logging sinks \
   describe $SINK_NAME \
   --project $CLUSTER_PROJECT_ID \
   --format="value(writerIdentity)")
gcloud projects add-iam-policy-binding $TENANT_PROJECT_ID \
   --member=$SINK_SERVICE_ACCOUNT \
   --role='roles/logging.bucketWriter' \
   --condition="expression=resource.name.endsWith\
   (\"locations/global/buckets/$CLUSTER_PROJECT_ID\"),\
   title=Log bucket writer from $CLUSTER_PROJECT_ID"

ロールベース アクセス制御(RBAC)によるテナントの権限の設定

Google グループと IAM を使用して、テナントが GKE クラスタの Google Cloud プロジェクトにアクセスするための権限を構成しました。テナントが GKE クラスタ内のリソースにアクセスするには、Kubernetes RBAC で権限を定義する必要があります。

クラスタロールを作成する

次のクラスタロールを定義して作成した後は、引き続きこれらのロールを使用して、クラスタの Google Cloud プロジェクトのテナントを追加できます。

UI ロール

このロールにより、テナントはすべての Namespace を照会できます。これは、ユーザーが /sdk/gcloud/reference/logging/sinks/create サービスの作成でアクセスする Namespace を検索する際に必要になります。

kubectl create clusterrole \
   namespace-lister \
   --verb=list \
   --resource=namespaces

このロールにより、テナントは Knative serving サービスを表示できます。これは、Knative serving UI でサービスの一覧を表示するために必要です。

kubectl create clusterrole \
   ksvc-lister \
   --verb=list \
   --resource=services.serving.knative.dev

クラスタロールを作成する

これらの権限の中で、いずれか 1 つが必要になります。最初の権限により、テナントは Namespace 内のすべてのリソースを操作できます。2 番目の権限では、Knative serving サービスを作成するためのより限定された組み合わせが許可されます。

kubectl create clusterrole \
   kubernetes-developer \
   --verb="*" \
   --resource="*.*"

kubernetes-developer 権限の制限が大きすぎる場合は、以下の操作により、テナントの Namespace での Knative サービスの作成と他の Knative リソースの表示をテナントに許可できます。

cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: knative-developer
rules:
- apiGroups: ["serving.knative.dev"]
  resources: ["services"]
  verbs: ["*"]
- apiGroups: ["serving.knative.dev"]
  resources: ["*"]
  verbs: ["get", "list", "watch"]
EOF

テナントの Namespace を作成して権限を割り当てる

ここでは、GKE 向け Google グループが設定済みであることを前提としています。これはテナントごとに行う必要があります。

export TENANT_GROUP=tenant-a@company.com

TENANT_GROUP は SECURITY_GROUP の一部である必要があります。

すべての Namespace を表示する権限

GKE クラスタにクエリを実行するため、すべてのテナントには Namespace の一覧を取得する権限があります。現時点で、アクションが利用可能な Namespace を返す auth can-i はありません。唯一の回避策は、Namespace の一覧を取得してから、Namespace ごとに個別にクエリを実行することです。

kubectl create clusterrolebinding \
   all-namespace-listers \
   --clusterrole=namespace-lister \
   --group=$TENANT_GROUP

Knative serving サービスを一覧表示する権限

kubectl create clusterrolebinding \
   all-ksvc-listers \
   --clusterrole=ksvc-lister \
   --group=$TENANT_GROUP

Namespace のリソースを操作する権限

まず、Namespace を作成します。

kubectl create namespace $NAMESPACE

kubernetes-developer ロールを使用する場合:

kubectl create rolebinding \
   kubernetes-developer \
   --namespace=$NAMESPACE \
   --clusterrole=kubernetes-developer \
   --group=$TENANT_GROUP

knative-developer ロールを使用する場合:

kubectl create rolebinding \
   kubernetes-developer \
   --namespace=$NAMESPACE \
   --clusterrole=knative-developer \
   --group=$TENANT_GROUP

テナントが外部 IP アドレスにアクセスできるように権限を追加する

cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: ingress-reader
rules:
- apiGroups: [""]
  resources: ["services"]
  verbs: ["get"]
EOF
kubectl create rolebinding \
   ingress-reader-$TENANT_GROUP \
   --namespace=istio-system \
   --clusterrole=ingress-reader \
   --group=$TENANT_GROUP

確認

Knative serving でテナントの Google Cloud プロジェクトを開き、GKE クラスタにサービスをデプロイすることで、エンタープライズ マルチテナンシーが正常に構成されていることを確認できます。

Knative serving に移動

これで、アクセスが許可されている GKE クラスタの名前空間内のサービスとリソースをテナントで操作できるようになりました。

マルチテナンシーのリファレンス