企业级多租户最佳做法


Google Kubernetes Engine (GKE) 中的多租户是指在租户之间共享一个或多个集群。在 Kubernetes 中,租户可定义为以下任意一项:

  • 负责开发和运营一个或多个工作负载的团队。
  • 由一个或多个团队运营的一组相关工作负载。
  • Deployment 之类的单个工作负载。

集群多租户的实现通常有助于降低费用或确保为各个租户施行一致的管理政策。但是,如果 GKE 集群或其关联的 GKE 资源配置不正确,可能会导致无法实现成本节约、政策施行错误,或不同租户的工作负载之间发生破坏性交互。

本指南介绍了为企业组织安全高效地设置多个多租户集群的最佳做法。

假设和要求

本指南中的最佳做法以企业环境的多租户使用场景为基础,因此存在以下假设和要求:

  • 该组织是一家拥有许多使用 Kubernetes 且希望共享计算和管理资源的租户(至少两个应用/服务团队)的公司。
  • 每个租户都是一个负责开发单一工作负载的团队。
  • 除了应用/服务团队之外,还有其他团队也使用和管理集群,包括平台团队成员、集群管理员、审核人员等。
  • 平台团队拥有集群并负责定义每个租户团队可以使用的资源数量;每个租户可以申请更多资源。
  • 每个租户团队都应该能够通过 Kubernetes API 部署其应用,而无需与平台团队沟通。
  • 每个租户都不应该能够影响共享集群中的其他租户,除非采用了 API 调用、共享数据源等这些明确的设计决策。

我们将以上述设置作为模型来演示多租户最佳做法。虽然这种设置可能无法完美地描述所有企业组织,但它可以轻松进行扩展,从而涵盖类似场景。

设置文件夹、项目和集群

对于在 GKE 中部署多租户集群的企业组织而言,需要在其他 Google Cloud 系统中进行额外配置,以便管理较简单的单应用、单团队 Kubernetes 部署中所不存在的复杂任务。这包括用于隔离管理关注点和将组织结构映射到云身份和账号的项目配置,以及用于管理数据库、日志记录和监控、存储和网络等其他 Google Cloud 资源的配置。

建立文件夹和项目层次结构

如需确定贵组织如何管理 Google Cloud 资源并实施关注点分离,请使用文件夹项目。文件夹可让不同的团队设置跨多个项目级联的政策,而项目则可用于隔离各种环境(例如生产环境与预演环境)和团队。例如,大多数组织都有一个负责管理网络基础架构的团队和另一个负责管理集群的团队。每种技术都被视为一个独立技术栈,需要专门的专业知识水平、问题排查流程和访问权限。

父级文件夹最多可以包含 300 个文件夹,并且文件夹最多可以嵌套 10 层。如果您拥有超过 300 个租户,则可以采用嵌套层次结构安排这些租户,以免超出限制。如需详细了解文件夹,请参阅创建和管理文件夹

使用 IAM 分配角色

您可以通过 IAM 政策控制对 Google Cloud 资源的访问权限。首先确定贵组织所需的群组及其操作范围,然后为该群组分配适当的 IAM 角色

您可以使用 Google 群组为用户高效地分配和管理 IAM。

集中控制网络

如需集中控制网络资源(如子网、路由和防火墙),请使用共享 VPC 网络。 共享 VPC 中的资源可以使用内部 IP 地址跨项目边界安全高效地相互通信。每个共享 VPC 网络均由一个集中式宿主项目定义和拥有,并可供一个或多个服务项目使用。

借助共享 VPC 和 IAM,您可以将网络管理与项目管理分离开来。这种分离有助于您实现最小权限原则。例如,集中式网络团队可以管理网络,而无需对参与项目拥有任何权限。同样,项目管理员可以管理其项目资源,而无需操作共享网络的任何权限。

设置共享 VPC 时,您必须在 VPC 中配置子网及其次要 IP 范围。如需确定子网规模,您需要了解预期的租户数量、预期运行的 Pod 和 Service 数量以及 Pod 的最大大小和平均大小。通过计算所需的总集群容量,您可以了解所需的实例大小,进而确定节点总数。了解节点总数后,您便可以计算总 IP 空间用量,进而确定所需的子网大小。

