5 月故障总结(post-mortem)

14 年 5 月,友盟的几个核心产品由于各种因素的影响,对外表现了一些不稳定的现象,我作为最主要的当事人之一,有不可逃避的责任,为了给开发者一个交代,我总结了这么一篇 post-mortem。目前对外发布的版本出于迎合普通工程师的口味,我们的运营阉割了部分内容,除此之外,我们还做了一个给纯小白用户看的时间轴图片,更加的简介明了,说是《友盟万兆网络升级日记》并不能概括这段事件发生的事情。下面这个是我写的一个未经删改的 post-mortem,透露的内容还是很多的,同时技术性也更强。该版本基本的思路是参照 github, cloudflare 的 postmortem 套路。


上个月(2014 年 5 月)的 13 日至 27 日,包括我们统计分析、在线参数在内的服务出现了间断性不稳定的情况,给热爱我们产品与服务的开发者带了很大的不便,为此我们深表歉意与自责。

未经缜密计划安排的停机维护时间对我们来说在任何时间段都是不被允许的,作为一名全程参与整个事件的工程师,我非常乐意给我们的开发者解释一下造成我们这一段时间服务不稳定的原因,为此我们做了什么以避免今后类似的事件再次发生。

背景

2011 年 7 月我们开始在全国最好的数据中心之一部署上架了我们的第一台服务器以及交换机,当时的网络架构还是简单的 2960G trunk 模式。

转眼,到了 2012 年第三季度,我们这套简易的方案在处理内部流量时出现了比较吃力的问题,于是我们在 2012 年年底完成了我们三层网络架构的升级,有兴趣的可以看看我们工程师当时总结该次升级的博客(1, 2, 3, 4)。

在接下来的一年多时间里面,我们的业务出现了爆发性的增长,带宽也由最初的几十兆飙升了目前的数 G,在 2013 年年底,我们决定对我们部署着统计分析等服务的核心数据中心再一次进行升级,这一次,我们将由原先的 3750X 三层堆叠架构升级到 Cisco 最新最强大的万兆 Nexus 752 架构以支持更高的吞吐、更低的延时以及更好的服务器网卡冗余。

2014 年年初到 3 月底的这段期间,我们的工程师马不停蹄的进行着大量的前期准备调研工作,包括我们目前线网的梳理确认、综合布线、网络设备的上架调试、新老网的设备互联以最大程度的减小停机时间。

进入 4 月以来,我们又紧锣密鼓的讨论制定了各种潜在风险以及应对措施,回滚计划、网络以及业务连通性监控确认等方案。最终决定,一共分为 4 次常规性的迁移,将数百台服务器由老网迁移到新网,1 次互联网入口/出口流量以及网关的迁移,前后历时一个月。

4 月 03 日晚上 08:00,我们的内网由于延时过高,导致了我们实时处理的时间过长,触发了 Storm 的 timeout 机制,最终导致了我们的实时处理延迟了 10 小时,这也是第一次向我们的开发者发出由于网络原因造成数据延时的通知。为了加快进度,我们决定将原先定于 5 月初的第一次升级提前到 4 月末进行。

4 月 24 日凌晨,我们进行了第一批服务器的迁移,在我们开发默契配合下,完美的完成了此次迁移。在后续的观察中,我们发现新老网(3750X – Nexus 7000)之间的 4G 链路带宽利用率可能会比较高,于是我们紧急调度了我们供应商的资源,在 4 月 28 日将这条关键的链路扩容到了 8G 以提高更大的带宽。

4 月 29 日、5 月 8 日,我们又分批顺利地完成了第二次、第三次服务器的迁移。


这段时间发生了什么

截止到 5 月 8 日为止,我们很顺利了完成了前三次的服务器以及网络升级,对我们的开发者没有造成任何的影响。

5 月 13 日凌晨 4:00,我们开始了最后一次常规性的迁移。由于涉及到我们前端 web server 以及后端的众多服务,我们计划了 5 分钟的停机不可用时间,5 月 13 日早上 8:00,我们本次的迁移顺利结束。

