Kubernetes实战:构建生产级应用平台
上QQ阅读APP看书,第一时间看更新

2.3 架构和拓扑结构

本节涵盖了一些配置和管理Kubernetes集群系统架构的最佳实践,包括etcd的部署模型和一些必须考虑的其他平台组件。在这些主题中,我们将研究如何根据服务水平目标(SLO)将管理层下的各种集群进行分层,还将研究节点池的概念以及它们如何在一个特定的集群中用于不同的目的。最后,我们将讨论你可以使用哪些方法来组织管理你的集群并部署其中的软件。

2.3.1 etcd的部署模式

作为Kubernetes集群中的对象数据库,它值得被特别关注。etcd是一个分布式的K-V数据库。由于它使用一致性共识算法来维护多个机器上的集群副本的状态,所以我们需要考虑etcd集群中的节点网络因素,以便它们能够通过网络连接可靠地维护该共识。另外,它有独特的网络延迟要求,所以我们在构建网络拓扑结构时需要针对此处进行设计(我们将在本节中讨论这个话题),同时还将了解在etcd部署模型中的两个主要架构方式:使用专属机器还是多服务共用一台机器,以及在容器中运行还是直接安装在主机上。

网络方面的考虑

etcd的默认设置是针对单个数据中心的延迟而设计的。如果你将etcd分布在多个数据中心,你应该测试成员之间的平均往返时间,并在必要时调整etcd的心跳间隔和选举超时。我们非常不建议使用分布在不同地区的etcd集群。如果必须使用多个数据中心来提高可用性,那它们至少应该在一个区域内且相距不远。

专属机器与多服务共用机器

关于如何部署,我们经常会遇到一个非常常见的问题:给etcd自己的专用机器,还是把它们和API服务器、调度器、控制器管理器等一起放在控制层面机器上?要解决这个问题,首先要考虑你要管理的集群的大小,也就是说,你要在每个集群中运行工作节点的数量。(本章后面将讨论围绕集群规模的权衡问题。你在这个问题上的立场将在很大程度上决定你是否需要为etcd提供专用机器。)

显然,etcd是至关重要的。如果etcd的性能受到影响,你控制集群中资源的能力也会受到影响。虽然只要你的工作负载不依赖Kubernetes API,它们就会受到影响,但保持你的控制层健康仍然非常重要。

比如你开着车在道路上行驶,方向盘停止工作了。虽然你可以安慰自己车还在路上行驶,但事实上你已经陷入了非常危险的境地。出于这个原因,如果你要把大型集群的读写需求放在etcd上,最明智的做法便是将etcd部署在专用机器上,用以消除与其他控制层组件的资源争夺。在这种情况下,“大型”集群依赖于正在使用的控制层机器的大小,当你的集群包含超过50个工作节点时便需要考虑是否需要使用专用的etcd集群。如果计划使用包含超过200个工作节点的集群,最好直接使用专用的etcd集群。如果你计划使用较小的集群,为自己节省管理开销和基础设施成本,那么你可以使用多服务共用的etcd来配合kubeadm(一个流行的Kubernetes启动工具。它支持这种较小集群模式,并解决了一些该环境相关的问题)。

容器化与主机安装

下一个常见的问题则是在机器上安装etcd还是在容器中运行它。让我们先得出一个简单的结论:如果你以多服务共用机器的方式运行etcd,那么你就需要在容器中运行它,因为这是在kubeadm引导Kubernetes时的最佳实践,并且经过了良好的测试。如果你选择在专用机器上运行etcd,你将会有几个选择:你可以在主机上安装etcd,这使你有机会将其纳入机器镜像,并解决了在主机上运行一个额外的容器运行时所造成的担忧。如果你选择在容器中运行,最好的模式是在机器上安装一个容器运行时和kubelet,并使用一个静态声明文件来启动etcd。这样做的好处是遵循与其他控制平面组件相同的模式和安装方法。虽然在复杂的系统中使用重复的模式是有用的,但这主要还是个人偏好问题。

2.3.2 集群分层

按照层级来组织集群是一种普遍的模式。这些层级通常包括测试层、开发层、生产镜像层和生产层。一些团队把这些称为不同的“环境”。然而,这是一个抽象的术语,可以有不同的含义。在这里我们将使用“层级”这个术语来具体解释不同类型的集群。特别是我们正在谈论可能与集群相关的SLO和SLA,以及集群的用途和集群在应用程序的生产构建过程中所处的位置(如果有的话)。对于不同的组织来说,这些层级的具体内容各不相同,但有一些共同的主题,我们将在接下来描述这四个层级通常是什么意思。我们需要在所有层级中使用相同的集群部署和生命周期管理系统。这些系统在较低层级的大量使用将有助于确保它们在应用于生产集群时能够按照预期工作:

测试层

测试层的集群是单租户的临时集群,通常有一个生存时间(TTL),在指定的时间后自动销毁,通常不超过一个星期。这些集群通常是由平台工程师为测试他们正在开发的特定组件或平台功能而启动的。开发人员也可以在本地集群不足以支撑本地开发或作为在本地集群上测试的后续步骤时使用测试层。当应用开发团队最初在Kubernetes上进行容器化和测试应用时更为常见。这些集群没有SLO或SLA。并且它们将使用平台的最新版本,或者选择使用pre-alpha版本。

