多租户

本页概述了可用的配置选项以及集群多租户的最佳实践。

共享集群可以节省成本并简化管理。然而,共享集群也带来挑战,例如安全性、公平性以及管理“吵闹的邻居”

集群可以通过多种方式共享。在某些情况下,不同的应用程序可能运行在同一个集群中。在其他情况下,同一个应用程序的多个实例可能运行在同一个集群中,每个最终用户一个实例。所有这些类型的共享通常都使用总称“多租户”来描述。

虽然 Kubernetes 没有最终用户或租户的第一类概念,但它提供了几个特性来帮助管理不同的租户需求。下面将讨论这些特性。

用例

确定如何共享集群的第一步是了解你的用例,以便评估可用的模式和工具。总的来说,Kubernetes 集群中的多租户分为两大类,尽管也有许多变体和混合模式。

多个团队

一种常见的多租户形式是在组织内多个团队之间共享集群,每个团队可能运行一个或多个工作负载。这些工作负载经常需要相互通信,并与位于相同或不同集群上的其他工作负载通信。

在这种场景下,团队成员通常可以通过 kubectl 等工具直接访问 Kubernetes 资源,或者通过 GitOps 控制器或其他类型的发布自动化工具间接访问。不同团队成员之间通常存在一定程度的信任,但 Kubernetes 策略(如 RBAC、配额和网络策略)对于安全公平地共享集群至关重要。

多个客户

多租户的另一种主要形式通常涉及软件即服务 (SaaS) 供应商为客户运行工作负载的多个实例。这种商业模式与这种部署风格紧密相关,以至于许多人将其称为“SaaS 租户”。然而,一个更好的术语可能是“多客户租户”,因为 SaaS 供应商也可以使用其他部署模型,而且这种部署模型也可以在 SaaS 之外使用。

在这种场景下,客户无法访问集群;从他们的角度来看,Kubernetes 是不可见的,仅由供应商用于管理工作负载。成本优化通常是一个关键问题,并且使用 Kubernetes 策略来确保工作负载彼此严格隔离。

术语

租户

在讨论 Kubernetes 中的多租户时,“租户”没有单一的定义。相反,租户的定义会根据讨论的是多团队还是多客户租户而有所不同。

在多团队用法中,租户通常是一个团队,每个团队通常部署少量工作负载,其数量随着服务复杂性而扩展。然而,“团队”的定义本身可能模糊不清,因为团队可以组织成更高级别的部门或细分为更小的团队。

相比之下,如果每个团队为每个新客户部署专用工作负载,则他们正在使用多客户租户模式。在这种情况下,“租户”仅仅是共享一个工作负载的用户组。这可能是一个完整的公司,也可能是该公司中的一个团队。

在许多情况下,同一个组织在不同的上下文中使用“租户”的两种定义。例如,一个平台团队可能向多个内部“客户”提供安全工具和数据库等共享服务,而 SaaS 供应商也可能有多个团队共享开发集群。最后,混合架构也是可能的,例如 SaaS 提供商将敏感数据的按客户工作负载与多租户共享服务结合使用。

一个显示共存租户模型的集群

隔离

有几种使用 Kubernetes 设计和构建多租户解决方案的方法。每种方法都有自己的权衡,这些权衡会影响隔离级别、实施工作量、操作复杂性和服务成本。

Kubernetes 集群由运行 Kubernetes 软件的控制平面和由工作节点组成的数据平面构成,租户工作负载作为 Pod 在这些工作节点上执行。可以根据组织需求在控制平面和数据平面应用租户隔离。

所提供的隔离级别有时用“硬”多租户(意味着强隔离)和“软”多租户(意味着弱隔离)等术语来描述。特别是,“硬”多租户通常用于描述租户之间互不信任的情况,通常从安全和资源共享的角度(例如,防范数据泄露或 DoS 等攻击)。由于数据平面通常具有更大的攻击面,“硬”多租户通常需要格外关注数据平面的隔离,尽管控制平面隔离也仍然至关重要。

