适用于 Knative serving 的跨项目多租户

本指南逐步介绍了如何配置 Knative serving,以允许一个或多个 Google Cloud 项目运行和管理在其他 Google Cloud 项目中的 GKE 集群上运行的工作负载。

Knative serving 具有的常见操作模型可供应用开发者团队使用自己的 Google Cloud 项目来跨其他团队的 Google Cloud 项目部署和管理在不同 GKE 集群中运行的服务。此功能称为多租户,让您可以作为平台运营者对您的开发团队访问权限进行定制,使其仅访问在您的组织的各种环境(例如生产环境与预演环境)中运行的服务。

Knative serving 明确支持企业多租户。这种类型的多租户使集群 Google Cloud 项目能够访问其 GKE 集群的特定资源。被授予集群 Google Cloud 项目的访问权限的 Google Cloud 项目为租户 Google Cloud 项目。集群 Google Cloud 项目的租户能够使用 Knative serving 访问、操作和拥有他们被授予访问权限的服务和资源。

从概念上讲,使用 Knative serving 配置企业多租户需要执行四个步骤:

  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
    

您的集群 Google Cloud 项目 ID 和租户 Google Cloud 项目 ID 现已用于以下所有指定了 $CLUSTER_PROJECT_ID$TENANT_PROJECT_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 Cloud 群组。如需详细了解如何使用安全群组,请参阅使用 Google GKE 群组

为您的 Google 群组创建以下本地环境变量:

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

Kubernetes Cluster Viewer

运行以下命令可以允许租户获取集群的凭据,但不允许租户读取或操作 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"

Monitoring Viewer

运行以下命令可以允许租户读取监控指标。

Monitoring 角色参考文档

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 界面,都还会在集群 Google Cloud 项目上看到其服务。这不会更改对集群 Google Cloud 项目或 GKE 集群的 IAM 权限,而只是从租户项目(或文件夹)到集群 Google Cloud 项目的映射。

  1. 在租户 Google Cloud 项目上启用 resourcesettings API。

    gcloud services enable resourcesettings.googleapis.com \
      --project=$TENANT_PROJECT_ID
    
  2. 运行以下命令,将 Organization Admin 特权 (roles/resourcesettings.admin) 添加到您的用户 ID:

    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,并将其添加到单个租户文件夹。
    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 项目的命名空间中的所有日志都会路由到存储桶。如需详细了解如何限制共享哪些日志,请参阅下面的设置。

创建以下本地环境变量:

  • 指定您的租户访问的 GKE 集群的命名空间。
  • 接收器的名称。为简化此步骤,该名称是您之前创建的集群 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 项目中指定的命名空间到租户 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 项目的所有后续租户。

界面角色

以下角色允许租户查询所有命名空间。这是查找哪些命名空间用户有权创建 /sdk/gcloud/reference/logging/sinks/create 服务所必需的角色。

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

以下角色允许租户查看 Knative serving 服务。这是在 Knative serving 界面中列出这些服务所必需的角色。

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

创建集群角色

您只需具有以下其中一种权限。第一种权限允许租户操作其命名空间中的任何资源。第二种权限允许一组条件更受限的租户仅创建 Knative serving 服务。

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

如果 kubernetes-developer 权限过于宽松,则以下权限允许租户在其命名空间中创建 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

创建租户命名空间并分配权限。

请注意,假设您使用 Google GKE 群组进行了设置。此操作对于每个租户来说都是非常必要的。

export TENANT_GROUP=tenant-a@company.com

TENANT_GROUP 必须是 SECURITY_GROUP 的一部分

有权查看所有命名空间

为查询 GKE 集群,所有租户都必须有权列出命名空间。目前,没有一个 auth can-i 会返回可对其执行操作的命名空间。唯一的解决方法是列出命名空间,然后单独查询每个命名空间。

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

有权操作命名空间中的资源

首先,创建命名空间:

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 集群命名空间中他们已被授予访问权限的服务和资源进行交互。

多租户参考文档