前言

大约在16年我们给某客户做百亿小文件计算引擎的时候,涉及到了对于大规模集群的性能测试的模拟,而本次问题恰好因为某个项目的某个需求涉及到需要通过小规模集群来模拟大规模集群的的需求,所以又开始仔细思考了这类问题的解决方案。

摘要

多数情况下我们需要在测试环境或者开发环境拥有和生产环境相同规模配置的环境是比较难的,无论是出于预算还是其他原因都会导致非生产环境外的其他环境配置缩水。另外即便是拥有了生产环境相同的配置,在设计系统的时候,我们已经希望当前集群的配置可以支撑未来一定规模的业务场景,所以很多时候我们需要基于手上的机器来大概的预估生产环境配置下的集群大概拥有怎样的性能指标,可以支撑未来多大的业务场景。

为了更加真实的反映出信息,所以不得不使用一系列的方法来进行所谓仿真操作,也就是如何使用小规模机器模拟大规模集群。

基于经验的人肉法

大部分情况下对于机器的配置预估都是基于QA性能测试的数据再由TL结合自身的经验进行一个输出,在规模不大的场景下这类方法通常是最有效也是最直接的方式。即便预估不足也可以快速的通过水平扩展进行立马补充,来应对突发的流量情况,甚至可以通过流量控制将大部分流量导向配置最高的机器,承担更高的负载。

b1

可以看到此类方法预估集群规模有一个特殊的地方,就是每一次请求是幂等的,每一次请求都可以由一个节点独立完成。再配合微服务架构,基本上可以将每一台机器变为相应一个请求的独立处理单元,所以对于机器规模的预估通常是一个常量。其影响因子主要来自外部,例如并发,流量,和请求类型,请求类型可能回影响IO,成为瓶颈。

干系因子

前面通过基于经验的预估方法,比较直白的介绍了如何预估特定规模下的集群指标,以及特定业务需要何种配置。仿佛大部分时候看起来这是一件很简单的事,其实不然。我们换个场景,我现在有100台机器,如何能够测试出10000台规模的Hadoop集群服务器下,任务运行的指标。

仔细看看里面有两个很重要的点:1万台,和Hadoop。

这里倒不是要强调Hadoop是多么高深的东西,恰巧是因为Hadoop代表了另一类计算,即分布式协作计算,也就是一个任务会被拆分成N个不同力度的任务,由多台服务器协作完成。第二个是1万台,也不是强调规模一定要多大,而是当我们的规模到达一定程度后,必然会面临一种情况就是集群自身就可能摇摇欲坠,比如一万台集群的机器,Master需要调度的节点有这么多,光是节点的同步信息就可能拉低整个集群性能,还不用考虑上面的任务运行。所以总结下来我们可以发现我们预估集群规模的场景通常有以下三种情况:

  • 单点任务集群:这个时候每一个节点都是可以独立处理任务请求,每个节点之间相互互不干涉。
  • 默认配置的中型规模协作集群:这里的每一台机器都只是一个节点,整个集群对外提供服务,每一次任务需要由不同的节点协作完成。但是整个集群的IO,带宽都是处于健康状况,机器采用默认配置就可正常运行。基本上参数调优并不会特别影响起指标,毕竟资源依旧大于诉求。
  • 瓶颈集群:我叫它瓶颈集群,并非有问题集群,而是相比前一个集群来说,默认配置并不能满足要求,甚至无法正常工作,我们需要针对这种规模的集群做单独的特殊调优,才能合理的发挥它的价值,这个时候基于历史小规模的一切预测指标,都可能被一次随意的调优参数改变而直接打破。

那么我们在总结一下他们的干系因子:

b2

可以发现影响因子依次由外部影响新增志内部影响因子,对于外部的因子我们通常可以很好的通过自身的经验去解决,但是对于内部因子则很难,是因为内部因子变多后,相互之间会有较大的影响,并非可以被穷举和逐步理解,那么我们如何去应对呢,继续下文。

基于机器学习的预测

机器学习相关的预测可以用在很多地方,上图大概总结了以下哪些场景可以怎么玩,可以看到里面其实没有提到今天我们讲到的场景,但是不代表这种场景是不可使用的,也就是说我们可以把机器学习这项技术用来预测我们的集群规模相关的场景。既然是预测,那么按理来说,应该可以解决一切的才对,其实不然,我们仔细分解机器学习的预测过程,可以看到它会经历如下流程:

训练集 -> 迭代训练 -> 每个迭代使用验证集 -> 测试集 -> 线上泛化验证

也就是说,做机器学习是使用线下采集的数据训练后去预测相同场景下的不同因子,那么要复合使用机器学习的场景,至少的满足这样的要求:

  • 场景相同
  • 相关因素相同
  • 相关因素的相关性大致相同

这样的情况我们可以发现对于集群规模来说,只有第一,二种情况才适合使用机器学习相关的算法来预测,但是第一种太过于简单,简单到掐指一算就能出来,所以只有第二种情况用的更多。那么第三种之所以不合适,是因为在小规模集群下和瓶颈集群相比,场景就完全不相同。影响集群性能的因素也完全不相同,因此根本无法使用小规模集群的压测统计数据去预测大集群。如果有集群能够模拟出这样的数据,那么它已经是一个庞大的集群,都无需做任何测试了,直接验证即可。

现在分解一下第二种情况的的特征表,如下:

b3

由此我们可以发现在这种场景下,我们通过机器学习算法,至少可以用来解决以下问题:

  • 预测在小规模集群上运行的任务在生产环境下的性能指标
  • 预测在给定的规模的集群上可以运行多少任务