然而,“硬”和“软”这两个术语常常令人困惑,因为没有适用于所有用户的单一明确定义。相反,“硬度”或“软度”最好理解为一个广泛的谱系,其中包含许多不同的技术,可以根据你的需求,用于在集群中维护不同类型的隔离。

在更极端的情况下,完全放弃任何集群级共享,为每个租户分配其专用集群,如果虚拟机不被认为是足够的安全边界,甚至可能运行在专用硬件上,可能会更容易或有必要。对于托管 Kubernetes 集群来说,这可能更容易,因为创建和操作集群的开销至少在某种程度上由云提供商承担。必须权衡更强的租户隔离的好处与管理多个集群的成本和复杂性。负责处理这些类型用例的是 多集群 SIG

本页的其余部分侧重于用于共享 Kubernetes 集群的隔离技术。然而,即使你正在考虑专用集群,回顾这些建议也可能很有价值,因为如果你的需求或能力发生变化,这将在未来为你提供转向共享集群的灵活性。

控制平面隔离

控制平面隔离确保不同的租户不能访问或影响彼此的 Kubernetes API 资源。

命名空间

在 Kubernetes 中,Namespace 提供了一种机制,用于在单个集群内隔离 API 资源组。这种隔离有两个关键维度

  1. Namespace 内的对象名称可以与其它 Namespace 中的名称重叠,类似于文件夹中的文件。这使得租户可以在命名其资源时不必考虑其它租户正在做什么。

  2. 许多 Kubernetes 安全策略都限定在 Namespace 范围内。例如,RBAC Role 和 Network Policy 是 Namespace 范围的资源。使用 RBAC,用户和服务账号可以被限制在某个 Namespace 内。

在多租户环境中,Namespace 有助于将租户的工作负载细分为逻辑上独立且易于管理的单元。实际上,即使多个工作负载由同一租户操作,一种常见的做法也是将每个工作负载隔离在其自己的 Namespace 中。这确保了每个工作负载都有其自己的身份,并且可以配置适当的安全策略。

Namespace 隔离模型需要配置其他几个 Kubernetes 资源、网络插件,并遵守安全最佳实践才能正确隔离租户工作负载。这些注意事项将在下面讨论。

访问控制

对于控制平面而言,最重要的隔离类型是授权。如果团队或其工作负载可以访问或修改彼此的 API 资源,他们就可以更改或禁用所有其他类型的策略,从而使这些策略提供的任何保护失效。因此,至关重要的是要确保每个租户仅对其需要的 Namespace 拥有适当的访问权限,不多也不少。这被称为“最小权限原则”。

基于角色的访问控制 (RBAC) 常用于在 Kubernetes 控制平面中强制执行授权,包括用户和工作负载(服务账号)。RoleRoleBinding 是 Kubernetes 对象,它们在 Namespace 级别用于在你的应用中强制执行访问控制;类似的资源也存在用于授权对集群级别对象的访问,尽管这些对于多租户集群不太有用。

在多团队环境中,必须使用 RBAC 来限制租户对适当 Namespace 的访问,并确保集群范围的资源只能由特权用户(例如集群管理员)访问或修改。

如果某个策略最终授予用户超出其所需的权限,这很可能表明包含受影响资源的 Namespace 应该被重构为更细粒度的 Namespace。Namespace 管理工具可以通过将通用的 RBAC 策略应用到不同的 Namespace 中来简化这些更细粒度 Namespace 的管理,同时仍然允许在需要时使用细粒度策略。

配额

Kubernetes 工作负载会消耗节点资源,例如 CPU 和内存。在多租户环境中,可以使用资源配额来管理租户工作负载的资源使用。对于多团队用例,其中租户有权访问 Kubernetes API,你可以使用资源配额来限制租户可以创建的 API 资源数量(例如:Pod 的数量,或 ConfigMap 的数量)。对对象数量的限制确保公平性,并旨在避免“吵闹的邻居”问题影响共享控制平面的其他租户。

