大规模机器集群-故障自动处理(二)

发布于 2019-12-29  约 23 分钟

本篇开始介绍具体的实现过程,为表述方便,先定义一些名词,

  • AutoRepairSystem: __故障自动维修系统, 缩写为ARS
  • 原子操作:_任务的最小操作,机器任务通常是指重启、重装_
  • 运维人员:运维工程师= SRE = OP,系统工程师 = sys
  • 远程管理工具: 远程控制操作物理机器的工具,如ipmi、ilo

先来看ARS的整体视图和流程图,

ARS的工作流程,

  1. 故障检测: 每5分钟发起一次故障检测,获取当前时刻整个集群的故障机器列表,推送到工作流子系统
  2. 安全策略: 遍历故障机器列表,依次执行安全策略,过滤不符合要求的机器,得到一个可安全执行重启、重装的机器列表
  3. 服务离线: 遍历可安全操作的机器列表,执行服务离线
  4. 故障维修: 服务离线后,发起重启、维修操作,轮询机器状态,直至重启成功或维修完成
  5. 环境初始化: 执行环境初始化,保证机器环境符合业务需求
  6. 服务上线: 恢复服务,检查服务达到可服务状态,流程结束

接下来将介绍工作流子系统,这是所有具体操作任务执行的基础;

再依次介绍上述流程中的关键环节: 服务上下线,故障检测,安全策略,维修工具及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要介入具体的维修逻辑,无异于“揽屎上身”,最终陷入泥潭里无法自拔。

我们的思路是: 对外提供一套机制,能简易地将维修逻辑嵌入工作流子系统,实现步骤如下,

  1. 将复杂任务拆解成多个原子操作,每个原子操作实现为一个python方法,返回值格式固定
  2. 定义原子操作的执行顺序以及分支

只要满足上述条件,系统就能支持任意数量、任意顺序的原子操作集合。

原子操作的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机器的宕机自动处理流程为例,_(这里为了方便表述,做了简化)_

  1. 检测宕机的机器
  2. 重启机器
  3. 如果能起来,检查程序版本,启动web 服务,流程结束
  4. 如果不能起来,则报修硬件故障
  5. 如果能修复,回到第3步
  6. 如果不能修复,则检查是否过保,如果是,则下架机器,流程结束

其流程树的配置如下,

可以看到,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日常处理的故障。

image.png

ARS主要覆盖了机器层、系统层,下面分别做说明。

磁盘故障

磁盘故障率高的业务类型很多,如hadoop、索引服务、分布式文件系统服务、机器学习模型训练服务等,这些服务的机器,磁盘块数最高多达36块,大量读写磁盘,造成磁盘故障率很高。

常见的磁盘故障类型有掉盘、读写错误(Input/Output Error)、漂盘、挂载错误、read-only错误、性能剧降(ls /disk/超过10分钟无反应);

磁盘故障的积累,有可能会导致数据丢失,以及拖慢整个系统的性能,所以要尽早检测到尽早处理。

宕机故障

宕机故障分为完全死机,假死。

完全死机(指连续3个小时失去心跳,并且主动ssh 探测失败的机器),这种情况容易处理,直接进入自动重启流程;

假死,有如下类型,

lConnection timed out

lConnection closed by remotehost

lConnection reset by peer

lConnection refused

lConnection 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等;此类操作往往不可逆且无法暂停,所以需要严格的安全策略保证机器操作不影响线上服务或影响最小。

经过“故障检测”这个环节后,得到一个当前时刻所有故障机器列表,安全策略会对这个列表进行分析过滤,下表是我们使用的安全策略列表,

image.png

这个安全策略表,是总结分析多个业务线的历史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个安全策略组成的过滤链,并支持低成本的增加新策略,保证自动化任务是安全的

在本文中,有一个重要的事项没有提到,就是环境初始化,这个再下一篇文章讲述。


排列文字,重组感受。

我是曲行人,日常写码,闲时写点儿文字,

如果你觉得有点意思,或者有点用,可以关注我,

我将在大脑里的思维原子做布朗运动时,输出文字。

公众号: qxren7

二维码: 

阅读 233发布于 2019-12-29

推荐阅读
目录