V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
LeanCloud
LeanCloud
LeanCloudRRY
V2EX  ›  LeanCloud

对六月六日 LeanCloud 多项服务发生中断的说明

  •  3
     
  •   LeanCloudRRY · 2015-06-08 18:07:08 +08:00 · 5140 次点击
    这是一个创建于 3216 天前的主题,其中的信息可能已经有所发展或是发生改变。

    各位 LeanCloud 的用户,大家好。

    LeanCloud 的多项服务在六月六日周六下午发生了大约四个小时的中断或不稳定。其中 16:10 到 19:09 为故障阶段;19:09 到 20:17 为限流恢复阶段。

    在故障阶段受到重大影响的服务包括:数据存储、网站及控制台、云代码、推送、工单系统、用户反馈、第三方登录、应用内社交;受到轻度影响的服务包括:短信、实时通信服务中获取聊天记录的 API;未受影响的服务包括:统计分析、离线数据分析、应用内搜索、文档。

    在限流恢复阶段受到重大影响的服务包括数据存储、网站及控制台、云代码、推送、短信、工单系统、用户反馈、第三方登录、应用内社交、统计分析、离线数据分析、应用内搜索、文档、实时通信中获取聊天记录的 API。实时通信在这个阶段未受影响。

    在这次事故中没有发生服务器端数据的丢失或损坏。

    我们知道这次服务中断给很多用户造成了实质性的影响。我写这封信的目的是为了向用户说明发生的事情,以及我们将如何改进产品和服务以降低类似事件发生的可能性。

    架构总览

    为了更好地解释这次事故,我想先简单介绍一下 LeanCloud 的后端架构。以下是一个简化版的架构图。

    LeanCloud 简化架构图

    虽然实际上系统间的关联比这个图要复杂,并且有一些做异步处理的系统没有画出来,但用以说明问题是足够的。

    在 LeanCloud 的最前端,除了负载均衡之外就是 API 服务集群,也就是实现 RESTful API 的部分。客户端应用通过 SDK 或直接调用 API 和它通讯。根据请求的类型,API 服务再调用后端的各个系统完成所需的功能。比如如果是统计服务的请求,事件信息会被发送到统计服务并最终被保存到 HBase 集群;如果是数据存储服务的请求,那么 API 服务就会调用 MySQL 和 MongoDB 集群完成数据访问。因为 MongoDB 存储了每个应用的大部分数据,所以不少服务都需要从这里获取一些信息,是整个系统中很重要的组件。

    MongoDB 集群存储了所有应用除了统计和备份数据以外的其他数据。由于数据量巨大,所以我们对 MongoDB 进行了分片(sharding),让数据分布在不同 shard。每个 shard 又由 5 台服务器(节点)组成,其中每台都保存着这个 shard 的完整数据,这样不但可以承担更高的负载,也保证了即使其中一些服务器坏掉,数据也是安全的。每个 shard 有一台称为 primary 的服务器,所有对数据的修改都会首先在这台服务器进行,然后再复制到其他服务器,而读取可以从任何一台服务器进行。当 primary 出现问题时,会有一个自动选举过程从其他成员里选出新的 primary,让服务可以继续,这是实现容错的方式。

    故障说明

    在故障发生前的一周,我们做了与本次故障相关的两个改动。

    为了更好地管理 LeanCloud 的大量服务器,我们在近期引入了 Apache Mesos,这是一个为服务器集群的资源管理提供高层抽象接口的系统。Mesos 会在每台受管理的服务器上运行一个 mesos-slave 进程。

    我们把 MongoDB 从 2.4 升级到了 2.6。新版本对地理位置的查询,以及 count 查询的准确性有所改进,所以我们认为这次升级对用户来说是值得的。但新版本的一个负面影响是,因为每次启动时要检查索引,所以启动时间大大延长了。而如果 MongoDB 是在出错的情况下重新启动,会导致大量的索引被重新建立,进一步延长启动的时间。

    大约在 16:10 我们的运维工程师收到 MongoDB 集群内存不足的报警,在初步诊断后,确定原因为 mesos-slave 启动的一个子进程占用内存太多,有可能是存在内存泄漏,所以我们开始终止各台服务器的 mesos-slave 进程。在这时我们发现 MongoDB 的其中一个 shard 中有三个节点因为可用内存不足进入异常状态,其中一个是 primary,所以我们开始重启其中两个节点。由于 MongoDB 2.6 在启动过程中需要校验数据并修复索引,所以这个过程很慢,而当时正是流量高峰期,这个 shard 剩下的节点不足以承担当时的负载,所以很快被压垮。由于 primary 的连接数被占满,这导致出问题的两个节点无法加入集群。在这种情况下,我们决定屏蔽所有 API 服务请求,并重启处于错误状态的三个节点。

    在此之后,这三个节点分别经历了多次重启,原因是出问题的 shard 大约有 10,000 个数据库,而新版 MongoDB 要验证每个数据库并修复索引。这不但使得重启很慢,而且因为数据库数量太多,这个过程会耗尽系统对子进程数的限制,所以在重启之后集群无法恢复正常可用状态。直到我们找到原因并调整了系统设置,各个节点才完成正常启动,集群恢复可用。

    在这个过程中,除了事故本身,我们在沟通上也犯了一些错误。当用户询问服务恢复时间时,我们给出了过于乐观的估计,但因为以上所说的原因,多个 MongoDB 节点经过了多次重启,实际恢复服务的时间晚了很多,这给用户造成了进一步的困扰。

    改进措施

    • 更新 MongoDB 节点内存报警值,预留更多响应时间。(已完成)
    • 在 MongoDB 节点暂停使用 mesos-slave。(已完成)
    • 设置各服务 OOM 优先级,意外情况发生导致剩余内存不够时重启低优先级服务以保护高优先级服务。(已完成)
    • MongoDB 集群增加硬件资源。
    • 拆分 MongoDB 集群为多个小集群,降低单个集群里的数据库数量。这也让我们可以对 MongoDB 进行滚动升级,降低风险。
    • 在重要系统升级前进行更多事故恢复的测试,并以滚动方式在较长时间内逐步完成升级。
    • 降低 MongoDB 启动时间。
    • 进行阶段性的 MongoDB 压测及故障恢复模拟。
    • status.leancloud.cn 从主站拆分,让用户得到更准确的故障信息。
    • 改进 API 服务线程池分配机制,避免因为一个 MongoDB shard 异常而堵塞线程池。
    • 改进 API 服务流量控制,紧急情况时做到可以仅切断对应特定 MongoDB shard 的请求。
    • 在发生线上事故时,我们将在 Blog 上开通直播贴,实时向用户通报进展。

    结语

    我们肩上承载着为数万开发者提供稳定服务的责任。周六傍晚事故持续的四个小时也是所有 LeanCloud 的同事最难熬的一段时间。我们了解到有的创业团队为了当天进行的一个活动,在过去的时间里非常辛苦地工作,而活动却受到了 LeanCloud 事故的影响。这样的事情让同为创业公司的我们非常地难受和惭愧。

    我们希望通过这个详细的报告让用户全面地了解整个过程,并将尽一切努力降低未来发生类似事件的可能性。虽然任何人都没有办法完全保证服务中断不会发生,但我们会采取一系列措施避免可预见的问题,并确保在发生意外的时候能更快地恢复。为了保持改进过程的透明,我们开放了一个追踪各项具体措施的 Trello board,用户可以通过它随时了解我们的执行过程。

    江宏

    LeanCloud Cofounder/CEO

    31 条回复    2015-06-17 16:13:49 +08:00
    leofml
        1
    leofml  
       2015-06-08 18:18:08 +08:00
    你好,2015-05-18 上午 18:47 至 19:07 青云北京2区线路出现问题,经排查是 IDC 链路出口流量出现异常(受到攻击),导致部分 IDC用户(包括青云用户)出现延迟大和丢包现象。

    我们对此造成的影响非常抱歉,并已根据您当时在北京2区的公网IP费用,乘以故障时间的10倍(约200分钟)予以赔付,可在充值记录页查看。

    此附件 是 IDC 的故障说明,可下载阅读。如有问题,可工单与我们联系。

    青云 QingCloud

    2015 年 5 月 20 日


    -------------------

    青云的这个做法挺好的
    aiguozhedaodan
        2
    aiguozhedaodan  
       2015-06-08 18:18:46 +08:00
    实在点:怎么补偿?
    9hills
        3
    9hills  
       2015-06-08 18:26:16 +08:00
    事故细节很详细,看来是踩了MongoDB的坑

    另外资源隔离方面,用OOM优先级比较原始,可以考虑用cgroups来做限制
    Jay
        4
    Jay  
       2015-06-08 18:45:45 +08:00
    我是不敢用 MongoDB 了,还是 PostgreSQL 吧。
    feisuzhu
        5
    feisuzhu  
       2015-06-08 19:28:56 +08:00
    @9hills
    这次事故的导火索其实就是 mesos 和 docker,也就是你建议的 cgroups。(当然不是事故的原因不是 cgroups……
    我们刚刚进行了 mongo 的升级,时间规划都是精确到分钟的。都这样了,本以为对 mongo 启动恢复什么的有一定了解,然而还是踩了坑……
    (真想把锅丢给 mongo

    利益相关: LeanCloud 运维,目测要背锅 _(:з」∠)_
    shiny
        6
    shiny  
       2015-06-08 19:30:19 +08:00
    MongoDB 坑也踩过不少,后来就回 SQL 去了
    ETiV
        7
    ETiV  
       2015-06-08 19:35:59 +08:00 via iPhone
    我用docker遇到过内核出错+母机重启,可能是艹的太猛了……
    9hills
        8
    9hills  
       2015-06-08 19:38:18 +08:00 via iPhone
    @feisuzhu 但是mesos的agent没有限制住。

    现在用mesos,kubernetes,docker的越来越多了,但是这些平台本身agent的资源限制却没有。可以用简易的直接写cgroups来做限制。
    9hills
        9
    9hills  
       2015-06-08 19:39:42 +08:00 via iPhone
    @ETiV 现在cgroups还不是非常稳定,必须尽可能用最新的内核,我厂用cgroups那些kernel panic简直要疯
    Livid
        10
    Livid  
    MOD
       2015-06-08 20:46:47 +08:00
    谢谢 LeanCloud 分享踩过的坑。
    feisuzhu
        11
    feisuzhu  
       2015-06-08 22:31:27 +08:00
    @9hills
    我们没有使用 mesos 自己的 containerizer,全部都在 docker 上,没有你说的这个问题。
    9hills
        12
    9hills  
       2015-06-08 23:02:47 +08:00 via iPhone
    @feisuzhu cgroups != docker

    从正文看就是mesos slave占用资源超过预期,如果预先用cgroups限制住,那就没这个问题了
    feisuzhu
        13
    feisuzhu  
       2015-06-09 01:07:46 +08:00
    @9hills
    我的理解是,docker 是用 cgroups 做资源限制的(直接划等号肯定是不合适)。
    所以说“预先用 cgroups 限制”实际是没有必要的,因为 docker 会帮你做。
    xiaogui
        14
    xiaogui  
       2015-06-09 04:14:40 +08:00
    后期会转为很多个独立的小集群提供服务吗?每个小集群能提供所有业务,把不同的用户分到不同的小集群中,控制每个小集群的客户量。然后在出问题的时候,只会有部分用户受到影响,不至于全部垮掉?
    evlos
        15
    evlos  
       2015-06-09 06:15:19 +08:00
    感谢分享 "架构总览" XD
    phoenixlzx
        16
    phoenixlzx  
       2015-06-09 07:49:31 +08:00 via Android
    喔....LeanCloud 也遇到 Mesos OOM 的问题了... 只不过是导致了 Mongo 一起 OOM ...
    9hills
        17
    9hills  
       2015-06-09 08:55:03 +08:00 via iPhone
    @feisuzhu mesos slave. Dockerd 自己。这些都无法被docker 托管,只能手动搞cgroups
    hepochen
        18
    hepochen  
       2015-06-09 09:38:51 +08:00
    不知道读到的信息是不是误解了,有几个问题挺奇怪的:

    1, 白天升级数据库?还临近流量高峰期的前几个小时,窗口期留得太自信了?

    2, 一个集群内5节点,不是一个个挨个升级,而是一起来?

    3, 一个集群5节点坏了,包括primary,重启其它两个节点未成功的前提下,整个集群应该是挂起的,这个时候能支撑外部的请求?如果可以支撑请求,并负载不住(远大于2/5=40%),四点到七点间,固然是高峰,但应该不是最高峰吧,那么,真正的高峰,如果所有的shard分流均衡,整个数据库的负载是多少?会不会很恐怖? Mongodb的这个集群设计,除了负载外,也是冗余的保证,2/5吃不下非真正高峰的流量,感觉不是太恰当。

    4, 原先的系统就没有针对OOM(内存耗尽)的对应策略?


    希望是我见识浅薄,都是误解。反之的话,哪怕一点,就没有办法让人鼓励你们了,除了佩服你们勇气之外,还剩什么呢。
    hepochen
        19
    hepochen  
       2015-06-09 09:41:45 +08:00
    抱歉,我真蠢:

    一个集群5节点坏了,包括primary --> 一个集群3节点坏了,包括primary
    feisuzhu
        20
    feisuzhu  
       2015-06-09 10:00:02 +08:00
    @9hills
    出现问题的并不是 mesos-slave 或者 dockerd 本身,而是一个奇怪的 lsof 进程,这个进程只在部署了 mesos-slave 和 docker 的机器上出现。而且并不是 mesos-slave 或者 dockerd 的子进程,并不接受 cgroups 的监管。
    feisuzhu
        21
    feisuzhu  
       2015-06-09 10:00:29 +08:00
    @phoenixlzx
    “也”?
    可以分享一下经验么?
    hjiang
        22
    hjiang  
       2015-06-09 10:17:33 +08:00 via iPhone
    @hepochen 确实都是误解。其实文章里写得很清楚,故障和升级过程本身是没有关系的。升级在几天前就做完了,并且是凌晨做的。

    另外一个数据库集群升级确实必须是停机一起做的,没法一台台做。
    hepochen
        23
    hepochen  
       2015-06-09 11:29:15 +08:00
    @hjiang Jiang,

    其它的数据库我不知道,MongoDB的其它版本,我也不确定。

    但是,2.4升级到2.6,集群内必须停机一起做?我不是你们某个员工口中的`学生`,这个要也是误解,我应该会怀疑自己做过的事情和智商了。

    http://docs.mongodb.org/manual/release-notes/2.6-upgrade/#upgrade-a-replica-set-to-2-6


    我觉得,你们应该遇到过单节点MongoDB故障的问题,应该也有改过甚至自己写MongoDB的drive来对应这些异常(如果没有,考虑重视),包括没有直接升到3.x,本身也很慎重,但是错了就错了,错了就是让人看笑话的,特别是你们产品这种性质的,赔笑、赔偿、赔不是,可惜你们只做到了最后一条。

    算了,也不是你们的用户,真给自己找麻烦。

    我应该也是误解了,特定的环境下特定的技术策略。我充其量就是半桶水的程序员而已,说的话没什么含金量。
    vanemu
        24
    vanemu  
       2015-06-09 12:02:08 +08:00
    感谢分享。
    我就说一点,感觉 status.leancloud.cn 不是很靠谱,目前来说,应该算是很鸡肋。因为之前是从 Parse 转过来的,比较而言 status.parse.com 就做得很好。基本上有什么故障都能及时的通知到,并且感觉也不会遗漏。status.leancloud.cn 有点摆设的意思,并且很多时候一些小的网络抖动也不会记录。

    refear99
        25
    refear99  
       2015-06-09 13:11:49 +08:00
    @LeanCloudRRY 我就想问什么事后出PHP SDK
    hjiang
        26
    hjiang  
       2015-06-09 13:36:07 +08:00 via iPhone
    @hepochen 我们的 mongodb 集群不是一个简单的 replica set,而是有分片的。如果要在一个分片的集群进行在线升级,在这个过程中元数据不能发生改变,也就是说不能 创建或者删除任何 collection 或 database。但这样的操作对我们的用户来说是很常见的。如果在在线升级的漫长过程中禁用这些操作,对用户来说是不能接受的。我们评估后认为在凌晨短暂停机尽快完成升级是最好的方案,虽然要离线,但受影响的时间短很多。

    我回复你想要指出的是,原帖说的故障和升级过程没有什么关系。
    hjiang
        27
    hjiang  
       2015-06-09 13:38:36 +08:00 via iPhone
    @vanemu 我们现在的 status 页面确实做得不好。这次总结里其中一项要改进的就是这个页面的可靠性和准确性。
    refear99
        28
    refear99  
       2015-06-09 16:03:07 +08:00
    @hjiang 所以说到底什么时候PHPSDK啦
    hjiang
        29
    hjiang  
       2015-06-09 16:15:20 +08:00 via iPhone
    @refear99 一直想做,但没法许诺具体时间。有一个用户自己写的 SDK,是不是不能满足需求?
    refear99
        30
    refear99  
       2015-06-09 16:46:04 +08:00
    @hjiang 是的,而且更新很慢,还不是PSR4,需要自己改很多东西
    其实就是想知道,官方有没有把PHPSDK列入日程呢?
    hjiang
        31
    hjiang  
       2015-06-17 16:13:49 +08:00
    @refear99 现在暂时还没有明确日期,因为一直没有人手 :-(
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   981 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 35ms · UTC 20:14 · PVG 04:14 · LAX 13:14 · JFK 16:14
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.