资源配额是 Namespace 范围的对象。通过将租户映射到 Namespace,集群管理员可以使用配额来确保租户不会垄断集群资源或使控制平面过载。Namespace 管理工具简化了配额的管理。此外,虽然 Kubernetes 配额仅适用于单个 Namespace,但一些 Namespace 管理工具允许 Namespace 组共享配额,与内置配额相比,这为管理员提供了更大的灵活性,并且所需的精力更少。

配额可以防止单个租户消耗超出其分配份额的资源,从而最大程度地减少“吵闹的邻居”问题,即一个租户对其他租户工作负载的性能产生负面影响。

当你对 Namespace 应用配额时,Kubernetes 还要求你为每个容器指定资源请求和限制。限制是容器可以消耗的资源量的上限。尝试消耗超出配置限制的资源的容器将根据资源类型被限制或杀死。当资源请求设置低于限制时,每个容器都能保证请求的资源量,但跨工作负载仍可能存在一些相互影响的可能性。

配额无法防范所有类型的资源共享,例如网络流量。节点隔离(下文描述)可能是解决此问题的更好方案。

数据平面隔离

数据平面隔离确保不同租户的 Pod 和工作负载得到充分隔离。

网络隔离

默认情况下,Kubernetes 集群中的所有 Pod 都可以相互通信,并且所有网络流量都是未加密的。这可能导致安全漏洞,例如流量被意外或恶意发送到非预期目的地,或者被受感染节点上的工作负载截获。

Pod 之间的通信可以通过网络策略来控制,网络策略使用 Namespace 标签或 IP 地址范围限制 Pod 之间的通信。在需要严格租户间网络隔离的多租户环境中,建议首先设置一个默认策略,拒绝 Pod 之间的通信,然后添加一个允许所有 Pod 查询 DNS 服务器进行名称解析的规则。有了这样一个默认策略,你就可以开始添加更宽松的规则,允许在 Namespace 内进行通信。还需要注意的是,在网络策略定义中,对于 namespaceSelector 字段,不建议使用空的标签选择器 '{}',以防需要在 Namespace 之间允许流量。这种方案可以根据需要进一步细化。请注意,这仅适用于单个控制平面内的 Pod;属于不同虚拟控制平面的 Pod 不能通过 Kubernetes 网络相互通信。

Namespace 管理工具可以简化默认或通用网络策略的创建。此外,其中一些工具允许你在整个集群中强制执行一致的 Namespace 标签集,确保它们是你的策略的可信基础。

更高级的网络隔离可以由服务网格提供,服务网格除了基于 Namespace 之外,还提供基于工作负载身份的 OSI 第 7 层策略。这些更高级别的策略可以更轻松地管理基于 Namespace 的多租户,特别是当多个 Namespace 专门用于单个租户时。它们通常还提供使用相互 TLS 的加密,即使在节点被攻陷的情况下也能保护你的数据,并且适用于专用或虚拟集群。然而,它们的管理可能要复杂得多,并且可能不适用于所有用户。

存储隔离

Kubernetes 提供了几种类型的 Volume,可以用作工作负载的持久存储。为了安全和数据隔离,建议使用动态卷配置,并且应避免使用利用节点资源的 Volume 类型。

StorageClass 允许你根据服务质量级别、备份策略或集群管理员确定的自定义策略,描述集群提供的自定义存储“类别”。

Pod 可以使用PersistentVolumeClaim 请求存储。PersistentVolumeClaim 是 Namespace 范围的资源,它可以在共享 Kubernetes 集群内隔离部分存储系统并将其专用于租户。然而,需要注意的是,PersistentVolume 是集群范围的资源,其生命周期独立于工作负载和 Namespace。

例如,你可以为每个租户配置单独的 StorageClass,并用它来增强隔离。如果 StorageClass 是共享的,则应该设置回收策略为 Delete,以确保 PersistentVolume 不能跨不同的 Namespace 重复使用。

沙盒容器

Kubernetes Pod 由一个或多个在工作节点上执行的容器组成。容器利用操作系统级虚拟化,因此提供的隔离边界比利用硬件级虚拟化的虚拟机要弱。

