自定义Kubernetes调度程序来编排高可用性应用程序
只要愿意遵守规则,在Kubernetes上进行部署和乘飞机旅行就可以很愉快。通常,事情会“正常工作”。但是,如果有兴趣与必须生存的鳄鱼一起旅行,或对必须保持可用状态的数据库进行扩展,则情况可能会变得更加复杂。为此,甚至可能更容易构建自己的飞机或数据库。除了爬行动物之外,扩展高可用性的有状态系统也不是一件容易的事。
扩展任何系统都有两个主要组成部分:
- 添加或删除系统将在其上运行的基础结构,以及
- 确保系统知道如何处理自己添加和删除的其它实例。
大多数无状态系统(例如Web服务器),在创建时不需要知道对等对象的。有状态的系统(包括CockroachDB之类的数据库)必须与对等实例进行协调,并在数据周围进行重新整理。幸运的是,CockroachDB能处理数据的重新分发和复制。棘手的部分是通过确保数据和实例分布在许多故障域(可用性区域)中,能够容忍这些操作中的故障。
Kubernetes的职责之一是将“资源”(例如磁盘或容器)放入群集中,并满足所要求的约束。例如:“必须在可用区域A中”,或“不能将我与另一个Pod放置在同一节点上”。
除了这些约束之外,Kubernetes还提供Statefulsets(状态集),该状态集为Pod提供身份,以及“跟随”这些已标识Pod的持久性存储。StatefulSet中的身份由Pod名称末尾的递增整数处理。该整数必须始终是连续的:在StatefulSet中,如果容器1和3存在,则容器2也必须存在。
CockroachCloud将CockroachDB的每个区域作为StatefulSet,部署在其Kubernetes集群中。在本文中,将研究一个单独的区域,一个StatefulSet和一个Kubernetes集群,该集群分布在至少三个可用性区域中。
一个三节点的CockroachCloud集群如下所示:
向集群添加资源时,将它们分布在区域之间。为了获得最快的用户体验,同时添加了所有Kubernetes节点,然后扩展了StatefulSet。
无论Pod分配给Kubernetes节点的顺序如何,都满足反亲和性anti-affinity。此示例中,分别将Pod 0、1和2分配给区域A,B和C,分别以不同的顺序将Pod 3和4分配给区域B和A。由于吊舱仍放置在不同的区域中,因此仍然可以满足抗亲和力anti-affinity要求。
从集群中删除资源,逆序执行这些操作。
首先缩小StatefulSet,然后从群集中删除缺少CockroachDB pod的所有节点。
小为n的StatefulSet中的pod的id必须在range内[0,n)
。将StatefulSet按m缩小,Kubernetes会从最高序数开始,向最低序数移动m吊舱,倒序添加。考虑下面的群集拓扑:
当从该群集中删除序号5到3时,状态集statefulset将在所有3个可用性区域中继续存在。
Kubernetes的调度程序并不能像最初预期的那样,保证上面的位置。
以下方面的综合了解,导致这种错误的原因。
考虑以下拓扑:
这些窗格是按顺序创建的,分布在集群中的所有可用性区域中。当序数5到3终止时,此群集将失去在C区的存在!
自动化将删除节点A-2,B-2和C-2。使CRDB-1处于非计划状态,持久卷积仅在最初创建区域中可用。
为了更正后一个问题,现在采用“狩猎和啄食hunt and peck”的方法从群集中删除计算机。不会从群集中盲目删除Kubernetes节点,而只会删除没有CockroachDB pod的节点。更艰巨的任务是纠缠Kubernetes调度程序。
头脑风暴会议为我们提供了3个选择:
1.升级到kubernetes 1.18并利用Pod拓扑扩展约束
虽然这似乎是一个完美的解决方案,公共云中的两个最常见的托管Kubernetes服务(EKS和GKE)尚无法使用Kubernetes 1.18。此外,pod拓扑扩展约束仍然是1.18中的beta功能,这意味着即使v1.18可用,也不能保证在托管群集中也可以使用它。整个过程让人想起了Internet Explorer 8仍然存在时检查caniuse.com的过程。
2.为每个区域部署一个有状态集。
与其在所有可用区域上分布一个StatefulSet,不如在每个区域具有节点亲和力的单个StatefulSet,将允许对区域拓扑进行手动控制。一直认为这是一种选择,这使其特别具有吸引力。最终,决定放弃此选项,因为这将需要对代码库进行大修,在现有客户集群上执行迁移,将是一项同样艰巨的任务。
3.编写一个自定义的Kubernetes调度程序。
编写自己的自定义Kubernetes调度程序。部署并运行了概念验证后, Kubernetes的调度程序,负责将持久卷映射到其调度的Pod。输出kubectl get events
,还有另一个系统在起作用。在寻找负责存储声明映射的组件的过程中,发现了kube-scheduler插件系统。下一个POC是一个Filter
插件,按Pod顺序确定合适的可用性区域,并且可以完美地工作!
自定义调度程序插件是开源的,可在所有CockroachCloud集群中运行。控制StatefulSet Pod的调度方式,充满信心地进行扩展。一旦GKE和EKS中提供了Pod拓扑扩展约束,可能会考虑淘汰插件,但是维护开销却出乎意料的低。更妙的是:该插件的实现与业务逻辑正交。部署或撤消它,就像更改schedulerName
的StatefulSet定义中的字段一样简单。