历经16年,猪八戒网的双活流量架构演进史
Posted dbaplus社群
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了历经16年,猪八戒网的双活流量架构演进史相关的知识,希望对你有一定的参考价值。
猪八戒网随着业务访问量的直线增长,用户增长达到一定规模后,同时面临着高并发业务和海量数据的挑战,传统单机房在服务器容量上存在瓶颈,而且在一些不可预知场景下,导致整个网站出现故障,例如机房断电、火灾等这些不可抗拒因素都会导致所有服务器出现宕机从而导致业务瘫痪,即使有备份,恢复业务花费的时间也比较长。所以公司根据实际业务情况选择了同城双活流量高可用架构,当然还有两地三中心、异地多活等方案。本文主要基于同城双活流量叙述猪八戒网这16年的双活流量架构演进之路。
2014年前猪八戒网的服务器都是托管于传统IDC机房,虽然业务应用、基础组件、数据库等都设计了高可用模式,但是避免不了机房宕机导致整个网站出现故障的问题。如图1:
图1:单点机房
直到2015年初的时候,由于机房人员日常维护的时候,不小心把我们的核心交换机网线碰松了,然后导致整个猪八戒网打不开,我们通过各种排错才定位到这个问题,经过此事件后,上层领导高度重视网站的可用性问题,为了解决机房不可控的因素,所以2015年中旬开始建设备用机房,解决机房单点问题。(注:当时基础组件及业务应用不具备双活条件,只支持主备架构模式)如图2
优点:
主机房发生故障时,通过DNS解析到备用机房,解决机房单点故障问题;
主备架构模式简单,持久化数据异步同步到备用机房,不用考虑脑裂问题。
缺点:
由于备用机房平时处于空闲状态,造成大量资源浪费问题。
图2:同城主备
经过1-2年的迭代,大概在2017年的时候,随着基础组件的升级改造,例如:数据库、缓存、队列、对象存储、配置中心等都支持了双活流量架构模式,然后业务应用纷纷改造适配双活流量架构模式。终于我们可以把网站域名同时解析到2个机房了。如图3:
图3:同城双活(基于DNS的双活)
基于DNS切换流量模式还是有瑕疵的,在猪八戒网运行了3年左右,在此期间多次出现某机房不稳定事件进行全局流量切换,主要问题体现在某些DNS服务器不跟着协议走,导致切换流量生效时间慢,流量切换不彻底的问题。
提示:一般情况我们遇到的单个机房故障,主要体现在基础组件、业务大面积瘫痪等问题上,不涉及到入网流量的问题,假如真的是入网流量异常了,也只有等DNS解析生效了。
鉴于以上遇到的问题,我们换个思路解决如何快速切换流量,最终方案是在HTTPS层的nginx(ngx_lua)代理服务器实现快速动态分流的功能(项目为:Signpost),为什么采用ngx_lua?因为ngx_lua在猪八戒网稳定运行5年+,不管是开发效率及性能,Lua有着天然的优势,语言表达能力也更强,而且ngx_lua是同步非阻塞(100% non-blocking)的,再次感谢章亦春 (agentzh)大佬的开源作品lua-nginx-module。如图4:
图4:HTTPS代理层分流
如何基于ngx_lua设计动态分流功能呢?简单通俗一点就是如何快速动态切换nginx的upstream进行分流而不执行nginx reload。关于Upstream是如何动态生成的?请查阅上篇:【技术分享】猪八戒网Nginx的动态服务发现演进之路
首先管理人员配置分流策略到CMDB系统中,然后CMDB同步到Redis缓存,当用户请求时,Nginx接受到请求后,先从worker缓存获取分流策略,如果worker缓存没有,就到lua_shared_dict缓存获取,如果lua_shared_dict也没有,那就到Redis中读取,然后依次缓存,根据匹配的分流策略执行指令。假如没有获取到分流策略,那就按照默认策略分流。如图5:
图5:动态切换Upstream架构
为什么会有三级缓存?因为worker缓存支持数据结构缓存,而且没有锁竞争,lua_shared_dict缓存有锁竞争,不支持数据结构存储,Redis缓存需要TCP连接消耗,最终都是为了性能!
经历了数月的开发及基础架构改造,终于在2020年9月初,基于HTTPS层的Nginx(ngx_lua)快速动态分流的功能成功上线了,主要支持了全局模式、单域名模式的流量切换,还额外支持了域名动态HTTP/HTTPS协议切换。如图6 - 9:
图6:同城双活(基于NGX_LUA的Signpost)
图7:双活流量控制台
图8:单域名切换功能
图9:单域名切换日志
最后,关于Signpost软件的主要特性,支持多活流量管理,根据不同维度进行分流,例如:IP段、地区、Cookie、浏览器、GET参数等,流量切换可以精确到秒级,而且全程用户零感知。
猪八戒网的DevOps进化论
一、写在前面
1、猪八戒网的系统架构演变史
2015年前,猪八戒网80%的项目都是php语言开发的,剩余少部分系统使用nodejs和java。2015年一个关键的里程碑,开启了猪八戒网SOA服务转变,这就是腾云7号行动。
腾云7号可谓意义深远,它使用java语言将核心业务代码进行了重构,建立了以dubbo为核心的SOA服务框架,使用zookeeper+swoole为核心的业务调用提供机制。
满足新业务使用java语言编写、老业务仍然使用php编写,同时支持两种语言调用dubbo服务的能力。
2、开发语言变迁
在SOA架构的基础上,2016年开始全面推行前后端分离,于是出现了三足鼎立的局面:
1、nodejs:负责前端
2、java:负责后端及老php项目迁移
3、php:负责老项目维护
剩余部分小系统或者边缘化的工具使用其他语言开发,或者在此三种语言基础上的一些变种:
3、疯狂增长的背后
系统架构以及语言体系的变化,随之而来的是项目工程的指数级增长:
项目工程的增长,给软件研发过程管理带来了较大压力,这时候公司开始推行敏捷。
区别于业界流行的敏捷开发模式,猪八戒网根据公司实际情况做了一点小小的变化,即增加了deploy的概念,整个层级变成:deploy->story→task
deploy是story的集合,作为一次上线发布的内容汇总,它主要负责从开发到测试到运维的交付件说明,以及开发-测试-性能-预发布-灰度-线上各环境代码发布的准入准出标准控制,deploy这部分将在后面DevOps中做详细介绍。
业务扩张对运维又带来了压力,大量的项目需要进行发布,所以虚拟机的数量也不断增长,管理难度随之加大,运维人数最多增加到三十多人:
为了降低维护难度,我们开始做CMDB,指定各种规范,同时进行多数据中心建设,业务上做异地双活。
nginx加载upstream进行灰度和生产环境流量控制,DNS负责数据中心切换,能达到在某个数据中心挂掉的情况下快速切换到另外一个数据中心。
敏捷开发、快速增长的项目工程、不同语言的编译构建、不同数据中心的部署和代码发布、不同数据中心和不同环境之间的服务监控以及反馈链路,如何满足这些需求并且将它们连起来,这是猪八戒网需要解决的重点问题。
二、DevOps进化论
为了满足业务快速扩张的需求,2016年末开始组建DevOps团队,集合了运维、配置管理、java技术人员等。团队专门负责DevOps方法论以及技术的落地,全公司使用统一的标准进行软件开发,并使用统一的工具进行项目管理。一张图看猪八戒网的DevOps:
上图中的DevOps是一个平台,它集成了从开发到测试到上线的整个过程,基于这个图重点介绍:
1、新建git工程
在2016年开始,公司搭建了gitlab,后来发现gitlab和svn大家都在用,那么整个发布系统需要支持两种版本库(那个时候的发布系统并不是jenkins,而是公司自己开发的syn2,基于Django框架的python前端,调用后端shell实现编译构建和发布),虽然当时都支持了,但为了统一版本库,降低维护成本,我们关闭了svn,将所有svn项目迁移到gitlab中;
2、构建
针对开发语言的不同,我们提供了12种编译方式,分别为:java、php、nodejs、utopia、scala、gnode、jello、fis、html、tpl、thrift、blg。
针对不同的编译方式,我们提供了不同的jenkins构建环境,当然,我们把它集成在了一起,jenkins采用master/slave的架构,支持任何语言的编译,jenkins将在后续介绍。
3、发布组件
java的组件,构建后发布到nexus组件仓库,原来我们的nexus组件仓库不受控制,开发人员可以随意上传组件,不管是snapshots还是release,所以导致很多release版本问题,后来我们做了权限控制,发布也统一起来,开发人员可以在本地将snapshots上传到nexus仓库,但是release的必须使用DevOps提供的能力上传,这极大的保护了release版本的稳定性。
4、发布API
java的API接口实现,这里有三个功能:发布组件到nexus仓库、自动生成API接口文档、将代码转换为php和nodejs生成物(这里没有历史,只有今生)
这三个功能用户可以一键触发,所以任何部门的API接口,只要一发布,必然有最新的接口文档,避免了接口文档认为更新不及时的问题。
5、发布流水线
流水线的演变是基于测试环境的变化而变化的,曾经我们的测试环境比较单一,流水线也相对简单,但随着业务的快速发展,单一的环境凸显大量的问题,所以我们开始在环境和流水线上进行优化改造。
曾经猪八戒网的测试环境都是由DevOps团队统一管理和维护的,随着环境和流水线的不断优化,后来交接给运维团队负责,目前已经在慢慢将测试环境交给开发和测试自行维护,但大家意识上还是比较多的依赖于DevOps和运维帮忙解决环境上的问题,这里先介绍流水线上发布模式进化的三个阶段:
1)、第一阶段:我们叫它大锅饭年代
这个时期的流水线很简单,环境也很简单,问题点主要也就是上图中的情况。
2)、第二阶段:又叫公交车模式
在这个阶段,我们的系统架构还没有dubbo,这次调整只是为了解决环境共用导致的相互影响,所以搭建了t1、t2、等t系列测试环境。测试完成后将代码合并到trunk推送预发布,在预发布做回滚测试,上线时直接将预发布的代码同步到生产环境,保证预发布的代码和生产环境代码一致。
公交车模式解决了第一个阶段提到的1和2两个问题,但项目仍然耦合。所以我们以公交车模式通过降低线上变更频率【每周二、四15点,公交车司机准点发车,将预发布上的代码同步到线上(并不是将trunk代码发布到线上)】来达到耦合带来的问题;虽然解决了一部分问题,但随之而来的需要大量人工劳动力去维护这大量的测试环境,环境一致性问题随之突出;
3)、第三阶段:也叫出租车模式
为了解决项目的耦合问题,以及环境一致性问题,我们开始给每个项目指定开发负责人,这种责任人制度,很大程度上解决了耦合的问题,大家都开始将各自负责的项目权限收起来了,业务范围和边界更清晰。同时引入docker技术,实现构建一次处处运行,解决了环境一致性问题。分支策略也变得非常简单:branches开发,master发布,tags存档。大家可以在此分支策略基础上做任意变种,满足各个事业部不同开发人员的工作习惯。
在出租车模式发布的各个环境里,使用了在敏捷阶段介绍的deploy,每个环境推送,都必须指定一个deploy,这个deploy必须和你推送的项目工程有关:
deploy必须到了某个阶段,才能推送对应环境,而deploy的每个阶段都有准出检查。
这就像一条河,河上有几个坝,每一个坝都会拦截一部分河里的垃圾,那这条河的下游相对上游肯定是更清澈干净一些,最终汇入大海的水才会清澈,海水就是我们的生产环境。
每一个节点(环境)又有各自的流水线,各节点下的流水线:
所以我们的流水线是二级流水线:第一级流水线对应我们的五大环境(测试环境用户可以自定义环境名称,所以又有子环境的概念);第二级流水线对应各环境所需要的原子任务组合。
所有功能我们做成原子任务,包括校验类和执行类。每个节点(环境)可以自由组合需要的原子任务(必须的原子任务由后台控制,非必须的原子任务由用户自行选择是否添加)
3.1、校验类:我们在执行任务之前,有很多校验(此类任务一般使用java实现),包括前面提到的和敏捷关联的deploy的流程校验,以及安全扫描是否通过,配置中心是否配置,告警中心是否有对应告警等多达十几个校验项;
3.2、执行类:各种校验内容通过后,才进入到执行类任务,包括从gitlab拉代码,编译构建,上传制品库,发布到代码源服务器,docker镜像制作,上传harbor仓库,启动容器等,其中大部分功能是jenkins来完成的。
为了保证流水线的高可用,设计了jenkins的多master+多slave的架构,具体架构情况如下:
因为jenkins的权限机制无法满足公司业务需求,所以我们将jenkins放在了后台,用户不能直接接触到jenkins,而是使用DevOps作为用户入口进行调度。
DevOps集群随机调度到对应的master上,当DevOps在创建jenkins的job的时候会在所有master上面创建;当DevOps在触发jenkins构建的时候会随机选择一个master执行(当某个master异常,DevOps不会调度到该master,当master恢复正常后,后台任务会自动将多个master间的任务置为一致),slave放在kubernets集群中,当slave队列满的情况下可自动增加slave节点(构建docker镜像的节点仍然在虚拟机中)。
jenkins上的任务首先以项目工程为单位建立folder,再在folder下建立对应的执行类任务。我们没有使用jenkins的pipeline的原因,就是因为pipeline统一维护困难,且没有层级划分。jenkins在面向企业级方面还需要提供更丰富的功能。
每日构建次数:每日构建次数包括所有环境的发布次数,平均每日大概在1500次左右。
每日上线次数:每次上线次数为项目发布到生产环境的次数,平均每日大概100次左右,同时支持hotfix发布,随时回滚等。
3、组件库
因为我们开发语言的原因,所以不同开发语言有对应的组件仓库,这在前面的发布API里面已经讲过,主要是java的nexus仓库、php的ppkg仓库、nodejs的npm仓库,这里就不重复了
4、制品库
制品库分两种,虚拟机和docker发布,其中虚拟机发布的我们直接将编译构建好的内容保存起来,供推送其他环境时可直接使用,达到一次构建,处处运行的目的(和环境有关的配置统一放在配置中心,每次发布前通过校验类流水线任务,校验完成配置后方可发布);
docker的是直接构建成镜像,上传到harbor镜像仓库,在触发kubernets容器集群启动容器时从镜像仓库获取镜像启动。
5、虚拟机发布
虚拟机发布是使用rsync服务进行文件同步,将代码从制品库同步到对应服务器,然后服务器上有一个守护进程,会实时监测文件是否更新,若有代码更新则进行重启操作;
6、docker发布
docker发布是调用kubernets进行发布,将事先生成好的yml文件传递给kubernets,然后在对应数据中心启动容器,并通过zbjcheck服务校验功能确保服务启动正常后,再删除老的容器,达到不停机升级的目的。
7、自动化测试
目前DevOps集成了单元测试(包括自动生成单元测试用例)、接口测试、性能测试的功能。单元测试大部分情况下还是需要开发人员写用例,自动生成单元测试用例的功能并不是那么好用,所以用户较少,但在流水线上会自动进行单元测试,并将结果反馈给用户:
单测结果是基于sonar的,在jenkins上执行完单测后连同静态检测结果一起发布到sonar,在sonar上配置了通用的过滤条件,可以过滤掉部分不需要统计单测的代码(如框架自动生成的代码不计入单测覆盖率),用户可以在此基础上配置其他过滤条件。
接口测试和性能测试方面,我们在DevOps平台上做了一个关联的功能,每一个接口测试或者性能测试用例,都对应一个项目工程,关联关系梳理好后可以实现在发布流水线的任意环节触发接口测试或者性能测试。
和项目工程关联:
发布流水线触发接口测试:
发布流水线触发性能测试:
自动化测试的结果展示方面目前做的不够好,只是在日志中提供链接,用户点击链接进去查看结果。
8、CMDB
CMDB作为底层的统一资源管理中心,负责跨数据中心的混合云管理,提供统一的资源管理平台,不管私有云还是公有云,不管虚拟机还是docker都统一管理,同时提供对底层服务的访问支持。提供机器与宿主机的拓补图关系等,对基础设施的管理提供了便利。
一套openstack表示一个虚拟化集群,一套kubernets表示一个容器云集群,每个数据中心有一套或多套openstack和kubernets。CMDB通过数据中心和可用区来标示不同的集群。任何需要和虚拟机和容器对接的功能统一由CMDB提供接口管理。
9、DNS解析
DNS作为附加的功能,可由用户直接点击一个按钮实现域名解析:
域名解析是一个比较复杂的过程,它需要和后台很多系统交互,还需要根据事先定义好的nginx模版生成配置,docker的服务还需要进行动态服务发现,最终将域名信息注册到DNS中。
10、扩缩容
DevOps平台同时支持对虚拟机和docker的扩缩容功能,只要对应数据中心存在对应的服务,就可以进行扩缩容,一次扩容节点不能超过10个,缩容节点数最少保留一个
我们能对不同数据中心的服务进行扩缩容,并且所有的操作都有事件机制,能将每个服务所使用的资源进行归档管理,最终可以对各个部门所使用的资源进行服务器成本费用结算。
11、控制台
从浏览器进入服务器,且通过权限控制,有权限人员才能进入,更加方便排查问题:
12、监控
DevOps已经集成了监控的部分功能,包括接口调用情况、全站可用性、监控数据排行榜、金丝雀分析等
1)可用性监控
2)监控数据排行榜
3)接口调用情况
4)金丝雀分析
金丝雀分析主要用于在代码发布到灰度环境时,分析灰度环境和线上环境的相关指标值对比,以确保新版本代码质量不必老版本差
目前只有以下几个指标数据,后续将不断完善指标内容:
灰度环境和线上环境都承担用户流量,通过nginx的upstream进行权重设置,我们可以根据金丝雀结果做一次上线发布前的质量评估。
三、未完待续
虽然我们完成了DevOps的大部分功能,要讲到技术细节,上面的每一个小节都能用一篇文章来讲解,所以以后我们会不断分享我们在DevOps中的一些技术细节,当然我们也还有很多需要优化和改进的。需求搜集和规划、项目管理和发布关联、自动化测试、金丝雀分析的改进、自助作业平台、CMDB对混合云的管理等都是后续需要改进的,整体的规划图如下:
DevOps平台功能架构分布中,还有很多工具以及功能未描述,上面讲到的每一个点都可以展开为一篇文章做详细介绍,这里主要为2017年做一个总结。
以上内容为猪八戒网最近几年来整个DevOps的发展演进史,公司的规模不同,整体架构以及方法可能不同,对于猪八戒来说,DevOps还在继续演进,同时也会不断学习业界优秀实践,为业务发展提供最基础的保障。
以上是关于历经16年,猪八戒网的双活流量架构演进史的主要内容,如果未能解决你的问题,请参考以下文章