以下是在设置网络时您还应考虑的一些因素:

  • 一个宿主项目最多可以附加 1000 个服务项目,一个组织最多可以有 100 个共享 VPC 宿主项目。
  • Node、Pod 和 Service 的 IP 范围都必须是唯一的。您无法创建主要 IP 地址范围和次要 IP 地址范围重叠的子网。
  • 指定 GKE 集群的最大 Pod 数和最大 Service 数受集群次要范围的限制。
  • 集群中节点的最大数量受集群子网的主要 IP 地址范围大小和集群的 Pod 地址范围大小的限制。

您可以借助 GKE IPAM 计算器开源工具来计算集群的子网数。IP 地址管理 (IPAM) 可有效利用 IP 空间/子网,同时避免重叠的范围导致连接选项无法正常运行。如需了解 VPC 集群中的网络范围,请参阅创建 VPC 原生集群

如果租户需要进一步隔离在共享集群(例如专用 Compute Engine 虚拟机)外部运行的资源,他们可以使用自己的 VPC 与由网络团队运行的共享 VPC 建立对等互连连接。这样做可提供额外的安全保障,但会增加复杂性和许多其他限制。如需详细了解对等互连,请参阅使用 VPC 网络对等互连。在以下示例中,所有租户均已选择每种环境共享一个租户 VPC。

创建安全可靠的高可用性集群

如需将您的集群架构设计为兼具高可用性和可靠性,您可以实施以下建议:

  • 为每个集群创建一个集群管理项目,以降低项目级配置(例如 IAM 绑定)对众多集群产生不利影响的风险,并帮助隔离配额和结算服务。集群管理项目与租户项目不同,后者供各个租户用于管理其 Google Cloud 资源等。
  • 将生产集群设为专用,以停用对节点的访问权限并管理对控制平面的访问权限。同时建议将专用集群用于开发和预演环境。
  • 确保集群的控制平面为区域级,以为多租户提供高可用性;对控制平面造成的任何干扰都会影响租户。请注意,运行区域级集群会影响费用Autopilot 集群已预配置为区域性集群。
  • 确保集群中的节点至少跨越三个可用区,以实现可用区级可靠性。如需了解同一区域的各可用区之间的出站流量费用,请参阅网络价格文档。
在三个可用区运行区域级控制平面的专用区域级集群
图 3:在三个可用区运行区域级控制平面的专用区域级集群。

自动扩缩集群节点和资源

为了满足租户的需求,请启用自动扩缩功能来让系统自动扩缩集群中的节点。

自动扩缩功能有助于确保系统在各种租户的命名空间部署了繁重工作负载的情况下或在应对可用区级服务中断之时,保持良好的运行状况和响应能力。

借助 Autopilot 集群,节点池会自动扩缩以满足工作负载的要求。

启用自动扩缩功能时,您可以根据预期的工作负载大小指定集群中的最小和最大节点数。通过指定最大节点数,您可以确保集群中有足够的空间供所有 Pod 使用,无论它们在哪个命名空间中运行。集群自动扩缩功能会根据最小/最大边界重新扩缩节点池,这有助于在系统负载减少时降低运营成本,并避免在可用集群资源不足时 Pod 进入待处理状态。如需确定最大节点数,请确定每个租户所需的最大 CPU 数和最大内存量,然后将这些值加在一起,这样即可获得在所有租户都达到资源用量上限时集群应能够处理的总容量。然后,您可以根据最大节点数并考虑可供集群使用的 IP 子网空间,选择实例大小和数量。

您可以利用 Pod 自动扩缩功能来根据资源需求自动扩缩 Pod。 Pod 横向自动扩缩程序 (HPA) 根据 CPU/内存利用率或自定义指标扩缩 Pod 副本的数量。Pod 纵向自动扩缩 (VPA) 可用于自动扩缩 Pod 资源需求。除非有自定义指标可用,否则不得将 VPA 与 HPA 一起使用,因为两个自动扩缩程序可能会互相竞争。因此,请先使用 HPA,稍后只有在需要时才使用 VPA。

