活动实录 | Design For Failure——饿了么技术运维实践

数人云“告别人肉运维”上海Meetup的实录第一弹来啦!本次分享的嘉宾是饿了么技术运营部资深专家王伟珣。从源头出发,饿了么基于架构和平台设计的"design for failure"实践,做到自动化运营和运维实践,防止人为操作失误。

王伟珣 / 饿了么技术运营部资深专家

曾是EMC Pivotal 亚太区CTO office资深架构师,成功在中国和亚太各个等行业推广使用了新一代分布式数据库和Docker。中国移动游戏基地和视频基地平台技术负责人。拥有五项技术发明专利,国内第一代互联网建设者,GNU/Linux开源运动和信息安全国内最早的推广者。

大家好!我加入饿了么大半年了,今天分享一下饿了么在技术架构运营方面的一些实践经验,基于这个架构饿了么已经做了一两年的时间。

Design for failure

首先,饿了么非常强调design for failure。饿了么现在的IT架构经过多年的发展,已经变得相当复杂了。在这个IT架构里面,要追求三个9、四个9,甚至六个9。在这种复杂的软硬件架构情况下,15分钟之内人肉去判断故障在哪里是很难的,更何况还面临着业务的中断带来各种原因。前有Gitlab这个事情人肉操作的出错,一个系统可能你要运行五年、十年甚至更长,谁都不敢担保再怎么样细心都不出错。这么多年,各种各样的软硬件bug、生产过程中的质量问题都会碰到,所以纯粹是靠各种各样对人的一些约束或者SOP,实际上不能彻底解决问题。

我的理念是采用负载分担多活,以分布式架构去做到design for failure。互联网多数会考虑负载分担多活,但实际上在广义的IT行业里面比如说芯片设计以及航空航天,飞机和卫星上的软硬件设计,本身也有这样的一些要求。大家如果从企业级去学习、被IOE洗脑的话,那么他们提这个比较少。因为要做到负载分担多活,就不会去买IOE那种大设备了。

做到负载分担多活,可以得到的一个收益是自动容错,无论是硬件的质量问题,还是外界环境,比如光缆挂断、掉电、软硬件的bug,它都能够自动判定并且自动容错切换。比如做卫星的软件,卫星发射上天,低温110度,高温180度,上天以后要工作15年,不能修、不能换,可想而知这个要求很高的。按照最高要求去对比,企业级的很多要求已经低得多了。如果做一个软硬件要求15年不能修、不能换,一直工作下去,而且环境那么恶劣,发射的时候加速度10到100个G,它要在实验室里面抗住100个G振动,才能抵得住发射过程中的挑战,所以这种负载分担多活相比航空航天软件是一个不算高的要求。

SOA分布式架构

在负载分担多活的架构上,首先要解决机房内的架构,这个架构按照业内的术语是叫SOA分布式架构,在这个图上看上去有点复杂,简单来讲就是每个模块实际上是多个的,每个单元它是多个负载分担,而且能够自动地扩展和容错切换。这个架构在一些友商,例如Netflex开源的部件里也有类似的设计理念,Google SRE的书里面也提到了他们的做法,饿了么跟Google、以及大家所知的腾讯阿里也实现了类似的架构。

先介绍一下它工作的结构。核心是SOAGateway去负责后面所有服务部件互相之间的负载分担、调度、征错、容错等等。前端有一个HTTPizza的环节,在它外围的外层有一个APIRouter,它是负责广域的负载均衡,会根据一些后台的情况,基于就近规则或者是出事故的时候切换规则,把请求业务指派到某一个机房去。进到这个机房以后会有一个转换的环节,把HTTP协议转成高性能的二进制的Thrift协议,然后通过SOAGateway,根据调用的服务转到不同的服务单元。所有的服务单元有一个公共的注册和健康检查的环节,还有Service的注册环节。在下面有几个公共的数据服务部分,比如DBA数据库部分,还有非常大量的Redis Cluster部分,以及一些异步的服务调用环节,用mq,饿了么有自研的 maxmq,也有在用老的Redis mq。

