多租户

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

共享集群可以节省成本并简化管理。但是,共享集群也会带来安全、公平性和管理“噪声邻居”等挑战。

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

虽然 Kubernetes 没有关于最终用户或租户的一流概念,但它提供了一些功能来帮助管理不同的租户需求。这些将在下面讨论。

用例

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

多个团队

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

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

多个客户

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

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

术语

租户

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

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

相反,如果每个团队为每个新客户部署专用工作负载,那么他们正在使用多客户租户模型。在这种情况下,“租户”只是一个共享单个工作负载的用户组。这可能与整个公司一样大,也可能与该公司的一个团队一样小。

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

显示共存租户模型的集群

隔离

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

Kubernetes 集群包含一个运行 Kubernetes 软件的控制平面,以及一个数据平面,数据平面包含执行租户工作负载作为 Pod 的工作节点。可以根据组织要求在控制平面和数据平面中应用租户隔离。

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

但是,术语“硬”和“软”往往会造成混淆,因为没有一个单一的定义适用于所有用户。相反,“硬度”或“软度”最好理解为一个宽泛的频谱,其中可以使用许多不同的技术来维护集群中不同类型的隔离,根据您的需求。

在更极端的情况下,可能更容易或有必要完全放弃任何集群级别共享,并为每个租户分配他们专用的集群,甚至在 VM 不被视为足够的安全性边界的情况下,甚至在专用硬件上运行。对于托管 Kubernetes 集群来说,这可能更容易,在托管 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 之间的通信,网络策略使用命名空间标签或 IP 地址范围来限制 Pod 之间的通信。在需要严格的租户之间网络隔离的多租户环境中,建议从默认策略开始,该策略拒绝 Pod 之间的通信,并使用另一个规则允许所有 Pod 查询 DNS 服务器进行名称解析。有了这样的默认策略,您可以开始添加更多允许在命名空间内进行通信的规则。还建议不要在网络策略定义中对 namespaceSelector 字段使用空标签选择器 '{}',以防需要在命名空间之间允许流量。可以根据需要进一步改进此方案。请注意,这仅适用于单个控制平面内的 Pod;属于不同虚拟控制平面的 Pod 无法通过 Kubernetes 网络相互通信。

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

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

存储隔离

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

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

Pod 可以使用 持久卷声明 请求存储。持久卷声明是一种命名空间资源,它使隔离存储系统的一部分并将其专门用于共享 Kubernetes 集群中的租户成为可能。但是,重要的是要注意持久卷是一种集群范围的资源,并且具有独立于工作负载和命名空间的生命周期。

例如,您可以为每个租户配置一个单独的存储类,并使用它来加强隔离。如果共享存储类,则应设置 Delete 的回收策略,以确保持久卷不能跨不同命名空间重用。

沙箱容器

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 节点选择器虚拟 Kubelet 实现节点隔离。

其他注意事项

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

API 优先级和公平性

API 优先级和公平性 是 Kubernetes 的一项功能,允许您为集群中运行的某些 Pod 分配优先级。当应用程序调用 Kubernetes API 时,API 服务器会评估分配给 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 的示例,它支持多个租户。

运营商

运营商 是管理应用程序的 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 年 1 月 1 日 下午 1:48 PST: 改进了 Kata 容器的措辞。(5b064812d8)