服务器容灾一直是云服务运维过程中无法避开的问题,我们常常会讨论如何对出现故障的机器进行数据库方面的恢复,却很少考虑到在机器出现故障后,是用一套怎样的处理流程将三节点副本集恢复如初的。
MongoDB采用的是什么方法,得以做到在有机器故障的情况下依旧能保证用户业务的高可用?最近举行的“MongoDB Sharding杭州用户交流会”中,针对这一问题,阿里云资深研发工程师果实分享了关于MongoDB 故障服务器如何下线方面的详尽的技术解密。
对于MongoDB数据库来说,MongoDB内核就像汽车发动机,是整个数据库运转的核心部分,而管控就像拼装汽车的过程。车子怎么跑,跑起来的效能如何,运转是否安全,出现故障如何维修,诸如此类的任务都由管控部门负责处理。而保证用户的业务能够达到高可用,则是运维任务的重中之重:
那么,什么是高可用?
MongoDB服务采用三节点副本集架构,三个数据节点位于不同的物理服务器上,分别自动同步数据。副本集提供三种角色,Primary节点(支持读写请求),Secondary节点(支持只读请求),Hidden节点(提供备节点的角色,默认不支持访问)。
而高可用,就是针对这一服务的容灾切换和故障转移的过程。这一过程有很高的自动化程度,通过Primary,Secondary和更多备用节点形成容灾,当Primary节点出现故障,系统会自动选举新的Primary节点。Secondary节点不可用,由备用节点接管并恢复服务,从多个方面保障服务的可用性。这便是MongoDB自身带来的高可用。
高可用的最高境界就是:“容灾故障关我何事?我只要业务ok”——从而做到将最稳定的服务提供给用户。对用户来说,能够看到的是Primary和Secondary两个节点和暴露出的相关访问链接。但在服务器上,同时还存在着另外一个Secondary节点处于Hidden状态,这个节点通常用于数据备份以及性能优化,在主节点出现故障时顶到前方,切换成Secondary节点继续承担用户的工作。
天有不测风云,服务器总会出现各式各样难以排查的硬件故障,极端情况下甚至出现罢工:例如内存ECC异常无法自动修复,硬盘IO异常读写失败,RAID卡状态有问题,电池断电,网卡网络满载等。面对这些形形色色的故障类型,阿里对所有对外服务的服务器都提供了监控,采用监控系统对这些点进行实时采集,一旦发现问题便会及时的进行报警。
此外,诸如服务器到达质保期的换新或者延保,系统升级OS,服务程序漏洞的修复,很多原因都可能导致服务器需要下线。
服务器下线了,用户服务还要接着用,怎样在拿掉机器进行线下升级的同时不影响用户在跑的业务,这就需要交给MongoDB管控团队去应对。
MongoDB用什么策略应对:
MongoDB高可用的实现流程分为以下三个部分:
故障检测:使用多种检测系统针对各种项目进行检测,各个系统中存在联动效应。
故障转移:发生故障后怎样将故障机器上的业务从该机器转移出来。
主机下线:故障机器下线维修以及相应的后续过程。
故障检测:
MongoDB服务集群里有大量不同型号的机器,例如D13、H43。每个服务器上都有与之对应的检测程序,通过大量的Monitor进行监控从而获取信息:无论是内部属于阿里云自己的部分,还是在用户的业务中由用户实现的部分,都存在着与之对应的接口。阿里云会通过推送或自取的方式获取实例并了解服务器的状态,如果获悉某台机器存在下线的必要,资源管理器就会对这台机器进行打标,确认异常后进入下一个阶段。检测和故障转移两个步骤之间并不是直来直去一步到位,其间实际上存在众多细化的检查过程。
故障转移:
对阿里云提供的三节点副本集架构而言,类似机器达到保修期,浪潮D13 RAID卡故障等许多情况下,都需要对任务的Primary节点机器进行下线维护。面对这些情况,资源管理器会对需要下线的机器进行打标,打标后的机器会进行实际的下线。而这些需要下线的机器往往还有业务正在运转。为了保证业务不受影响,MongoDB会借用自身机制,把Secondary节点替换为Primary节点,从而使打标的节点变成Secondary状态,之后会把打标节点从Secondary变成Hidden,即隐藏服务节点。原有的Hidden节点则作为容灾节点被替换上去。
此时的Hidden(打标)节点上依旧存留着实例的数据,不能轻易下掉机器,这里会进入节点重搭的步骤——从资源池里额外再选一台机器生产一个Hidden节点,等新的节点加入副本集后,并且完成三节点同步的情况下,被打标的机器才会被摘下,从而正式进入下线流程,这个过程往往需要一段时间才能完成。况且被打标的机器上面很可能存在多个实例,这台机器上可能不只是某个实例的Primary节点,可能还存在其他实例的Secondary或者Hidden节点,但主体流程是类似的,打标机器上的所有节点最终都会替换成Hidden状态,直到这台机器达到没有任何用户访问请求的效果。
为了不影响用户对云服务的正常使用,整个切换过程都会在用户提供的运维时间窗口里进行。
主机下线:
面对被下线的机器,系统并不会直接草率的将其置于主机资源池中,而是会有24H的滞留期,在滞留期内,监测系统会检测滞留机器上是否还有其它访问请求或IO读写操作。检测结束,确定机器可以下线时才会将其放入主机资源池。资源池里的机器将进入另外一套系统进行后续操作,此时和MongoDB业务已经关联不大。阿里云会通过专用的IDCfree系统对机器进行恢复,待到确定机器没问题后,我们才会重新将其放入资源池内,通过自动上线系统重新加入MongoDB集群,这部分内容由自动资源控制平台来负责。接下来,我们就以实际的故障转移业务场景为例子,阐释关于高可用实现更具体的过程。
故障转移业务场景:
一台副本集出现问题:
故障机器打标确认进入转移流程后,名为Robot的自动运维系统会先获取机器上的实例信息,然后在用户设定的可运维时间内开始正式转移(即使不在用户设定的使用时间内,阿里云依旧会通过短信对用户进行告知)。在判定Role是Primary节点的情况下,先把Primary和Secondary节点进行置换,如果发现已经是Secondary节点,那就进行Secondary和Hidden节点之间的的角色切换。这一步骤通过下发任务流的方式完成,后端完成置换的速度很快,对用户的影响可以忽略不计。当确定故障机器全部变成Hidden节点时,开始触发重搭Hidden流程,将新建的节点加入副本集。此时,有故障的节点已经没有任何实例存在,自动运维平台会将这一空闲的问题机器置于可下线列表中,不再继续进行即时的实例检查。
故障迁移的时候存在一种独特的说法:防风暴,波澜不惊。例如对于阿里云K级别的服务器集群,重搭Hidden的过程中要新生产实例,这其中就很可能牵扯到一些数据恢复和同步的工作,在集群量较小,自建主机机房不够情况下,实例将面临批量的操作。因为每台主机上都存在众多实例,实例的恢复以及备节点的恢复往往会在另外一台主机上完成。由于实际负荷量巨大,此时目标机器便可能存在网络满载,IO调用巨大的情况。
为了平衡这一点,能够让恢复尽可能平缓的进行,阿里云极大的扩充了主机资源池的总量,同时在资源调度的分配上尽可能使每台机器做到均衡。例如,迁移时优先选择负荷低的机器新建Hidden节点,同时将多个任务尽量打散到不同机器上;通过应用资源管理平台进行分析,在资源池水位无法达到预留期望的时候及时补充所需的机器资源——从而尽可能满足资源池每时每刻都能接受更多运维服务和新建实例上云的需要,达到一种整体上低负荷平稳运行的状态。
两台以上副本集出现问题:
如果有两台以上的副本集同时出现问题,则通常不是由硬件故障导致,但在机器集中升级时也存在出现的可能。如果需要下线的机器数量较少,阿里云会优先采用一台台分别下线的方式以减少异常情况发生的概率,但一旦出现必须批量操作的情况,则会跳出原本Switch Role的算法。资源管理器会对所有需要下线的机器统一进行打标,用Transfer流程加以处理。
在该流程中,阿里云会生产一批目标实例(同样规格,用户在使用的实例),把这些实例以Secondary节点的状态加入副本集当中,再将新生产目标实例的Hidden节点和原始实例中的Hidden节点做替换(这一过程对用户是没有影响的)。排除原实例中的Hidden机器后,因为服务中心会提供两个VIP,首先更换Secondary节点提供服务的VIP,将其切换到目标实例的Secondary节点上,再把Secondary节点在副本集的层面进行互换,由原实例的副本集换成新实例的Secondary节点,这时再进行Primary节点的VIP互换,使原实例两个VIP均切换到目标实例上,最后再进行两个Primary节点之间的互换,达到目标实例上的节点变成Primary的效果,从而完整的实现由原实例到目标实例的过渡。此时便可以释放原来的实例,三台机器上所有的实例完成迁移后,共同进入机器下线流程。
同时升级多台机器对用户的正常使用可能会存在影响,因为是迁移而不是角色互换的缘故,VIP切换过程中对用户的影响难以避免。因此,即使这个过程中在运维窗口期完成,阿里云依旧会用短信的方式对用户进行及时的告知。
关于Sharding版本故障转移:
在Sharding版本中同样存在有三个节点。对于CS和Shard来说,迁移和切换和副本集差不多,整体步骤类似,除了因为没有VIP所以省略切换VIP的步骤。而在Mongos一处则略有不同,因为Mongos是一个相当轻量的实例,不存在大量的数据缓存,至多就是本信息或者一些VIP的挂载,在它出现故障时,系统首先会尝试能不能拉起一个新的实例,如果机器被打标下掉,它就会直接到另一台可用机器上创建一个新的Mongos,把VIP切换过来。将新的Mongos节点加入到整个Sharding的副本集中,把原本出现问题的Mongos实例下掉,把故障机器上的Mongos都清空后进入机器下线的流程。
转移流程的管控实现:
在上文中,我们已经阐述了很多具体的Mongo实例切换与迁移流程,而实际上,这类流程在管控中是用一套比较成熟的分布式任务流来实现的。
任务流有几个角色,每个切换和迁移都相当于一个task(任务),由自动管控平台收到后下发,由Task Master(任务调度端)进行整体调度和分发,Pengine Master负责任务步骤调度,Pengine(每一台具体DB节点)完成节点主机操作。
如图所示,Task Master会周期性的获取迁移实例的任务,同时进行最大容量限制与流控相关处理。例如一台机器需要下线,那么机器上运转的数百个实例都要进行调节,此时Task Master并不会即刻切掉所有任务。例如程序不稳定,或者有误操作行为存在时,要下线的实例数目超过了其设定的批量操作阈值,Task Master会立刻向运维人员报警,考虑是否存在人为或误操作的可能,从而达到流控的效果。
经过取舍和排序后,运维任务便会在接下来流入到Pengine Master端,在相关调度下由空闲的Pengine Master接收。Pengine Master会对收到的任务进行基本初始化与步骤调度,考虑是在本地处理还是交给下一层的DB节点。超时机制和监控巡检两大功能可以保证任务正常进行。
作为第三层级,Pengine接受操作命令后,会通过处理任务队列的方式进行具体操作,操作结束后返回对应的Pengine Master,再由Pengine Master根据结果来决定是继续操作任务还是返回给Task Master,以这样的流程来进行不同任务的并发和分布处理。
故障主机下线后续的处理:
确定机器不存在实例并可以下线后,后续的过程一般分L1,L2两个阶段完成:
L1阶段中,管控人员会对撤下的机器进行彻底的排查,并出具报告,确定是否能够修复,可修的机器送进维修系统,不可修的机器通过iclone系统洗白,防止保留历史数据。
L2阶段中,机器会被置入恢复系统(实际的资源机器上线系统),重新安装基础OS模块和引擎基础模板后进行验证,如果验证通过,便将机器重新加入资源池内通过上线系统加入MongoDB集群,如果机器难以修复或过保,则对其进行报销处理,宣布其完成使命。
针对MongoDB或者MongoDB Sharding集群,idc系统会定期针对机器是否可用并进行打标,将打标后的主机放在大盘资源池内。系统会从资源池里检查机器是否应当维修或进行其他处理,如需克隆则交付iclone模块来完成。克隆完毕并确定无异常后,主机会被发送到资源池内部进行服务准备。由部署系统将可上线的机器自动加入Sharding集群,并做好相应的性能监控和配置,再由资源管理器将新机器纳入资源调度系统,重新开始工作。
总览:
最后,让我们来总览一下阿里云对故障机器维护的流程:
运维人员发现机器出现问题或需要检测,通过运维控制台人工操作进行打标,同时可进行批量管理,将所得内容发送给已经work的移山系统,通过用户设定的运维时间进行通盘考虑生成下线计划,同时对破坏性实例进行监控并及时将其迁移。
接下来,自动检测系统(idc,天象系统)会对问题主机进行打标与故障原因筛查,并提供对应的解决方案,将记录置于数据库内,通过自动化运维系统对用户进行及时有效的通知。到达运维时间时,运维系统下发任务,再由资源池控制系统接纳清空的主机进行维修或者克隆,而后完成整套维护流程。这其中最重要的目标,就是在用户体验上的无感知或者无影响。
或许有一天,我们能够实现这样的愿景:
A:听说了吗?这几年阿里云MongoDB主机下线了几批故障机器。
B:我擦,我用了N年了,怎么一点没感觉呢!
而阿里云做到的正是对用户安全;性能和可用性方面的多重保证,对用户用户,关心自己的业务发展和业务功能就足够了,一切就是这么简单。
喂,所以说,没事儿来玩玩MongoDB吧。