被雷劈中

[DRAFT]随便记录一下,以后会补充完整。

夏天到了,天干物燥,不仅人躁动起来,各种“事故”也频发,比如支付宝被挖断光缆,携程无法服务,血淋淋的教训在旁人看来都是饭后的谈资……谁想这样的事情我们也遇到了:IDC被雷劈中,准确的说是我们的云服务提供商的IDC机房被雷劈中了……当写这篇文章的时候,事故已经是昨日的往事,但是我觉得在恢复的过程中也暴露了一些问题。

事情经过

06-06 13:56 展鹏收到DNSPod报警。

06-06 13:57 【青云通知】广东1区所在的 IDC 遭受雷击,导致突然断电。现电力已恢复,青云的物理设备正在重启逐步恢复中。任何消息我们会第一时间通知。

06-06 14:20 展鹏发现线上故障,杨志随即加入修复。

06-06 15:00 【青云】更新通知:因为云平台是个庞大而复杂的系统,在全部物理设备断电重启之后需要Bots系统检查各项状态,然后才能陆续恢复各项业务。这个过程需要60分钟左右,其间大家的服务会陆续恢复。

06-06 16:13 还在睡梦中的我被一阵敲门声惊醒(没错,下午4点还在睡觉),开门一看是我司的产品经理:“青云被雷劈中了,服务都下线了,展鹏让我过来找你!”这才发现手机没电了,以后还是要买诺基亚呀。

06-06 16:20 登录青云后台,发现广东机房所有线上的服务器已经自动重启,但是主系统后台、数据库、ElasticSearch 和 消息队列全部出现异常。

06-06 16:30 【青云通知】全线恢复服务。

06-06 16:31 登录hipchat,拉起来正在线上的工程师,开始远程 skype 沟通。然后,大家开始看不同的模块、不同的服务,期间遇到了系统盘文件系统故障、路由器和负载均衡故障、老的系统无法自动启动等。

06-06 17:13 主系统恢复,数据库、消息队列、搜索恢复。

06-06 18:40 应用系统恢复。

问题

排除青云自己恢复服务期间的时间损耗,我们使用了3个小时左右恢复了所有服务,我现在觉得这个速度是可以再优化,或者说不能接受的,主要几种在以下方面:

  1. 要有一台诺基亚的手机

  2. 遗留系统没人照顾
    系统中存在很多老的项目和服务,随着业务方案的改进,已经处于没有人维护的状态,因为打算这一两个月就把所有的老系统全部切换掉,而且这些系统线上已经跑了很长的时间,期间也没有遇到过问题,所以我就大意了,比如没有设置随系统自动启动的配置,这次的宕机事件还是让这一部分系统的恢复成了最麻烦的事情。墨菲定律说过,会出问题的地方一定会出问题,所以这些遗留的系统一定要首先解决掉。

  3. 没有预案
    遇到问题不可怕,可怕的是大家手忙脚乱,而且线上所有的服务都在重启以后需要检查,那么谁应该负责哪个服务?检查的轻重缓急又应该是怎样的?比如,对我们而言,首先应该保证的是微信的消息回复系统一切正常,那就需要首先去确保周边的数据库、消息队列等都已经自动恢复而且一切正常。这个时候,主系统后台是否能够立马访问不是最重要的事情!大周末的,运营的妹子们也不会上线做任何事情。之前大家会去想怎么让开发更快,部署更快,但是如果遇到问题尤其是重大问题以后的处理方案,是属于都觉得重要,但是没有立马做的事情,起码这次事件以后,我们将会出一个具体的预案和操作手册,以后如果再次遇到类似的问题也不至于分不清轻重缓急。

  4. 项目拆分的太小
    是的,这也是一个问题,由于我们把项目按照具体的职能拆分的比较细,内网的git大概已经有100多个repo,线上在跑而且直接提供服务的也有30多个项目,4种数据库,2种消息队列,广东机房70多台主机实例,那么出问题以后,每个项目都需要检查一遍只能由具体的负责人来去做,这是不合理的。

  5. 没有完善的服务监控系统
    在#4 中提到的问题,服务太多,容易出现被遗漏的情况,如果这个服务很小,而且覆盖的面也很小时,可能出现自认为恢复了所有系统,却几天以后发现忘记的情况。没有一个dashboard来监控所有的服务状态,所以大家只能挨个去看,这个太不科学了!!!起码之前部署的hubot机器人要使用起来,帮助大家来排查基本的错误。

  6. 代码中的疏忽
    主要是代码中一些可以避免的问题,例如有一部分项目,线上服务地址只写了主库地址,导致数据库主库磁盘错误停止服务以后,即使从库升级到主库,服务也无法自动切换,这个属于十分低级的错误,将会在这一周的代码review中改正。

  7. 降级依赖
    每个项目都会集成一堆的系统服务组件,例如异常处理、缓存等,但是这些组件如果出现不可用,应用不能处理异常或者绕过去这些非必要的组件,就会导致部分系统异常。例如,这两周部署了Sentry来捕捉前后端的异常,故障发生之后,Sentry无法启动,其中一个集成Sentry的系统初始化时,当时的写法是无法连接就panic,导致应用启动不起来,但是现在想想才发现,Sentry的服务不应该是系统启动的必要条件,所以应该能够在不可用时绕过去。同理,缓存之类不可用也应该是允许绕过而直接提供服务的。

做的不错的地方

虽然说多了这么多问题,但是之前所做的工作在这次的系统故障中也起到了很大的作用:

  1. 项目模板化
    在我们团队,所有新的项目都采用内部的两套代码生成器来创建Golang项目代码,一来是减少工程师的代码量(业务代码的一致性太高),二来是每个项目都是按照约定来写,起码框架都已经搭出来了,大家也不会写的貌合神离。所以这次出问题,大家都能按照项目的约定和惯用的方式快速定位并做调整。

  2. 自动部署
    从去年新项目启动开始,我们就一直在强调自动部署的重要性,这次故障期间遇到一个业务子系统系统盘故障,与其修复系统磁盘,我们选择直接启动自动部署流程,借助于之前的自动部署工具,十分顺利就完成了部署,系统也恢复服务。

  3. 小团队
    因为我们团队比较小,所以之前任何线上的问题大家都会知晓,虽然没有遇到过一次性所有服务全部下线的情况,但是具体处理起来和平时的错误处理方案八九不离十。

在这样的一个阶段,遇到一次系统集体故障的问题,虽然处理起来比较棘手,但是对于团队和公司也是有好处的,平时大家谁也不会闲着蛋疼把所有系统搞挂然后修一遍,“注意:这不是演习!”,我们看到了故障发成以后暴露出来的问题,接下来也会从事故中总结经验和教训,让系统更稳健,同时团队经历这次“故障”的洗礼,以后在处理问题上也能够心里更有底。

未完待续……