确定集群的大小

在确定集群的大小时,您需要考虑以下几个重要因素:

  • 集群的大小取决于您计划运行的工作负载类型。工作负载的密度越高,成本效益越高,但发生资源争用的可能性也会越大。
  • 集群的最小规模由其所跨越可用区的数量决定:可用区级集群有 1 个节点,区域级集群有 3 个节点。
  • 对于每个项目,每个区域最多有 50 个集群,每个地区最多有 50 个地区级集群。
  • 对于每个集群,每个集群最多有 15000 个节点(对于 GKE 1.17 及之前的版本,则最多 5000 个节点),每个节点池最多有 1000 个节点,每个集群最多有 1000 个节点(如果您使用 GKE Ingress 控制器),每个节点最多有 256 个 Pod(对于 1.23.5-gke.1300 之前的 GKE 版本,则最多 110 个 Pod),每个集群最多有 150000 个 Pod,每个集群最多有 300000 个容器。如需了解详情,请参阅“配额和限制”页面

安排维护期

为了减少集群/节点升级和维护期间的停机时间,请将维护期安排在非高峰时段内。在升级期间,如果移动工作负载以重新创建节点,则可能会出现暂时性中断。为了确保将此类中断所带来的影响降到最低程度,请将升级作业安排在非高峰时段进行,并对您的应用部署进行设计,使其尽可能无缝处理部分中断。

使用 Ingress 设置外部应用负载均衡器

为帮助管理租户的已发布 Service 以及管理发往这些 Service 的传入流量,请创建一个 HTTP(s) 负载均衡器以允许每个集群使用一个 Ingress;在这种情况下,每个租户的 Service 均会在该集群的 Ingress 资源中注册。如需创建和配置 HTTP(S) 负载均衡器,您可以创建一个 Kubernetes Ingress 资源来定义流量如何到达您的 Service,以及如何将流量路由到租户的应用。通过将 Service 注册到 Ingress 资源,这些 Service 将会采用一致的命名惯例,并反映同一 Ingress,例如 tenanta.example.comtenantb.example.com

保护多租户集群

使用网络政策控制 Pod 通信

如需控制集群的每个命名空间中各 Pod 之间的网络通信,请根据租户的要求创建网络政策。初步建议是屏蔽托管不同租户的应用的命名空间之间的流量。您的集群管理员可以应用 deny-all 网络政策来拒绝所有入站流量,以避免一个命名空间中的 Pod 意外将流量发送到其他命名空间中的 Service 或数据库。

例如,以下网络政策会限制从所有其他命名空间到 tenant-a 命名空间的入站流量:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all
  namespace: tenant-a
spec:
  podSelector:
    matchLabels:

  ingress:
  - from:
    - podSelector: {}

使用 GKE Sandbox 运行工作负载

运行不受信任的工作负载的集群比其他集群更容易受到安全漏洞的影响。您可以借助 GKE Sandbox 来强化多租户环境中各工作负载之间的隔离边界的安全性。为管理安全性,我们建议先从 GKE Sandbox 入手,然后使用基于政策的准入控制来弥补任何不足。

GKE Sandbox 基于开源容器沙盒项目 gVisor 构建,可在容器和主机操作系统之间额外增加一层保护,从而使多租户工作负载得以进一步隔离。容器运行时通常以特权用户身份在节点上运行,在这种情况下,它可以访问对主机内核的大多数系统调用。在多租户集群中,一个恶意租户可以获得对主机内核和其他租户的数据的访问权限。GKE Sandbox 可缓解这些威胁,因为它可减少容器与主机直接交互的需求,从而缩小主机的受攻击面并限制恶意操作者的行动。

GKE Sandbox 在容器和主机操作系统之间提供了两种隔离边界:

  • 使用 Go 编写的用户空间内核,可处理系统调用并限制与主机内核的交互。每个 Pod 都有自己的独立用户空间内核。
  • 用户空间内核还会在命名空间和 seccomp 过滤系统调用中运行。

设置基于政策的准入控制

如需阻止违反安全边界的 Pod 在集群中运行,请使用准入控制器。准入控制器可以根据您定义的政策检查 Pod 规范,并且可以阻止违反这些政策的 Pod 在集群中运行。

GKE 支持以下类型的准入控制:

  • Policy Controller:声明预定义政策或自定义政策,并使用舰队在集群中大规模强制执行政策。Policy Controller 是开源 Gatekeeper Open Policy Agent 的实现,是 GKE Enterprise 的一项功能。

使用适用于 GKE 的工作负载身份联合授予对 Google Cloud 服务的访问权限

为安全地向工作负载授予对 Google Cloud 服务的访问权限,请在集群中启用适用于 GKE 的工作负载身份联合。适用于 GKE 的工作负载身份联合可帮助管理员管理 Kubernetes 工作负载用于访问 Google Cloud 服务的 Kubernetes 服务账号。创建启用了适用于 GKE 的工作负载身份联合的集群时,系统会为集群所在的项目建立一个身份命名空间。通过该身份命名空间,集群可以将 Kubernetes 服务账号名称映射到虚拟 Google 服务账号句柄(用于对租户 Kubernetes 服务账号进行 IAM 绑定),从而自动验证 GKE 应用服务账号的身份。

限制对控制平面的网络访问

为了保护您的控制平面,请仅允许已获授权的网络进行访问。在 GKE 中,启用授权的网络后,您可以向多达 50 个 CIDR 范围授权,并仅允许这些范围内的 IP 地址访问您的控制平面。GKE 已使用传输层安全协议 (TLS) 和身份验证机制来提供从公共互联网对控制平面端点的安全访问。您可以通过授权网络进一步将访问限制到一组指定 IP 地址。

租户预配

创建租户项目

如需托管租户的非集群资源,请为每个租户创建一个服务项目。这些服务项目包含特定于租户应用的逻辑资源(例如日志、监控、存储分区、服务账号等)。所有租户服务项目都会连接到租户宿主项目中的共享 VPC。

使用 RBAC 优化租户访问权限

您可以使用 Kubernetes RBAC 为租户定义更精细的集群资源访问权限。在最初通过 IAM 授予租户群组的只读权限之外,为每个租户群组定义命名空间级的 Kubernetes RBAC 角色和绑定。

之前我们确定了两个租户群组:租户管理员和租户开发者。 对于这些群组,我们将定义以下 RBAC 角色和访问权限:

群组 Kubernetes
RBAC 角色
说明
租户管理员 Namespace Admin

授予列出和监控其命名空间中的 Deployment 的权限。

授予在租户群组中添加和移除用户的权限。

租户开发者 Namespace Editor、
Namespace Viewer
授予在其命名空间中创建/修改/删除 Pod、Deployment、Service 和 ConfigMap 的权限。

除了创建 RBAC 角色和绑定来为 Google Workspace 或 Cloud Identity 群组分配其命名空间内的各种权限之外,租户管理员通常还需要能够管理这些群组中的用户。为此,您可以向租户管理员委派 Google Workspace 或 Cloud Identity 权限来管理其各自的群组成员资格,也可以让租户管理员与您组织中拥有 Google Workspace 或 Cloud Identity 权限的团队合作处理这些更改,究竟选择哪种方式要视贵组织的要求而定。

您可以将 IAM 和 RBAC 权限与命名空间结合使用,以限制用户与 Google Cloud 控制台上的集群资源的交互。如需了解详情,请参阅按命名空间启用访问权限和查看集群资源

使用 Google 群组绑定权限

如需高效地管理集群中的租户权限,您可以将 RBAC 权限绑定到您的 Google 群组。 这些群组的成员资格由您的 Google Workspace 管理员维护,因此您的集群管理员不需要详细了解您的用户。