开发层

开发层集群通常是没有TTL的“永久”集群。它们是多租户的(如果适用),并具有生产集群的所有功能。它们用于应用程序的第一轮集成测试,并用于测试应用程序的工作负载与平台的alpha版本兼容性。开发层也用于应用程序开发团队的一般测试和开发。这些集群通常有一个SLO,但没有一个与之相关的正式协议。在工作时间其可用性是接近生产水平的,因为中断会影响开发人员的生产力。相比之下,当应用程序在开发集群上运行时,其SLO或SLA为零,并且非常频繁地更新,处于不断变化之中。这些集群将运行正式发布平台的alpha或beta版本。

生产镜像层

与开发层的集群一样,生产镜像层的集群也是永久性集群,通常由多个租户使用。它们被用来进行最后的集成测试和交付,然后再推广到实际生产。它们被那些没有参与开发的人员或外部利益相关者所使用,其中包括项目经理、产品经理和管理人员,也可能包括需要访问软件预发布版本的客户或外部股东。它们通常有一个类似于开发集群的SLO。如果外部利益相关者或付费客户访问集群上的工作负载,生产镜像层可能有一个与之相关的正式SLA。如果平台团队严格遵循向后兼容的原则,这些集群将运行正式发布的平台测试版。如果不能保证向后兼容,生产镜像集群应该运行与生产中使用的相同的稳定版本的平台。

生产层

生产层的集群是赚钱的。这些集群用于面向客户、产生收入的应用程序和网站。只有经过批准的、可用于生产的、稳定的软件版本才能在这里运行,而且只使用经过充分测试和批准的平台稳定版本。通常使用定义明确的SLO并详细跟踪记录。另外,具有法律约束力的SLA也适用。

2.3.3 节点池

节点池是一种在单个Kubernetes集群内将各类节点分组的方式。这些类型的节点可以通过其独特的特征或所扮演的角色而被分组。在我们讨论细节之前,了解一个集群中是使用多个节点池还是配置独立的不同集群的优缺点是很重要的。如果使用节点池,你将需要在工作负载上使用节点选择器,以确保它们最终出现在适当的节点池中。你还可能需要使用节点污点,以防止没有节点选择器的工作负载无意中落在不合适的地方。此外,集群内节点的扩展变得更加复杂,因为你的系统必须监控不同的节点池,并分别扩展每个节点池。如果你选择使用配置独立的不同集群,你将需要更多的集群来处理集群管理和软件组织的问题。并且你还需要把工作负载正确地定位到正确的集群上。表2-1总结了使用节点池的优点和缺点。

表2-1:节点池的优点和缺点

基于特征的节点池是由具有某些特定工作负载所需或者适合的组件或属性的节点组成的。例如,存在一个类似于图形处理单元(GPU)的专用设备是一种特征,它使用的网络接口类型或是机器上内存与CPU的比例可能是另一个特征。(我们将在2.4节中更深入地讨论可能会以不同比例使用这些资源节点的原因。)现在我们只需要了解这些特征适合不同类型的工作负载,如果你在同一个集群中运行它们,你将需要把它们分组到池中,以管理不同Pod的位置。

基于角色的节点池是一个具有特殊功能的节点池,你经常想让它们避免资源争夺。分配到基于角色的节点池的节点不一定有特殊的特征,它可能只是有不同的功能,比如将一个节点池专门用于集群中的入口层。在入口池的例子中,专用池不仅使工作负载不受资源争夺的影响(在这种情况下特别重要,因为资源请求和限制目前还不能用于网络使用),并且简化了网络模型和暴露于集群外流量来源的特定节点。与基于特征的节点池相比,这些角色往往不是你置换到不同的集群中的问题,因为这些机器在特定集群的功能中发挥着重要作用。也就是说,要确保你把节点切分至池中是有充分理由的。不要不分青红皂白地创建池,因为Kubernetes集群已经够复杂了。

请记住,无论你是否使用节点池,你都很可能需要解决许多不同集群带来的多集群管理问题。很少有企业在使用Kubernetes时不积累大量的不同集群。因此,如果你想引入基于特征的节点池,请考虑在开发和完善多集群管理方面投入工程量。然后你才有机会为你需要提供的不同机器特征无缝地使用不同的集群。

2.3.4 集群联邦

集群联邦指的是如何集中管理你所控制的所有集群。因为Kubernetes具有“成瘾性”,所以当你部署了一个集群并享受过它的便捷后,你将会迫不及待地想要部署下一个。但是,如果你不对这种习惯进行控制,整个集群的生态将会变得非常混乱。使用集群联邦架构来管理企业中的软件依赖则可以防止其演变为昂贵的、具有破坏性的“瘾”。

常见且有用的方法是先在区域内联合,然后在全球联合。这就减少了这些联邦集群的事故半径,并降低了计算负荷。当你第一次开始联合工作时,你可能没有分布全球或大量的基础设施来证明多级联邦方法的合理性,但是,你要记住它的设计原则来为未来做准备。