而对于机器学习算法来说,选择的种类就会很多,但是一般来说我们在做机器学习项目的时候,通常会遵循如下的节奏:

数据复杂度 -> 算法复杂度

也就是说一开始并不会采用高大上的神经网络,仿佛神经网络包治百病,相反一开始我们会使用最简单的算法,例如KNN或者线形回归,这类算法比较简单,但是我们需要花费更多的时间去准备处理和分析数据,而深度学习算法非常复杂,甚至没有办法用人为思考的逻辑去解释,但是相对来说数据准备的工作不会太复杂,这个过程就是由数据复杂度逐步到算法复杂度演进的过程。

基于环境的仿真

聊完了前面使用机器学习来预测的场景,我们再看看第三种场景,和上面的思路类似,我们在扩展一下特征,变成这样了:

b4

可以看到特征是无法穷举,更何况我们还没有考虑多列特征相关性的问题,所以其可能性太多,多到无法进行计算,另外一个维度就是一个任务在哪个节点上运行不是人为可控的,极端情况假设每次提交都是相同的节点,只要保障这几个节点资源可用,无论规模多大结果可能都是常数。那么这样的场景是否能够被解决,想想围棋。围棋面临的是相同的问题,没有办法穷举每一步的操作后可能出现的情况,所以下围棋转换了思路,不再预测走哪一步,而是变成搜索的问题,从历史上走过的路里面搜索下一步。这种解法就成了我们听到的Alpha Go和Alpha Zero。

同样基于这样的搜索的思路自然也可以应用在我们对大规模集群的预测上面,业界也给了非常多的思路,最近看了一本书《Model and Data Engineering》中讲述了如何对Hadoop集群做模拟,也就是:Modeling and Simulation of Hadoop Distributed File System in a Cluster of Workstations 部分,纯英文比较绕口,看的也很慢,还没看懂,所以我暂时把这本书删了。

假设我们以解决围棋的思路来对大规模集群下的各项性能做预测,我们想一下我们的思路是什么?我们并不是去预测一个任务在分布式集群上大概会运行多长时间。

b5

对比这张图可以发现同样的任务每次提交会被分配到不同的节点上,所以我们将预测结果的思路变成:

寻找任务提交在集群中会由哪些节点执行,经过哪些步骤是最优的路线。

于是我们很好的把一个预测回归或者分类问题转变成了搜索的问题,那么要照着这个思路来解决,我们需要做这样的事:

建模

建模是对集群建模,每一次任务都由Master作为入口,再运行到不同的节点,所以我们可以将集群抽象成一个有向无环图:

G(V,E)

其中 V 是所有可由 Master 调度的节点集合,E 是连接各 Worker 节点的网 络集合。只需要对任务找到最合适的节点即可。每一次任务提交可选节点网络集合是不同的,因此必须在V中排除那些已经分配给其他任务的非空闲节点,将第 i 次节点选择中可用节点网络 集合记为 Ei,其节点集合记为 Vi 。

那么我们再做分解可以发现有以下的因素会影响寻找路径的动作:

  • Vi中每个节点的处理能力,由节点的性能如处理器主频、内存大小、缓存大小和可用磁盘空间等因素。
  • Vi各节点的网络状态,如网络带宽和网络延迟等。
  • 需要处理的数据块的大小及相对Vi中各节点.
    的位置分布。

因此需要一个公示用来计算以上三个因素和任务执行时间之间的计算逻辑,也就是说只需要知道上面的因素大概就可以得出任务具体执行的时间,分别对应下来是:

  • 节点的预计执行时间,从开始节点到结束所消耗的时间。
  • 网络延迟时间,获取的是网络的数据,指的是路径行走过程中,网络的延迟。
  • 数据获取的时间,由路径的长度、网络带宽和数据块的大小共同决定,和数据块的大小及路径的长度成正比关系,和网络带宽成反比关系,转换成公示如下:
    数据获取时间=数据大小*节点的距离/数据处理buffer窗口大小 * A(常量系数)

所以一个任务的执行时间等于:

a*节点的预计执行时间+b*网络延迟时间+c*数据获取的时间

通过上面的动作,我们建立了一个数据模型,接下来要做的就是如何根据当前的节点数据,和上面的逻辑公示,在给定的节点配置,规模之下,去查找对应的节点,找出合理的路径,从写代码来讲我们可以模拟实现N个节点,来仿真大规模集群,给每个节点赋予可调整的网络,io等参数信息,然后再这样的大的结构下进检索以此来计算在此集群下,任务运行的具体的情况,从而达到模拟和仿真的效果。所以接下来的动作就是需要对数据进行检索。并且可以根据于此来推动我们对集群实现参数配置的优化的动作。

启发式搜索

对于这样的场景,如果需要搜索显然启发式搜索效果相对较好,目前来说启发式搜索的算法非常多,这个场景使用较多的则是:和声搜索算法BHS.

BHS灵感发源于音乐,对于乐队来说,需要反复调整乐器的音调,可以看到乐器有非常多的音调,这个动作等于是从无法穷举的音调种,找出了一个最合理的组合,形成了最优美的和声,后来有人提出了和声搜索算法BHS 。

和声算法的实现需要有以下几个步骤:

  • 问题公式化
  • 算法参数设置
  • 随机的记忆库初始化
  • 随机选择、记忆的考虑、调整
  • 记忆库更新
  • 算法迭代及终止

(具体的算法这里就不细展开了,需要的可以自行研究)

结尾

仿真的过程是一个具有高度不确定性的过程,即便是再完美的操作和算法也无法解决一切问题,所以需要掌握多种不同的思考方式和解决方案。


扫码手机观看或分享: