多租户

此页面概述了集群多租户可用的配置选项和最佳实践。

共享集群可以节省成本并简化管理。但是,共享集群也带来了一些挑战,例如安全性、公平性和管理 吵闹的邻居

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

虽然 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 中,命名空间提供了一种机制,用于隔离单个集群中的 API 资源组。这种隔离有两个关键维度

  1. 命名空间中的对象名称可以与另一个命名空间中的名称重叠,类似于文件夹中的文件。这允许租户在命名其资源时无需考虑其他租户正在做什么。

  2. 许多 Kubernetes 安全策略的作用域限定于命名空间。例如,RBAC 角色和网络策略是命名空间范围的资源。使用 RBAC,用户和服务帐户可以被限制在命名空间中。

在多租户环境中,命名空间有助于将租户的工作负载分段为逻辑上不同的管理单元。事实上,一种常见的做法是将每个工作负载隔离在其自己的命名空间中,即使多个工作负载由同一租户操作。这确保了每个工作负载都有其自己的身份,并且可以使用适当的安全策略进行配置。

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

访问控制

控制平面最重要的隔离类型是授权。如果团队或其工作负载可以访问或修改彼此的 API 资源,则它们可以更改或禁用所有其他类型的策略,从而否定这些策略可能提供的任何保护。因此,必须确保每个租户都拥有对其所需命名空间的适当访问权限,并且不能更多。这被称为“最小特权原则”。

基于角色的访问控制 (RBAC) 通常用于在 Kubernetes 控制平面中为用户和工作负载(服务帐户)强制执行授权。角色角色绑定 是在命名空间级别使用的 Kubernetes 对象,用于在应用程序中强制执行访问控制;存在类似的对象来授权访问集群级别的对象,但这些对象对于多租户集群不太有用。

在多团队环境中,必须使用 RBAC 来限制租户对适当命名空间的访问,并确保只有集群管理员等特权用户才能访问或修改集群范围的资源。

如果策略最终授予用户超出其需要的权限,则这很可能表明包含受影响资源的命名空间应重构为更细粒度的命名空间。命名空间管理工具可以通过将通用的 RBAC 策略应用于不同的命名空间,从而简化这些更细粒度命名空间的管理,同时仍然允许在必要时使用细粒度的策略。

配额

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

资源配额是命名空间对象。通过将租户映射到命名空间,集群管理员可以使用配额来确保租户不会垄断集群的资源或使其控制平面不堪重负。命名空间管理工具简化了配额的管理。此外,虽然 Kubernetes 配额仅在单个命名空间内适用,但某些命名空间管理工具允许命名空间组共享配额,从而使管理员能够以比内置配额更小的精力获得更大的灵活性。

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

当您将配额应用于命名空间时,Kubernetes 还会要求您为每个容器指定资源请求和限制。限制是容器可以消耗的资源量的上限。尝试消耗超过配置限制的资源的容器将根据资源类型被限制或终止。当资源请求设置低于限制时,保证每个容器所需的资源量,但工作负载之间仍然可能存在一些潜在的影响。

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

数据平面隔离

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

网络隔离

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

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

命名空间管理工具可以简化默认或常用网络策略的创建。此外,其中一些工具允许您在整个集群中强制实施一组一致的命名空间标签,确保它们是策略的可信基础。

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

存储隔离

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

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

Pod 可以使用PersistentVolumeClaim来请求存储。PersistentVolumeClaim 是一个命名空间资源,它可以隔离存储系统的各个部分并将其专用于共享 Kubernetes 集群中的租户。但是,重要的是要注意 PersistentVolume 是一个集群范围的资源,并且其生命周期独立于工作负载和命名空间。

例如,您可以为每个租户配置单独的 StorageClass,并使用它来加强隔离。如果共享 StorageClass,则应设置Delete 的回收策略,以确保 PersistentVolume 不能在不同的命名空间之间重用。

沙箱容器

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

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

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

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

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

节点隔离

节点隔离是您可以用来将租户工作负载彼此隔离的另一种技术。通过节点隔离,一组节点专用于运行来自特定租户的 Pod,并且禁止租户 Pod 的混合。此配置减少了嘈杂的租户问题,因为在节点上运行的所有 Pod 都将属于单个租户。使用节点隔离时,信息泄露的风险略低,因为设法从容器逃逸的攻击者只能访问容器和已挂载到该节点的卷。

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

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

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

其他注意事项

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

API 优先级和公平性

