作者:vivo 互联网容器团队 - Zhang Rong
本文主要讲述了一些对于K8s多集群管理的思考,包括为什么需要多集群、多集群的优势以及现有的一些基于Kubernetes衍生出的多集群管理架构实践。
随着K8s和云原生技术的快速发展,以及各大厂商在自己的数据中心使用K8s的API进行容器化应用编排和管理,让应用交付本身变得越来越标准化和统一化,并且实现了与底层基础设施的完全解耦,为多集群和混合云提供了一个坚实技术基础。谈到多集群多云的数据中心基础架构,会想到为什么企业需要多集群?
1.单集群容量限制:
集群上限5000个节点和15万个Pod。同时单集群的最大节点数不是一个确定值,其受到集群部署方式和业务使用集群资源的方式的影响。
2.多云混合使用:
避免被单家供应商锁定,不同集群的最新技术规划,或是出于成本等考虑,企业选择了多云架构。
3.业务流量突发:
正常情况下用户使用自己的 IDC 集群提供服务,当应对突发大流量时,迅速将应用扩容到云上集群共同提供服务,需具备公有云 IaaS接入,可以自动扩缩容计算节点,将公有云作为备用资源池。该模式一般针对无状态的服务,可以快速弹性扩展,主要针对使用 CPU、内存较为密集的服务,如搜索、查询、计算等类型的服务。
4.业务高可用:
单集群无法应对网络故障或者数据中心故障导致的服务的不可用。通常主要服务的集群为一个,另一个集群周期性备份。或者一个集群主要负责读写,其他备份集群只读,在主集群所在的云出现问题时可以快速切换到备份集群。该模式可用于数据量较大的存储服务,如部署一个高可用的mysql集群,一个集群负责读写,其他2个集群备份只读,可以自动切换主备。
5.异地多活:
数据是实时同步的,多集群都可以同时读写,这种模式通常针对极其重要的数据,如全局统一的用户账号信息等。
6.地域亲和性:
尽管国内互联网一直在提速,但是处于带宽成本的考量,同一调用链的服务网络距离越近越好。服务的主调和被调部署在同一个地域内能够有效减少带宽成本;并且分而治之的方式让应用服务本区域的业务,也能有效缓解应用服务的压力。
当前基于 K8s 多集群项目如下:
1.Federation v1:
已经被社区废弃,主要原因在于 v1 的设计试图在 K8s 之上又构建一层 Federation API,直接屏蔽掉了已经取得广泛共识的 K8s API ,这与云原生社区的发展理念是相悖。
2.Federation v2:
已经被社区废弃,提供了一个可以将任何 K8s API type 转换成有多集群概念的 federated type 的方法,以及一个对应的控制器以便推送这些 federated 对象 (Object)。而它并不像 V1 一样关心复杂的推送策略(V1 的 Federation Scheduler),只负责把这些 Object 分发到用户事先定义的集群去。这也就意味着 Federation v2 的主要设计目标,其实是向多集群推送 RBAC,policy 等集群配置信息。
3.Karmada:
参考Kubernetes Federation v2 核心实践,并融入了众多新技术,包括 Kubernetes 原生 API 支持、多层级高可用部署、多集群自动故障迁移、多集群应用自动伸缩、多集群服务发现等,并且提供原生 Kubernetes 平滑演进路径。
4.Clusternet:
是一个兼具多集群管理和跨集群应用编排的开源云原生管控平台,解决了跨云、跨地域、跨可用区的集群管理问题。在项目规划阶段,就是面向未来混合云、分布式云和边缘计算等场景来设计的,支持海量集群的接入和管理、应用分发、流量治理等。
5.OCM:
OCM 是一款 Kubernetes 多集群平台开源项目,它可以帮助你大大简化跨云/多云场景下的集群管理,无论这些集群是在云上托管,还是部署在自建数据中心,再或者是在边缘数据中心中。OCM 提供的基础模型可以帮助我们同时理解单集群内部的容量情况,也可以横跨多集群进行资源/工作负载的编排调度。与此同时,OCM 还提供了一系列强大的扩展性或者叫做插件框架(addon-framework)来帮助我们集成 CNCF 生态中的其他项目,这些扩展性也可以帮助我们无侵入地针对你的特定使用场景手工扩展特性。
以上多集群项目主要功能为资源分发和调度,还有如多云基础设施管理cluster-api,多集群检索Clusterpedia,多集群pod互通submariner,multicluster-ingress解决多集群的ingress,服务治理和流量调度 Service Mesh,如istio、cilium等网络组件实现的multi cluster mesh解决跨集群的mesh网络管理,以及结合存储相关项目实现跨集群存储管理和迁移等。
非联邦多集群管理系统主要是进行资源管理、运维管理和用户管理,提供导入K8s集群权限功能,并通过统一 Web 界面进行查看和管理。这类工具不引入额外集群联邦的复杂,保持每个集群的独立性,同时通过统一的 Web 界面来查看多个集群的资源使用情况,支持通过 Web 界面创建 Deployment、Service 和负载均衡等,并且会集成持续集成、持续交付和监控告警等功能。由于集群联邦技术还在发展,大多数企业倾向于使用这种方式来运维和管理多集群 Kubernetes 环境。当前vivo主要是通过这种方式管理多集群。
联邦集群主要将资源联邦化,实现资源的统一管理和调度。随着K8s在数据中心大量使用,K8s已成为基础设施管理的标准,不同区域部署已非常普遍。如K8s运行在云上来托管集群、企业自建数据中心的私有云、边缘计算等。越来越多的企业投入到多集群管理项目,当然联邦集群肯定会增加整体架构的复杂度,集群之间的状态同步也会增加控制面的额外开销。尽管增加了所有的复杂性,但普遍存在的多集群拓扑引入了新的令人兴奋的潜力。这种潜力超越了目前所探索的通过多个集群进行的简单静态应用程序编排。事实上,多集群拓扑对于跨不同位置编排应用程序和统一对基础设施的访问非常有用。其中,这引入了一种令人兴奋的可能性,可以透明而快速地将应用程序从一个集群迁移到另一个集群。在处理集群灾难或关键基础设施干预、扩展或布局优化时,移动工作负载是可行的。
vivo在联邦集群的探索方向主要有以下四个方面:
资源分发和编排
弹性突发
多集群调度
服务治理和流量调度
本次主要分享资源分发和编排、弹性突发和多集群调度以K8s为核心的联邦多集群探索。网络为核心的能力建设,主要为不同集群的网络可以通过如 Service Mesh或者 Mesh Federation打通,就可以实现网络流量的灵活调度和故障转移。实际上,也有很多应用通过隧道或者专线打通多个集群,进一步保证了多集群之间网络通信的可靠性。vivo网络和服务发现主要是开源的基础上自研,可以持续关注后面分享。
云原生技术的发展是持续输出“事实标准的软件”,而这些事实标准中,应用的弹性、易用性和可移植性是其所解决的三大本质问题。
应用的弹性:对于云产品的客户来说等价于业务可靠性和业务探索与创新的敏捷性,体现的是云计算所创造的客户价值,应用弹性的另一个关注点在于快速部署、交付、以及在云上的扩缩容能力。这就需要完善的应用交付链路以及应用的云原生开发框架支撑;
易用性:能更好地发挥应用的弹性,在微服务软件架构成为主流的情形下,易用性需要考虑通过技术手段实现对应用整体做全局性的治理,实现全局最优。这凸显了 Service Mesh 的服务能力;
可移植性:实现多集群和混合云无障碍迁移等。
那么一个以应用为中心,围绕应用交付的多集群多集群管理具备统一的云原生应用标准定义和规范,通用的应用托管和分发中心,基于 Service Mesh 的跨云的应用治理能力,以及 K8s 原生的、面向多集群的应用交付和管理体系,将会持续不断的产生巨大的价值。vivo当前主要结合Karmada和CNCF周边项目来探索以上挑战。
上图是面向应用的多集群持续发布架构,我们主要的工作如下:
管理注册多个Kubernetes集群并接入Karmada,Karmada负责多个集群的资源调度编排和故障转移。
容器平台统一管理K8s资源、Karmada策略和配置。
CICD平台对应用进行单元测试、安全测试、编译镜像等操作,配置应用的存储、密钥、环境变量、网络和资源等,最终对接容器平台的API生成K8s对象,统一交付。
一个应用真正的能管理起来其实很复杂,如特定的场景需要原地升级和灰度发布等。为了可以提供更加灵活、高级和易用的应用发布能力,更好地满足应用发布的需求,最终选择使用Openkruise。比如上图有个游戏的应用game-2408。它涉及到K8s资源有configmap、secret、pv、pvc、service,openkruise的cloneset、自研的服务发现和访问资源、以及Karmada的PropagationPolicy和OverridePolicy等资源,都能达到12个资源配置。比如存储等资源都是按需申请和分配的,为了有效管理这些资源和关系,当前主要通过关联数据库进行管理,并打通cicd和容器平台的交互,记录应用发布的状态转换,实现应用的滚动、灰度等发布能力,达到可持续发布的能力。
为了方便问题定位、K8s资源和Karmada资源的策略关系,当前Karmada 策略命名规范如下:
可以识别策略属于那个workload
避免策略重复,需要加入workload类型
策略名超过63个字符,需要hash处理
xxx为非workload的资源名
遇到的问题总结:
一个资源无法被多个策略匹配,导致如configmap、secret等公用资源无法再次下发到其它集群。
多个集群之间串行发布,如发布完A集群才能发布B集群的控制。
当前vivo的应用主要通过OpenKruise的Cloneset(无状态)和AdvancedStatefulset(有状态)控制器进行发布。Kamrada目前只能识别K8s默认的资源,其它的资源都需要开发资源解析。为了解决上面提到的问题,Karmada 引入了 Resource Interpreter Webhook,通过干预从 ResourceTemplate-> ResourceBinding ->Work ->Resource 的这几个阶段来实现完整的自定义资源分发能力。
(1)InterpretReplica:
该 hook 点发生在从 ResourceTemplate 到 ResourceBinding 这个过程中,针对有 replica 功能的资源对象,比如类似 Deployment 的自定义资源,实现该接口来告诉 Karmada 对应资源的副本数。
(2)ReviseReplica:
该 hook 点发生在从 ResourceBinding 到 Work 这个过程中,针对有 replica 功能的资源对象,需要按照 Karmada 发送的 request 来修改对象的副本数。Karmada 会通过调度策略把每个集群需要的副本数计算好,你需要做的只是把最后计算好的值赋给你的 CR 对象。
(3)Retain:
该 hook 点发生在从 Work 到 Resource 这个过程中,针对 spec 内容会在 member 集群单独更新的情况,可以通过该 hook 告知 Karmada 保留某些字段的内容。
(4)AggregateStatus:
该 hook 点发生在从 ResourceBinding 到 ResourceTemplate 这个过程中,针对需要将 status 信息聚合到 Resource Template 的资源类型,可通过实现该接口来更新 Resource Template 的 status 信息。
跨集群HPA这里定义为FedHPA,使用了K8s原生的HPA,在Karmada控制平面通过FedHpaController实现跨集群的弹性调度扩缩容。
FedHPA流程:
用户创建HPA资源,如指定workload、cpu上限、min和max值。
FedController开始计算clusterA和clusterB资源,在clusterA和clusterB创建HPA,并按比例分配集群的min和max。
当集群clusterA和clusterB触发定义的cpu资源上限,clusterA和clusterB开始扩容。
Karmada控制平面的clusterA和clusterB的HPA work对象的status里有记录集群扩容副本情况。
FedHPAController感知到work的变化,并获取到每个集群真实的副本数,开始更新调度资源RB和控制平面的workload副本数。保证了控制平面和member集群的调度和副本数一致,不会出现重复调度和副本不一致。反之cpu流量下去开始缩容,计算过程一样。
同时添加了资源再度均衡的能力,当集群资源变化时,FedHPAController会计算集群总资源、节点碎片、是否可调度等情况,重新分配每个集群HPA的min和max值,确保在扩容时候有充足的资源。
定时扩缩容是指应在固定时间点执行应用扩容来应对流量的高峰期。K8s本身没有这个概念,这里在Karmada控制平面定义了CronHpa资源,并通过Karmada-scheduler对多集群统一调度。避免非联邦化集群在多个member集群创建多个cronhpa。定时功能通过go-cron库实现。
CronHpa流程:
用户根据业务需求,创建CronHPA。定义扩容时间和缩容时间。
到扩容时间点,CronHPAController开始扩容workload。
Karmada-scheduler开始调度,根据每个集群的资源开始合理分配每个集群的副本数。
到缩容时间点,CronHPAController开始缩容workload。
手动扩缩容流程:
用户指定workload,进行扩容或者缩容。
Kamrada-scheduler开始调度,合理分配扩容或者缩容值到每个集群。
指定缩容,这里用到了openkruise能力。如训练模型需要将一批性能差的pod进行缩容。
指定缩容流程:
用户在clusterA 指定workload下面一个pod进行缩容,需要在
ScaleStrategy.PodsToDelete指定pod。
需要在Karmada实现openkurise实现该字段的资源解析,不让它被控制平面覆盖。
并在控制平面更新workload的副本和pp资源,保证副本数和调度结果一致。
member集群的openkruise开始删除指定的pod。
也可以尝试从Karmada控制平面指定删除pod和更改调度的结果,这样更加合理些,也不用添加Karmada资源解析。
Karmada多集群调度主要实现跨集群资源的合理分配和集群故障快速迁移业务。如上图所示主要通过Karmada scheudler和emulator配合实现,其中emulator主要负责每个集群的资源的估算。
workload调度流程:
用户定义workload和策略匹配,生成RB资源。
doSchedulerBinding开始对RB进行调度,通过预选和优选调度算法选择合适的集群,当前不会进行资源计算,和K8s调度预选和优选不一样。
selecClusters根据筛选的集群,进行副本分配。这里有2种模式,主要根据用户配置的策略去选择。
a.Static scheduler 只计算所有资源的request,不考虑调度规则。
b.Dynamic scheudler 会在计算所有request的同时,也会考虑一部分调度规则。
最终计算出每个集群分配的副本数并更新RB资源,调度结束后其它控制器会根据RB进一步处理。
故障调度:
比如当集群clusterA发生故障,在一定判定条件内,会触发Karmada-scheduler重新调度。
Karmada-scheduler会将故障集群的资源,调度到clusrerB和clusterC。
重调度的存在主要解决应用下发到member集群没有真正的运行起来,导致出现这样的情况可能是集群资源在不断的变化,应用正在Karmada-scheduler多集群调度的时候可能满足,但经过member集群二次调度时候无法调度。
重调度流程:
过滤RB资源,发现RB调度没有达到预期。
对workload发起重新调度。
进过预选、优选等流程,再次分配调度结果。
最终将workload的所有pod调度起来。
目前社区单集群的调度估算器,只是简单模拟了4种调度算法。和实际的调度算法有很大差距,目前线上有很多自研的调度算法和不同集群需要配置不同算法,这样估算器的精确度就会下降,导致调度出现pod pending的情况。可以对单集群的调度模拟器进行优化。
使用fake client 去模拟线上集群。
fake client启动k8s默认的调度器以及自研的调度算法,修改binding接口。并配置到每个member集群。
podRequest请求每个集群调度模拟器,运行真实的调度算法,并计算调度结果。
对于通过非联邦化资源管理的应用,不能直接删除在创建,需要平滑迁移到Karmada管理,对于用户无感知。
主要流程如下:
管理员通过容器平台,将需要迁移的应用插入迁移白名单。
用户通过cicd发布,容器平台会进行发布接口调用。
isKarmada模块会查看迁移名单,在白名单内将资源联邦化,接入Karmada管理。不在白名单内保持原有的静态集群管理。
最终完成应用的发布,用户完全无感知。保持2种管理方式并行存在。
有了应用迁移的能力,是否就可以保证整个流程百分百没有问题,其实是无法保证的。这就必须有应用回滚能力,提升用户的迁移满意度。
回滚的必要性总结:
应用发布迁移的过程中发生了未知的错误,并且短时间无法恢复。避免阻塞应用正常发布,需要回滚。
应用被Karmada接管后发生未知的错误,需要避免资源联邦化后无法控制,需要回滚。
回滚流程:
管理员通过容器管理平台,将需要回滚的应用从迁移白名单删除。
并对应用对应的workload以及关联的资源打上注解。
修改exection-controller源码,exection-controller发现以上注解,最终调用update/create时不做处理。
修改defaultInterpreter源码,发现以上注解ReviseReplica不修改副本数。
这样就可以阻断Karmada控制平面和member集群的联系。这里为什么没有直接从Karmada删除资源,主要避免删除这种高危操作以及方便后期恢复后重新接入Karmada。
应用迁移Karmada原则:
先测试、再预发、最后生产
重大变更,分批次灰度,按照1:2:7比例灰度迁移
责任人双方点检验证,并观察监控5~10分钟
灰度后确认没有异常后继续迁移,否则走回滚流程
vivo当前主要通过非联邦多集群管理,结合CICD实现了应用静态发布和管理,具备了应用的滚动、灰度、手动扩缩容、指定缩容和弹性扩缩容等能力。相对于非联邦多集群部分能力不足,如跨集群统一资源管理、调度和故障转移等,在联邦集群进行部分能力的探索和实践。同时联邦集群增加了整体架构的复杂度,集群之间的状态同步也会增加控制面的额外开销和风险。当前社区在联邦集群还处在一个探索和不断完善的阶段,企业在使用联邦集群应结合自身需求、建立完善的运维保障和监控体系。对于已经存在的非联邦化的资源需要建设迁移和回滚能力,控制发生故障的范围和快速恢复能力。
参考项目:
手机扫一扫
移动阅读更方便
你可能感兴趣的文章