SOAGateway

在整个分布式架构里面核心是SOAGateway,现在也有称之为微服务的中间件,这跟已经开源了很长期的像Spring、JBoss这些中间件是不一样的。Netflex开源了一个,有点像v1.5的版本,但是它还依赖Spring,没有彻底做到完全的语言中立化和完全的松耦合。

整个分布式系统现在有1100多个服务部件,有7000多台服务器,所有服务的启动、配置管理,启动的时候会自动登记,服务发现,健康检查,以及断路器、智能路由、控制总线等等都在这个中间件去做。核心是一个分布式服务架构,模块之间为了做到高性能和语言中立是用了Thrift RPC协议,这样就使得每一个模块的开发者不用关心具体的开发语言了。内部有PHP、Pyhton、Go还有Java来做不同的部件、不同的模块。

design for failure最核心的就是这个中间件,具有服务高可用性的功能,能够做到自动负载均衡、自动的熔断与容错。熔断是指某一个部件如果响应时间或者是连接数超过阀值的情况下,它会对某些请求限流,或者在有新请求的时候会把非关键业务环节临时的自动关闭掉熔断。容错是比如这个系统部署了80个或者100个服务器,部署了那么多份,但其中某一个服务器由于网络或者硬件的各种故障,因为在服务注册环节是有一个心跳检查的,包括服务自己也有一个心跳上报的机制,一旦发现某一个服务器有问题就会自动把它排除,所以对攻击、故障、网络的拥塞以及服务的过载能够自动的应对、自动的限流切换,达到服务的高可用性。

Thrift协议是一个高性能的语言中立的协议,已经支持了各种开发语言。当然,这个分布式服务架构涉及的系统非常庞大,前面也提到有7000多台服务器在生产,所以配套了一个Web管理界面。另外,饿了么已经能够做到程序的分布式分发,现在在做Docker化的软件分发,有关功能正在开发中。

微服务治理实践

design for failure是一个很重要的架构设计理念,在这个设计过程中饿了么也有一些技术的实践。SOA架构或者另外一个名词叫微服务化,主要是对这个服务进行有效的拆分,使得部署能够方便化,不会各个环节互相的牵扯。在整个治理服务架构的设计中,我们也参考业内一些成功经验,业务模块基本上是先按照业务流程的BU再划分,根据业务来进行划分大的模块,那么由一系列的服务来组成某个BU的业务系统,每一个服务是可以单独部署的,而且所有的这些服务版本是根据业务周期去确定而不是说根据某一个项目。

在新一代的SOA架构里,强调的是强内聚弱耦合,前面提到的这些服务功能和流程逻辑是每一个服务组件自己承担的,中央的SOAGateway只负责派发以及服务寻址等。在初次调用的时候SOAGateway会通过状态查询和服务注册,帮你指派到哪一个服务单元,服务单元之间是P2P直连的。在某一些业务模块流程,它也通过SOAGateway集群进行派发调度。前面所说的整个1000多个模块的系统,它的发布、部署是由软件开发的各个小团队做DevOps,即整个发布系统,他们可以自己去操作。在发布过程中,分布式架构里的灰度发布、随时回滚,发布过程也就是生产过程中遇到各种差错跟切换的容错也是整个架构必备的一个部分。

Corvus redis cluster架构

饿了么在自研SOAGateway的同时,也自研了一个Redis Cluster的软件Corvus,现已开源。Corvus要比Redis Cluster官方的版本稍微早一点出来,在Redis Cluster架构的设计理念在部件本身也是实现了同样的分布式多活负载分担的架构,这里着重介绍一下它在数据Cache的层面做到数据的分片,主从同步,在磁盘做一个AOF的缓存等等,这样的结构来保证高性能的同时高可靠,做到多活主从,它的slot节点也是可以读的。

多活平台数据保护实践

