如果我们想在 Kubernetes 上运行一组给定的应用程序,那么会遇到一些使用集群的基本问题:

  • 我应该在同一集群中运行所有应用程序,还是在多个集群中运行?
  • 每个集群应该要多大?
  • 我要在各个集群中分别运行哪些应用程序?

我们通常遵循两个基本方向:大型集群(数量较少),其中运行相对大量的应用程序;小型集群(但数量较多),每个集群中只运行几个应用程序。

这两种方法都非常有效。本文分别讨论了其优缺点,希望可以帮助大家确定哪种策略才是最适合自己的。

所需容量

要运行一组给定的应用程序,我们需要一定的计算资源,例如 CPU 内核和内存。我们将这种所需的资源量称为“所需容量”。

Kubernetes 集群提供一定数量的计算资源(本文称为集群容量)。我们可以通过多种方式将所需容量映射到 Kubernetes 集群中去。例如,我们要 80 个 CPU 核心和 160GB 内存的所需容量。以下是使用 Kubernetes 实现此目标的两种方法:

第一种方法直接通过单个集群提供全部的所需容量。因此,所有应用程序都在同一集群中运行(该集群可能由多个或大型工作程序节点组成)。第二种方法是通过 10 个集群实现所需容量,每个集群只提供所需容量的十分之一。这意味着每个集群仅托管应用程序的一个子集(这些集群可能仅由几个小型工作节点组成)。

例子举好了,现在让我们看一下“少数大型集群”和“多个小型集群”这两种相反方法的利弊。

大型集群

这种方法在于拥有数量少但规模庞大的集群。在最极端的情况下,一个集群就运行所有的应用程序。

易于管理

单个集群比多集群更易于管理。Kubernetes 自身和 DNS 服务、CNI 插件等其他集群组件更新就可以一次性完成。其他例如集群范围服务的设置、配置和维护,包括监视、日志记录、跟踪、服务网格等也是同理。如果只有一个或少数集群,那么这些任务的操作成本都会相对较低。

经济高效

大多数情况下,Kubernetes 集群会产生一些与集群容量无关的固定成本。例如,在使用 AWS 和 GCP 的 Kubernetes 托管服务时,无论集群大小,我们每小时都要为每个集群支付 0.10 美元。这意味着,即使两种集群提供的总容量相同(就像上一节中的示例一样),如果只有一个大型集群,每月的固定成本是 73 美元,但如果是 10 个小型集群,那么每月的固定成本就是 730 美元。即使我们不使用 Kubernetes 托管服务,而是自己(在本地或在云中)管理 Kubernetes,多个小型集群的成本也会比少量大型集群高。为什么?由于要实现控制平面的高可用性,每个集群应具有多个主节点(通常为三个)。因此,如果我们有 10 个集群,需要保留作为主节点的计算机数量是单个集群计算机数量的 10 倍,这些计算机是需要付费的。

资源利用率高

Kubernetes 旨在高效利用集群计算资源(CPU 和内存)。这是通过 Kubernetes 调度程序实现的。该调度程序基于 Pod 的资源配置文件会为每个 Pod  选择一个最佳位置来运行。如果所有计算资源都在一个集群中,由于集群中总有剩余的资源块是无法再使用的,相比多个集群,单个大型集群浪费的资源总量会更少。举个例子,如果每个集群有 0.25 个 CPU 内核和 250MB 的内存不能用于任何工作负载,这样的集群一共有 100 个,那么我们将浪费 25 个 CPU 内核和 25GB 的内存。另一方面,如果只有一个集群,那么浪费的资源就相对少很多。另外,每个集群的管理组件,例如主节点、etcd 和节点代理,都需要一定数量的资源。如果集群较小,那么仅能用于管理(因此无法用于工作负载)的资源比例要比大型集群的资源高得多。

看到了大型集群的优点之后,让我们转向它的弊端。

单点故障

单个集群显然是单点故障。如果所有主节点都发生故障,那么集群的整个控制平面将关闭。但即便是 Kubernetes 版本的某些设置或更新出现意外,控制平面也可能会出现问题。在这些情况下,依赖 Kubernetes API 的应用程序(例如控制器、Operator 或监控组件)将无法正常运行。尽管不依赖 Kubernetes API 的应用程序看似可以继续运行,但是这些应用程序无法再进行扩展、重新配置和自动修复,并且我们还不能部署任何新的应用程序。这同样适用于集群范围内的其他组件故障,例如 DNS 服务、CNI 插件以及基础网络结构。如果所有的应用程序都在一个集群中运行,那么它们都会受到此类故障的影响。反之,如果应用程序分布在多个集群中,那么其中一个集群中的故障所造成的影响将小得多。

