作者注:本文是作者在GIAC全球互联网大会上的一个分享整理成稿子,介绍了微服务以及腾讯游戏数据应用在微服务中的实践,整理时间仓促,如有偏颇,请联系小编修正。
各位架构师们,大家下午好。我是来自于腾讯游戏数据中心的张志欢, 今天主要给大家分享的是微服务在腾讯游戏数据应用场景中的实践。
本次分享大致会分为以下几个内容:
第一小结会介绍下微服务的一些理念以及应用微服务带来的问题;
第二小结会介绍腾讯游戏数据应用datamore是什么,我们是如何在游戏数据领域中应用微服务架构解决一系列问题;
第三小结会介绍我们微服务架构中,mock体系的架构以及应用;
第四小结介绍如何设计devops模块,帮助提升服务的开发运维效率;
最后一个小结会结合一些我们在应用微服务架构碰到的一些难题和初步的解决方案,包括性能优化,调用跟踪优化 以及 容量评估等问题。
总的来说,这次分享是想给大家介绍一些场景,让大家了解为什么我们使用微服务,以及如何利用一些架构解决服务化带来的诸多问题。
从2014年微服务的具体的理念被提出以来,以其业务高度灵活性和强大的解耦设计,越来越多的软件企业,特别是互联网企业都在研究和应用这种架构理念, 对于微服务的落地都相继研发了自己的开发框架和服务化套件。
我们看看国外,像netfilx 的spring cloud, google,ibm,lyft 基于service mesh的istio框架 以及 微软的微服务框架ServiceFabric, 国内的大公司同样也紧随潮流相继推出或开源了自己的微服务解决方案。
比如腾讯的 tars, 阿里的dubbo 华为的servicecomb等等。
总之微服务的热度一直持续到了现在,而国内的互联网公司更是蜂拥而至的宣称自己的底层都已经使用了微服务架构。
这些年也和一些公司和团队进行过一些微服务的交流,发现了一个共性。就是有些团队对于微服务的理念的理解还是仅限于它的字面意思,微小化,服务化。导致一些团度根据这样的理念强制对自己的架构进行了字面化的改造,最后也导致了一系列的问题。
最经典的改造方案莫过于以下这种:
把微服务理解成“接口化”,这类团队的做法是将自己的系统进行接口化封装,将所有的业务模块之间调用改成rpc调用(有一些还会遵循restful的标准)。最夸张的是我有见过一个团队,硬生生的把自己代码中的一些函数调用变成api调用。
这样的改造不仅让系统复杂性加倍,系统的性能也被严重的影响,后续带来的巨大的维护和运营成本也极大的消耗开发者的精力。
那么,我们这里就要提出一个问题,什么是微服务? 业务一定使用微服务就好吗?
想要理解这个问题, 还是要从互联网架构的历史着手。
最早的互联网web1.0年代,大部分的应用其实都是一些静态网站或者低访问量的服务,此时互联网架构大部分是单体服务架构,一台机器+一套应用运行环境+应用服务搞定一切,这种架构的优点是开发更加快捷简单,一个开发人员往往兼职运维人员,一站式完成程序的开发,发布,运维和运营。开发以及调试的效率都比较高,不过一般需要开发人员掌握一些运维的知识。
对于这种应用,存在一个致命的缺点。就是性能瓶颈依赖于单台机器服务的极限。虽然也可以通过不断的优化程序的性能,但是始终都有一个极限。
随着互联网业务的发展,越来越多的应用都要支撑成千上万的用户访问,单机服务已然无法支持互联网的爆发式的增长,这时候发展出集群服务架构。以代理为中心,聚合多种无状态可平行扩容的服务,从而扩展服务能力,支持更高的并发服务。
当然这样的架构下,带来了一系列需要解决的问题需要开发人员去处理。
如:为了支持平行扩容,需要服务进行无状态改造(包括存储(如session)需要用中心化存储代替本地存储,文件,甚至日志都不能单纯在本地存储)。同时,由于多机服务存在单服务不可用的问题,开发者需要制定合理的容灾和负载均衡策略以保障整体服务的稳定性。
上面两种架构的发展针对的主要是服务的性能上面的优化,而互联网的发展,也让互联网的业务变得越来越复杂,特别是企业内部的一些服务,各种逻辑越来越复杂,逻辑之间也互相交错,各个逻辑也由不同团队开发和维护(语言和环境都不尽相同)。
这种情况下,面向业务的服务架构,SOA架构诞生。通过对系统进行面向业务的拆分,通过esb总线进行集成,让服务之间更加独立,而ESB层则专注于服务的集成与连接。这种架构让企业架构有更好的扩展性,各个服务提供者也更加专注于自身服务的优化。
当然,带来便利的架构,一定会带来一些问题需要程序员去解决,这种总线架构下,其实会让所有的业务集成中心化,很多服务都依赖总线的集成开发,从而让这个总线逻辑越来越复杂,极大的增加了维护和学习的负担。而中心化的架构也让所有的业务逻辑流量都经过总线,带来比较大的性能问题。
微服务架构其实是对SOA架构的衍生和优化,不仅具备soa架构的优点,同时将总线层弱化,以服务组合各个服务,达到更加好的扩展性。
相对于soa架构,微服务带来的问题更加明显:我们例句几个问题:
1. 服务管理的问题,这么多服务进行错综复杂的调用,如何去管理他们(比如流量的管理,服务间的权限控制,路由管理等等);
2. 服务集成问题,使用何种方法可以快速将服务进行组合,同时提供何种工具,让这些服务开发者快速将应用开发上线;
3. 问题排查,不同于单应用可以使用各种调试工具,服务间大部分通过rpc调用结合,各个服务甚至连开发语言都完全不一样,如果除了问题,怎样定位;
4. 容量管理,由于每个上游服务都会调用海量下游服务,服务之间存在各种请求放大,当一方服务进行扩容时,如何对后面的服务进行容量估算;
5. 测试问题,等等。
所以总结起来,越是简单的架构,管理维护起来越简单。但是简单的架构未必能够解决复杂的问题,而复杂架构的出现,是为了解决特定场景下的问题而生,但是你却要花费更多的时间去维护这份复杂。
对微服务概念的,我的理解是,如果你宣称你们的业务实现微服务化,除了服务化屏蔽语言差异,除了对业务的拆分,还需要解决微服务带来的一系列问题(高效集成部署,海量服务治理,快速问题排查,服务柔性可用,服务测试,容量管理),这些也是信通院组织国内微服务应用厂商推出微服务标准的原因。
我们可以看下标准里关于微服务化的描述,包括分布式事务,服务注册,服务契约,CI/CD,CO,服务治理,熔断隔离,链路跟踪,网关等等),如果完全按照标准去自己实现,那实现微服务架构也未免太难了,所以,企业可以根据自己的实际应用场景,选择性实现一些个性化的服务,而一些标准化的服务,可以依赖于业界开源的一些产品。
感谢业界底层技术的发展,我们不需要对于上面每一项的技术都要自己独自去实现。
K8s+ Docker技术让我们可以对服务进行更快的部署、更细粒度的资源分配以及更快的扩缩容,pipline极大的提升的服务开发和集成的效率,微服务网关帮我们干了大部分的服务治理的活儿,分布式事务组件解决了服务事务依赖, consul解决了服务注册发现以及服务治理策略的存储下发问题,微服务开发框架集成了调用跟踪,日志等服务。
所以大家可以看到,实现一整套微服务架构并不是简单的进行api拆分,或者业务逻辑拆分就够了。了解完微服务的概念后,我们还需要知道另一个重要的点,就是究竟微服务解决了什么样的问题?什么场景下使用这种架构?
终于要到我们的正题了也是本次分享需要重点阐述的,我会以腾讯游戏数据应用DataMore为场景,以实践为例,给大家解释这个问题。
腾讯在游戏最早是使用以hadoop, spark等离线计算引擎,基于离线计算能力,我们设计和创造了腾讯游戏数据分析平台 iData,包括数据报表功能,提供海量的,通用的数据报表。多维分析功能,能够让用户自助化完成数据的透视分析操作,便于定位运营问题。用户画像服务则提供游戏画像分析能力,让业务能够精准的了解游戏人群。最后的数据提取服务支持快速海量用户多条件的提取。
这一系列的服务为我们的游戏业务提供了决策辅助以及通过数据发现和定位运营问题的能力。市场上的一些数据产品像神策,talkingdata,thinking data等都是提供此类服务的。
随着大数据技术的发展,以storm为代表的各种实时计算框架也相继推出,相比于离线计算,实时计算无法做更加复杂的数据加工,但是却可以对数据进行更快的加工。这一点非常吸引我们。
我们一直在研究这类计算是否可以让大数据产业发生更大的变化,特别是在游戏行业中,是否可以体现更大的价值。
为此我们针对腾讯游戏大数据实时应用的场景进行了一系列的调研,最终提出datamore数据应用服务,目标是利用实时数据和离线数据,为业务提供更具价值的服务。
我们知道,游戏的服务分为两部分,一方面是游戏server服务,另外一个是游戏运营活动服务。
游戏server服务一般负责游戏核心逻辑的开发(比如副本系统,聊天系统,社交系统等),而运营活动服务一般是一些特殊节点的活动(如一些节日促销,抽奖等活动)。
游戏运营活动非常依赖于游戏服务器提供的各种接口,如用户数据接口,发货接口等。运营活动端一般根据一定的逻辑组合这些接口。
这样带来的一系列的问题:
1. 运营对研发侧的依赖非常大,很多个性化的接口(一般都是数据相关的接口,比如查询xx用户的数据,查询XX资格,查询排行之类的),都需要研发侧封装。而研发侧封装完成后需要通过版本发布。这样对于运营来说,节奏不可控(有些活动可能要等待版本发布);
2. 是运营的特点就是变更频繁,而为了保障游戏服务稳定性,服务变更都是走版本,甚至停机维护;
3. 是游戏的计算能力有限,一般游戏内都是存储一些最终值,计算也相对简单,对于一些复杂的计算,比如任意时间段的和,他们需要做非常多的工作。同时,更加海量的数据加工也会影响游戏服务器本身的效率;
4. 精细化运营能力有限,由于很多精细化能力依赖于大数据的复杂离线计算(比如:用户画像与标签,推荐能力)等,所以游戏业务一般只能做标准化通用的运营服务支持。
为了解决这种问题,我们构建了datamore数据应用服务的架构,核心在于两点:
1. 数据存储方面,提供游戏标准化日志服务,让业务能够通过日志的形式或者我们主动采集游戏日志的形式,将数据上报到数据中心;
2. 标准化游戏服务接口:统一全业务的发货接口,将接口服务能力接入到datamore服务中心,底层我们通过日志,将整个游戏数据计算能力搬离出游戏。同时与业务对接标准化的营销触达能力。打造了数据应用服务标准集。
在这些标准服务上,我们构建了datamore应用中心,它由一系列的标准化数据应用系统构成应用集合,让游戏运营能够自助化的使用这些系统进行快速高效的运营。系统之上,我们沉淀了海量的运营方案,业务用户可以选取这些方案,而每个方案都会对应一个或多个应用系统,用户选择方案后,方案自动给系统填充内容。
比如你选择了一个用户关怀方案。方案会自动选择用户关怀系统,并自动选取易受挫用户以及受挫指标(如连败xx局,胜率低于XX),并在这些用户实时达到这些条件后给予用户奖励或者干预,提升用户的留存。
这样,我们打造了一整套独立于游戏的数据营销应用引擎,由于系统底层通过游戏日志与游戏解耦,使得建构在datamore平台上的应用都具备不依赖游戏版本(配置化计算),超强数据计算能力,更加丰富的计算内容(比如游戏内走了多少步)以及精准化能力。
这个是datamore的产品架构,我们可以看到,datamore分为三层,最底层是我们的基础能力包括用户触达,分析,实时&离线计算,营销服务。基于这些服务能力,我们建构了三大方向工程化服务应用。
包括用户实时运营系统,游戏数据功能以及创新数据玩法。这些应用通过对底层能力的封装为业务提供了一系列数据服务。
那这套架构和我们今天的主题,微服务有什么关系呢?
大家可以看到,我们所有的工程化的系统,都是依赖于底层能力去构建的,上层应用都来自于底层能力的组合和集成,这些底层服务天然是独立的,非常符合微服务架构。
设计这些底层服务,在我们的场景下需要解决几个问题:
1. 这些底层能力是由不同团队开发的,他们的开发语言,开发环境各不相同(比如实时计算服务使用java开发,数据分析服务使用c++,用户触达使用go开发);
2. 由于上层系统会相对复杂,他们会整合越来越多的底层能力,如何管理这些庞大的能力(如游戏内一个任务系统会同时用到实时计算,游戏营销(奖励能力),用户触达能力,画像能力等数10种能力);
3. 单个服务面临的是腾讯游戏千万级的用户访问,如何能够更安全,更低成本的支撑?
这些问题都指向了一个方案,就是微服务化,将这些能力以微服务的形态组合在一起,同时提供微服务支撑全套服务以及治理能力,以restfu其他形式统一服务接口,为上层提供高效,稳定的服务。
一般要将模块进行服务化改造,使用一些开源的微服务框架,比如springcloud,dubble等就可以了,这些框架底层会集成微服务的基础中间件(如负载均衡,服务发现与注册,流控等),使用这些框架以及框架自带的一些工具,可以快速将系统以微服务的架构落地。
对于我们来说,使用这套框架的成本比较大,原因有几个:
1. 数据运营相关的支撑团队比较多,各种团队的开发语言,开发环境都不一样,如果使用开发框架的方式,无疑需要准备多种语言和环境下的开发框架,对于我们来说成本比较高;
2. 对于那些已经使用另外的一些框架开发的服务来说,迁移成本比较高,需要将代码迁入新框架;
3. 框架的维护,升级是一个比较大的工程,侵入式太强。
基于这种情况,我们采用的是servicemesh架构,在我们的devops模块中,当你服务发布时,会自动创建一个代码网关sidecar部署在目标服务同一个pod中,通过iptables等方式劫持流量,将所有的流量管理的能力都放置于网关中。后面有一个大的服务治理中心负责管理所有网关的行为。
对比了目前市场上的一些网关服务(如linkerd,envoy等),我们最终选择了开源支持度较高,插件比较丰富、并且性能较好的envoy作为服务网关并对其进行了一系列优化(性能,插件等),使用他为微服务提供(服务监控告警,调用跟踪,日志服务等)。
同时,也开发了一个相轻量的微服务框架Dori组合成微服务基础。
同时,我们以consul为基础,搭建了一整套的微服务配置中心,负责管理和下发微服务策略。
刚才我讲的是利用微服务的理念,对底层的可复用的能力进行服务化改造,通过使用微服务的能力,为上层提供了统一的,健壮的公用服务支撑,利用这些独立的微服务,我们可以开发各种各样的应用,可以更低成本且不用重复建设。
现在我们需要面对的一个问题就是,如何能够更快的组合这些微服务成为一个业务应用,这个就需要提到服务集成的概念了,只有将服务集成与服务治理搭配起来,才可以为开发者提供一站式,高效率的微服务体系建设能力。
传统的方案类似于SOA的方案,各个业务逻辑独立以api服务的方式为集成中心提供服务,而应用开发人员通过一个统一的业务集成中心通过开发或者配置的方式组合这些业务逻辑,将业务逻辑开发完成后,通过统一的网关提供服务。
这样的平台在传统的企业架构中非常常见,优势也非常明显,不仅通过业务集成中心和业务逻辑的拆分让企业业务单元解耦,同时集成中心提供了统一的开发方式和对外接口使得前端对接更加标准化和规范。
但是对于互联网或者我们的场景中,这种架构其实存在一定的问题。
1. 是随着业务集成中心中的业务逻辑越来越多,其中的功能也错综复杂,集成的业务和业务之间甚至会存在不规范的互相调用,导致业务集成中心任何一个业务的变更或者平台本身的变更,都会影响上层大批应用;
2. 是开发成本高,往往集成中心会指定开发的语言和环境,同时为了兼容广大的开发者,这些集成系统会越来越复杂,开发人员的上手成本也会更高;
3. 上手成本高,意味着维护成本也很高,同时由于业务的兴衰,导致集成中心存在大量不会使用的代码,想要清理需要考虑里面的错综复杂的关联,基本上日积月累,平台会变得更加臃肿;
4. 第四个也是对于互联网公司的一个很大的问题,就是这种中心化的部署,在互联网应用的响应快,高并发的场景中,性能堪忧。
我们对于这种soa架构进行了优化,参考了云平台的设计方式,进行了更加细致的分层:
底层是iaas层,对接各种存储,网络,计算资源,具体来说就是各种云iaas服务。第二层是paas层,以微服务的形式存在的paas,它不仅仅是一个api,为了让上层开发人员使用他们,他必须满足paas的标准,即需要提供标准的接口描述文档,资源申请流程,控制台用于配置api,权限控制等,这样开发人员可以以“服务”的形式使用他们。
在上层就是我们的核心层,我们称之为ipaas,即integration paas。我们对这一层的定义是:一站式的微服务开发和组合的平台,提供从开发,运维到运营和治理的一站式服务,我们可以看到,这个平台应用具备devops的能力,服务编排,代码管理,自动化测试,治理等能力,利用他,我们可以开发我们的paas服务,同时也可以将paas组合起来成为saas应用,利用ipaas开发的服务都是以微服务的架构存在的,用户无需关注微服务的具体实现细节,平台测会帮助完成。
这样的分层我们可以看到,ipaas 开发的应用都是以独立的微服务存在,他们通过sidecar的方式直接连接paas,服务治理能力都几种在网关当中,项目之间完全独立且去中心化,非常符合我们的互联网应用的场景。同时paas+ipaas方式可以让开发人员更低成本,低门槛的开发复杂应用,让更小的团队通过这样的架构更快的进行创新迭代,提升创造力和创新能力。
刚才我们讲了微服务的架构,应用场景以及需要解决的问题,我们也看到,市面上针对这些问题都有比较好的解决方案或者开源框架来支持。
这些都是基于某个特定问题给出的解决方案。一般都是直击痛点,而且研究深度都不错。
但是在实际中,我们解决问题一般是以场景的方式来考虑的,我们可以通过建立一整套架构并且结合市面上的一些开源框架或者软件,搭建一套面向场景的解决方案。
所以下面,我会以场景化为主线,告诉大家,如何组合一些能力,解决实际问题。
第一个场景化是我们搭建的一套基于mock的架构,用于提升开发效率,测试效率;
第二个场景是我们搭建的一套devops架构,用于帮助不同语言和环境开发者完成他们微服务架构落地。
Mock这个词大家都非常了解,在一些前后端开发分离的团队,后台开发经常会使用一些mock数据去伪造接口返回,从而达到前后端并行开发的目的。
往深里来看,数据模拟这个功能,远不止用于前后端开发,下面是在我们的微服务架构下,mock能够帮忙解决的问题。
开发阶段(并行开发,开发自测)测试阶段(多分支测试,故障注入) & 运营阶段(服务容量评估以及服务降级)。
那就有人会问了,不对呀,mock还能做容量评估?这个不是全链路压测做的事情嘛。某种意义上,是的。其实我们在搭建整套基于mock的体系中发现,只需要添加一些模块,就可以复用mock架构的链路,实现更多的功能。
我们可以看下单一的mock功能如何解决我刚才提出的问题。mock仅仅是数据模拟服务,一般用于开发之间(如前后端)由于协议文档对接的一种方式。后端开发人员在未开发完接口之前,通过一个mockserver配置自己的模拟接口以及返回,提前提供给前端,前端人员开发按照这个模拟数据开发完后,后面只需切换到正式环境的域名即可完成前置开发。
当然mock机制还可以用于在测试场景当中,比如一个服务需要同时调用3个子服务,我们可以使用mock策略强制让其中两个服务返回固定的数据,这样就可以完成对第三个服务的单元测试而无需修改你的代码。
第三种是用于线上运营过程中,mock可以用于服务降级当中,由于mock数据大部分是静态化或者存储在redis中的数据,所以它自身能够抵御高并发的读取。我们可以为一个服务配置一个mock降级数据,如果某一个服务出现不可用,通过网关可以路由到mock数据。比如 你的服务一个分类列表服务不可用,可以配置一个常用的默认分类,用于返回默认分类,即可完成降级。
上面是mock使用的一些场景,如果自己独立开发市面上都有一些组件供大家使用,但是如果是集成到我们的ipaas平台中,就需要解决一系列的问题了。比如怎么样才可以让开发者无需修改自己的代码就可以使用Mock功能,海量服务组合的场景下如何能够使用mock完成多链路的测试,如何让降级服务配置化自动化。
达到这些可以让开发者更加简单的使用mock完成他的开发,测试以及运营。但更加高阶的玩法是使用了这些mock的能力后,如何才能更加直观和全面的观察结果,比如使用mock进行全链路测试后,如何才能根据测试结果找到系统的性能瓶颈,找到服务的能力便捷。这些都需要一系列的工具链支持。
这个是我们mock体系架构:
核心是依赖于servicemesh中的微服务网关来完成各种mock策略的执行,配合几个部分:
1. mock中心:
上层是mock构造器,通过图形化配置mock数据,上面支持几种策略的配置,如数据模拟配置,故障注入配置以及 mock降级配置。这些配置文件会存储起来,模拟数据会通过mock标准化接口提供对外服务。
故障注入以及降级配置会通过配置中心下发到相关服务的网关当中。
最上层是压测中心,和mock中心一样,他也是基于ipaas开发的微服务,拥有独立的资源管理能力。上层有几大功能模块,包括数据录制与重放,数据构造(使用mock管理端配置),数据引流模块以及数据导入模块。
通过顶层的压力输入与流量控制模块,将数据塞入微服务链路当中。
不同的是,构造的数据会连同自动嵌入到header头中的mock策略(故障注入与路由策略)进入微服务调用链条中,各个微服务会根据预先配置的mock策略,自动选择哪些服务使用模拟数据,哪些服务需要模拟故障,整条链路的所有数据反馈会通过统一日志上报到数据分析中心。
数据分析中心有三大分析利器:
第一个是基于ES与strom的调用跟踪服务,用于观察整条链路的调用情况;
第二个是基于storm+durid的性能统计服务,用于进行业务重要指标的统计;
第三个是基于promtheus以及grafana的实时监控,用于监控服务粒度
的指标,如QPS,延迟等。
这三大数据分析利器可以全方位的帮助我们观察,定位以及分析整体服务的稳定性,更好的是,由于配置和执行都是基于sidecar执行,让所有的服务都无需做任何的代码修改,通过配置化即可完成,提升了效率。
这边讲下这套体系的应用,其中一个是用于我们服务的单链路的快速测试,实际我们的场景中,我们经常需要单独测试服务的某一条链路,比如我做了一个报名排行发奖的服务,他会按照一个流程分别调用报名服务,排行榜服务以及发奖服务。
由于报名服务是一个经过测试的稳定服务,我需要观察的是排行榜和发奖的逻辑是否正常。这个时候,通过mock策略中心结合压力中心,我们将mock策略配置好并将在请求入口中配置mock策略。
我们的请求在途径报名服务的微服务网关时自动路由到mock数据,返回已报名。然后在请求另外的服务。这样就完成了单链路的测试。与此同时,前面的请求数据都会通过sidecar上报到回放服务中。只需点一个按钮,在你修改了业务逻辑后,压测中心将会回放您在一段时间内的所有请求。
这样的架构下,测试变得更加方便,无需侵入,仅需配置即可完成mock策略的下发,执行以及重放。通过mock策略可以屏蔽一些你不需要测试的服务。
这套体系衍生出一个比较方便的应用,借助这套链路,我们开发了用于测试数据的自动化清理模块。
这个场景是这样的,开发人员经常要对已经集成的服务进行测试,而测试过程中往往会产生很多的测试数据,这些测试数据在使用过后往往需要清理,一般清理的过程比较繁复,比如找到服务负责人清理,或者编写逻辑脚本等,为了解决这个问题。
我们这边在我们的服务网关上做了一层透明数据代理,所有的对于数据存储的请求都会拦截下来(比如redis,mysql,hbase),每次进行数据库插入删除或者修改都会自动记录undo日志,存储到测试数据清理中心(当然,对于一些比较复杂协议的存储,我们也提供sdk的方式– 这种方式的好处就是无需对协议解析坏处就是有一定的侵入性),测试数据清理中心接收清理命令,可以按照时间段,存储类型以及一些自定义的规则自动去清理测试数据。
这样,我们的开发人员在进行测试时候就无需关注如何清理数据了(特别是redis这样的kv存储,清理起来确实很费事)。
第二个在微服务平台中需要关注的地方是devops,这个是我在wiki上摘录的关于devops的经典的解释和图。
我们可以看到,所谓的devops是建立一套快速,频繁,稳定的构建测试,发布软件的文化与环境,个人愚见,任何的文化或者流程都比不上有一套好的系统,让大家使用过程中就能感受到这个流程,从而最大化的发挥流程的作用。我们可以看到,一个标准的devops流程包括 代码编写,持续集成与构建,测试,部署,运营和监控。
那为什么微服务需要这一套机制呢?
1. 微服务化场景下,开发一个应用等于组合这些强有力的微服务的过程,这让更小的团队能够开发更大规模和难度的应用,低成本的同时,也保持了小团队的创新力。最终的影响是,服务的迭代和发布更加频繁。
这种情况下,传统的集中式的运维场景无法满足这种需求;
2. 由于微服务之间使用标准rpc,如http接口进行交互,导致不同团队之间,开发环境和开发语言不同,一套标准化的,统一的运维系统无法满足这种需求
3. 是在互联网快节奏的产品开发场景下,开发和运维的界限更加模糊,一个业务开发人员往往需要兼顾开发和运维运营整个流程,这个时候,需要有devops,让他们能够一站式完成微服务生命周期
4. devops相比于传统的开发运维场景,他们提供标准化安全和质量工具(如代码扫描,安全扫描),可以实现更加自动化和低门槛的安全保障。
对于复杂的大型互联网应用,他的集成和发布一般是非常复杂的,各种子系统的相互配合,上下依赖以及大量的配置文件同步,甚至人工介入到发布中,动辄从晚上发布到第二天。为了提升集成的效率,同时,更为了规范集成&发布流程,很多大型互联网团队都会开发类似这样的复杂的构建集成部署系统。这类系统的优势在于将很多业务封装成一个个模块,以流程化的方式调度。同时也便于让运维了解系统的集成部署的流程。
但是它也存在许多的问题:
1. 上手比较困难,各个模块都需要特别了解;
2. 由于系统非常复杂,需要很多人工参与的部分,往往这类系统无法做到完全自助化,需要专门运维配合才能集成部署;
3. 此类系统往往和业务结合比较紧密,一般没有通用的标准化流程,可能换一种业务整合系统都需要修改,同时扩展性也比较差。
而在微服务场景下,事情就变得大不一样了。我们可以看一个经典的微服务架构。
我们在做一个游戏内的排行榜的服务,这个服务由许多微服务组成,比如报名服务,排行榜服务,奖励服务等等。这些服务之间相对独立,单个服务职责单一简单,一般都是以http-restful形式提供对外服务。
只要服务之间对接上,单个服务发布对其他服务依赖较少。各个服务开发语言,环境不相同, 服务的变更频繁。
这种情况下,使用原来的方案,效率非常低下,表现为:
1. 各个服务集成,发布相对简单,不需要这么多组件干预;
2. 各个服务环境不一样,一套系统不能对所有服务通用;
3. 服务发布频繁,需要自动化以及开发自助化。
所以在这种场景下,我们需要抛弃以往复杂的ci/cd系统, 拥抱微服务的devops。
我们的devops方案本质上也是以微服务为基础搭建的解决方案。大家可以从上往下看。
最顶层是我们的devops原子服务,包括构建服务,代码扫描服务,部署服务等,这些服务都是以微服务方式组织,对外提供restful的接口。
下层的是我们的调度引擎,我们以jekins定义的pipline为标准调度语言,实现了一套调度框架。可以根据标准Yaml文件进行调度devops原子。所有的产出都会在devops的产出模块输出(比如度量数据,监控数据,日志数据以及生成报告的服务)。
在下层是我们实现了一整套的编排UI,利用这个UI可以拖拽devops原子服务,通过连线的方式完成调度语言的生成。开发者可以在这个截面上自定义属于自己微服务的devops模版(如php项目模版,go项目模版等)。
根据这个模版创建的项目实现自动化构建,以及部署。
这个是我们整个平台的devops的架构,基本上满足了wiki上关于devops的定义,实现平台的集成部署的自助化和自动化。
持续集成和交付我们使用pipline实现,从代码拉取,质量管理,编译,测试 到最后的通知阶段,每个阶段都有多种插件让用户自定义。完成CI/CD TEST后,平台将会将程序打包成镜像。
然后在CD模块中 通过K8S 将镜像部署到现网环境中(期间会有一些自动化的操作,比如路由呀,自动灰度来支持)。发布完成后,整个CO模块将会提供基于日志的实时监控,日志模块,告警模块以及扩缩容模块的支持。
在这里我们有一个和业界的有一点不同,那就是CD模块我们并没有放在pipline中,理论上一个设计比较好的pipline应该可以覆盖整个 集成到部署。我们这么做的原因还是因为业务场景限制,因为我们的所有的服务其实都和数据有关,服务之间和其他服务调用也非常复杂,一个发布可能涉及到别的服务的调整(最复杂的就是容量的评估),所以其实我们并没有完全做到自动化部署。所有的服务部署都会通过另外一个评估的系统,在评估完成后还有一些资源的确认以及发布的协同才可以部署。所以我们这边将CICD,test实现了基于piplie的自动化,而部署和运营我们都以独立的模块支撑。
这一章,主要是给大家介绍我们做微服务化面临的几个问题以及预期的一些解决方案。
第一个是我们采用service mesh架构场景下,对这种架构做的一些性能优化;
第二个是对于调用跟踪的设计和一些优化。
这两个场景都是我们实践中经常用到的场景,由于腾讯游戏的数据应用是针对所有游戏业务,并发量和性能都要求很高,对于网关性能要求就非常高,同时由于数据量大,调用跟踪服务因此产生大量数据从而承担更大的压力。这些都是高并发服务下必须面对的问题。
在上面的分享中,我们有给大家提到service mesh的概念,为了降低微服务治理的开发成本,我们使用sidecar模式部署envoy网关到各个服务中,所有服务之间的通讯都会经过网关。这样使得网关的负担也增加了许多。目前来看,影响网关性能主要分为几个部分:
1. 我们为网关添加了许多插件,比如鉴权服务,代理服务,路由服务等,这些插件会让服务性能受损;
2. 通过配置中心会给网关同步许多配置(比如名字服务,熔断配置,路由配置等),这些配置越来越多,经常也会有变更,严重影响envoy的性能;
3. envoy本身在服务转发上也存在一定的性能开销。
所以这几块的问题加在一起,其实影响了微服务的性能,
这里我们针对这三个方面都做了一定的优化以及有一些预期的方案在探索:
1. 我们的中间件实现按需编译,每个服务的部署都会根据他在平台上选择的服务编译到envoy中,和服务逻辑同时发布;
2. sidecar中的策略按照 全局配置,服务配置以及业务配置进行分级,通过配置中心进行分级策略发布,做到精准策略推送;
3. 尝试使用dpdk 或者 ebpf + xdp的方案对底层进行优化,减少数据在内核态到用户态的复制。(可以提升超过50%的性能);
4. 还有可能就是协议的优化,比如使用quic协议代替TCP+TLS。
另外一个比较常用的就是调用跟踪模块,和大部分调用跟踪功能类似,基于opentracing标准提供api和各种语言的sdk开开发者,开发者通过sdk自动生成traceid+ spanid 以及附加的信息上报到队列中。一个实时计算服务消费队列并将数据丢入es中,前端使用开源UI jaeger提供查询聚合。
一般这种架构可以适应大部分的情况,不过很多市面上的文章或者分享对于其中的一些细节没有做详细的阐述,其实在高并发场景下,调用跟踪的设计还是有很多优化的地方。
一般集中在两个优化方向:
1. 是请求量过大带来的数据量,带来查询的延迟或者需要大量的机器来存储这些数据(特别是对于es这种全索引的存储,写入的性能比较差,导致结果大量延迟);
2. 由于调用跟踪侵入式比较强,需要修改许多代码才能使用这个功能,提高了使用这个能力的成本。
目前我们有几个地方的优化:
1. 是入口进行采用,采用一定的算法(算法也可以通过离线计算得到一个模型,对于重要性比较高的采样率要调高一些)或者按照百分比+ 最低采用条目进行采用,提高有效信息采集成功率;
2. 提供一套标准存储日志协议,服务打印日志到本地,angent负责采样。一方面angent可以进行一定的优化(比如 错峰上报,满足条目上报等),另外一方面agent也可以采集一些自身其他信息上报;
3 是在开发层面统一业务的开发框架,这样可以在框架层面上实现自动trace传递。当然,我们也在探讨其他的方式,比如类似于pinpoint或者c#注解的方式,在我们的devops模块的代码编译插件中进行自动调用跟踪替换;
4 是设置一个开关,只对错误信息进行上报。
当然,这些多是为了提升调用跟踪的吞吐量以及查询效率,但是个人认为,根据调用跟踪查服务间的问题只是调用跟踪很小的一个方向,真正调用跟踪能够发挥他大的作用的地方,还其实单纯的调用跟踪查询服务只是调用跟踪的一个很小的应用场景,调用跟踪发挥更大的地方应该在于对于数据的统计和分析,如:
服务运营中,目前我们对于调用跟踪的应用有以下几个方面:
1 性能分析;
2 异常调用分析;
3 路由分析;
4 容量校正;
5 等等等。
这些会在后续相关的分享中给大家介绍。