多租户

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

共享集群可以节省成本并简化管理。但是,共享集群也带来了安全、公平性和管理*嘈杂邻居*等挑战。

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

虽然 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 提供了几种类型的卷,可以用作工作负载的持久存储。为了安全和数据隔离,建议使用动态卷配置,并应避免使用节点资源的卷类型。

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

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

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

沙盒容器

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 收费。它还具有更少的兼容性和性能问题,并且可能比沙盒容器更容易实现。例如,可以使用污点配置每个租户的节点,以便只有具有相应容忍度的 Pod 才能在其上运行。然后可以使用突变 webhook 自动将容忍度和节点关联性添加到部署到租户命名空间中的 Pod,以便它们在为该租户指定的特定节点集上运行。

可以使用Pod 节点选择器虚拟 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 定制版本 的示例。

运算符

运算符 是管理应用程序的 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)