API 优先级和公平性是 Kubernetes 的一项功能,允许您为集群中运行的某些 Pod 分配优先级。当应用程序调用 Kubernetes API 时,API 服务器会评估分配给 Pod 的优先级。来自具有较高优先级的 Pod 的调用会在来自具有较低优先级的 Pod 的调用之前得到满足。当争用较高时,可以对较低优先级的调用进行排队,直到服务器不太忙为止,或者您可以拒绝请求。

除非您允许客户运行与 Kubernetes API 交互的应用程序(例如,控制器),否则在 SaaS 环境中使用 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)服务,为所有服务和 Pod 提供从名称到 IP 地址的转换。默认情况下,Kubernetes DNS 服务允许跨集群中的所有命名空间进行查找。

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

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

操作器(Operators)

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

在多租户环境中使用操作器应遵循更严格的准则。具体来说,操作器应:

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

实现

有两种主要方式可以共享 Kubernetes 集群以实现多租户:使用命名空间(即每个租户一个命名空间)或虚拟化控制平面(即每个租户一个虚拟控制平面)。

在这两种情况下,还建议进行数据平面隔离,并管理其他考虑因素,例如 API 优先级和公平性。

Kubernetes 很好地支持命名空间隔离,资源成本可忽略不计,并提供了允许租户适当交互的机制,例如允许服务到服务之间的通信。但是,它可能难以配置,并且不适用于不能命名空间的 Kubernetes 资源,例如自定义资源定义、存储类和 Webhook。

控制平面虚拟化允许隔离非命名空间资源,但代价是资源使用量稍高,且跨租户共享更加困难。当命名空间隔离不足,但由于维护成本高(尤其是在本地部署)或由于其更高的开销和缺乏资源共享,而不希望使用专用集群时,这是一个不错的选择。但是,即使在虚拟化的控制平面中,您也可能会发现使用命名空间的好处。

以下部分将更详细地讨论这两种选项。

每个租户一个命名空间

如前所述,您应该考虑将每个工作负载隔离在自己的命名空间中,即使您使用的是专用集群或虚拟化控制平面。这可确保每个工作负载仅有权访问其自己的资源(例如 ConfigMap 和 Secret),并允许您为每个工作负载定制专用的安全策略。此外,最佳实践是为每个命名空间提供在整个集群中唯一的名称(即,即使它们位于单独的集群中),因为这使您可以在将来灵活地在专用集群和共享集群之间切换,或使用服务网格等多集群工具。

相反,在租户级别而不是仅在工作负载级别分配命名空间也有其优点,因为通常有适用于单个租户拥有的所有工作负载的策略。但是,这会带来自身的问题。首先,这使得难以或不可能为单个工作负载自定义策略,其次,可能很难提出应分配命名空间的单个“租户”级别。例如,一个组织可能包含部门、团队和子团队 - 应该为哪个分配命名空间?

为了解决这个问题,Kubernetes 提供了 分层命名空间控制器(HNC),它允许您将命名空间组织成层次结构,并在它们之间共享某些策略和资源。它还可以帮助您管理命名空间标签、命名空间生命周期和委派管理,并在相关命名空间之间共享资源配额。这些功能在多团队和多客户场景中都非常有用。

下面列出了提供类似功能并有助于管理命名空间资源的其他项目。

多团队租户

多客户租户

策略引擎

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

每个租户的虚拟控制平面

另一种形式的控制平面隔离是使用 Kubernetes 扩展为每个租户提供一个虚拟控制平面,从而实现对集群范围 API 资源的分割。数据平面隔离技术可以与此模型一起使用,以安全地管理跨租户的工作节点。

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

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

通过使用每个租户的专用控制平面,可以解决由于在所有租户之间共享一个 API 服务器而引起的大多数隔离问题。示例包括控制平面中的嘈杂邻居、策略配置错误造成的无法控制的爆炸半径以及集群范围对象(如 Webhook 和 CRD)之间的冲突。因此,虚拟控制平面模型特别适用于每个租户都需要访问 Kubernetes API 服务器并期望完全集群管理能力的情况。

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

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

其他实现

此页面上的项目引用了 Kubernetes 所需功能的第三方产品或项目。Kubernetes 项目作者不对这些第三方产品或项目负责。有关更多详细信息,请参阅 CNCF 网站准则

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

上次修改时间:2024 年 11 月 13 日凌晨 2:00 PST:添加 mto 链接 (#48648) (7f9c368c4b)