让我们来讨论这个领域的一些重要的主题。在本节中,我们将看到管理集群如何帮助巩固和集中区域服务,然后将考虑如何整合不同集群工作负载的指标。我们还将讨论这种以集中管理方式在不同集群中部署对工作负载的管理有何影响。

管理集群

管理集群就是管理其他集群的Kubernetes集群。一些企业和组织发现,随着使用范围的扩大和所管理集群数量的增加,需要借助软件系统才能使集群正常运转。并且,正如你所料,它们经常使用基于Kubernetes的平台来运行这些软件。Cluster API(https://cluster-api.sigs.k8s.io)已经成为实现这一目标的流行项目。它是一套Kubernetes operator,使用自定义资源(如集群和机器资源)来代表其他Kubernetes集群及其组件。常见的模式是将Cluster API组件部署到一个管理集群,以便为其他工作负载集群部署和管理基础设施。

然而,以这种方式使用管理集群确实有缺陷。通常来说,更严谨的做法是严格区分生产层和其他层之间的关联。因此,企业通常会有一个专门用于生产的管理集群。但是这会进一步增加管理集群的开销。另一个是集群自动缩放的问题,集群自动缩放是一种根据工作负载的扩展而增加和删除工作节点的方法。集群自动缩放器(Cluster Autoscaler)通常在它所缩放的集群中运行,以便等待触发需要缩放事件的条件。管理集群包含这些管理控制器来管理工作节点的部署和退役。这也为任何使用集群自动缩放器的工作负载集群引入了对管理集群的外部依赖,如图2-1所示。当管理集群需要向外扩展以满足需求,从而导致管理集群处于繁忙不可用状态时该怎么办?

图2-1:集群自动缩放器访问管理集群来触发缩放事件

解决这个问题的策略是:在工作负载集群中以独立的方式运行Cluster API组件。在这种情况下,集群和机器资源也将在工作负载集群中存在。你仍然可以使用管理集群来创建和删除集群,但是工作负载集群在很大程度上是自动的,并且在常规操作(如自动缩放)方面摆脱了对管理集群的外部依赖,如图2-2所示。

图2-2:集群自动缩放器访问本地集群API组件以执行缩放事件

这种模式还有一个明显的优势:如果集群中的任何控制器或工作负载需要Cluster API用户资源中包含的元数据或属性,则可以通过本地API读取资源来访问它们。不需要访问管理集群API。例如,如果你有一个命名空间(Namespace)控制器,它可以根据已经包含在其所在集群资源中的信息来判断它是在开发集群还是生产集群中,从而改变它的行为。

此外,管理集群也经常托管共享服务或区域服务,这些服务被其他各集群的系统访问。但这些都不是管理功能。管理集群通常只是一个运行这些共享服务的逻辑场所。这些共享服务的例子包括CI/CD系统和容器注册中心。

可观测性

当管理大量的集群时,第一个映入眼帘的问题便是如何收集整个基础设施的运行指标,并将它们或其子集带到一个收集中心。高水平且可衡量的数据可以让你清楚地了解所管理集群和工作负载的健康状况,这在集群联邦架构中是非常重要的。Prometheus(https://prometheus.io)是一个成熟的开源项目,许多组织使用它来收集和存储指标。即使你没有使用过它,了解它的集群联邦架构模型也是非常值得的,它可以在有可能的情况下与你使用的工具进行聚合,并且支持区域性的集群联邦架构,允许联邦内的Prometheus服务器从其他较低级别的Prometheus服务器上抓取指标子集,所以它可以适应你采用的任何联邦策略。我们将在第9章更深入地探讨这个话题。

联邦软件部署

在管理各种远程集群时,另外一个重要的问题是如何将软件部署到这些集群,因为能够管理集群本身是一回事,但组织终端用户的工作负载部署到这些集群则完全是另一回事。这也就是拥有这些集群的意义。也许你有关键的、高价值的工作负载必须部署到多个区域以实现高可用性。或者你只是需要根据不同集群的特点来组织工作负载的部署位置。但如何做出这些决定是一个值得深思熟虑的问题,这从社区内缺乏对该问题的良好解决方案中可以看出。

一段时间以来,Kubernetes社区一直尝试以一种大众可以接受的方式来解决这个问题。现在最新的是解决方案是KubeFed(https:// github.com/kubernetes-sigs/kubefed)。虽然它还解决了集群配置问题,但在这里我们更关注针对多个集群的工作负载的定义。它提供了一个有用的设计概念,能够联合Kubernetes中使用的任何API类型。例如,你可以使用Namespace和Deployment类型的联合版本,用于声明资源应该如何应用于多个集群。这是一个强大的概念,你可以在一个管理集群中集中创建一个FederatedDeployment资源,并让它在其他集群中创建多个远程Deployment对象。我们期望在未来看到这一领域的更多进展。但在目前,我们在这个领域中看到管理这个问题的最常见方法仍是使用CI/CD工具,这些工具在部署工作负载时被配置为针对不同的集群。

现在,我们已经讨论了架构问题,这些问题将决定你的集群的组织和管理方式。接下来让我们详细探讨一下基础设施问题。