Yelp是如何做到每天运行成千上万个测试

Lerachee4a7u 7年前
   <p>快速进行功能开发对企业至关重要,开发团队都试图通过减少测试、配置和监控变化的时间来提高开发人员的效率。为了让开发人员安全地敲代码,Yelp使用内部分布式系统Seagull运行2000多万个测试。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/c8ce0c2ff954993b4e026cd322e873be.jpg"></p>    <h2><strong>什么是Seagull?</strong></h2>    <p>Seagull是一个容错和故障恢复的分布式系统,用来并行执行测试包。Seagull由以下几部分组成:</p>    <ul>     <li>Apache Mesos(管理Seagull集群上的资源)</li>     <li>AWS EC2(提供组成Seagull和Jenkins集群的实例)</li>     <li>AWS DynamoDB(存储调度器的元数据)</li>     <li>Docker(隔离测试需要的服务)</li>     <li>Elasticsearch(追踪测试运行次数和集群使用数据)</li>     <li>Jenkins(搭建代码项目并运行Seagull调度器)</li>     <li>Kibana和SignalFx(提供监控和报警)</li>     <li>AWS S3(为测试日志提供真实数据来源</li>    </ul>    <h2><strong>挑战</strong></h2>    <p>在准备单片Web应用和Yelp主机配置新的生产代码之前,Yelp开发人员在特定的主机上运行整个测试包。测试前,开发人员需启动用来调度集群测试的Seagull。以下两点需要着重考虑:</p>    <ul>     <li>性能:每个Seagull-run包含将近10万个测试,依次运行完毕需要大约2天时间。</li>     <li>规模:通常一天有300多个seagull-runs在运行,高峰时段要同时运行30-40个测试。</li>    </ul>    <p>这项任务的挑战在于执行每一个Seagull-run所花费的时间是以“分钟”而不是以“天”来计算,同时保证在这个时间段内成本效益好。</p>    <h2><strong>Seagull怎样运作?</strong></h2>    <p>首先,开发人员在控制台启动Seagull-run,即启动Jenkins搭建代码项目并生成测试列表。然后,将测试打包传给Seagull集群上的调度器进行测试。最后,将测试结果存储在Elasticsearch和S3中。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/e8cdddd56d17467b9b476b44c051ba89.png"></p>    <p>1、开发人员为特定的代码版本(基于其分支git SHAs)启动Seagull-run,设git分支为test_branch。</p>    <p>2、生成test_branch的代码项目和测试列表并上传到S3。</p>    <p>3、Bin Packer抓取测试列表和测试的历史时序元数据,从而构建包含测试的多包。有效打包是一个装箱问题,可以用以下两个算法解决,(选择哪种算法)取决于研发人员传给Seagull的参数:</p>    <p>(1)、贪婪算法:首先根据历史测试时长将测试分类,然后将测试时长设置为10分钟的测试归档。</p>    <p>(2)、线性规划(LP):为了防止相关测试,一个测试需要和另一个测试在同一个包里运行。因此,我们使用LP进行打包,LP方程的目标函数和约束条件定义如下:</p>    <p>1)目标函数:对生成的包总数取最小值</p>    <p>2)主要约束条件:</p>    <p>单包的测试时长少于10分钟;</p>    <p>一个测试只能放入一个包;</p>    <p>相关测试放入同一个包。</p>    <p>我们使用Pulp LP解算器求解方程:</p>    <pre>  <code class="language-javascript"># Objective function:  problem = LpProblem('Minimize bundles', LpMinimize)  problem += lpSum([bundle[i] for i in range(max_bundles)]), 'Objective: Minimize bundles'    # One of the constraint, constraint (1), looks like:  for i in range(max_bundles):      sum_of_test_durations = 0      for test in all_tests:          sum_of_test_durations += test_bundle[test, i] * test_durations[test]      problem += (sum_of_test_durations) <= bundle_max_duration * bundle[i], ''</code></pre>    <p>其中,bundle和test_bundle是LP变量,max_bundles和bundle_max_duration是整数。</p>    <p>通常,在LP约束条件中,我们会考虑测试用例的搭建和解除的时长,但是为了简便,在这里我们忽略不计。</p>    <p>4、在Jenkins主机上启动调度器进程,Jenkins主机抓包然后搭建mesos架构。我们为每一个Seagull-run创立一个新的调度器。每一次运行生成300多个包,将运行时长在10分钟左右的包归为一类。调度器为每一个包建立一个Mesos执行器,只要Mesos控制器提供充足的资源,就将Mesos执行器列入Seagull集群时间计划表。</p>    <p>5、一旦执行器被列入到集群,执行器内部将进行以下步骤:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/b77cc720590c35c04e89db001d71890b.png"></p>    <p>每一个执行器建立一个沙箱,从S3(在步骤(2)中已上传)下载新建项目。然后下载对应于相关测试服务的Docker图片就可以建立Docker容器(服务)了。当所有的容器都启动并运行,就开始进行测试了。最后,测试结果和元数据存储在Elasticsearch(ES)和S3中。我们用内部代理服务Apollo写入ES。</p>    <p>如果你生活在一个分布式系统的世界,你一定无法避免主机故障。Seagull对任何实例故障都具有容错功能。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/7cbec6269df694415f65efc04e1c4a24.png"></p>    <p>例如,假设一个调度器要运行两个包。Mesos会提供一个代理(A1)的资源给调度器。假设调度器认为资源充足,那么这两个包会被安排在A1上。假如出于某种原因,A1出故障了,那么Mesos会通知到调度器。调度器的任务管理器决定重试或者丢弃那两个包。如果重试,当Mesos下一次提供充分的资源(这种情况下,是提供代理A2的资源)时,那两个包会被重新安排到时间计划表里。为了防止包被丢弃,调度器会将那两个包测试标记为“未执行”。</p>    <p>6. Seagull UI用Apollo从ES获取结果,并将结果加载到UI供开发人员查看。如果结果全部通过,就可以准备配置了。</p>    <h2><strong>规模化挑战</strong></h2>    <p>为了确保测试包的及时性,尤其是在高峰期,Seagull集群需要保证大量实例一直处于可用状态。之前我们使用AWS的按需实例AWS ASGs,但对我们来说,要想达到这个容量成本太高了。</p>    <p>为了降低成本,我们开始使用一个叫FleetMiser的内部工具去维持Seagull集群。FleetMiser是一个用来测量基于不同信号的集群的自动缩放引擎,这些信号包括当前集群使用率、管道内的流量数等等。FleeMiser主要由以下两部分组成:</p>    <ul>     <li>AWS Spot Fleet:AWS有现场实例,现场实例比按需实例成本低很多,Spot Fleet能提供交互界面更简单的现场实例。</li>     <li>自动缩放:集群的使用时间不稳定,使用高峰主要集中在10:00-19:00(太平洋标准时间),此时,开发人员工作强度最大。为了动态调整范围,FleetMiser使用集群在不同优先级别下的当前和历史使用数据。Seagull集群每天的波动范围大概介于1500CPU内核和10000CPU内核之间。</li>    </ul>    <p style="text-align:center"><img src="https://simg.open-open.com/show/dcc95d3875c555e3ae5285d0ad0f3a31.png"></p>    <p>FleeMiser为我们的集群节省了大约80%的成本。在使用FleetMiser之前,我们是使用AWS按需实例,没有自动缩放功能。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/d3496f66a547fa03f3e107280fc365cf.png"></p>    <h2><strong>总结</strong></h2>    <p>Seagull使测试结果时长从2天缩短到了30分钟,同时也大大降低了执行成本。从今以后,开发人员可以安心敲代码,不用再花大量时间等待验证变化却束手无策。</p>    <p> </p>    <p>来自:http://www.iteye.com/news/32424</p>    <p> </p>