在共享环境中,应用程序和系统层中未修补的漏洞可能被攻击者利用来进行容器逃逸和远程代码执行,从而允许访问宿主机资源。在某些应用程序中,例如内容管理系统 (CMS),客户可能被允许上传和执行不受信任的脚本或代码。无论哪种情况,都希望使用强隔离机制进一步隔离和保护工作负载。

沙盒提供了一种隔离在共享集群中运行的工作负载的方法。它通常涉及在单独的执行环境(例如虚拟机或用户空间内核)中运行每个 Pod。当你运行不受信任的代码时,通常建议使用沙盒,因为在这种情况下,工作负载被认为是恶意的。需要这种类型隔离的部分原因是容器是在共享内核上运行的进程;它们从底层宿主机挂载像 /sys/proc 这样的文件系统,这使得它们不如运行在拥有自己的内核的虚拟机上的应用程序安全。虽然可以使用 seccomp、AppArmor 和 SELinux 等控制来增强容器的安全性,但很难将一套通用的规则应用于在共享集群中运行的所有工作负载。在沙盒环境中运行工作负载有助于保护宿主机免受容器逃逸的影响,在容器逃逸中,攻击者利用漏洞获得对宿主系统的访问权限以及在该宿主机上运行的所有进程/文件的访问权限。

虚拟机和用户空间内核是沙盒的两种常用方法。以下是可用的沙盒实现

  • gVisor 拦截容器的系统调用,并通过一个用 Go 编写的、对底层宿主机访问受限的用户空间内核来运行它们。
  • Kata Containers 提供一个安全的容器运行时,允许你在虚拟机中运行容器。Kata 中可用的硬件虚拟化为运行不受信任代码的容器提供了额外的安全层。

节点隔离

节点隔离是另一种可用于相互隔离租户工作负载的技术。通过节点隔离,一组节点专门用于运行特定租户的 Pod,并且禁止租户 Pod 混杂。这种配置减少了“吵闹的租户”问题,因为节点上运行的所有 Pod 都属于单个租户。通过节点隔离,信息泄露的风险略低,因为成功从容器逃逸的攻击者只能访问挂载到该节点的容器和卷。

尽管来自不同租户的工作负载运行在不同的节点上,但需要注意的是,kubelet 和(除非使用虚拟控制平面)API 服务仍然是共享服务。熟练的攻击者可以利用分配给 kubelet 或节点上运行的其他 Pod 的权限在集群内横向移动,并访问在其他节点上运行的租户工作负载。如果这是一个主要担忧,请考虑实施补偿控制,例如 seccomp、AppArmor 或 SELinux,或探索使用沙盒容器或为每个租户创建单独的集群。

从计费角度来看,节点隔离比沙盒容器更容易理解,因为你可以按节点收费而不是按 Pod 收费。它也具有更少的兼容性和性能问题,并且可能比沙盒容器更容易实现。例如,可以为每个租户的节点配置 Taint,以便只有具有相应 Toleration 的 Pod 才能在其上运行。然后可以使用一个 Mutating Admission Webhook 自动将 Toleration 和节点亲和性添加到部署到租户 Namespace 的 Pod 中,以便它们在指定给该租户的特定节点集上运行。

节点隔离可以使用Pod 节点选择器Virtual Kubelet来实现。

其他注意事项

本节讨论了与多租户相关的其他 Kubernetes 构造和模式。

API 优先级与公平性

API 优先级和公平性是 Kubernetes 的一个特性,它允许你为集群内运行的特定 Pod 分配优先级。当应用程序调用 Kubernetes API 时,API 服务器会评估分配给 Pod 的优先级。高优先级 Pod 发出的调用将优先于低优先级 Pod 发出的调用得到处理。当竞争激烈时,低优先级调用可以排队,直到服务器不那么繁忙,或者你可以拒绝这些请求。

在 SaaS 环境中,使用 API 优先级和公平性不会很常见,除非你允许客户运行与 Kubernetes API 交互的应用程序,例如控制器。