举例来说,假设我们有一个 tenant-admins@mydomain.com Google 群组,并且 admin1@mydomain.com 用户是该群组的成员,那么以下绑定可为该用户提供对 tenant-a 命名空间的管理员访问权限:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  namespace: tenant-a
  name: tenant-admin-rolebinding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: tenant-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: "tenant-admins@mydomain.com"

创建命名空间

如需在同一集群上的各租户之间提供逻辑隔离,请实施命名空间。在 Kubernetes RBAC 流程中,集群管理员会为每个租户群组创建命名空间。租户管理员在其各自的租户命名空间内管理用户(租户开发者)。然后,租户开发者可以使用集群和租户特定的资源来部署其应用。

避免达到命名空间限制

理论上,一个集群中最多可以包含 10000 个命名空间,但在实践中,有很多因素可能会导致您无法达到这一限制。例如,在达到命名空间数量上限之前,您可能已达到整个集群的最大 Pod 数量 (150000) 和最大节点数量 (5000);Secret 数量等其他因素可能会进一步降低有效限制。因此,除非实验表明您的使用场景运行良好,否则建议一次仅尝试接近一个限制条件的理论限值,并与其他限制保持大约一个数量级,这是一个很好的初步经验法则。如果您需要的资源量超过单个集群可支持的数量,则应创建更多集群。如需了解 Kubernetes 可伸缩性息,请参阅 Kubernetes 可伸缩性阈值一文。

实现命名空间命名标准化

如需简化跨多个托管在不同集群中的环境进行的部署,可以对您使用的命名空间命名惯例进行标准化。例如,避免将环境名称(开发、预演和生产)与命名空间名称绑定在一起,而应在环境之间使用相同的名称。使用相同名称可以避免各种环境使用不同的配置文件。

为租户工作负载创建服务账号

您可以为租户命名空间中的每个不同工作负载创建特定于租户的 Google 服务账号。这样做可以提供安全保障,确保租户能够管理其在各自的命名空间中拥有/部署的工作负载的服务账号。每个命名空间的 Kubernetes 服务账号会使用适用于 GKE 的工作负载身份联合映射到一个 Google 服务账号。

实施资源配额

为确保所有共享同一集群的租户都能够公平地访问集群资源,请实施资源配额。您可以根据每个租户部署的 Pod 数量以及每个 Pod 所需的内存量和 CPU 数量,为每个命名空间创建资源配额。

以下示例定义了一个资源配额,其中,tenant-a 命名空间中的 Pod 最多可以请求 16 个 CPU 和 64 GB 内存,且 CPU 和内存的上限分别为 32 个和 72 GB。

apiVersion: v1
kind: ResourceQuota
metadata:
  name: tenant-a
spec:
  hard: "1"
    requests.cpu: "16"
    requests.memory: 64Gi
    limits.cpu: "32"
    limits.memory: 72Gi

监控、日志记录和用量

跟踪用量指标

如需获取集群中各个命名空间和标签的费用明细,您可以启用 GKE 费用分配。GKE 费用分配功能会跟踪有关集群工作负载的资源请求和资源用量的信息,您可以按命名空间和标签进一步细分这些信息。借助 GKE 费用分配功能,您可以估算共享同一集群的各部门/团队的费用明细,了解个别应用(甚至单个应用的组件)的使用规律,帮助集群管理员对用量高峰进行分类,并提供更好的容量规划和预算。

启用 GKE 费用分配后,GKE 工作负载的集群名称和命名空间会显示在将账单导出至 BigQuery的 labels 字段中。

提供特定于租户的日志

如需为租户提供特定于其项目工作负载的日志数据,请使用 Cloud Logging 的日志路由器。如需创建特定于租户的日志,集群管理员需要创建一个接收器,以将日志条目导出到租户的 Google Cloud 项目中创建的日志存储桶

如需详细了解如何配置这些类型的日志,请参阅 GKE 上的多租户日志记录

核对清单摘要

下表汇总了在企业组织中创建多租户集群时建议执行的任务:

范围 Tasks
组织设置
身份和访问权限管理
网络
高可用性和可靠性
安全
日志记录和监控

后续步骤