图片
从无到有
DevOps,标准先行
在 DevOps 实践中,标准化是非常重要的一环。弹性云的所有机器都是围绕服务树管理的。由于之前是由人工管理,弹性云机器在服务树上的挂载情况非常混乱。因此,为了机器管理更标准,弹性云首先定义了服务树节点标准和规范,将宿主机生命周期与服务树节点进行关联。具体而言:
- 机器上线:机器由 backup 节点挂载到 kube-node-init 节点初始化,然后挂载到线上 kube-node 节点进行报警关联;
- 机器维修:线上机器挂载到 maintain 节点进行维修,维修完成挂载 kube-node-init 节点进行上线;
- 机器下线:容器漂移后,线上机器挂载到 pre-offline.backup 节点进行关机退换。
图片
流程拆解,需求分析
标准定义好后,就是对平台需求进行分析,我们首先将宿主机上线、下线和维修的流程进行拆解,如下图所示:
图片
通过分析拆解后的流程,我们得出机器管理平台至少需要具备以下功能:
- 整个流程耗时较长且为流式任务,所以任务需要异步执行。
- 平台依赖许多第三方服务,需要支持跳过、重试、暂停等功能,以灵活应对下游异常场景。
- 各流程之间有重复的步骤,步骤需要能被自由组合,以提升程序的灵活性。
- 按照 double-check 原则,任务需要以工单形式展现。
架构设计,代码开发
在软件开发中,程序分层是一种常见的软件架构模式,它可以使软件系统更模块化、可扩展和易于维护。需求明确后就可以进入开发流程,从平台的产品形态来看,平台将来的使用流程是:用户创建工单,审批通过后用户执行工单,程序感知到执行动作,执行任务,程序实时反馈任务进度,任务异常时通知用户,用户介入处理,任务执行完成后,工单结束。因此,平台可以分成4层:
- 准入层:用户访问控制,机器准入控制。
- 控制层:工单控制,工单创建、执行、关闭等。
- 调度层:任务调度、分发。
- 执行层:组合步骤,执行任务,反馈状态。
组内服务大多由 go 语言开发,所以此次开发也选择了 go 语言。准入层、控制层可以使用常见的 web 框架,调度层和执行层需要一个异步任务调度框架。
在 go 语言中,优秀的 web 框架很多,但是调度框架却少得可怜。在对比了几个框架后,最终选择了 star 最多的异步任务框架 machinery。但是在使用过程中发现,虽然 machinery 支持任务流、任务重试等功能,但是对任务暂停、跳过等功能缺乏支持,需要再封装一些功能。在经历了一段时间的尝试后,我发现读代码比写代码要难上许多,再加上 go 原生对并发和异步支持比较好,最终决定调度层还是自己实现一个。虽然最终的开发周期有所加长,但是程序的可维护性和可扩展性会更好些。就这样,第一版宿主生命周期管理服务 (mmachine) 在 2 个月后上线了。
图片
宿主下线,维修
在第一版宿主生命周期管理中只上线了机器上线的功能,基本上实现了机器上线由人工到无人值守的转变,但是机器的下线,维修还是由人工支持。
由于历史原因,弹性云部分服务间通过容器 IP 进行通信,所以部分容器无法变 IP 漂移,在机器下线时很难把机器上的容器漂空。人工下线时会把可漂容器漂移走,不可漂容器在联系业务侧进行服务迁移,从经验来看,人工 push 业务服务迁移都要花费很长时间,如果通过程序来 push,那效果比人工也不会好太多。
那么一台机器,要达到什么状态,就认为这个机器可以下线且不会对在线业务造成影响呢?于是我们定义了一个新的标准-1、2、10原则。在机器下线时,对于未漂移容器的集群进行反查:
- 当机器存在副本数小于5且非 running 容器数大于1的集群,机器不可被下线;
- 当机器存在副本数大于5小于20且非 running 容器数大于2的集群,机器不可被下线;
- 当机器存在副本数大于20个且非 running 容器数大于总副本数10%的集群,机器不可被下线。
机器下线时,首先对机器上的容器进行漂移,在正式下线前对机器进行兜底检查,在不打破1、2、10原则的前提下,弹性云会对机器进行关机操作。
得益于调度器设计的灵活性,将机器下线和机器上线的步骤整合后就成为了机器维修的流程(下线-报修-上线),而且机器维修需操作的机器比较明确所以自动化程度也相对较高,几乎不需要人工介入处理,具体策略如下:
- 报修:周一至周五每个机房报修 1 台机器。
- 上线:每周一、三、五扫描维修完成机器,上线机器。
效率提升
在第一阶段弹性云完成了宿主生命周期管理服务(mmachine)从0到1的建设,机器上线耗时也从天级别缩短到小时级别,但是在服务使用过程中我们发现服务还有很多可优化点。
上线流程优化
在第一版的机器上线流程中,所有机器都在一个工单中。然而,一台机器上线失败将会影响整个工单的进度。例如,如果在上线 100 台机器时,有1台机器重装失败,那么其他 99 台机器必须等失败机器修复完成才能继续上线。这严重影响了机器上线的效率。实际上,机器上线并没有相互依赖性,因此可以将上线流程由串行改为并行,以大大缩短单台机器的上线时间。因此,我们重新设计了上线工单,以减少机器相互影响所带来的时间消耗。
然而,如果将上线流程改为并行,那么下游服务也需要支持并行。但在重构过程中,我们发现有两个关键点无法并行处理:
- 机器上线需要进行重装,而重装操作必须在所有机器都完成后才能结单。
- 上线依赖部署系统来初始化脚本,而部署系统不支持单模块的并发操作。
因此,我们与系统部门合作,将每台机器的重装状态暴露出来,并将初始化脚本由部署系统改为使用 Zeus 执行,解决了下游无法并行处理的问题,并将上线流程升级为每台宿主对应一个任务流,各个任务流之间相互独立,经过优化后,弹性云的宿主上线耗时由之前的小时级缩短到分钟级。
备机管理
虽然机器上线的流程得到了极大的缩短,但在真实机器交付的过程中,机器上线只是整个交付过程中的一步。从发现资源不足到机器上到线上的完整流程包括以下几个步骤:"资源不足->申请机器->交付机器->重装机器->上线->调度容器"。当线上流量突增时,通过这个流程补充机器无异于远水救近火。然而,从弹性云的视角来看,交付流程可以简化为"资源不足->加入资源->调度容器",只要能提前完成"加入资源"这一步,就能实现分钟级的资源交付。
通过分析,我们发现系统部门有部分用于日常资源交付的备机。同时,弹性云的离线集群计算资源相对紧缺。离线集群在弹性云中属于隔离集群,而隔离集群和公共集群的本质是通过 Kubernetes 的 taint 特性来控制的。如果备机一直运行在离线隔离集群中,当线上资源不足时,只需移除备机上的 taint,即可将其加入公共集群,从而实现分钟级的资源交付。此外,离线容器对稳定性要求不高,因此当备机需要用于非弹性云业务线交付时也可以快速退回。因此,弹性云与系统部门合作开发了备机管理模块,具体功能如下:
- 自动上线:当备机交付到弹性云备机池后,它们会被自动上线到离线资源池。离线服务检测到有空闲容量自动扩容服务。
- 快速补充:当线上资源不足时,备机管理模块通过修改 taint 将机器转移到弹性云的公共资源池中。
- 快速退还:当系统部门需要备机时,可以快速缩容离线服务并将机器退回。
通过这种方式,弹性云的容量得到了保障,公司的备机池得到了充分利用,备机的利用率也得到了提升。
云上管理
随着云原生在公司的开展,弹性云机器管理也迎来了新的挑战。在 IDC 内,物理机的单次上线数量通常不超过几百台,而云上的弹性伸缩每天需要扩缩数千台虚拟机。IDC 内物理机的上线频率、数量与云上存在显著差异,而且云上资源用于承载核心链路的流量,因此扩容效率也需要更快。随着云上机器数量的增加,问题也逐渐暴露出来:
- 全流程服务有 8 个下游服务,而不同的下游服务具有不同的限速策略。
- 机器初始化时间过长,单次初始化耗时超过 5 分钟。
- 单次上线超过千台机器时,工单可能会卡死。
为了解决限速的问题,根据不同限速策略全流程做了最大限度地兼容。例如:当挂载的机器超过 200 台时,全流程会将机器按每组 200 台进行分组挂载,并确保容器的检查频率不超过 10 毫秒/台。
对于耗时问题,利用云上的镜像功能,将需要部署的服务提前打入镜像中。这样在上线虚拟机时,无需对机器进行初始化,大大缩短了机器上线的时间。然而,这种情况下的机器上线流程与之前完全不同。得益于调度器最初的设计便利性,我们为云上虚拟机上线单独增加了一套任务流,并将上线流程从 23 步精简到 10 步,大幅缩短了机器上线的时间。
图片
对工单卡死的问题,通过排查发现瓶颈主要出现在 etcd 和 MySQL 上。在调度器设计之初,为了确保步骤执行的连贯性,调度器会持续获取未关闭的 MySQL 任务并尝试在 etcd 上获取锁。已获取到锁的进程执行任务,未获取到锁的任务会不断尝试获取。在高并发情况下,这种模式可能导致锁丢失。因此,针对任务,我们在数据库中新增了 isRunning 字段,用于判断任务是否已在运行。对正在运行的任务,调度器不再获取任务列表,也不再尝试抢锁。此外,我们对数据库关键字段添加了索引以减少任务查询耗费的时间。虽然这样做会损失部分任务执行的连贯性,但却换来了稳定性的极大提升。通过一系列优化措施,云上虚机的上线效率也提升到了1.5分钟/千台,满足了日常弹性伸缩的需求。
成本优化
弹性云成本主要集中在三部分,即服务器费用、定价服务费用和人力费用。其中,服务器费用每个月占了总成本的大部分。为了降低成本,弹性云可以采取容器治理和宿主缩容等措施来减少服务器定价支出,并降低对弹性云平台服务器的投入。弹性云机器资源可分为三类:线上实际使用资源、线上 buffer 资源和低负载(闲置)资源。通过对这些资源进行优化,有效降低了弹性云机器方面的成本。
图片
机器下线加速
经过之前的优化,弹性云机器的上线耗时已基本符合预期。由于机器下线和各业务的稳定性相关,下线耗时仍处于一个较高的水位。随着各类成本优化项目的开展,弹性云在2023年预计退还大量机器,但根据以往的经验来看,完全退还这些机器可能需要约2年时间,这个速度显然无法满足需求。机器下线耗时较长的原因主要有两个:
- 机器下线时需要按机器串行漂移容器,每台宿主机器漂移容器约需10分钟,每天最多可漂移50台机器。
- 弹性云存在一些容器无法漂移,并且还存在许多无主容器。在机器下线时,这些无主容器没有人处理,由于不清楚其影响范围,这些机器也不能轻易关机。
为了提升容器漂移速度,我们对下线流程进行了优化。将漂移方式由“按机器串行漂移”改造为“按集群并行漂移”,在不打破1、2、10原则的前提下尽可能地漂移容器。在确保稳定性的前提下,容器漂移速度由每天50台提升到了每小时100台。针对无主容器,我们与 SRE 及各业务线同学合作进行清理,并对长期无人处理的容器制定了下线标准,即:
- 每天向负责人发送通知,连续三次通知后,对不符合兜底策略的集群负责人进行拉群通知。
- 在拉群通知后观察一天,对仍未处理的集群,关闭对应宿主机并观察三天。
- 如果在三天内用户反馈异常,我们将在一小时内对容器进行恢复操作。如果三天后仍然无人反馈,将进行机器退还操作。
通过新的漂移流程和下线标准,可以有效提升弹性云机器的下线宿主,同时也能确保各优化项目的稳定推进。
图片
低负载资源治理
弹性云2022年低负载时间大于10天的机器存在不少,优化低负载机器的数量能降低部分弹性云成本,低负载资源在弹性云可分为以下两类:
- 预期内低负载:新机房交付还未投入使用、控制面机器利用率较低等
- 预期外低负载:机器交付忘记上线、测试后忘记退还等原因导致机器处于闲置状态,部分机器过保、维修后无法上线且未进行置换、退换等。
全流程为此开发了闲置资源管控模块,自动对闲置机器操作人进行通知。指定用户可对预期内低负载机器进行豁免,而长期未豁免的机器将自动退还。这样可以最终消化存量的预期外低负载机器并减少新增情况。
总结
在弹性云的发展过程中,宿主机的生命周期管理是一个重要的问题。最初,机器的上线操作是手动进行的,导致上线速度缓慢。
为了解决这个问题,弹性云开发了宿主生命周期管理平台(mmachine)。通过不断优化和改进,mmachine 缩短了机器上线的耗时,提高了效率,并通过定制下线标准、并发漂移等方式,缩短了机器下线周期,在保障稳定性的前提前下,加速退还弹性伸缩、机器置换等项目节省下来的资源。通过自动报修、低负载治理等模块,mmachine 将资源充分利用起来,提升了弹性云平台的整体资源利用率,降低了弹性云服务器的成本。