达达双云双活实践
在过去6年业务飞速发展中,达达集团始终秉承“万千好物 即时可得“的初心和愿景,不断迭代升级技术能力,持续提升履约效率和服务体验。为了保障系统持续稳定和业务无间断高效运行, 我们在数据源高可用架构升级、数据库垂直/水平拆分、 微服务治理及可观测性、容量弹性和多活容灾等方面进行了深耕并取得显著成果。本文则主要分享达达在双云双活容灾能力建设的实践和经验。
01.为什么做双活这件事
首先,介绍下高可用High Availability和容灾Disaster Recovery,这两者相互联系和补充,但也有明显区别:
从故障角度,高可用主要处理单组件故障导致的 负载在集群内服务器之间做切换,容灾则是应对大规模故障导致的 负载在数据中心之间做切换。
从网络角度,局域网是高可用的范畴,广域网是容灾的范畴。
从云的角度看,高可用是一个云环境内保持业务连续性的机制,容灾是多个云环境间保持业务连续性的机制。
从目标角度,高可用主要是保证业务高可用,而容灾则是在保证数据可靠的基础上 业务对外连续可用,以尽可能降低RPO(灾难发生时丢失的数据量)和RTO(灾难发生时系统恢复时长)。
可以看出,高可用往往指本地的高可用系统或架构,表示在任一服务器出现故障时,应用程序或系统能迅速切换到其他服务器上运行的能力;而容灾是指异地(同城或者异地)的冷备/热备/双活系统,表示在大规模集群或云级别灾难发生时,数据、应用以及业务在灾备数据中心的恢复能力。通常容灾可以从网络接入、应用治理、主机、数据存储等层面去实施,业界如大型银行的“两地三中心”同城双活+异地灾备、蚂蚁金服的“三地五中心”异地多活、饿了么的北京上海双机房“异地双活”都是典型的容灾架构方案。
从支付宝527大规模宕机事故、携程528数据库全线崩溃、AWS北京光缆被挖断、腾讯云云硬盘故障、谷歌云K8S服务(GKE)宕机19小时不可用、微盟删库事件,再到近期华为云香港机房因制冷设备故障导致整个机房崩溃,不论天灾还是人祸,居安思危并建立容灾能力对一个企业的重要性不言而喻。
达达在2017-2018年饱受网络故障之苦,特别是单云出入口网络故障让我们束手无策。为规避单云灾难级风险,我们在调研并比较业界容灾方案后,最终选择 双云架构 做同城双活的探索。
02.双云双活一期
双活一期主要是围绕服务双云部署、业务分流和接口功能/性能等方面的探索验证。
2.1 双云双活一期架构图
2.2 双云双活一期方案
和业界大多数双活方案类似,达达双活一期方案相对朴素,即把J云当做U云的第三个机房进行服务扩容,主要有以下几个方面:
跨云专线:双云间通过2家供应商建立了4根高可用的跨云专线,带宽4Gb,时延3-4ms。
服务注册:达达基于Consul实现了服务注册、链路隔离、数据源发现及高可用切换等高级功能。Consul采用了一致性算法Raft来保证服务列表数据在数据中心中各Server下的强一致性,这样能保证同一个数据中心不论哪一台Server宕机了,请求从其他Server中同样也能获取的最新的服务列表数据。数据强一致性带来的副作用是当数据在同步或者Server在选举Leader过程中,会出现集群不可用(在接下来遇到问题中会分享我们遇到的问题)。
配置中心:双云共用原有生产配置中心Config,通过Config获取服务相关参数、缓存和数据库的连接地址。
服务部署:J云只部署了核心服务,并注册到原有Consul集群,而配置中心、缓存、队列、数据库等均跨云访问U云侧原有集群。
流量分发:负载均衡LB是基于OpenResty+Consul建设的,并结合自身业务特点自研了一套流量控制逻辑,具备生产流量、灰度流量、压测流量的转发控制。在双活一期针对J云类似灰度链路的流量控制,根据J云机器的tag标签,可做到指定域名 根据CityId实现可调百分比的外网流量经由跨云专线打到J云的服务节点之上。
监控告警、日志系统、应用性能、发布系统等均共用原有U云生产环境。
2.3 双云双活一期遇到的问题
J云侧接口响应时间过长:由于只有部分核心服务部署在J云,其依赖的Redis、DB、队列及其他服务等仍需要跨云读写访问,跨云专线的3-4ms时延在多次跨机房请求下把延迟问题彻底放大了,例如U云侧一个发单接口响应时间约在200ms,而在J云侧需要500ms+,这个响应时间对业务而言是无法接受的。
灰度用户前后请求体验有明显差异:双活一期的流量分发方案可指定到域名+按百分比分发流量+默认负载均衡轮训后端,这样虽然可以灰度验证业务功能,但被灰度城市的骑士前后两次请求如果流向不同云,接口响应时延差别较大,体验上响应也有差异。
跨云共用同一Consul集群有偶发风险:跨云专线的网络波动和3-4ms时延,会造成跨云使用LAN Gossip协议通信的同一个Consul集群发生紊乱的情况,我们曾遇到过J云侧Consul节点将U云侧正常节点投票下线 继而影响U云生产集群的问题,也遇到过这种紊乱情况下J云的节点会疯狂地消息广播投票确认并导致专线带宽会急剧上升。基础架构团队针对这些问题专门优化了Consul Gossip协议以降低对网络延迟的敏感度(见下图)
03.双云双活二期
针对同城双活一期遇到的跨机房调用时延问题、跨云网络波动可能引发Consul集群紊乱,以及业务流量精准分发一致性体验等问题我们进行了深度思考和复盘,形成了“服务间交互单云内内敛”、“数据库双云双向复制”、“流量总控精细化配置”三大核心点,并且结合业务场景依据数据访问频次、时延要求和数据一致性等纬度,对数据和关联服务进行了梳理和RCG模型分级。
此外,我们还在统一双云配置中心、服务一致性部署和发布、系统可观测性、工具系统改造适配、容量规划和成本控制方面做了相应功能迭代和能力建设。接下来会针对以上几点分别阐述。
3.1 双云双活二期方案的三大核心点
3.2 Consul多DataCenter方案
双活一期遇到的跨机房调用时延问题 及 跨云网络波动可能引发Consul集群紊乱,是我们更清楚地理解到服务间请求单云内内敛的重要性,对 Consul LAN Gossip与WAN Gossip协议适用不同场景的理解。因此双活二期,我们采用了Consul多数据中心方案,官方示意图如下:
每个云有各自的Consul Server集群,Consul Client默认以LAN Gossip协议join到所在云的Consul Server,不同云之间Consul Server通过WAN Gossip协议join到一起。在达达内部,每个服务会默认携带一个Sidecar并join到所在云的Consul集群, Mysql和Redis也同样以Consul Client方式join到所在云的Consul集群,这样通过Consul多DC架构和Consul服务发现,我们实现了服务间交互、服务与绝大多数数据源间交互 在单云内内敛,也尽可能规避了一期遇到的问题。
3.3 数据库双云双向复制
为了实现RZone模型的DB本地读写,且在数据库水平项目没有大规模开展的情况下,我们调整了双云两侧数据库的自增步长为2,主键也做奇偶数区分,即U云侧主键是奇数,J云侧主键是偶数。
为实现双A数据库双云双向稳定同步,我们采用了阿里开源的分布式数据库同步系统Otter,Otter架构图如下
生产环境中,我们通过Otter不同的Channel管理不同双A数据库的双向同步
目前数据库双向复制时延平均0.9s,最大时延为2.2s(瞬间),如下是生产环境Otter同步列表的TOPN
3.4 LB流量精准分发
我们通过定制开发OpenResty的Lua模块,从外部请求头Header提取CityId/从请求体Body中提取TransporterID/ShopID并结合mapping关系统一转换为CityId,最终依据双活流量总控平台配置的域名+URI以精准分流到J云。
如下图,相比一期我们可以做到,针对pop.imdada.cn的某两个具体的和订单相关的URI,对城市ID 313和146 流量分发到J云,其余城市或pop其他接口默认分发到U云(默认主IDC)。
同时,我们也做到骑士ID对齐城市ID,即假设某个骑士属于这个城市,且这个城市针对该骑士对应的业务也开启了双活流量,那么该骑士的每次请求都会流向到J云。
3.5 双云双活二期方案与业界双活方案对比
3.6 双云双活二期方案之RCG模型分级
参考业务双活解决方案,并结合业务场景依据数据访问频次、时延要求和数据一致性等纬度,我们对数据和关联服务进行了梳理和RCG模型分级,如下
3.7 双云双活二期架构图
结合二期方案三大核心改进点及RCG模型分级,双云双活二期的架构图如下所示
3.8 双云双活二期之工具/系统适配双活
双云双活二期主要有以下几个方面的工具系统改造支持和适配。
配置中心:配置中心从Config迁移至Apollo,涉及达达数百个服务,生产环境的Namespace下通过不同集群Cluster以区分U云和J云的个性化配置,且Apollo的Cluster名对齐 Consul 的datacenter名,服务通过读取本地配置文件server.properties以正确取得所在云对应配置信息。同时,Apollo本身依赖的几个DB也在双云间做了数据库主从同步,以备灾难时切换。
发布系统:得益于配置中心和中间件包版本的统一,我们实现了服务一次打包双云一致发布,这里包含发布系统对双云两侧同一服务版本的一致、发布/回滚/重启等变更批次的一致、变更逻辑步骤及消息通知的一致性。
业务监控:业务监控系统为了更好的支持双云服务监控,对齐Consul集群datacenter名,业务只需选择对应云标签,即可查看具体某个云上服务的监控图表。
应用性能监控:为了更好的监控服务间在单云内敛的交互情况,我们在双云各自部署了一套Pinpoint集群。
NTP时间同步服务:双云间时间步调不统一会直接影响业务问题的排除,而且在流量切回瞬间同一个未完成状态的订单可能会发生意想不到的情况。我们在双云间建立了NTP服务主备,双云所有服务器默认与U云的NTP Server每分钟同步时间。
镜像仓库:目前Harbor在双云各部署一套,默认是从U云增量同步到J云,灾难时会切换J云的Harbor为主镜像仓库。
3.9 双云双活二期之容量弹性和成本控制
通常建立双活会使云资源成本接近等量的增加,对于达达我们也面临同样的挑战。如何做好双活成本控制,我们的经验是从容量规划和弹性扩缩容方面下功夫。
数据库:目前双云两侧MySQL几乎是等量的(全局数据库除外),U云侧数据库为一主多从集群方式+等量冷备集群,目前我们正在调研并往MGR方案迁移,目标是节省冷备集群又不失高可用性。
无状态服务:在业务流量总量一定的情况下,流量在双云间调配,意味了双云无状态服务的负载也在随之变化。鉴于此,我们基于K8S HPA算法自研了Auto Scaling自动扩缩容系统支持双活架构下的容量弹性,通过实时收集业务系统的多种metrics指标,实现了每多云依据各自业务负载实时调节所需资源。我们还引入原生容器Kata,相比虚拟机容器更快速地让服务拉起,扩缩容效率也因此得到大幅提升。这样我们在保障业务系统双云稳定性的同时,又做到了成本有效节约。
04.双云双活现状
目前落地配业务已稳定运行在双云之上,并且我们已具备切换任意城市流量到指定云的能力。
05.后续规划与总结
未来需要在业务入层增加更多的Sharding Key(城市ID/骑士ID/商家ID/订运单ID等),并引入API Router以便于其他业务更方便接入双活架构,业务流量可分发到准确的云后端服务;兜底Job改造为Pulsar延迟消息并在各个云内执行,以避免因数据库双向同步时延且Job单边运行造成业务上数据的错乱。
业务监控特别是单量的监控水位,需要能及时对齐流量切换比例;账户服务目前是默认在U云的全局服务,拟采用TiDB方案以适配双活架构;最后,双活需要对齐数据库水平拆分,流量切换从CityId维度变化为ShardingId维度便于流量调配和管理。
双云双活的建设是一项复杂的系统工程,需要有点、线、面、体的维度思考和规划。在此特别感谢云平台团队、落地配研发团队、物流平台研发团队、落地配运营团队对双云双活项目的大力支持。
回顾双云双活两期,我们希望尽可能借助架构和运维的方式来解决同城双活中遇到的问题,尽量对业务透明及减少业务改造成本,以落地配业务为同城双云双活的试点业务,减少全局风险,同时我们希望积累相关经验,为后续开展单元化和多云多活打好基础。
作者简介
杨森,达达云平台DevOps & SRE Leader,同城双云双活项目负责人。
本文转载自"达达京东到家技术"公共号
原文链接: https://mp.weixin.qq.com/s/YOjcNjQVUM9D0QFiG8uvNw
评论