服务质量 (QoS)

当你运行 SaaS 应用程序时,你可能希望能够向不同的租户提供不同服务质量 (QoS) 等级的服务。例如,你可能有一个提供较少性能保证和免费增值服务的套餐,以及一个具有特定性能保证的付费服务套餐。幸运的是,Kubernetes 提供了几个构造可以帮助你在共享集群内实现这一点,包括网络 QoS、存储类和 Pod 优先级及抢占。每种构造的理念都是为租户提供他们所付费的服务质量。我们首先来看网络 QoS。

通常,节点上的所有 Pod 共享一个网络接口。如果没有网络 QoS,一些 Pod 可能会以牺牲其他 Pod 为代价消耗不成比例的可用带宽。Kubernetes 带宽插件创建了一个用于网络的扩展资源,允许你使用 Kubernetes 资源构造(即请求/限制)通过使用 Linux tc 队列对 Pod 应用速率限制。请注意,根据网络插件文档,该插件被认为是实验性的,应在生产环境中使用前进行全面测试。

对于存储 QoS,你可能会希望创建具有不同性能特征的不同存储类或配置文件。每个存储配置文件可以与针对不同工作负载(如 IO、冗余或吞吐量)优化的不同服务层相关联。可能需要额外的逻辑来允许租户将其工作负载与适当的存储配置文件关联。

最后,还有Pod 优先级和抢占,你可以在其中为 Pod 分配优先级值。在调度 Pod 时,当没有足够的资源调度分配了更高优先级的 Pod 时,调度器会尝试驱逐优先级较低的 Pod。如果你的用例中,租户在共享集群中拥有不同的服务等级(例如免费和付费),你可能希望使用此功能为某些等级赋予更高的优先级。

DNS

Kubernetes 集群包含一个域名系统 (DNS) 服务,为所有 Service 和 Pod 提供从名称到 IP 地址的转换。默认情况下,Kubernetes DNS 服务允许在集群中的所有 Namespace 中进行查找。

在租户可以访问 Pod 和其他 Kubernetes 资源,或需要更强隔离的多租户环境中,可能需要阻止 Pod 查找其他 Namespace 中的服务。你可以通过配置 DNS 服务的安全规则来限制跨 Namespace DNS 查找。例如,CoreDNS(Kubernetes 的默认 DNS 服务)可以利用 Kubernetes 元数据将查询限制在 Namespace 内的 Pod 和 Service。有关更多信息,请阅读 CoreDNS 文档中关于配置此功能的示例

当使用每租户虚拟控制平面模型时,必须为每个租户配置一个 DNS 服务,或者必须使用一个多租户 DNS 服务。这里是一个支持多租户的CoreDNS 的定制版本的示例。

Operator

Operator 是管理应用程序的 Kubernetes 控制器。Operator 可以简化应用程序多个实例的管理,例如数据库服务,这使得它们成为多消费者 (SaaS) 多租户用例中的一个常见构建块。

在多租户环境中使用 Operator 应遵循一套更严格的指南。具体来说,Operator 应:

  • 支持在不同的租户 Namespace 中创建资源,而不是仅在其部署的 Namespace 中创建。
  • 确保为 Pod 配置资源请求和限制,以确保调度和公平性。
  • 支持配置 Pod 以实现数据平面隔离技术,例如节点隔离和沙盒容器。

实现

共享 Kubernetes 集群实现多租户主要有两种方式:使用 Namespace(即每租户一个 Namespace)或通过虚拟化控制平面(即每租户一个虚拟控制平面)。

在这两种情况下,也建议进行数据平面隔离,并管理诸如 API 优先级和公平性等额外考量。

Kubernetes 对命名空间隔离提供了良好的支持,其资源成本可以忽略不计,并提供了允许租户适当交互的机制,例如允许服务间通信。然而,它可能难以配置,并且不适用于无法进行命名空间化的 Kubernetes 资源,例如 Custom Resource Definitions、Storage Classes 和 Webhooks。

