背景
SOFAStack 是蚂蚁团体的商业化金融级云原生架构产品,基于 SOFAStack 可疾速搭建云原生微服务体系,疾速开发更具可靠性和扩展性、更加易于保护的云原生利用。在宏观架构层面,提供单机房向同城双活、两地三核心、异地多活架构演进路线,使零碎容量能在多个数据中心内任意扩大和调度,充分利用服务器资源,提供机房级容灾能力,保障业务连续性。
在利用生命周期治理层面,SOFAStack 提供了一个多模利用 PaaS 平台——SOFAStack CAFE (Cloud Application Fabric Engine) 云利用引擎。它提供利用治理、流程编排、利用部署、集群运维等全生命周期治理的 PaaS 平台能力,满足金融场景中经典和云原生架构的运维需要,帮忙传统架构平滑过渡、保障金融技术危险。
在云原生架构运维上,SOFAStack CAFE 通过单元化混合云产品 LHC (LDC Hybrid Cloud) 提供单元化利用的云原生多集群公布运维能力,实现利用的多地区、多机房、多云混合部署。本文将揭开 LHC 的神秘面纱,来具体谈谈咱们在其底层 Kubernetes 多集群公布体系中的一些实际。
挑战
在 LHC 产品诞生之初,咱们首要面临的问题便是为其抉择一个适合的底层 Kubernetes 多集群框架。彼时 Kubernetes 社区刚刚实现了其官网多集群我的项目 KubeFed,其提供了多集群的纳管、Kubernetes 资源的多集群散发与状态回流等一系列多集群根底能力,天然成为了咱们过后的最佳抉择。
但正如后面所说,社区的多集群框架提供的仅仅是“根底能力”,这些能力对于咱们的单元化混合云产品来说存在着很多不满足甚至有抵触的点。其中,最突出的一个问题就是社区没有“单元化”的概念,其多集群就是纯正的多 Kubernetes 集群,对任何一个多集群 Kubernetes 资源(在 KubeFed 中咱们称其为联邦资源)来说,它的散发拓扑只能是按集群。但在单元化模型中,一个应用服务的资源是散布在多个部署单元中的,而部署单元和集群之间的关系的灵便的——在咱们目前的模型中,集群和部署单元之间的关系是 1:n,即一个 Kubernetes 集群能够蕴含多个部署单元。这时候,咱们便遇到了和社区框架的分歧点,也是最大的挑战:下层业务须要按部署单元维度来进行 Kubernetes 资源的治理,底层社区框架则只认集群。
除此之外,KubeFed 本身所涵盖的根底能力也还不足以满足咱们的所有需要,比方不足集群的租户隔离能力、不反对资源 annotation 的下发、主集群和子集群之间的网络连通性要求低等等。由此,解决抵触并补齐能力便成为了咱们在建设 LHC 产品底层多集群能力上的重点课题。
实际
上面咱们就来分模块谈谈建设 LHC 产品底层 Kubernetes 多集群能力中的一些具体实际。
多拓扑联邦 CRD
在社区 KubeFed 框架中,咱们通过联邦 CR 来进行 Kubernetes 资源的多集群散发。一个典型的联邦 CR 的 spec 如下所示:
能够看到其次要蕴含三个字段,其中 placement 用于指定所需散发的集群,template 蕴含了该联邦资源的单集群资源体,overrides 用于指定每个子集群中对 template 中资源体的自定义局部。
后面咱们提到,对于单元化利用的 Kubernetes 资源而言,它须要按部署单元维度而非集群维度进行散发,因而下面的社区 CRD 显然是无奈满足要求的,须要对其进行批改。通过批改后,新的联邦 CR 的 spec 如下所示:
能够看到,咱们没有齐全摒弃社区的 CRD,而是对其进行了“降级”,通过将具体的“集群”转变为形象的“拓扑”,将联邦资源的散发拓扑齐全自定义化,突破了繁多集群维度的限度。如下面的 spec 中咱们将 topologyType 设置为 cell 即指定了该资源以部署单元维度进行散发,反之指定为 cluster 则能齐全兼容社区原生的集群维度散发模式。
当然,仅仅定义一个新的 CRD 无奈解决问题,咱们还须要批改其对应的实现,能力让其工作起来。然而,如果要让社区的 KubeFed controller 感知多拓扑模型,势必会对其底层实现进行大量批改,最终很可能就变成了半重写,研发老本高,且无奈再持续回流社区上游的批改,带来的保护老本也比拟高。咱们须要寻求更好办法,把咱们的多拓扑模型和 KubeFed 的原生多集群能力解耦开来。
独立并扩大联邦层 ApiServer
既然咱们不想对社区 KubeFed controller 进行过多侵入式批改,那么咱们必然须要一个转换层,来将上述的多拓扑联邦 CRD 转换成相应的社区 CRD。对于某种特定拓扑来说,其转换逻辑也是确定的,因而最简略高效的转换便是间接通过 Kubernetes ApiServer 来进行解决,而 ApiServer 对于 CRD 的 Conversion Webhook 能力则刚好可能满足这一转换层的实现需求。
因而,咱们给 KubeFed controller 搭配了一个专属的 Kubernetes ApiServer 造成了一个独立的 Kubernetes 管制面,咱们称其为“联邦层”。这一独立的管制面仅蕴含联邦多集群相干的数据,确保不会与其余 Kubernetes 资源相互烦扰,同时也防止了部署时对外部 Kubernetes 集群的强依赖。
那么,联邦层的 ApiServer 有何特别之处?其实它的主体还是 Kubernetes 原生的 ApiServer,提供 ApiServer 所能提供的所有能力,咱们所做的则是对其进行了“包装”,将咱们须要扩大到联邦层的能力装了进去。上面具体介绍几项要害扩大能力。
内置多拓扑联邦 CRD 转换能力
正如上文所说,这是联邦层 ApiServer 提供的最重要的能力。借助 Kubernetes CRD 的多版本能力,咱们将本人的多拓扑联邦 CRD 和社区 CRD 定义为了同一个 CRD 的两个版本,而后通过在联邦层 ApiServer 中集成针对该 CRD 的 Conversion Webhook,即可自定义二者的转换实现了。这样一来,在联邦层管制面上,任何联邦 CR 都能同时以两种模式进行读取和写入,做到了下层业务仅关怀部署单元(或者其余业务拓扑),而底层 KubeFed controller 仍仅关怀集群,实现了其对多拓扑联邦 CRD 模型的无感反对。
上面以部署单元拓扑为例,简略介绍下其与集群拓扑之间的转换实现。在 KubeFed 中,咱们通过创立蕴含了子集群拜访配置的 KubeFedCluster 对象来使其纳管这一子集群,随后咱们就能够在联邦 CRD 的 placement 中通过 KubeFedCluster 对象的名称来指定须要散发的子集群。那么,咱们的转换逻辑所要做的就是将多拓扑联邦 CRD 中的部署单元名称转换为其所对应集群的 KubeFedCluster 对象名称。因为集群和部署单元是 1:n 的关系,因而咱们只需为每个部署单元额定创立蕴含其所在集群拜访配置的 KubeFedCluster 对象,并通过对立的命名规定为其生成可能通过部署单元所在命名空间(即租户与工作空间组名称)与名称寻址到的名称即可。
以此类推,咱们能够通过相似的形式很容易地反对更多拓扑类型,极大地提高了咱们在联邦模型应用上的灵便度。
反对间接应用 MySQL/OB 作为 etcd 数据库
对于任何一个 Kubernetes ApiServer 来说,etcd 数据库都是必不可少的依赖。在蚂蚁主站,咱们有丰盛的物理机资源和弱小的 DBA 团队来提供继续高可用的 etcd,但对于 SOFAStack 产品简单繁多的对外输入场景而言则不是这样。在域外,运维 etcd 的老本要比运维 MySQL 要高得多,此外,SOFAStack 经常会搭配 OceanBase 一起输入,咱们也心愿可能充分利用 OB 提供的成熟的多机房容灾能力解决数据库高可用的问题。
因而,在一番调研和尝试后,咱们将 k3s 社区开源的 etcd on MySQL 适配器 Kine 集成进了联邦层 ApiServer,使其可能间接应用通用的 MySQL 数据库作为 etcd 后端,省去了独自保护一个 etcd 的懊恼。此外,咱们也针对 OB 与 MySQL 的一些差异化行为(如切主自增序跳变)进行了适配,使其也能完满兼容 OB,以享受到 OB 带来的数据高可用与强一致性。
此外,咱们还在联邦层 ApiServer 内集成了一些用于校验与初始化联邦相干资源的 Admission Plugin 等,因为大多和产品业务语义相干,这里不再赘述。
值得一提的是,咱们所做的这些扩大都具备拆解为独立的组件与 Webhook 的能力,因而也能实用于社区原生插件化装置的模式,不强依赖独立的 ApiServer。目前咱们将 ApiServer 独立进去次要是为了隔离联邦层的数据,同时便于独立部署和保护所需。
总结一下,从架构层面来讲,联邦层 ApiServer 次要起到了一个联邦资源南北向桥接器的作用,如下图所示,其通过 CRD 多版本的能力,南向为 KubeFed controller 提供了承载社区联邦资源的 ApiServer 能力,北向则为下层业务产品提供了从业务拓扑(部署单元)到集群拓扑的映射转换能力。
KubeFed Controller 能力加强
后面提到,除了联邦模型外,社区 KubeFed controller 在本身底层能力上也无奈满足咱们的全副需要,因而,咱们在产品化过程中对其进行了一些能力加强。其中一些通用的加强咱们也奉献给了社区,如反对设置 controller worker 并发数与多集群 informer cache 同步超时、反对 Service 非凡字段保留等。而一些高阶能力咱们都通过 Feature Gate 的模式进行了可插拔的加强,做到了与 code base 与社区上游的实时同步。上面咱们就来介绍其中几个有代表性的加强能力。
反对子集群多租户隔离
在 SOFAStack 产品中,无论是在私有云还是专有云,所有资源都是按租户与工作空间(组)的粒度进行隔离的,以确保各个用户及其上司的各个环境之间不会相互影响。对于 KubeFed 而言,其所关怀的次要资源是 Kubernetes 集群,而社区的实现对其是不做任何隔离的,这一点从联邦资源的删除逻辑上就可看出:在联邦资源被删除时,KubeFed 会查看其管制面中纳管的所有集群以确保该资源在所有子集群中的单集群资源都被删除。在 SOFAStack 产品语义下,这么做显然是不合理的,会产生不同环境之间相互影响的危险。
因而,咱们对联邦资源与 KubeFed 中代表纳管子集群的 KubeFedCluster 对象进行了一些无侵入的扩大,通过为其注入一些 well known labels 的形式使其持有了业务层的一些元数据,如租户与工作空间组信息等。借助这些数据,咱们在 KubeFed controller 解决联邦资源时对子集群退出了一次预选,使其对联邦资源的任何解决都只会将读写范畴限度在其所附属的租户与工作空间组内,做到了多租户多环境的齐全隔离。
反对灰度散发能力
对于 SOFAStack CAFE 这样一个金融生产级公布部署平台而言,灰度公布是一项必不可少的能力。对于任意的应用服务变更,咱们都心愿其可能以用户可控的形式灰度公布到指定的部署单元中。这就对底层的多集群资源管理框架也提出了相应要求。
从上文联邦 CRD 的介绍中能够看到,咱们通过 placement 为联邦资源指定须要散发的部署单元(或其余拓扑)。在首次下发一个联邦资源时,咱们能够通过逐渐向 placement 中增加欲公布的部署单元来实现灰度公布,但当咱们要更新该资源时,就没有方法进行灰度了——此时 placement 中曾经蕴含了所有部署单元,对联邦资源的任何批改都会立即同步到所有部署单元,同时,咱们也不能通过将 placement 从新设置为欲公布的部署单元来做灰度,因为这会导致其余部署单元内的资源被立刻删除。此时,为了反对灰度公布,咱们就须要一项能力反对咱们指定 placement 中的哪些部署单元是要被更新的,其余则需放弃不变。
为此,咱们引入了 placement mask 的概念。如其名所示,它就像是 placement 的一个掩码一样,当 KubeFed controller 解决联邦资源时,它所更新的拓扑范畴就变成了 placement 和 placement mask 的交加。此时,咱们只须要在更新联邦资源时同时指定它的 placement mask,即可精细化地管制本次变更影响的部署单元范畴,实现了齐全自主可控的灰度公布能力。
如下图所示,咱们为该联邦资源增加了仅蕴含部署单元 rz00a 的 placement mask,此时能够看到位于 rz00a 子资源被胜利更新(更新后子资源的 generation 为 2),而 rz01a 的资源则不做解决(因此没有产生更新后的 generation),实现了灰度公布的成果。
值得一提的是,placement mask 的引入不仅解决了灰度公布的问题,也解决了容灾公布的问题。在因机房劫难导致局部集群(部署单元)不可用的状况下,咱们能够通过 placement mask 持续失常公布其余可用的部署单元,不会因部分异样而阻塞整个多集群的公布。而在集群复原后,placement mask 的存在可能避免对刚复原部署单元的预期外主动变更,保障了公布变更的强管控性。
反对自定义 Annotation 下发策略
KubeFed 对于资源的下发有一项准则,即只下发 spec 类的属性,不下发 status 类的属性。这一准则的出发点很简略:咱们须要保障子集群资源的 spec 被联邦层强管控,但又要放弃其 status 各自独立。对于任何 Kubernetes 对象而言,其绝大多数属性都是非 spec 即 status 的——spec 和 status 属性自身就不用说了,像 metadata 中的 name、labels 等就属于 spec,而 creationTimestamp、resourceVersion 之流则属于 status。然而,凡事皆有例外,这其中有一个属性是既能充当 spec 又能充当 status 的,它就是 annotations。
很多时候,咱们无奈把一个 Kubernetes 对象的所有 spec 和 status 都收敛在真正的 spec 和 status 属性内,一个最典型的例子就是 Service。对 Service 利用有所理解的同学应该晓得,咱们能够应用 LoadBalancer 类型的 Service 并搭配不同云厂商提供的 CCM(Cloud Controller Manager)来实现不同云平台下负载平衡的治理。因为 Service 是 Kubernetes 内置对象,其 spec 和 status 都是固定不可扩大的,而不同云厂商 CCM 反对的参数都各不相同,因而 Service 的 annotations 便天然地承载起了这些配置,起到了 spec 的作用。与此同时,局部 CCM 还反对回流负载平衡的一些具体状态到 annotations,比方创立负载平衡过程中的一些中间状态包含错误信息等,这时候 Service 的 annotations 又起到了 status 的作用。这时候 KubeFed 就面临了一个问题——到底要不要下发 annotations 字段?社区抉择了齐全不下发,这诚然不会影响 annotations 作为 status 的能力,但也失去了 annotations 作为 spec 时对其的管控能力。
那么,有没有可能做到两者兼得呢?答案天然是有的。在 KubeFed 中,对于每一种须要多集群化的 Kubernetes 资源类型,都须要在联邦层为其创立一个 FederatedTypeConfig 对象,用于指定联邦类型与单集群类型的 GVK 等信息。既然 annotations 字段的 spec/status 个性也是和具体资源类型无关的,那么咱们便能够在这个对象中做文章,为其退出了一项 propagating annotations 的配置。在该配置中,咱们能够显式地指定(反对通配符)该资源类型的 annotations 中,哪些 key 是用作 spec 的,对这些 key,KubeFed controller 会进行下发和管控,而其余的 key 则作为 status 看待,不会对子集群资源上的值进行笼罩。借助这项扩大能力,咱们便能灵便地自定义任意 Kubernetes 资源类型的 annotation 下发策略,实现残缺的多集群资源 spec 管控能力。
以 Service 为例,咱们对该类型的 FederatedTypeConfig 对象进行了如下配置:
上面第一张图为 FederatedService 的 template 所指定的下发模板,第二张图为理论子集群中被管控 Service 的状况。能够看到,咱们在联邦资源中指定的 spec 类 annotation(如 service.beta.kubernetes.io/antcloud-loadbalancer-listener-protocol)被胜利下发到了子资源上,而属于子资源本身的 status 类 annotation(如 status.cafe.sofastack.io/loadbalancer)则被失常保留,没有因 annotations 字段的强管控而被删去或笼罩。
此外,咱们还加强了 KubeFed controller 的状态回流能力,使其可能实时回流所有联邦资源类型的 status 类字段;反对了联邦层子集群拜访配置的 KMS 加密存储以满足金融级平安合规需要等等,受篇幅所限不再一一开展介绍。
到这里,联邦层曾经满足了下层单元化利用公布运维产品的绝大多数需要,但正如前文所提到的,咱们做的是一款“混合云”的产品,在混合云场景下,异构集群与网络连通性的限度是咱们运维 Kubernetes 集群会遇到的最典型的问题。对联邦层而言,因为其次要关注 Kubernetes 利用资源管理,因而集群的异构性不会带来太多影响,只有是合乎肯定版本范畴内 Kubernetes 标准的集群实践上都能间接纳管;而网络连通性的限度则是致命的:因为 KubeFed 采纳推模式进行子集群管控,它要求 KubeFed controller 须要能间接拜访到每个子集群的 ApiServer,这对于网络连通性的要求是相当高的,很多状况下的网络环境都无奈满足这样的要求,就算有方法满足也须要很高的老本(比方买通中枢集群与所有用户集群之间的网络)。因而,咱们势必要寻求一种解法来升高联邦层对集群间网络连通性的要求,以使咱们的产品可能兼容更多的网络拓扑。
集成 ApiServer Network Proxy
既然 KubeFed controller 到子集群 ApiServer 的间接正向连贯可能受限,那么咱们就须要在这两者之间架设一个可能反向建连的代理,通过该代理建设的长连贯通道进行正向拜访。ApiServer Network Proxy(ANP)是社区为了解决 Kubernetes 集群外部网络隔离问题而开发的 ApiServer 代理,它刚好提供了咱们所须要的反向长连贯代理的能力,使得咱们无需正向的网络买通也能失常拜访到子集群的 ApiServer。
然而,ANP 次要解决的是单集群外部拜访 ApiServer 的问题,其连贯模型是多个 client 拜访一个 ApiServer,但对于联邦层这样的多集群管控而言,它的连贯模型是一个 client 拜访多个 ApiServer。为此,咱们对 ANP 的后端连贯模型进行了扩大,使其反对了按集群名称进行动静选址——通过在于 ANP 服务端进行建连时告知本次连贯须要拜访的集群,ANP 服务端就会将后续申请路由到其与上报了该集群名称的 agent 所建设的长连贯通道上。最终的架构如下图所示,通过集成这一“多集群扩大版”的 ANP,咱们就可能在更为严苛的网络环境下轻松地进行多集群治理了。
总结
最初,让咱们通过具体产品能力来进行总结,以简要地体现 SOFAStack CAFE 多集群产品相较于社区版 KubeFed 的一些亮点:
● 借助可扩大的多拓扑联邦模型原生反对了 LDC 部署单元维度的多集群利用公布,屏蔽了底层 Kubernetes 基础设施
● 反对多租户,可能在底层对 Kubernetes 集群等资源进行租户、工作空间级别的隔离
● 突破申明式的掣肘,反对精细化的多集群灰度公布,同时反对容灾公布
● 反对自定义 annotation 下发、残缺状态回流、集群拜访凭证 KMS 加密等高级能力
● 借助 ANP 反对在异构混合云等网络连通性受限的状况下持续以推模式治理所有用户集群
● 多集群管制面可不依赖 Kubernetes 集群独立部署,且反对间接应用 MySQL/OB 作为后端数据库
目前,SOFAStack 曾经利用在国内外 50 多家金融机构,其中浙江农信、四川农信等企业正是借助 CAFE 的单元化混合云架构来进行容器利用的全生命周期治理,构建多地区、高可用的多集群治理平台。
将来布局
从上文的实际局部中能够看出,目前咱们对于底层多集群框架的利用次要还是集中在 Kubernetes 集群纳管和多集群资源治理上,但多集群的利用还有着更广大的可能性。将来,咱们也会逐渐演进包含但不限于如下的能力:
● 多集群资源动静调度能力
● 多集群 HPA 能力
● 多集群 Kubernetes API 代理能力
● 间接应用单集群原生资源作为模板的轻 CRD 能力
后续,咱们也会持续分享对这些能力的思考与实际,欢送大家继续关注咱们的多集群产品,也随时期盼任何意见与交换。