缺乏隔离

Kubernetes 是共享基础架构,而不是在应用之间进行隔离。在同一 Kubernetes 集群中运行的所有应用程序都共享基础架构,例如网络和 DNS 服务。此外,在同一工作节点上运行的应用也共享该节点的硬件(CPU 和内存)以及操作系统。从安全性和合规性的角度来看,这可能是个问题,尤其是当 Kubernetes 用作多租户系统(即在同一集群上运行不同客户的应用程序)时。例如,恶意应用程序可能利用其工作程序节点操作系统内核中的安全漏洞来访问数据,或者以其他方式影响在同一工作程序节点上运行的其他应用程序。反之,如果不同的应用程序在不同的集群上运行,那么这些应用程序之间就存在了硬隔离(它们不共享任何基础结构),这样的话就不存在安全漏洞了。

无命名空间资源的管理

虽然可以使用 Kubernetes 命名空间来组织集群,但这会限制 Kubernetes 资源的范围和可见性。命名空间还是在集群中访问权限的基础。但是,并非所有 Kubernetes 资源都具有命名空间。有很多资源,例如 PersistentVolume、PodSecurityPolicy、PriorityClass、StorageClass 和 CustomResourceDefinition,都没有命名空间,这意味着它们的范围是整个集群。

我们可以列出所有无命名空间的 Kubernetes 资源 kubectl api-resources --namespaced=false

如果大型集群中有许多应用程序和用户,这些无命名空间的资源会累积,就可能会出现名称冲突或其他冲突。所以我们必须设计一些协调或限制来管理集群中无命名空间资源的使用。此外,管理无命名空间的资源需要集群范围的权限。如果我们向某些用户授予此权限(例如,允许他们自己管理自定义资源定义),这个权限的范围就会很大。它可以让用户访问所有用户的无命名空间资源以及集群中的其他应用程序和用户。

集群大小有限

Kubernetes 集群不能无限增长。Kubernetes 项目定义了 5000 个节点、150000 个 Pod  和 300000 个容器的上限。

规定这些限制的原因是,大量节点、Pod 和容器会给 Kubernetes 控制平面和其他集群范围的组件(例如 DNS 和 Pod 网络实现)太多压力。

例如,大量工作节点可能会使节点控制器或 etcd 存储后端过载,因为每个节点代理都会引发 etcd 数据库上的监视程序。

虽然我们可以通过使用更大的节点来避免此问题(这样就需要更少的节点),但这只会带来新的问题,每个节点中大量的 Pod 可能会让节点代理过载(Kubernetes 项目建议每个节点运行不超过 100 个 Pod)。所以一般规模较大的集群,硬件也比较强大。

小型集群

这种方法是使用多个小型集群来实现所需容量。在最极端的情况下,可能每个应用程序都有单独的集群。

小型集群的优点其实与大型集群的缺点相对应。但还是总结一下最重要的几点。

强隔离

在不同集群中运行的应用程序完全隔离,没有共享的硬件、网络和操作系统,因此,应用程序之间出现安全漏洞的可能会大大降低。

如果节点本身只是在共享基础硬件上运行的虚拟机(VM),可能不完全隔离。但是,这样的隔离仍然比 Kubernetes 提供的隔离强得多。

当应用程序有严格的安全性要求或属于不同的客户时,这一点就尤其重要。在这种情况下,有一种解决方案是为每个客户提供一个单独的集群,让不同客户的应用程序尽可能彼此隔离。

影响范围小

如果我们有许多集群,并且其中一个集群发生故障,那么损失仅限于该集群中运行的应用程序。因此,拥有许多小型集群是一种分摊意外事件风险的策略。如果一个集群很小且集中(即仅托管一小组应用程序),那么访问集群的人员(开发人员、测试人员、客户等)会更少。因为大多数错误都是人为导致的,所以这样可以大大降低发生负面事件的风险(即便发生某些情况,损坏也仅限于一个集群)。这里有种特别有用的方法是将应用程序的生产版本移至单独的集群中,并限制对该集群的访问。理想情况下,该集群只能通过 CD 工具访问(也就是说,没有人可以访问它),这可以消除最关键的人为错误风险。

定制集群