控制平面虚拟化允许隔离非命名空间化资源,其代价是资源使用率略高,并且跨租户共享更加困难。当命名空间隔离不足但专用集群不可取时,这是一个很好的选择,因为维护专用集群成本很高(特别是在本地部署),或者由于其更高的开销和缺乏资源共享。然而,即使在虚拟化的控制平面内,使用命名空间也可能会带来好处。

以下各节将更详细地讨论这两种选项。

每个租户一个命名空间

如前所述,即使您使用专用集群或虚拟化控制平面,也应考虑将每个工作负载隔离在其自己的命名空间中。这可以确保每个工作负载只访问其自己的资源,例如 ConfigMaps 和 Secrets,并允许您为每个工作负载量身定制专用的安全策略。此外,最佳实践是为每个命名空间赋予在整个集群组中唯一的名称(即,即使它们位于不同的集群中),因为这使您将来可以在专用集群和共享集群之间切换,或使用服务网格等多集群工具。

相反,在租户级别而非工作负载级别分配命名空间也有其优势,因为通常存在适用于单个租户拥有的所有工作负载的策略。然而,这也会带来自身的问题。首先,这使得为单个工作负载自定义策略变得困难或不可能;其次,确定哪个层级的“租户”应该获得一个命名空间可能具有挑战性。例如,一个组织可能有部门、团队和子团队——应该将命名空间分配给哪个层级?

一种可能的方法是将命名空间组织成层级结构,并在它们之间共享某些策略和资源。这可能包括管理命名空间标签、命名空间生命周期、委托访问以及相关命名空间之间的共享资源配额。这些功能在多团队和多客户场景中都很有用。

下面列出了一些提供类似功能并帮助管理命名空间资源的第三方项目。

多团队租户

多客户租户

策略引擎

策略引擎提供用于验证和生成租户配置的功能

每个租户一个虚拟控制平面

控制平面隔离的另一种形式是使用 Kubernetes 扩展为每个租户提供一个虚拟控制平面,该控制平面能够对集群范围的 API 资源进行分段。数据平面隔离技术可与此模型结合使用,以安全地管理跨租户的 Worker 节点。

基于虚拟控制平面的多租户模型通过为每个租户提供专用的控制平面组件,从而扩展了基于命名空间的多租户模型,并因此对集群范围的资源和附加服务拥有完全控制权。Worker 节点在所有租户之间共享,并由通常租户无法访问的 Kubernetes 集群管理。此集群通常被称为 超级集群(有时也称为 宿主集群)。由于租户的控制平面未直接关联到底层计算资源,因此被称为 虚拟控制平面

虚拟控制平面通常由 Kubernetes API server、controller manager 和 etcd 数据存储组成。它通过元数据同步控制器与超级集群交互,该控制器协调租户控制平面和超级集群控制平面之间的更改。

通过使用每个租户专用的控制平面,解决了因所有租户共享一个 API server 而引起的大部分隔离问题。例如,控制平面中的“吵闹的邻居”,策略配置错误的不可控爆炸半径,以及诸如 webhooks 和 CRDs 等集群范围对象之间的冲突。因此,虚拟控制平面模型特别适用于每个租户需要访问 Kubernetes API server 并期望获得完整集群管理能力的情况。

改进的隔离性是以运行和维护每个租户一个独立的虚拟控制平面为代价的。此外,每个租户的控制平面并不能解决数据平面中的隔离问题,例如节点级别的“吵闹的邻居”或安全威胁。这些问题仍必须单独解决。

Kubernetes Cluster API - Nested (CAPN) 项目提供了一个虚拟控制平面的实现。

其他实现

本页中的项目是指提供 Kubernetes所需功能的第三方产品或项目。Kubernetes 项目作者不对这些第三方产品或项目负责。有关更多详细信息,请参阅 CNCF 网站指南

在提议添加额外第三方链接的更改之前,应阅读内容指南

上次修改时间:2025 年 4 月 18 日 太平洋标准时间下午 3:26:移除对已归档的 HNC 项目的引用 (c5441a5da7)