万圣节的点滴

自从到西雅图工作来,我所在的组牵涉到了好几次生产环境的事故。
最著名的事故当属2012年2月29号闰日事故。事发当天是我入职第三天,早上来发现全组人都不见了,后来才知道都到楼上某会议室去调查处理事故了。最愚昧的事故当属管理员忘记更新https的证书,导致证书过期后全球不能访问,而恢复个证书又来来回回耽搁了将近整个周末。
今天要说的是我导致的事故。
两年多前我开始接触云服务里面虚机和宿主交流的组件。这个组件是一个跑在宿主机上的低权限进程。宿主机没有托管运行时,所以所有的东西都是用native写的。我添加了一些虚拟机和宿主交流的协议。其中的一些设置是保存在宿主注册表里,我添加了一个通用的从注册表里读取设置的函数。随着功能的逐渐增多,越来越多的地方开始调用和依赖这个函数。
但这个函数在使用完注册表后,没有调用CloseHandle关闭句柄。所以任何人读取一次设置,就泄漏一个句柄。
通常来说,宿主机上的组件会每个月更新一次。更新的时候这个低权限进程也会更新和重启。当进程重启后,泄漏的句柄就被系统回收了。在这样的情况下句柄泄漏的问题一直没导致啥不好的现象。
直到2015年10月。
当时公司要准备一个大会,接下来要备战淘宝双十一,好像还有其它的啥重要节点。大家商量了一下决定把十月份的宿主机上组件更新和十一月的合并到十一月中执行。也就是说十月份这个进程将不会被重启了。
感觉到事情正在其变化,某些正要变傻逼了是不是?
当句柄泄露到一定程度后该进程就不能再创建新的句柄了,很多功能都没法做了。熟悉Win32传统开发的都明白,native错误是通过返回值表达的,不是异常。传统处理方法就是记一下日志然后继续,进程不会崩溃也不会重启。问题的最终表现就是虚拟机和宿主彻底无法通信。当通信停止,宿主检查不到虚拟机心跳包后,宿主会以为虚拟机挂了,然后会对虚拟机进行一些修复工作,比如重启虚拟机啥的。但这根本不是虚拟机问题,就算虚拟机重启后,还是无法和宿主交流,虚拟机的重启会周而复始。
在2015年的万圣节前一天,也就是10月的最后一个星期五,问题爆发了。刚开始有两个集群上的部分服务被中断,原因就是虚拟机被重启,导致客户无法访问。花了一个多小时查出来是句柄泄漏,但还没定位到问题代码。当时公司在开万圣节party,奇装异服的小孩和菇凉们在我门外跑来跑去,有的进来要个糖看我黑脸大口自己在吃糖都被吓走了。在下班前找到问题代码了。我看了一下提交记录,发现已经在生产环境里面跑了半年了。想应该没事啊,不需要去紧急修复,把出问题的集群上的进程重启下就好。这两个集群出问题可能是最近压力大访问多。把问题修复了等下次宿主机组件更新就修复了。所以给老板简单汇报下就回家了,因为晚上还去小明家开party呢。
晚上在小明加打UNO打到一半老板来电话了。我老板以前是混迹win32的老革命,后来变成分布系统的老司机,他说他感觉不太对,问我要不要去公司再仔细扫描下生产环境看到底安全不。我虽还抱着侥幸心理,但军令如山啊。丢下小何婕继续打UNO,我开车去公司开始查。我查我查,我擦,只看了几个环境就想起来乖乖这个月没重启,大家的句柄数量感觉都快到极限值了啊。立刻给老板报告说要坏事,搞不好所有虚机从这个周末开始都要无限自动循环重启了。这种事故堪比闰日事故,后果不可想象。下午出事的两个集群就是雪崩前兆,冰川即将颠覆啊!老板马上也回办公室了,还叫了周同事一起。我们想了想咋办呢,赶紧把所有宿主机上受影响的进程重启吧。云上的操作,一切以安全为重。要上去搞个啥,权限流程一堆,现有的工具也都是从安全出发不让随意并发的。无奈开始撸脚本,然后从我和周同学的工作机器上开始对全球所有宿主机发起进程重启请求。当时很有蓝翔特工的赶脚!我们公式生意还有点大,生产环境上机器太多,我们工作机都推到极限了,也得跑个十几个小时。这十几个小时吧我们还不能闲着,因为连生产环境的口令会很快失效,我们得一直输口令。周六中午老板买来午饭吃了继续干,干到周日早上基本上重启完了。
后来发现,在周末重启过程中,在个别的集群上,因为句柄已经泄漏太多,重启过程还花了挺长时间,最后心跳包还是丢了虚机还是重启了。不过这部分数量极少,而且没有循环重启,所以总的结局还算圆满。
查漏补缺,后来赶紧把句柄数量放入监视范围。当初监视了内存内核线程,为啥就忘了句柄呢?

今天刚过2016年万圣节,纪念该事故一周年。

补充材料:
关于句柄的极限
https://blogs.msdn.microsoft.com/oldnewthing/20070718-00/?p=25963

补充说明:
淘宝的双十一当然不是跑我们云上,但是很多生意是围绕双十一做的,这些生意是跑在我们云上的。比如说商品描述里面的图片视频啊,广告推送啊,当然还有接双十一发钓鱼骗钱的。

此条目发表在Uncategorized分类目录。将固定链接加入收藏夹。

留下评论