背景
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 能力
本文分享自微信公众号 - 支付宝技术(Ant-Techfin)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。