对《GitHub服务中断24小时11分钟事故分析报告》的分析
声明
本文是根据公众号“高效开发运维”翻译整理的GitHub服务中断分析报告的信息进行分析。可能存在某些观点有误,如果有欢迎指出。
原文地址:GitHub服务中断24小时11分钟事故分析报告
官方报告英文地址:October 21 post-incident analysis
背景
UTC 时间 10 月 21 日 22:52,为了更换发生故障的 100G 光纤设备,美国东海岸网络中心与美国东海岸数据中心之间的连接被断开。连接在 43 秒后恢复,但这次短暂的中断引发了一系列事故,导致 24 小时 11 分钟的服务降级。
简单分析
根据报告里信息描述,大致意思是要做一个计划中的变更,替换东海岸网络中心和数据中心之间的光纤,网络中断了43秒。在43秒内一个全局的容灾高可用机制(Orchestrator)检测到网络中断(发生分区事件Partition),就自动做了高可用切换(使用raft协议),激活了西海岸的数据中心。而东海岸数据中心的数据库有几秒的数据还没完全同步到西海岸。激活西海岸的数据库(MySQL)后,虽然后面东海岸的网络恢复了,但是西部到东部的复制链路却是因为数据不一致中断了。报告里没有提这个复制机制是什么。从其他公众号文章(GitHub 的 MySQL 高可用性实践分享信息推测这个复制就是MySQL的半同步。半同步在网络中断的时候超过500ms(github设置的)降级为异步了,在东海岸写入了几秒数据没有同步到西海岸,导致两边不一致,从而导致西海岸的数据没办法同步回东海岸(因为mysql slave replication中断了)。后面运维选择了一个从备份中还原然后重跑某些任务来在东海岸构建一个一致的实例,从而恢复同步链路的方法,这个用了大概24小时。
暴露的问题
这里只是从技术角度分析报告中反映的问题。由于Github对外的报告不一定把所有缘由都讲清楚了,这里的分析可能存在一些不对的地方。
MySQL的问题
MySQL的主从同步是逻辑同步,在原理上无法绝对保证主备一致。半同步只是尽可能的保证让主库的事务日志发送备库。由于事务日志可以不落盘,以及半同步在网络超时或中断时还可以降级为异步同步,所以Mysql的这个主从同步不是绝对可靠的。虽然平时都说网络中断是小概率事件,但是一旦它发生,这个数据不一致就很高概率发生了。Github运维在构建这个体系的时候应该早就判断出一旦发生中断切换时数据就存在不一致,然而并没有作出充分准备,最后还是成为整个事件的背锅侠。
高可用
Github的高可用使用的是orchestrator。这个在判断不可用事件的时候使用的是raft协议,跟阿里电商mysql高可用基于zookeeper去判断道理一样,可以避免脑裂,可以正确识别出异常事件,并且延伸出自动切换机制。 mysql的主备自动切换就要面临数据不一致问题,做自动切换就是把可用性放在数据一致性要求上面。然后报告里却又说把用户数据一致性放在第一位,这是有点矛盾的。
数据质量体系问题
少数数据不一致导致主备切换就中断了,还不敢强制切换,影响了所有用户。这个是很划不来的。报告里没有解释为什么不去修复这部分mysql不一致的数据。靠人肉比对肯定不行。如果平时就搭建了主从数据校验和订正平台,7*24小时去校验数据的差异并提供自动订正的手段,那也就不用在出现故障的时候不能订正数据了。 当然可能背后还有其他原因导致没有去订正。也许因为mysql里只是一些业务元数据,github主要业务数据(如代码等)可能不是放在mysql吧(这点是猜测)。
业务架构设计问题
一小部分的数据不一致导致不敢做主备强制切换,结果影响到全部用户,mysql成了背锅侠,这个我觉得也有点冤。如果业务上能做一些拆分设计,比如说按项目维度或者按用户维度做拆分。当有数据不一致的时候识别出是哪些项目或者用户相关的,可以在qia
强制切换的时候屏蔽掉这部分项目或者这部分用户的写操作,对这部分受影响的只提供纯读服务(有延时);对于其他无关的项目和用户提供读写服务,那么这个事件的影响就非常小,就不会搞得人人皆知了。
供参考的解决方案
Github面临的这个问题在异地容灾或者多活架构里是个绕不过去的问题,在阿里和蚂蚁早就碰到过且充分考虑过。这里列举一下阿里和蚂蚁的解决方案供参考。
架构设计
架构上首先要尽可能的将业务按模块,按某个维度做垂直和水平拆分。基于mysql做这个异地数据不一致难以避免,但是可以利用拆分技术把影响控制在很小的范围内。
按业务特点做单元化。如买家业务,可以按买家用户id拆分,在多地同时提供读写服务,然后地区之间进行双向同步。如果是三地,选择一个中心节点做同步中继。有些业务不能多地同时写,如卖家业务,库存必须在同一个地方修改,并为其他地方提供读服务,则按卖家用户拆分,中心读写,异地单元只提供读服务(业务可以接受读延时)。
在切换的时候,应用层设计了 “禁止更新”和“禁止插入”规则,可以保证源端某用户正在修改的数据在没有同步到目的地之前,目的库的该部分数据是不能写的,避免脏数据产生。然后有后台任务以及数据库的数据校验和订正程序一起设法将这部分不一致数据同步到目的端。等一致之后就清除这个规则。而对不受影响的用户数据在切换后是完全不受影响。
简单来说是临时容忍了少量的不一致,保障了可用性,最终一致。这就是BASE的思想。
实际做起来总会有些想不到的问题和困难,总会找到解决办法。方案准备的越充足,发生故障时要人做的就越少,恢复实际就越快。
数据同步
MySQL自身的同步性能并不好,在延时60ms的情况下半同步会限制主库的性能。在阿里已经放弃用mysql同步做异地同步。内部用DTS产品同步。dts也是逻辑同步,理论上也不能保障数据强一致,所以需要配套的有数据校验和订正产品。要做到实时校验,会有短暂的不一致。
高可用
阿里异地机房的数据库切换没有做自动切换。每个地方是一个独立的mysql master-master同步结构,异地之间用dts同步,所以数据库的切换是需要判断dts同步延时的。前面那种机制只是保证在切换后将影响尽可能的减少。是否要切换还是要有个判断。 这个机制说好也好,说不好也对,这是选择问题。这是mysql 主备架构的问题。
当然随着paxos协议和三副本同步技术的发展,alisql发展出 xdb,蚂蚁这边的oceanbase 已经彻底解决了这个数据同步和高可用问题,都是自动保障的,不给运维人员纠结的机会。
异地容灾和多活
异地容灾和多活的层次
做异地多活,个人认为可以做到几个层次,从简单到复杂如下:
- 1.应用双机房部署且多活,数据库主备架构双机房部署,读写集中在其中一个机房(主库)。这就是异地容灾。
- 2.应用双机房部署且多活,数据库主备架构双机房部署,读写集中在其中一个机房(主库),另外一个机房提供部分读业务(业务能接受延时),这是读写分离。
- 3.应用双机房部署且多活,数据库也双机房部署,主备交错双向同步。同一个应用只读写一个机房数据库,不同应用读写不同机房数据库。这是应用混部给人的多活感觉,实际不同应用读写不同的库。
- 4.应用多机房部署且多活,数据库多机房部署且都提供读写服务。同一个应用在不同机房都可以读写本地数据库。这个是数据库提供了多地同时读写的能力,实际读写的是不同的物理表,但是借助拆分设计可以是读相同的业务逻辑表。即对应用而言,它也是写同一个表,代码同一份。
- 5.应用多机房部署且多活,数据库多机房部署且都提供读写服务。同一个应用在不同机房都可以读写本地数据库,并且读写是相同的物理表。这里也会借助拆分设计将业务表拆分为多个物理分表。
传统的IOE架构的数据库由于是主备架构,所以在异地容灾多活建设上只能做到1和2。有很多数据复制产品,无论是数据库层面的复制还是存储层面的复制,其思路也是一样,虽然宣称自己做到多活,通常也是到1和2,少数做到3(给人异地多活的假象)。 如果应用使用了拆分设计(分库分表),倒是有可能做到5.不过分布式数据库中间件也是最近5年才出来的技术,只在互联网行业里大量使用,传统行业估计不会使用。掌控成本相对高,且风险不敢承担(找不到承担责任的厂商)。
蚂蚁的OceanBase,使用paxos同步协议,没有主备概念,多地节点可以同时读写。可以不依赖外部产品仅凭数据库自身就做到1、2和3. 如果要做4和5,则需要业务做拆分设计(分库分表),从应用的接入层就设计流量拆分规则,跟数据库拆分规则保持一致。然后OceanBase就可以做到4。由于三副本的leader只允许一个写入点,所以OceanBase单集群是做不到5.
淘宝的MySQL,使用主备架构,异地同步用外部产品dts。多地的mysql集群彼此独立,可以同时读写相同的表。在应用做拆分设计(分库分表)后,在接入层设计流量拆分规则,跟数据库拆分规则保持y
一致。基于MySQL的分布式数据库集群是可以做到5,实现三地同时读写相同的表。 不过问题也是存在的。如前面说的mysql切换的强一致问题、高可用自动切换问题。所以蚂蚁并没有选择这个方案。
OceanBase在异地容灾和多活的特别能力
OceanBase是很方便做异地容灾和多活建设的,不管是三机房还是五机房,都可以作为一个OceanBase集群。其内部自动维护各个机房之间的数据同步,并保证这种同步在集群层面是强一致的和可靠的(对业务而言,数据绝对不会丢)。在正常运行期间这种保障就一直在。此外,单个或少数机房故障时,OceanBase内部会自动切换,并且切换后数据是不丢的。
OceanBase这种数据库架构方案在未来在核心业务里一定会逐步替换掉传统数据库基于主备同步思路的方案。