前面介绍的是单机房内的分布式多活的架构实现。另外,饿了么也在做多活的平台架构。大家天天在用的微信、QQ以及3G、4G网络,都是全部IP化的,也是由非常庞大的多机房分布式系统来为大家服务。无论打开微信也好,还是打开饿了么也好,客户端其实会自动上报自己的一些ID状态,后来会有一个Gateway,各家可能叫法不同,会根据用户的各种属性状态把用户派发到某一个服务的单位,比如饿了么现在初步分成南北两部分,北方用户在打开客户端连入系统的时候,没有感知地就被派发到了北京的数据中心。如果南方的话,那么就会被自动派发到了南方的数据中心。

当然,南方和北方之间是作为互备双活的,假设北京的系统挂了,最外层的API Router会知道,它就会把所有请求全部给派发到南方去。因为南方、北方其实都有所有用户的数据,即持久化层的数据是全量的,只是应用层服务模块的逻辑是分别部署。里面的数据复制是多活的核心环节,之后我会细讲如何做到双向的复制。南北是双活,同时又是为互备。饿了么做到了多活以后,灾备一般也达到了同样的要求,本质上是互为灾备、互为备份的。

IOE常常讲的“两地三中心”,那个是卖盒子的做法,不是真正考虑到design for failure的做法。在多活的这个架构里有一个基础,必须对用户在业务入口逻辑的时候就马上进行sharding,不是传统数据库sharding,数据库sharding实际上是为了负载分担的而不是为了做多活的。所以在业务入口的这个环节基于业务逻辑做sharding以后,后面分成两个双活还是分多活本质上没有区别了。饿了么的商家是按地域分布的,在上海做的消费请求与在北京做当然是不同的,所以业务的逻辑就是天然分割、天然分布了。

互联网公司比如微信,也有类似的逻辑。用户连上去的时候会就进到某一个常用服务机房,如果用户从上海到成都,用户短期内接入成都以后,它会帮你转到上海,但如果在成都多待了多少天,有一个后台逻辑,会把该用户持久化数据的归属主服务点切入到成都。现在3G、4G的通讯网络全IP化了,也是有类似的结构,开户的所在地比如上海就在上海,但是到北京去以后,北京的访问登记中心会自动跟上海HLR连接获得用户的属性和注册,才使得用户在北京能够通话,通讯网络天然也是一个多活的。

在饿了么架构里面因为涉及到比较多的商户逻辑和用户逻辑,持久化数据量比较大,核心是数据库里面有一个DRC的环节,承担数据复制的工作。这个工作有两个用途,一方面用户换地方的时候,比如从上海到北京,那么用户开机是连到北京的,该信息在北京也要有,所以持久化数据是全量的。前面也提到双活即灾备,起到灾备的作用。在数据复制的技术上面,是基于数据库软件层面来做的,也具有灵活性和可以做多路分发的好处,这里只是最简单的广域的双向复制功能。复制过程中还会去通知远端Redis Cache的刷新,会去通知一些mq的状态变更,都是N对N的一个多向的功能。

总结

最后总结一下我的理念,真正要做到整个系统让大家晚上不会总是接到电话去修故障,关键在于一开始的架构设计要设计成一个双活或者多活,使得大家的工作更加的轻松,更加不需要人肉或者紧急修理。

可以举另外一个例子,比如大家有些时候会纠结安全,因为平时用的Linux、Windows在设计之初就不是安全的。在前几年出了一个seL4的新操作系统,它是从设计之初就考虑安全的,所以它获得了最高级的安全认证:EAL6、世界唯一的一个最高级安全认证。从设计开源以后到现在那么多年,它是被数学证明安全的,到现在为止还没有发现它的漏洞。这就是设计出发点和设计基础不同,所以会导致最后有很多工作的不同。最安全的操作系统,没有人发现有安全漏洞,因为它是重新按照安全去设计的。但是,Linux或者是Windows,它设计之初就是为了赶时间,很多安全没有考虑,所以后面各种各样的漏洞就被发现出来,这个例子就是基础不同导致的结果不同。

我的分享就到这里,谢谢大家!