【转】Yelp是如何实现每天运行数百万个测试的
Yelp每天要運行數(shù)百萬個測試,確保開發(fā)人員提交的代碼不會對已有的功能造成破壞。如此巨大規(guī)模的測試,他們是怎么做到的呢?以下內(nèi)容翻譯自 Yelp 的技術(shù)博客,并已獲得翻譯授權(quán),查看原文 How Yelp Runs Millions of Tests Every Day 。
開發(fā)速度對于一個公司的成敗來說是至關(guān)重要的。我們總是通過減少測試、部署和監(jiān)控變更的時間來提升開發(fā)效率。為了讓開發(fā)者能夠安全地提交代碼,我們每天通過內(nèi)部的分布式系統(tǒng) Seagull 運行了超過數(shù)百萬個測試。
Seagull 是什么?
Seagull 是一個具備容錯能力和彈性的分布式系統(tǒng),我們用它來并行執(zhí)行我們的測試套件。我們使用了如下技術(shù)來構(gòu)建 Seagull 。
- Apache Mesos(用于管理 Seagull 集群的資源)
- AWS EC2(為 Seagull 和 Jenkins 集群提供實例)
- AWS DynamoDB(保存調(diào)度器的元數(shù)據(jù))
- Docker(為測試要用到的服務(wù)提供隔離)
- Elasticsearch(跟蹤測試的運行時間和集群數(shù)據(jù)的使用情況)
- Jenkins(構(gòu)建代碼并運行 Seagull 調(diào)度器)
- Kibana 和 SignalFx(用于監(jiān)控和告警)
- AWS S3(作為測試日志的真實來源)
挑戰(zhàn)
在將我們的單體 Web 應(yīng)用 yelp-main 的新代碼部署到生產(chǎn)環(huán)境之前,Yelp 的開發(fā)人員針對 yelp-main 的特定版本運行了整個測試套件。開發(fā)人員通過觸發(fā) seagull-run 作業(yè)來運行測試,這個作業(yè)將會在我們的集群上安排調(diào)度以便運行測試用例。這里需要考慮兩方面的因素。
- 性能:每一個 seagull-run 作業(yè)都有將近 10 萬個測試用例,如果逐個運行它們需要差不多 2 天的時間。
- 規(guī)模:一般情況下,每天會有超過 300 個 seagull-run 作業(yè)被觸發(fā),高峰期有 30 到 40 個作業(yè)并行運行。
我們所面臨的挑戰(zhàn)是如何在分鐘級別運行每一個 seagull-run 作業(yè),而不是按天來運行,同時還能保持較低的成本。
Seagull 的工作原理
首先,開發(fā)人員在控制臺觸發(fā) seagull-run,它會啟動一個 Jenkins 作業(yè),用于編譯代碼,并生成測試清單。這些測試清單被組合在一起,傳遞給一個調(diào)度器,調(diào)度器將會在 Seagull 集群上執(zhí)行測試。最后,測試結(jié)果被保存到 Elasticsearch 和 S3 上。
1.一個開發(fā)人員為某個版本的代碼(基于 git 某個分支的 SHA 值)觸發(fā)了一個 seagull-job,我們假設(shè) git 分支的名字叫作 test_branch。
2.為 test_branch 生成代碼包和測試清單,并上傳到 S3 上。
3.Bin Packer 獲取測試清單和測試歷史時間元數(shù)據(jù),用于創(chuàng)建多個包含了測試用例的 bundle。如何進行有效的 bundle 其實是一個裝箱問題,我么使用了如下兩種算法來解決這個問題。至于使用哪一種算法,由開發(fā)人員傳給 Seagull 的參數(shù)來決定。
- 貪婪算法(Greedy Algorithm):測試用例先是按照它們的歷史測試時長來排序,然后我們開始按照 10 分鐘的工作量(測試用例)來填充 bundle。
-
線性編程(Linear Programming):如果出現(xiàn)了測試依賴,一個測試需要與同一個 bundle 里的另一個測試一起運行。對于這種情況,我們將會使用線性編程。線性編程方程式的目標(biāo)函數(shù)和約束定義如下。
- 目標(biāo)函數(shù):最小化生成 bundle 的數(shù)量
-
主要的約束:
- 單個 bundle 的運行時長不超過 10 分鐘
- 每個測試只能存在于一個 bundle 里
- 具有依賴關(guān)系的測試需要被放在同一個 bundle 里
我們使用了 Pulp 來解開這個方程式。
# 目標(biāo)函數(shù): problem = LpProblem('Minimize bundles', LpMinimize) problem += lpSum([bundle[i] for i in range(max_bundles)]), 'Objective: Minimize bundles' # 其中的一個約束: for i in range(max_bundles):sum_of_test_durations = 0for 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], ''在這里,bundle 和 test_bundle 是 LpVariable 類型,max_bundles 和 bundle_max_duration 是整數(shù)。
一般情況下,我們會在線性編程約束里考慮測試用例的 setup 和 teardown 時長,不過為了簡單起見,我們在這里把它們忽略了。
4.在 Jenkins 服務(wù)器上啟動一個調(diào)度器進程,它將會獲取 bundle,然后啟動一個 mesos 框架。我們?yōu)槊恳粋€ seagull-run 創(chuàng)建一個新的調(diào)度器。每次運行會生成300多個 bundle,每個 bundle 大概需要 10 分鐘的運行時間。調(diào)度器為每一個 bundle 創(chuàng)建了一個 mesos 執(zhí)行器,并被安排在 Seagull 集群上執(zhí)行,只要 Mesos Master 能夠提供可用的資源。
5.在執(zhí)行器被安排到集群上之后,執(zhí)行器內(nèi)部將執(zhí)行如下幾個步驟。
每個執(zhí)行器啟動一個沙箱,并從 S3 上下載軟件包(它們是在第二步時上傳到 S3 上的)。測試服務(wù)所依賴的 Docker 鏡像被下載下來,用于啟動 docker 容器(也就是服務(wù))。在所有的容器都運行起來之后,開始執(zhí)行測試。最后,測試結(jié)果和元數(shù)據(jù)被保存到 Elasticsearch(ES)和 S3 上。我們使用了內(nèi)部的代理服務(wù) Apollo 將數(shù)據(jù)寫到 ES 上。
如果你處在一個分布式環(huán)境里,那么遭遇主機崩潰是一件不可避免的事情。不過,Seagull 具有容錯能力。
例如,假設(shè)一個調(diào)度器需要調(diào)度兩個 bundle。Mesos 將代理(A1)的資源分配給調(diào)度器。假設(shè)調(diào)度器認為已經(jīng)分配到足夠的資源,那么兩個 bundle 就會被安排在 A1 上。A1 因為某些原因發(fā)生崩潰,那么 Mesos 會讓調(diào)度器知道 A1 已經(jīng)崩潰了。調(diào)度器的任務(wù)管理器決定進行重試,或者直接取消任務(wù)。如果進行了重試,當(dāng) Mesos 提供了足夠的資源時(比如 A2),那么 bundle 就會重新被安排執(zhí)行。如果任務(wù)被取消,調(diào)度器會將這些 bundle 的測試用例標(biāo)記為未執(zhí)行。
6.Seagull UI 通過 Apollo 從 ES 上獲取測試結(jié)果,并將它們加載到一個 UI 上,讓開發(fā)人員可以看到結(jié)果。如果測試通過,就可以進行部署!
我們所談?wù)摰囊?guī)模是多大?
我們每天有 300 多個 seagull-run,高峰期每小時有 30 到 40 個。它們?yōu)榇嗣刻靻映^ 200 萬個 Docker 鏡像。為了應(yīng)付這些場景,我們的 Seagull 集群在高峰期需要差不多 1 萬個 CPU 核心。
這種規(guī)模所帶來的挑戰(zhàn)
為了保持測試套件的及時性,特別是在高峰時期,我們需要確保 Seagull 集群里有數(shù)百個可用的實例。我們曾經(jīng)使用過AWS ASG 和 AWS On-Demand 實例,不過它們對于我們來說太昂貴了。
為了降低成本,我們開始使用一個叫作 FleetMiser 的內(nèi)部工具來維護 Seagull 集群。FleetMiser 是一個自動擴展引擎,我們用它基于一些信號來擴展集群,比如當(dāng)前集群的使用情況、管道里運行的工作負荷數(shù)量,等等。它有兩個主要的組件。
- AWS Spot Fleet:AWS 的 Spot 實例比 On-Demand 實例要便宜,Spot Fleet 為使用 Spot 實例提供了簡單易用的接口。
- 自動伸縮:我們對集群的使用是動態(tài)變化的,主要集中在太平洋時間 10:00 到 19:00,開發(fā)人員的主要工作都集中在這段時間。為了能夠自動伸縮,FleetMiser 使用了集群的當(dāng)前和歷史數(shù)據(jù)。每天,Seagull 集群在 1500 個 CPU 核心到 10000 個 CPU 核心之間伸縮。
自動伸縮:幾周前的容量數(shù)據(jù)
FleetMiser 為我們節(jié)省了 80% 的集群成本。而在那之前,我們的集群部署在 AWS On-Demand 實例上,無法進行自動伸縮。
我們已經(jīng)達成了什么樣的目標(biāo)?
Seagull 將測試結(jié)果的時間從 2 天降低到 30 分鐘,而且減少了大量的運行成本。我們的開發(fā)人員能夠自信地提交代碼,無需等待數(shù)個小時甚至數(shù)天來驗證他們提交的變更沒有造成任何破壞。
轉(zhuǎn)載于:https://www.cnblogs.com/wangyayun/p/6828942.html
總結(jié)
以上是生活随笔為你收集整理的【转】Yelp是如何实现每天运行数百万个测试的的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: XVID基本参数解析
- 下一篇: 王长震《非财务经理的财务管理沙盘推演课程