如果集群仅运行一小部分应用程序,那么我们可以根据它们托管的应用程序来定制这些集群。例如某个应用程序需要 GPU 支持,那就将 GPU 节点放入其集群中,该节点就不用暴露给其他应用程序(还可以防止将其他应用调度到该节点上,它们可能带有 Taints 和 Tolerations)。这同样适用于其他集群配置,例如 CNI 插件。我们可以选择最适合该集群中应用程序的 CNI 插件(例如是否加密、是否支持 NetworkPolicy 等等)。准入控制器、身份验证方法、Kubernetes beta 功能等也是同理,我们可以分别为每个集群定制配置。

看了小型集群的优点之后,我们再看看它的弊端。

昂贵

上文提到过的,每个 Kubernetes 集群通常具有固定的成本,而固定成本与其大小无关。通常,10 个集群(每个集群提供所需容量的 10%)都会比单个集群(提供所需容量的100%)要贵。

资源利用效率低

每个集群需要一定数量的固定资源用于其运行,而这与其规模无关。例如,每个集群至少需要一个主节点(为了高可用性,最好使用多个主节点),这与工作节点的类型和数量无关。例如有 100 个集群,其中包含 3 个工作程序节点,并且为了控制平面的高可用性,每个集群有 3 个主节点,那么 50% 的资源将用于管理。另一方面,如果单个集群具有 300 个工作节点(提供 100 个小型集群相同的容量),但仅需 3 个主节点来管理该集群。这样仅使用约 1% 的集群资源用于管理。

通常,数量更多的工作节点需要功能更强大的主节点(但不一定要有数量更多的主节点)。因此,大型集群的资源利用率也有所降低,但没有小型集群下降那么多。

总而言之,如果使用较小的集群,则需要配置更高的总量资源,来实现给定的运行工作负载容量。

运营费用高

有效且一致地管理多个小型集群是一项艰巨的任务。考虑更新、调整和修补集群范围的组件,例如 Kubernetes 本身、CNI 插件或容器运行时,我们必须多次执行所有的这些操作,最好同时进行,以免出现任何差异和配置偏差。

如果开发工具或流程来自动执行这些任务,也需要时间和精力。因此,搭建多个小型集群就需要面对管理问题。

大家如何看待

不久前,《The New Stack》进行了一次小型的调查,提出了本文中刚刚讨论的话题:“大家喜欢少量大型集群还是多个小型集群?”

在回复中,大约有一半选择了小型集群,仅有四分之一选择大型集群。但是,比原始数据更有趣的是大家选择的原因。选择大型集群的主要原因是:

  • 资源利用率高
  • 成本效益高
  • 运营开销少

选择小型集群的主要原因是:

  • 大型集群性能注意事项繁琐
  • 隔离性和安全性高
  • 影响范围小

正如本文讨论的的,这些原因很好地总结了两种方法的优缺点。就个人而言,我认为小型集群的投票数较高是因为,在概念上使用小型集群会比大型集群更简单。因为搭建小型集群,往往可以避免搭建大型集群所带来的不可预见且难以解决的问题风险,例如隔离、安全性、性能问题以及访问权限管理。

另一方面,使用大型集群,我们必须处理并解决这些问题。这可能需要大量的实验和工作。所以许多人会更偏向小型集群。

但是,Kubernetes 还很年轻,并且发展迅速。大型集群的支持和稳定性在将来会持续增加,我们很可能会在此领域看到一些不错的实践。因此,大型集群将来很有可能会获得更多认可。

结论

综上所述,使用少量大型集群还是多个小型集群的答案取决于用例,本文所讨论的优缺点就是抉择的关键点。

以下是一些要考虑的重要事项:

  • 如果有严格的安全要求,最好在单独集群中运行关键的工作负载。Kubernetes 目前无法提供足以满足多租户等用例的硬性隔离和安全性保证。
  • 如果想要最大程度降低成本,包括资源和人工成本,那么大型集群无疑会更好。
  • 无论选择哪种方案,都可以在单独的集群中运行应用程序的生产版本,这些是真正重要的工作负载。

对了,还有一种策略是搭建两个集群,一个用于开发和测试,另一个用于生产。然后根据需要,我们将对安全性敏感的工作负载分到单独集群。通常,多个团队可以使用同一集群,但是如果有需要(例如团队非常大时),集群也可以根据团队进行拆分。

原文请点击:https://mp.weixin.qq.com/s/ckEQfaf1wGkqI5lVZ2ZZSA