@gaoxiaoyunwei2017
2020-03-17T17:43:52.000000Z
字数 10129
阅读 769
白凡
作者简介
王维栋,网易游戏监控团队负责人
接下来由我给大家做一个分享,我上午讲的都是比较宏观的东西,把干货留在下午。现在我们简单跟大家介绍一下我们是如何在游戏这边做到千亿级的游戏监控体系,还有我们在智能监控方面的一些探索。
我是网易游戏监控团队的负责人,七年时间一直在做运维平台相关的开发工作,我们擅长的领域就是智能监控领域以及应用性能调优相关的东西。
简单来说我们会分为四个章节说这件事情。
首先就是游戏领域的监控会有什么区别,全球布局游戏监控又有什么样的挑战?我们面对海量的时间序列时我们是如何做处理的,第三点说说我们在可视化和报警方面做的比较有亮点的地方,最后讲一讲我们在智能监控方面的实践。
首先说说传统游戏架构是什么样的,以前游戏架构都是单体架构,比如大家非常熟悉的《梦幻西游》,就是单服单机器,承载量非常有限。另外基础设施现在比较单一,我们之前基本都是物理机,另外基本就是瞄准国内市场做事情,最后监控的层次也非常简单,无非就是硬件、网络、操作系统、进程、业务指标。
我们的产品优势蛮清晰,但现阶段而言我们实在变化的太快了,游戏的架构多样化,另外一个方面就是混合基础设施在我们公司逐渐出现。还有就是我们公司开始立足海外,在海外有一个非常好的增长,最后就是传统监控,在可观测性方面去扩展。
说话说一下游戏架构的变迁,从最开始的单机架构,我们后来扩展到分布式架构,也就是说玩家看到一个游戏服务,在后面会有十几台机器,有的甚至多达百台机器,取决于玩法不同。后续很多游戏的开发就会接触到微服务这些概念,开始逐渐的把游戏里面比如说大厅、聊天服务这些东西从游戏的核心逻辑里面拨出来,然后变成一些微服务的组织,对游戏服务提供一些支持。这种情况下微服务场景开始逐渐在游戏场景里面出现。
第二方面我们事实上一直在做游戏上云这件事情,一开始我们是在物理集团部署游戏服,后来做一些私有云,私有云在虚拟机上部署一段时间之后,我们在出海的过程中开始逐渐采购海外的公有云IDC的机器去用。
后续开始做容器化,在容器化进行到一定程度,现在有一些游戏开始尝试云原生了,但是我们这个过程不是一蹴而就的,毕竟体量比较大,而且一个公司可能会有几百个游戏,在这种场景下就会出现一个混合云的状态,就是说有些游戏还是物理部署的,有些游戏已经云原生了,这种情况下挑战是非常大的。
另一方面我们公司目前的游戏业务已经覆盖到全球数十个国家,监控也会有二三十个region去覆盖到全球的游戏服务;另外,我们会在海外采购多个云服务商,这种情况下监控的挑战也会逐一增加。
从传统监控向可视化过渡的过程中,我们不仅有报警、可视化,还有debugging、profiling。尽力可让系统更加透明,更可视化,从而形成更好的理解,去优化我们的产品,更好的度量,形成一个良性闭环,最终优化我们的产品。
基于上面这些思路,我们目前的游戏监控架构是这样一个样子,简单介绍一下,从小到上是一个监控数据,从产生到处理到消费是一个过程,然后右边会有一些控制层和统一的东西,这里面只划出最关键的一些点。
我们采集层面会做很多数据入口,比如说有SDK,agent,有日志指标,还有第三方数据库,通过多区域部署的就近介入层,去把这些数据接过来导到中央去,中央会用一个kafka的数据队列做解耦和路由,上面支持到多系统数据的订阅,还有聚合层面的一些事情,一些数据存储,往上层,我们逐渐迭代过程中,是有一些历史包袱的。
我们一开始是看业务场景来做监控的,所以就会出现一堆数据子公司,比如说有客户端监控,用户体验的监控,有服务端监控,有资源相关监控,有网络监控,还有性能优化的监控。
这一堆东西我们目前正在逐一整合,并且对外提供统一人力。报警层面其实我们一直基于一个标准化的规则引擎做报警这件事情,现阶段我们也在逐渐去把异常检测,事件关联这些事情加进去,也会有像问题生命周期关系和故障结合,以及一些事件升级,确保问题可谈和不可谈,被处理一些机制。
最上层我们提供一些数据可视化的功能,报警功能,还有实施分析,还有性能优化等一系列能力。重点说控制层,这个控制层面我们做到跟CMDB深度结合,然后去订阅CMDB变更去减少监控的配制成本;另一方面会有一些区域管理,全区化的监控。另一方面我们的anget能够支持到丰富插件的自定义,所以这里会有一个插件仓库。
最后我们做了一个命令管道,其实就是类似于像Ansible,pipeline这些东西,只不过是跟我们的agent相互集成的,然后这套东西最直接的价值就是我们上面说的配置的分发还有故障治愈这些东西,都可以依托这样一套基础架构基础线。
简单说一说我们面对海量时间序列的时候做的一些事情,这里简单列几个点,首先就是面对海量,而且异构的一个监控录像,我们去做了一个监控对象的抽象,不完全直接面对基于虚拟机这样的概念,而是尝试去做一套通用的抽象,把所有的概念都能够自定义落进去。
另一方面我们做到一个低延迟高准确性的接入,通过与CMDB的结合,我们做到比较小的管理成本,给出多种采集方式去适配不同的业务场景,然后做了一个多采集的入口,并且在统一的数据总线做了数据的对齐、预处理等事情。
最后我们做了一个面积比较大的,海量时间序列存储,首先来说监控对象抽象,为什么要做这件事情呢?首先我们常规场景面对的事情就是监控一个物理机、虚拟机、容器,再到一个进程。对于硬件还有CPU,一个网卡,只要能标注它,能给它把数据关联上去就可以了,随着改变不断的扩展,在游戏场景里面要监控某个游戏场景,要监控某一次battle,某一个NPC的属性甚至像游戏进程之间IPC的顺序。
这种场景下如果我们不停的去写死了,适配一个两个三个的场景,对于监控人员来说开发成本很高,所以我们做了一层抽象,其实也跟业界比较流行像OpenTSDB、Prometheus有很多做法有共同之处。我们把所有的监控对象抽象为一个EntityType,描述它是属于什么类型的,然后tags描述它的属性,同时tags也会有一个类似交叉表的功能,就是把entity关联在一起。
目前我们的规模大概是100多个EntityTypes,差不多500万的entity监控对象,差不多4亿级别的timeseries。刚才讲到全球就近接入层,这是一个最基础的架构,。最中间的红色是一个Arbiter,它的一个角色就是仲裁,它负责去订阅CMDB的变更,产生一些配置,监控配置就会被分发到每一个region里面,当agent入网的时候会先去询问Arbiter,我是属于哪个region的?然后Arbiter会告诉它一个配置,告诉它node列表,尝试连接,成功就会入网,入网之后就会把它产生的数据全部交给Node去中转,到我们中央会做很多网络优化,会比它直接连到中央会快很多。
另一方面保持长连接这个有助于配置变更的时候,实时从Arbiter这边推送配置下去产生变更,这一套架构我们做高可用的保障,首先Arbiter这边是一个单点,我们做了Arbiters Active-Standby的模式,一个主备的模式,出现问题的话会马上起来接管主Arbiters的东西。
Arbiter这里面跑的所有容器基本都是幂等的,所以不会担心数据和集群一致性的问题,另一方面node会和Arbiter保持心跳,如果这个node失联了,就说明它挂了,我们的Arbiter调度到其他的节点上去。
另一方面,每个region里面会有多个node冗余,在node之间分配agent时,我们用了agent hash,去确保增删node时,尽量少的agent分配的抖动。除此之外这一整套流程,配置管理一部分和数据流的一部分是分离的,如果上层Arbiter和node和中央完全失联的情况下,agent仍然是不会断连的,会跟node一直保持连接,直到接到新的配置分发。这种情况下,即使我们中央出现了毁灭性的问题,agent仍然会上报数据上去。
这里说一下我们的region是怎么划分的,首先我们会有一批机器对应IP,CMDB那边会用网络、ISP等一系列信息,把IP分类,分成CMDB的region,通过网络质量和地域相关的去判断,我们做了一点自动化的工作。
就是说当一个新的CMDB region产生的时候,我们会拿到变更事件,去根据几种条件判断可能跟哪个region比较近,或者我们的质量比较好,在几个region之间选一些点发起互相探测的任务,走到最终的rtt and loss,再根据这些东西给一个列表有关人员,这时候管理员直接看到一个可供决策的列表,他只要去点一下这个region就会自动加入到monitor region里面去。
刚才提到我们的最小管理成本,这里是通过订阅CMDBS的register去实现的,我们的SRE通过去管理各种CMDB,各种管理系统去管理一些资源,还有一些对应关系。这种场景下我们在arbiter里面对需要生成配置的数据,做一个内存的ORM,同时在CMDB后面装了一个dbtrigger,把它所有资产改的事件全部都导MQ里面去,在editor订阅,然后去实时更新DB里的ORM,所以是完全事件驱动的ORM。
然后基于这个ORM我们会做配置生成,这些配置生成完之后就会推到region log,然后推到region去,最初这个架构做完之后,我们可以实现秒级的配置更新,但是后续规模变的非常庞大的情况下,这个秒级配置更新用的资源不划算,所以后续在这里面加了一个窗口,十秒或者一分钟内变更的事件,我们会统一一起往下分发,尽量减少我们这边配置的抖动。
在数据采集方面我们做了蛮多的事情,因为游戏面临的场景非常多,游戏开发发散性思维比较强的人,我们主要有几个采集的方式,首先就是agent插件,我们这个anget完全插件化,它通过服务绑定到server上去,跟着CMDB变更会决定anget的产品分发,同时主要是使用供应链场景,从机器上拉数据然后推出去,checker插件其实也是类似的,我们提供一系列的探测点,来去做从外围的主动探测,同时提供一套框架,能让用户配置,从任意一台主机上发起探测。
我们支持到ping/tcp/http等一系列协议,因为是插件化的,SRE可以直接开发插件,我们可以支持到业务协议的
第三个跟下面两个采集方式不太一样的就是pusher,其实是一套SDK在一个server端,主要用处是由进程内向外部署数据的时候去用,游戏开发人只要把SDK引入到自己的代码里面,然后调用几个接口,然后就可以布置数据了,目标就是能够实现任意环境下的数据部署接口。
最后我们游戏开发经常使用日志暴露一些统计指标,这种情况下我们也做了一个log metric同步,从实施日志流里面过滤一些标准字段,把它直接导到监控数据流这边,就可以直接导入主流去处理了。
我们公司云原生的一些事情,相对来说比较快,所以我们相对把prometheus这样非常火爆的监控系统引进来了,相当于我们直接对接prometheus exporter的协议,有一组分布式的agent去拉exporter暴露出的端口数据,同时把它的服务发现模块集成进来,相对应的k8s/etcd等一些系统直接接进来。
另一方面对于K8S的监控,我们直接用了hypervisor,包了一层插件去实现,另一方面就是为了兼容公司内的其他持续的数据,我们提供了第三方db插件,用来从其他的DB里面导数据。
有了这些数据采集方式,我们就可以相对来说比较从容应对这样一个混合云的情况,那么物理机和虚拟机,跑agent在上面,让SRE去部署接口。对于容器,如果我们把anget跑在里面是非常大的开销,云原生的概念本身就只有1号进程,而且一个物理机如果跑了几个十容器,而且里面再跑一个agent开销是非常大的,所以我们比较倾向于用pusher或日志导出数据,对于一些比较固定的场景支持从宿主直接 attach 到容器采集数据。云原生这块基本是社群这套,Cadvisor、Prometheus Exporter、日志指标同时也是比较通用的方式。
刚刚提到我们 agent 是完全是插件化框架,目前我们是用Python做这套东西的,所以其实对于SRE来说入门成本非常低,一个SRE想要开发一个插件,就可以在Email里点一下,得到一个Gitlab的repo,在一个框架里面填要采集数据或者做控制代码,push上去只要布置到一些导入分支,就会自动打包成一个pip包,然后丢到 pip 源上面去,接下来只需要跟服务绑定插件,这些服务机器就会get到这个配置,然后 pip 就会装这个插件,然后去跑。
现阶段而言我们已经800多个Python插件在跑了,覆盖了绝大多数的业务场景。包括前面讲到的,像前面提到的多点探测、故障治愈相关功能都是通过插件支持的。
当数据收集上来之后,我们有一个统一总线做这些事情,首先所有数据都会进到一个 Kafka Origin Data Topics 里,这个 Topics 会到 MainFlow ,做一个 PreProcessor, 主要的目的就是做一些数据清洗,不合规的非法数据。然后做一些数据对齐。
比如说有些人喜欢通过日志打数据的时候,是非时间对齐的,一般都是时间对齐比较友好,所以这时候我们会做一些时间对齐的操作,除此之外优惠做一些缺点的补点这些事情。
进到 MainFlow 这边,我们会有一个Flink Aggregator 去负责做聚合,用户会在系统上面,比如可视化或者报警方面配一些规则,这些规则延伸出一些统一规范下的聚合规则,这些聚合规则就会Flink里去,按规则聚合数据,再把聚合好的数据丢回 Aggregator 去。
这里有一个漏洞,可能会产生一个(英文),这个后续需要加强一下。咱们(英文)的数据,后面会有几部分的系统去订阅,首先就是 storge,就是去存,然后另一方面第三方数据会去做一些 Subscriber,还有一个 Visualization Updater 会做一个自动化的可视化方案生成。
系统依赖我们的数据,需要做一些(英文),然后有一个(英文)其实是做一个自动化的可视化方案的生成,最后就是报警。
这里是一个存储的架构,我们 Kafka 的 MainFlow 在存储这边对接了三个模块,首先说一下我们的存储架构,其实我们存储方式方式会和 NTSDB,Prometheus也是有类似的方式,我们把 metadata 和时序分开了。Metadata 就是简单描述 Metrics 的 Tags 属于哪个 Entity 我们把它全部拆出来,按意见生成 UID,接下来存储的时候拿这个 UID 和后面 TimeSeries 存在一起就可以了。
这个 Redis 的集群会缓存六个小时数据,会有一个TopData每半个小时把数据merge往下挡比一次,在(英文)这边事实上分了好几个层次的库,有1分钟,有5分钟,有30分钟还有一天的,用处后面再讲。(英文)每天跑一次,负责把 MongDB 中的数据再弄出来,做一些合并。
接下来第三个模块是Visualization Updater,其实就是通过订阅数据,然后按数据的组织形式来去生成与数据相对应的一些可视化的配置,这样大部分情况下用户只要托数据,就可以在我们系统中看到图表,接下来他想要定制细化、业务化的一些视图的时候,可以再拖拉这些图表。
这部分说完之后说一下读的部分,用户或者第三方的一些下游平台会通过我们统一API和UI拉数据,拉数据的过程有一个策略图的模块,这个模块主要责任就是决定从哪里读数据,按用户的(英文)索引到要取哪些ID的数据,然后接下来按用户的读取方式。
比如说没有什么要求就是要展示图表,然后我取得一小时的数据,我们就直接从 Redis 里面取一小时,如果我取一天的数据,我们就会字一次降级,因为一分钟的点会非常密,也是不利于观察的。这时候我们会直接把它降成5分钟的检查,从 MongDB 这边去读。如果取一天甚至更久的数据,我们就可能依次降级,做一次展示上友好的可视化。
说完存储这一部分,我们简单说一下可视化方面我们做了哪些事情,那相对来说比较通用的像图表的展示,重点说一下一些比较有意思的地方,首先就是我们提到抽象一层概念,有 Entity Types,Entities。这种情况下我们可以作为一个任意组织架构的一个业务视图的组装,什么意思呢?就是当您有123几层不同组织架构的时候,是不需要要按监控给出一个通用的模式来组成的架构,完全可以按业务实际形态去组织。
这时候你只需要选择你要看哪些Entity Tags,这些 Entity Tags 之间有什么关系去描述出来,然后可以走出数据结构,这个数据结构就可以直接关联到(英文)上相关所有数据和图表。这里有一个例子,这是一个最典型的机器视图,有project,群组到 services,组成一个三层树形结构。
目前我们有200多个自定义视图,这是机器视图,这是一个容器视图,这是用户使用的容器视图,我们直接就从K8S-Pod-Container 这样一个视图。
这里就是一个业务视图,事实上就是监控后端的一个视图,我们把 Arbiter-Region-Node 这三个层级渲染上去了,然后最下面这一层看到的就是所有 Node 节点的一些信息,同时我们这里做一点就是,如果你想做聚合,事实上不需要到很多次数据的,比如说我们每一个 Node ,这时候只需要 Node 报自己有多少就可以了,根据这个视图的组织关系配一个向上聚合的规则,然后前面那个模块就会帮我们搞定所有聚合相关的事情。这时候其实你只要点到(英文)节点就可以看到所处 Node 总共加起来多少数据。
我们之前一直发现一个很典型的问题,被报警这件事很能调,我们配了一个规则,但是我们不知道这个规则是不是能生效,所以我们需要造数据,不才能试一下这个规则是不是有用。基于这个痛点,我们迭代新系统的时候,所有的东西就是基于所见即所得这样一个原则去做的。
比如说这里用户给一些(英文),筛选出一批数据,这些数据就会直接呈现在上面,给你选一批数据来看,也可以选一些统计数据,比如说(英文)这类东西。根据数据的阈值,就会突然显示出来,你阈值数据相关是多少,做报警模板调试的时候也是类似的,只要选一条已经存在的数据,就可以按这条数据直接把模板选出来。
报警主要做了如下的策略,首先就是指标阈值,然后就是变化率,还有一些用户资金异常消息,异常检测,还有组合报警,这些报警SIE配完之后,可以用策略模板分享出来的。我们自己公司内部情况比较特殊,我们会有几百个项目,有很多运维人员在维护,这些项目可能相对来说是同构或者说是延伸出来的,这种情况下面事实上他们的报警策略很多都是相同的,这种情况下我们可以用策略模板做分享和订阅,减少人工配置的成本。
再简单说一下收敛方面做的事情,我们前面基于整体的规则引擎处理数据,产生消息,后面有一些问题合并模块做一些问题的合并,目的尽可能减少报警,在合并文化上目前做了人工策略,比如说我们可以选择做一个十秒钟的合并,相当于做了一定报警时效性,同时增加一下报警的准确性和结转回报。
另一方面我们根据项目,根据分类,根据策略一系列的维度做综合合并的,除此之外其实也是在跟CMDB合作做一些事情,如果CMDB能够帮我们描述网段和机器的关系,这些我们可以把储物层的合并全部做出来。基于以上问题产生之后,我们做了一个策略确保问题的处理,策略就是产生新问题之后,简单的通知值班,有各种方式,泡泡、邮件、电话、短信之类的。
通知到之后,如果这个值班正好手机不在旁边,超时了,这时候就会通知到一个backup值班,后面还有2级值班,3级值班,如果都没有通知到,再回来通知,这样一个方式确保了值班的(英文)机制和升级的统计。
这其中每一支,只要有任意一个人收到这个消息点击处理问题,然后这个报警整个被抑制了,同时基于一些指标类的报警,我们也做了一个指标恢复正常的关闭问题的逻辑,也就是说如果你的指标超过了某个阈值报警了,它在短时间内又恢复了,事实上是不会发出报警的,但是会有一个问题记录。
说完报警相关的事情,首先传统的报警里面像阈值同比用的都比较多,能解决一些问题,还是有一些(英文)或者是一些特殊情况是没办法覆盖的,异常检测能做到很多事情,基本上按照数据的特征去做。另一方面能够增量学习,去适配数据变化,比如说以前某个数据维持在一个基线上面,如果我们用阈值,有一天这个数据明显偏离了基线,然后又长期稳定下来的,我们又调阈值,如果这时候检测的话,通过线上的增量学习更新模型实际上需要做这件事情。
我们整体流程大概是这样的,首先是数据的抽取、存储还有标注,接下来做一系列的预处理,还有对于非对称的样本级,做一些标准化的脱敏。
接下来在特殊工程方面做了蛮多的努力,目前我们线上比较有价值的特征大概360多个,接下来是一线模型训练的路程,其实我们线上已经有很多无监督和有监督的模型在跑,也做了一些集成,同时这里会有一个模型实时评估反馈。模型训练完之后就会丢到S3的存储上,有一套线上实时检测流,订阅模型的变更,拉过去读取数据做报警。
我们这边尝试一些模型,首先说说最传统的一个统计学基于距离、密度等方法,这些方法有一个共同特点,使用特别简单,不需要标注,但是有一个问题就是效果随缘,它们在一些场景下表现的很好,但是其他场景下,其他数据特点下表现的很差。
第二个阶段我们尝试 IsolationForest ,这个算法是我们见过无监督算法里面最好的一个,它的Baseline相当高,在大部分场景下能得到比较好的效果,基本上不用太多条,但也有另外一个问题,上限一般,毕竟无监督,没有标注介入,事实上很难按你的意图区分很多细节情况,所以比有监督来说还是差距大一点。
最后还是走有监督的老路子,一开始尝试一下STN等一系列的东西,后续后是回归到比较基础的(英文)东西去了,当然一开始其实就有尝试做集成,机器学习的赛季有意思的话就是不管什么场景,无脑上集成一般都有效果,所以我们根本没有考虑不上成集成的情况。
目前我们这个集成模型效果上线好一些。接下来我们发现一个问题,就是我们在更新我我们的样本集和特征的时候,会发现当我们想要满足一个场景,就有可能会对未来场景造成误导。
比如说有一些业务它的曲线本身抖动比较厉害,它需要大幅度抖动进行报警,如果我们把抖动比较厉害,用户认为正常的情况,去导入单向级就可能影响比较平滑的曲线检测,当然有人说我们可以对前面曲线做平滑,再去丢模型,但这样的话其实会降低抖动幅度比较厉害的那几个峰值的值,也会影响到结果,所以这种情况下我们就尝试做一个曲线分类,抽取一些曲线特征,比如说相关的一些系数,比如说自身的抖动那些相关东西,把这些东西作为一个特征来做一下数据分类,根据不同的分类来预训练模型,尝试解决这个问题,在某一个场景下我们加入样本就不会影响到另外一个场景模型。
可能不是很清楚,简单说一下我们的样本,有一堆(英文)展开,这些展开之后用(英文)做了个人选择,最后有一个集成,有(英文)一系列的模型,最后用LR做一个简单的(英文)。这套模型在我们线上十万条曲线上面,最终测试下来的结果大概是85%的(英文)准确率,因为我们是重点瞄准(英文)做优化的,偏低一些,但是准确率比较蛮高的,在十万条曲线上的(英文)效果算是比较不错的情况。
说完场景检测说说另外一件事,我们在尝试去寻找问题之间的关系,这时候我们用到关联分析,这几个关联分析主要是指时间序列的关联分析,我们的目标从几百条曲线中定位出故障原因或者确定故障影响分析,或者影响整体一个指标的局部指标。
这种情况下来我们会用到这样一个方法,整体流程是报警出发这样一个流程,通过CMDB通过业务配置,通过一些策略,去确定我们要搜索哪些曲线,要搜索曲线拿出来,跟当前发生报警这条曲线做相关计算,最后按相关性排名,然后给用户,用户这时候也会有一些反馈,比如说这个东西推的很准,那个东西不准,没有什么关系,我们拿回来之后做一些相应优化。
这是一个简单的模型介绍,之前做这件事情的时候,我们也做曲线相似性计算模型,一直没有取得很好的效果,后来我们看到一个论文,这个论文一个观点很有意思,不去搜索两个曲线之间的关系,只搜索一个事件和曲线之间的关系。
因为我们知道前面的曲线已经有问题,我们按这个时间节点前后划分子序列,抽出两个子序列出来,再到这条曲线上随机取一个子序列,再对比这三个子序列之间是不是相同,如果前面的子序列和随机子序列不同,我们认为这个序列的变更导致这个事件,如果是随机这个子序列,后面那个子序列不同,我们就认为是这个事件导致这个序列的变更,这样就可以大致构建出一条序列之间影响的链条,就是一个传播链。
这里面有一定装彩票的嫌疑,就是(英文)的选择非常影响模型的效果,我们也会做一些随机多次选择,统一预算的方式,去降低它的偶然性。整体而言这个模型在测试效果中相对来说比直接计算好不少。我今天分享差不多到这里结束,谢谢。