在经过短暂的休息之后,我们的工程师于 5 月 13 日早上 11:30 全部回到公司准备接下来原计划定于 5 月 15 日凌晨的最后一次也是最重要的一次迁移。到了 5 月 13 日下午 2:23,我们的工程师发现,在迁移完我们所有的设备之后,统计分析线上的 Storm 集群等实时处理服务并没有得到质的提升,尽管最终的 log 没有丢,但是数据出现了延时。

5 月 13 日下午 2:49,我们讨论分析并且发现了出现目前问题的原因:由于我们的网关依然在老网的3750X 上,所有新网下面的服务器如果要访问其他 VLAN 的服务器,势必要先经过链接新老网的 8G port-channel 链路,然后返回到老网的 3750X 网关上,然后再通过该设备将流量路由到对应的 VLAN,最终再回到新网。由于中间这条 8G 链路流量分配算法的问题,导致其中一条链路的带宽被完全打满,从而导致 6%+ 的丢包,最终导致了数据的延时。为此,我们做了下面的一些工作:

  1. 确定修改流量分配算法带来的风险以及应对措施
  2. 重新梳理业务,尽量介绍跨网段/VLAN 的访问
  3. 如果丢包率进一步恶化,我们会提前启动最后一次的网关迁移


从上面这张图可以看到,我们的一条物理链路在当时被打满

5 月 13 日下午 15:37,我们将实时计算的节点重新划分,减少了跨网段的访问,处理速度得到了明显的上升,由原先的 210M/min 达到了 340M/min。

5 月 13 日下午 16:53,为了告知开发者,我们在后台发出了第一条信息,通知我们的开发者目前的情况以及最新的进展。

5 月 13 日下午 17:17,经过讨论,为了避免数据进一步的延时,以及中间那条 8G 链路等不稳定因素的存在,我们决定将原先计划在 5 月 15 日进行的最后一次迁移提前到 5 月 14 日的凌晨 3:00 开始进行。由于要切换网关,会导致内网 10s 左右的短暂不可用状态。

5 月 13 日晚上 21:45,由于晚上高峰期的因素,导致我们那条 8G 的链路负载进一步加重,加上我们 SDK 重传的机制,导致我们入口的丢包达到了 50%+,我们当机立断,调整了链路的负载均衡算法。
5 月 13 日晚上 22:07,服务恢复,前端丢包降为 0,8G 的负载链路也以比较均匀的方式进行流量的接受发送。

5 月 14 日凌晨 03:00,我们工程师在经过短暂的休息之后,准备开始最后一次的网关迁移以及我们上联入口/出口流量的迁移。

5 月 14 日凌晨 04:00,我们正式开始了我们上联互联网入口/出口的流量迁移,这个时间点也是我们全天流量最低的时刻,10 分钟后,我们顺利的将原先放在老网的两个上联接口迁移到了新的 Nexus 7000 设备上,再经过 10 分钟的验证,我们确认了网络层面,应用层面以及业务层面没有问题之后,我们开始了最重要的网关迁移。所谓网关迁移,就是要将我们原先在老网 3750X 的网关迁移到新的 Nexus 7000 的两台设备上,这样,以后所有跨 VLAN 的访问,服务器都会通过 Nexus 7000 进行路由,而不必再绕回到老网进行路由,真正的实现了所有机器的流量迁移。

5 月 14 日凌晨 04:20,我们开始了网关的迁移。我们的工程师之间配合操作,同时在新网的数台 Nexus 设备以及老网的 3750X 上刷上新的配置,凌晨 04:25 全部刷完之后,我们发现了部分服务器的跨 VLAN 访问出现了问题,并且我们的 VPN 服务器等其他的服务器也都不同程度的出现了网络访问失败的情况,在现场进行了很短暂的简单 debug 之后没有发现问题的原因,我们决定在凌晨 04:30 执行回滚。

5 月 14 日凌晨 04:40,所有的回滚操作均已执行完毕,到凌晨 04:50,经过多方确认,我们的服务恢复正常。接下来,我们一部分工程师回酒店休息,剩余一小部分工程仍在现场模拟当时的情况,查找的问题的缘由。凌晨 05:49,我们发出邮件告知所有参与者,这次升级不是很顺利,仅仅完成了一半的任务,目前服务已经回滚,恢复正常。

