大规模机器集群-故障自动处理(二)
本篇开始介绍具体的实现过程,为表述方便,先定义一些名词,AutoRepairSystem: 故障自动维修系统, 缩写为ARS原子操作:任务的最小操作,机器任务通常是指重启、重装运维人员:运维工程师= SRE = OP,系统工程师 = sys远程管理工具: 远程控制操作物理机器的工具,如ipmi、ilo先来看ARS的整体视图和流程图,
ARS的工作流程,故障检测: 每5分钟发起一次故障检测,获取当前时刻整个集群的故障机器列表,推送到工作流子系统安全策略: 遍历故障机器列表,依次执行安全策略,过滤不符合要求的机器,得到一个可安全执行重启、重装的机器列表服务离线: 遍历可安全操作的机器列表,执行服务离线故障维修: 服务离线后,发起重启、维修操作,轮询机器状态,直至重启成功或维修完成环境初始化: 执行环境初始化,保证机器环境符合业务需求服务上线: 恢复服务,检查服务达到可服务状态,流程结束接下来将介绍工作流子系统,这是所有具体操作任务执行的基础;再依次介绍上述流程中的关键环节: 服务上下线,故障检测,安全策略,维修工具及SLA;然后通过一个线上例子,说明整体的工作流程;最后分享系统上线后的运行数据。2.1 工作流子系统工作流最基本的功能,是驱动一系列预定义任务顺序执行,达到明确的结束状态;在机器故障自动处理这个问题域里,对工作流还有闭环、扩展性的要求(详见第一篇的分析).经过分析统计机器相关的操作任务, 比如机器重启、重装、初始化环境、启动/停止服务、查看信息等,抽象出机器操作的任务模型,即”对一组机器执行相同的任务,且任务可以进一步拆分为一系列更小的原子操作组合”,如图所示,
上图表示对一组机器执行相同的任务,下图表示,这个任务具体有4个原子操作。由此,我们可以定义工作流的几个关键类,以及他们的关系,
注:为了简化表述,这里只列出和流程执行、任务分支、通用性相关的字段和逻辑,工作流子系统的完整信息,后续会另写文章介绍。Job,定义了任务类型以及要操作的目标集合Task,定义了一个具体的操作目标,以及action_tree的root节点Action,定义了业务逻辑的内容和加载方式Scheduler, 调度Job的运行Monitor, 监控 Job、Task、Action 的状态Executor, 控制Job下的task/action 的执行顺序、并发等接下来重点看看工作流系统是如何达到前文提到的扩展性、闭环要求的。第一点,扩展性。
扩展性需求,最初来自于不同服务上下线操作的差异,主要是有状态服务。它们之间的差异,体现在操作步骤的数量和顺序不同。例如,推荐模型服务,要求先寻找可用的机器资源,在新资源上部署相同版本的服务,启动服务加载数据,判断数据加载进度,直到达到某个阈值,才算是完成“迁移”,此时才达到可维修的状态Docker服务,相对简单,只需向docker发起迁移命令,等待docker返回迁移进度,迁移完成后即可维修Hadoop服务,主要痛点在磁盘故障上,要求维修过程中不能长时间停服,所以维修逻辑很复杂,要先停止本机服务,umount故障磁盘,启动服务,维修故障磁盘,修复之后再停服,起服,让Hadoop重新使用这块磁盘其他无状态服务相对简单,通常直接维修即可可见,不同服务的差异化是不可穷举的,如果ARS要介入具体的维修逻辑,无异于“揽屎上身”,最终陷入泥潭里无法自拔。我们的思路是: 对外提供一套机制,能简易地将维修逻辑嵌入工作流子系统,实现步骤如下,将复杂任务拆解成多个原子操作,每个原子操作实现为一个python方法,返回值格式固定定义原子操作的执行顺序以及分支只要满足上述条件,系统就能支持任意数量、任意顺序的原子操作集合。原子操作的python实现如下图所示,
action1为原子操作名字,do_hard_work()方法由业务sre 完成,工作流子系统只负责调用, is_succ表明本次操作是否执行成功,result通常是操作结果信息。只要按照这个约定编写的任务,都可注册到系统里被执行,哪怕提交人只是用python 包了一坨 shell 脚本,也是可嵌入系统的,虽然我们在review的时候会“建议”他重写。有了原子操作的实现,就可以定义它们的执行顺序,我们使用了“树”的概念,如下图json配置示例所示,
可以看到,整棵树有多棵子树组成,每棵子树指向一个nodes list,每个node就是一个action, action的数量和顺序可以在nodes list里任意配置扩展。在example_trees里, action1~action6就是原子操作,执行的顺序有两种可能分支,action1-> action2 (true)--> action3->action4->结束;action1-> action2 (false)--> action5->action6->结束;假设现在业务有一个大的改动,需要在action2之前增加一个操作action7, 并在action6之后,增加一个分支action8, 这只需在配置上小改动即可实现,
example_trees 会被保存在Action类的action_definition字段里,这个配置记录了执行逻辑的python 文件,类和方法; 工作流在运行时,会动态加载相应的类,根据方法名调用方法,如下图所示,
凭借这些特性,业务sre可以灵活多变的定义自己的任务树,其中公共部分,由平台sre编写,与业务相关部分由业务sre编写。第二点,闭环。以无状态的 web机器的宕机自动处理流程为例,(这里为了方便表述,做了简化)检测宕机的机器重启机器如果能起来,检查程序版本,启动web 服务,流程结束如果不能起来,则报修硬件故障如果能修复,回到第3步如果不能修复,则检查是否过保,如果是,则下架机器,流程结束其流程树的配置如下,
可以看到,reboot_host、check_host_alive、repair_host等action为原子操作;这棵树有两个分支节点,如果 reboot_host之后 check_host_alive为Ture, 则执行online_service 分支,流程结束;如果为false, 则执行repair_host 分支,如果能修好,则回到 tree2 ,最后也达到 online_service的状态, 流程结束;(只要是没过保,都能修好)如果修不好,那么则进入 off_rack 下架流程,流程结束。(通常是机器过保)这里之所以反复强调任务分支,是因为有了任务分支,就可以在各个可能执行失败的环节,指定下一步的操作,最终将目标操作到一个可预期的状态(机器要么被修好重新投入使用,要么修不好被下架),形成闭环,不用人工介入,真正提高自动化程度。同时,由于在一开始就设定了维修只有两种操作:重启,重装,这两种操作都由sys来保证交付时间,所以这棵树能保证流程是闭环的。在ARS上线之前,早期的自动工具发起重启命令之后,机器起不来,通常是人工通知sys 报修,报修之后 sys 再根据机器是否过保来给sre 反馈维修状态,这个过程,如同黑洞,吞噬了rd-sre-sys-机房外包四方大量的沟通时间, 如图所示,
工作流子系统还涉及状态机、并发控制、重试、任务重入、超时、执行进度等,后续另写文章介绍。在下一节里,将介绍故障检测、安全策略等内容。2.2故障检测故障检测的完整性、正确性是故障维修自动化的前提。通过分析历史机器故障类型,可将故障分为5个层次,如下表,基本覆盖了sre日常处理的故障。层次异常类型常见问题检测方式业务相关执行异常磁盘空间不足、部署时调用的control脚本返回值异常、目录权限Falcon运维系统平台自身异常账号异常、Executor执行任务异常、部署系统异常Falcon+运维系统接口基础环境依赖异常环境异常(依赖库/文件缺失)、版本不符(内核/python/perl)、limits.conf不符Falcon+shell/python系统层读写异常文件系统错误(Input/Output Error)、文件系统挂载错误(read-only、home未挂载)Falcon+shell/python机器层连接异常硬盘故障, 宕机故障, 内存故障, 电源故障, 风扇故障, CPU/GPU故障ping/ssh/Falcon+ipmiARS主要覆盖了机器层、系统层,下面分别做说明。磁盘故障磁盘故障率高的业务类型很多, 如hadoop、索引服务、分布式文件系统服务、机器学习模型训练服务等,这些服务的机器,磁盘块数最高多达36块,大量读写磁盘,造成磁盘故障率很高。常见的磁盘故障类型有掉盘、读写错误(Input/Output Error)、漂盘、挂载错误、 read-only错误、性能剧降(ls /disk/ 超过10分钟无反应);磁盘故障的积累,有可能会导致数据丢失,以及拖慢整个系统的性能,所以要尽早检测到尽早处理。宕机故障宕机故障分为完全死机,假死。完全死机(指连续3个小时失去心跳,并且主动ssh 探测失败的机器),这种情况容易处理,直接进入自动重启流程;假死,有如下类型,l Connection timed outl Connection closed by remotehostl Connection reset by peerl Connection refusedl Connection closed by这些假死状态,可能会造成业务受损。比如机器假死,服务端口还能连接,但实际业务进程内部无法正常工作,如果是前端web机器出现这种情况,会导致业务5xx监控飙升;此时,想手动重启,ssh已经无法连接,只能通过ilo重启,或者紧急联系机房,处理耗时往往超过半小时。内存故障内存故障时,通常机器还没有死机,(在/var/log/message 里显示CE error on CPU#1Channel#2_DIMM#1)rd认为机器还能跑,不愿意停服务;如果积攒到多台机器出现类似错误,极有可能在短时间内出现连续死机,导致服务容量突然减少,服务性能大幅下降的业务故障,所以对于一些敏感服务,出现这种故障,还是要当作死机来处理。电源故障双电源是突然断电、市政施工的保障,如果电源坏了不修,在这种情况下,机器会断电关机,如果积攒多了,服务容量会突减,影响业务。风扇故障不会马上造成死机,但是会产生连锁反应。风扇故障会导致cpu温度升高,引发死机。上述故障检测的实现,主要是通过 Falcon监控系统 + scripts 实现,涉及了 ping/ssh/ipmi/dmesg/proc/sar…等大量系统命令和系统信息。Falcon 运行这些scripts,检测故障,外部应用就可以从接口里查询故障列表信息,如下,
ARS从Falcon拉取当前时刻集群内所有故障机器的列表,附带了相应的故障信息,推送到工作流里,进行维修。2.3 安全策略对机器的操作,通常是重装、重启、root环境修改、部署基础agent等;此类操作往往不可逆且无法暂停,所以需要严格的安全策略保证机器操作不影响线上服务或影响最小。经过“故障检测”这个环节后,得到一个当前时刻所有故障机器列表,安全策略会对这个列表进行分析过滤,下表是我们使用的安全策略列表,策略作用&应用场景实现filter_bw _lists黑白名单;通常用于敏感服务,如支付、隔离环境服务只处理白名单内的机器跳过黑名单内的机器filter_alive_hosts过滤掉处于非死机的机器;防止误判,重启了非死机的机器1、3分钟内连续ping机器,如果有响应,则过滤机器2、过滤可以响应 ssh 请求的机器3、防止网络抖动误判,在多个机房 ping,交叉验证filter_switch_fail防止交换机故障引起误判,比如批量机器无法联通按分钟统计机器的故障时间,如是同一分钟内报上来的(falcon采集周期是1分钟),本次就不会向后端推送任何机filter_base_agent过滤掉基础agent端口存活的机器;某些特殊服务会禁止ping/ssh命令,所以通过基础agent端口来判断存活通过 telnet/curl ip:port 判断基础agent是否存活,基础agent存活代表机器存活,过滤机器filter_running_service_hosts过滤掉有服务处于running状态的机器;防止误判,操作了服务还在运行的机器检查机器网卡流量、磁盘io等指标,超过阈值则认为存在服务,过滤机器filter_capacities根据服务容量过滤;防止因操作了机器,导致服务容量不足计算服务容量, 当前running实例数/总实例数 < 阈值(90%),过滤机器filter_duplicate去重,保证同一时刻同一台机器只有一个操作任务在执行;防止多个任务叠加到同一台机器,出现未预期结果遍历系统所有任务,如果此机器有处于running的任务,跳过此机器filter_pattern过滤指定patterns的机器;这是最严格的过滤器,通常是单点服务使用此策略对机器的机器名、节点名、运行服务名、在各类配置中心注册名进行正则匹配,如果匹配,则过滤机器filter_threshold阈值保护,保证同时进行的机器操作任务数低于允许维修的数量1、按机房粒度,对于不同的任务类型,如果当前此机房的机器操作任务数大于阈值,跳过此机器2、按服务(app)粒度,如果当前此服务(app)的机器操作任务数大于阈值,跳过此机器例如这个例子,当前app1任务数是3,而允许维修的阈值是5,所以只能再发起2台机器的维修current_repairing/beijing/app1: 3threshold /beijing/app1: 5filter_by_date遵循分级发布原则,在一个星期内的某一天,只能维修对应机房的机器1、指定每天允许维修的机房2、遍历所有机器,如果一台机器所属的机房不是当天允许维修的,跳过此机器这个安全策略表,是总结分析多个业务线的历史case study得出来的, 在线上运行以来,未出现过误判,保证了自动任务的安全性。每一个安全策略,实现为工作流里的一个原子操作,即action,结合上述重启的例子,json配置如下,(维修的流程也可以使用这些安全策略,这里不再单独列出)
这些策略,可复用也可自由组合、调整顺序,这对于接入不同业务的机器进行自动维修,有很大的便利性和灵活性,同时降低了接入成本。如果业务有自己的安全策略需求,只需按照上述的action 方法规范,自己写一个安全策略方法,在配置里指定即可使用。2.4 维修工具及SLA机器硬件故障维修,是真实世界中的事件,这个过程需要人去到机房现场,从仓库拿出配件,走到机架旁边,拆卸机器,装配硬件。所以这个环节是“不可抗力”产生的地方,比如配件备货不足;节假日厂商人员放假,无法赶赴机房;赶上两会,机房封禁,不让进入等各种问题。1 交付时间为了达到流程闭环,我们(甲方)和机器厂商(乙方)约定机器维修交付时间, 通常是36小时交付(不同公司、厂商可能不一样),至于怎么解决上述“不可抗力”,由乙方负责。2 远程管理工具可用率远程管理工具是机器操作自动化的必备工具,reboot_host/repair_host底层调用的就是ipmi;为了尽可能地减少机房现场人员操作,我们要求sys保证远程管理工具可用率达到 99.9%,比如,ilo,ipmi有了这两个SLA,我们可以认为 reboot_host、repair_host 这两个原子操作的最长耗时为36小时,所以维修流程是一定可以闭环的,避免了因任务中断导致的人工介入。当然,有了这些,也只是修复了硬件,还有系统参数设置、环境初始化、基础agent的问题,这个内容比较多,在下一篇讲。将上述提及的技术细节汇总,得到ARS的完整视图,
最后,看一个自动重启的例子,
可以看到任务树定义的actions 是怎么执行的,先是执行一系列的 filter_*安全策略,然后屏蔽报警,执行服务离线,发起重启,然后轮询机器状态,直到任务结束。2.5 系统运行数据ARS上线后,覆盖数万台机器的故障自动处理,死机数量保持在10台左右,所有硬件故障总数量保持在100台以下,这对于一个数万台机器的集群来说,是非常理想的状态了。人力方面,对于20人的sre 团队,机器故障只需要 0.5人力维护系统正常运转,例如新服务的接入、业务要求紧急修复之类的情况;当机器规模增长时,人力并不需要相应的增加。2.6 总结最后,总结一下几个关键点,标准,定义了有哪些类型的故障,什么故障执行什么样的修复,修复的标准流程闭环,对于机器的操作,用任务分支覆盖操作成功或失败的情况,用SLA约束厂商在约定时间内交付机器,保证流程可达到明确的结束状态,避免人工介入安全,10个安全策略组成的过滤链,并支持低成本的增加新策略,保证自动化任务是安全的在本文中,有一个重要的事项没有提到,就是环境初始化,这个再下一篇文章讲述。