5 月 14 日中午 12:30,我们的工程师在短暂休息之后都已回到公司,讨论网关迁移失败的各种原因已经解决方案。接下来的下午时间,我们紧急模拟出来了一套跟线上几乎一样的环境进行测试,用尽各种手段,验证我们的种种猜想,最有可能的 arp cache 的猜想也被我们的实践所否决。到此,我们并没有得到很好结果,鉴于这次升级预留的时间太过紧凑,根本没有时间在线上 debug,我们决定在 5 月 15 日 03:30 开始再操作一次,这次,我们将停机时间由原来的 10 分钟不到扩大到了 2 个半小时。这也是我们开发者在 5 月 14 日下午 17:51 收到第二封系统通知的原因。

5 月 15 日凌晨 02:00,我们的工程师早早的来到现场准备最后一次的「背水之战」,凌晨 03:30 在检查对比了若干遍的网络配置之后,我们在 5 分钟之内,将老的 3750X 降级删除了网关,新的 Nexus 7000 升级加上了新的网关。同时,我们的另外一拨工程师也在线上直接插上示器在几台关键的服务器上进行 debug,跟预期一样,确实有部分的服务器到其他 VLAN 的不通,经过 20 分钟的高强度奋战,凌晨 04:10,我们的终于发现了问题的所在。

5 月 15 日早上 06:24,网络方面的问题都已经得到解决,线上的服务全部恢复。至此,我们网络升级暂告一段落,工程师们全部回酒店休息。

5 月 15 日晚上 20:35,我们前端 web server 到后端的 upstream 突然出现了大量的 timeout,入口的流量翻了一番,upstream 的数十台服务器均出现不同程度的 JVM FD(file descriptor) 暴表,造成这段时间内日志/数据量暴跌。


部分机器的 FD 已经达到了 250K

5 月 15 日晚上 21:00 我们最初判断是后端的 upstream 的服务器出现了瓶颈,于是紧急扩容了数十台服务器,情况得到了好转。截止到深夜 23:00,服务已经全部恢复,问题出现的原因已经有了一些头绪,但是未能完全梳理清楚。

5 月 16 日晚上 20:00,在平静了一天之后,我们机器再次出现跟昨晚一样的现象。这次,我们深入分析了异常机器的连接数,TCP 状态数量,进程的 strace 分析,服务的响应时间,JVM 的监控以及 TCP/IP 协议栈里面的列队分析,得到的现象是落在某个 VLAN 的所有 upstream 都会出现问题,如果将这些节点摘掉,我们的服务状况会得到大大改善。

5 月 17 日下午 13:35,在经过一天的通力排查之后,我们终于发现了问题的原因系我们的前端机器到该 VLAN 的路由不通导致,不通的根本原因可以参考我们工程师写的深入技术分析

5 月 19 日下午 18:24,由于在这几天的调查分析中,我们发现 upstream 的  TCP queue 经常被打满从而导致 overflowed,我们同时对代码做了一些调整,加大了 backlog,修改之后,我们的 listen queue 几乎降为了 0。



在代码里面替换掉默认的 backlog=50 之后,情况进一步好转

到这里为止,我们万兆网络升级终于可以暂告一段落,升级带来的一些问题在我们工程师数个日夜的通宵排查中终于得到了完美的解决。



升级完我们的万兆之后,可以看到整个网络的负载都很低,大部分不到 10%

5 月 22 日晚上 22:00,我们收到报警,在线参数服务的请求量突然下跌。

5 月 26 日中午 11:39,我们运维团队接到请求负责处理,通过前后近一个月的对比,发现这段时间内请求量上涨了 3 倍多,由峰值的 20K/s 涨到了 60K/s。我们很快查出原因系某 APP 误用了我们的服务,导致了一次小型的类 DDoS 攻击。我们商务积极联系开发者确认此问题。

5 月 27 日下午 15:25,做了一系列的网络层面、系统层面、应用层面的优化之后,我们的服务完全恢复。


接下来,我们会做什么

1. 以后再做类似的基础的重要的升级,除了制定出缜密的计划安排,严格的时间执行点之外,上线之前,务必模拟出一套跟线上环境无限接近的测试环境,确保该环境无误再推动后面的工作。

2. 监控以及报警的持续完善

  • 增加每个服务集群下面包括丢包(loss)、延时(latency)等在内的网络质量监控
  • 加强目前 TCP/IP 协议栈方面的监控,比如 Ring Buffer、Syn 队列等

3. 其他方面的监控

  • 完善目前的 JMX 监控
  • 完善日志监控

4. web server 方面的改进

  • 为我们前端的 web serve 添加上检测 upstream 健康程度的模块以便更智能的发现调整到后端的请求
  • 同时将我们的配置管理放入到 git 中以便更好的维护
  • 变更前先确保有工程师 review 确保配置没有问题

5. 业务层方面

  • 对于在线参数,通过在 SDK 增加 Throttling 逻辑判断这类异常现象
  • 设计新的策略,对请求明显异常的 APP,禁止其在特定时间内发送请求
  • 为核心的业务申请更多的资源,保留充足的富裕,避免因为业务正常的增长而导致的瓶颈

6. 多数据中心的方案,避免单点故障,作为一项长期的任务,这个我们预计会在 2015 年 Q1 完成

7 . 出现类似影响开发者的问题,第一时间通知我们的商务、运营等人员,根据事态的严重程度决定是否通知我们的开发者


总结

我们意识到,友盟的产品与服务是广大开发者日常工作中不可或缺的组成部分,对于近期的不稳定服务,我代表我们的工程师团队表示深深的道歉。我们以提供给开发者优质高效稳定的服务而自豪。偶尔,我们会很不幸的出现上面类似的情况,正是这些事故的发生以及开发者的反馈驱使我们工程师团队一次次的升级、优化、总结,以避免类似的事故再次发生。我们以取得开发者的信任为生存的根基,我们非常乐意的为我们的开发者提供稳定高效以及愉悦的产品服务体验,诚挚的感谢您对友盟的支持与关注。

最后,感谢参与最近一个月辛勤工作,日夜通宵排查问题的工程师团队,包括本文作者所在的运维团队、实时分析团队、数据平台团队、social 团队、push 团队以及包括产品、商务、运营等在内的众多团队的通力配合,最后还是要感谢我们可爱的开发者,没有你们的支持,友盟不可能在四年的时间内取得这么好的成绩。


附招聘一则

随着友盟产品线的快速增长及扩张,运维团队也急需新鲜血液的加入。如果您有兴趣,简历请发送到 w # umeng.com。
高级运维工程师(PE)

职责描述

  • 全程跟踪、紧密结合产品线,保障友盟各产品线线上系统稳定高效运行
  • 完善运帷体系流程,运维文档
  • 探索、研究新的运维技术,跟进社区新技术

 
职位要求

  • 熟悉 Linux,熟悉 TCP/IP,具有互联网行业运维经验
  • 熟悉至少一门脚本语言,根据不同业务进行自动化部署变更及监控
  • 很强的分析问题、解决问题的能力,熟悉基本运维流程
  • 扎实的计算机专业基础知识,细心负责,热爱运维相关工作
  • 能独立负责解决复杂的问题,推进产品线发展

 
加分项

  • 包括但不限于:有独立博客,给社区提交过 bug(如果有,请附在简历后面)
  • 熟悉如下关键词的的优先:Nginx、LVS、MongoDB、MySQL、Storm、Kafka、Redis、Hadoop、Hbase、JVM、Finagle/Netty、Scala、C/C++
  • mathewxiang

    太丰富了.惊心动魄,让我想起去年我们应用服务器数据中心搬家,无法直接copy os 的情况下,重新搭建服务器,部署服务启动服务,掉用于被调用联调,最后通宵的上线, 后来听到’搬家’都要吐了…

    • http://jaseywang.me/ Jasey Wang

      在这之前我们已经搬过好几次机器了,到目前为止情绪还算稳定……

  • Pingback: 最近半个月的工作[14P] | Jasey Wang()