Yelp是如何做到每天運行成千上萬個測試
快速進行功能開發對企業至關重要,開發團隊都試圖通過減少測試、配置和監控變化的時間來提高開發人員的效率。為了讓開發人員安全地敲代碼,Yelp使用內部分布式系統Seagull運行2000多萬個測試。
什么是Seagull?
Seagull是一個容錯和故障恢復的分布式系統,用來并行執行測試包。Seagull由以下幾部分組成:
- Apache Mesos(管理Seagull集群上的資源)
- AWS EC2(提供組成Seagull和Jenkins集群的實例)
- AWS DynamoDB(存儲調度器的元數據)
- Docker(隔離測試需要的服務)
- Elasticsearch(追蹤測試運行次數和集群使用數據)
- Jenkins(搭建代碼項目并運行Seagull調度器)
- Kibana和SignalFx(提供監控和報警)
- AWS S3(為測試日志提供真實數據來源
挑戰
在準備單片Web應用和Yelp主機配置新的生產代碼之前,Yelp開發人員在特定的主機上運行整個測試包。測試前,開發人員需啟動用來調度集群測試的Seagull。以下兩點需要著重考慮:
- 性能:每個Seagull-run包含將近10萬個測試,依次運行完畢需要大約2天時間。
- 規模:通常一天有300多個seagull-runs在運行,高峰時段要同時運行30-40個測試。
這項任務的挑戰在于執行每一個Seagull-run所花費的時間是以“分鐘”而不是以“天”來計算,同時保證在這個時間段內成本效益好。
Seagull怎樣運作?
首先,開發人員在控制臺啟動Seagull-run,即啟動Jenkins搭建代碼項目并生成測試列表。然后,將測試打包傳給Seagull集群上的調度器進行測試。最后,將測試結果存儲在Elasticsearch和S3中。
1、開發人員為特定的代碼版本(基于其分支git SHAs)啟動Seagull-run,設git分支為test_branch。
2、生成test_branch的代碼項目和測試列表并上傳到S3。
3、Bin Packer抓取測試列表和測試的歷史時序元數據,從而構建包含測試的多包。有效打包是一個裝箱問題,可以用以下兩個算法解決,(選擇哪種算法)取決于研發人員傳給Seagull的參數:
(1)、貪婪算法:首先根據歷史測試時長將測試分類,然后將測試時長設置為10分鐘的測試歸檔。
(2)、線性規劃(LP):為了防止相關測試,一個測試需要和另一個測試在同一個包里運行。因此,我們使用LP進行打包,LP方程的目標函數和約束條件定義如下:
1)目標函數:對生成的包總數取最小值
2)主要約束條件:
單包的測試時長少于10分鐘;
一個測試只能放入一個包;
相關測試放入同一個包。
我們使用Pulp LP解算器求解方程:
# 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], ''
其中,bundle和test_bundle是LP變量,max_bundles和bundle_max_duration是整數。
通常,在LP約束條件中,我們會考慮測試用例的搭建和解除的時長,但是為了簡便,在這里我們忽略不計。
4、在Jenkins主機上啟動調度器進程,Jenkins主機抓包然后搭建mesos架構。我們為每一個Seagull-run創立一個新的調度器。每一次運行生成300多個包,將運行時長在10分鐘左右的包歸為一類。調度器為每一個包建立一個Mesos執行器,只要Mesos控制器提供充足的資源,就將Mesos執行器列入Seagull集群時間計劃表。
5、一旦執行器被列入到集群,執行器內部將進行以下步驟:
每一個執行器建立一個沙箱,從S3(在步驟(2)中已上傳)下載新建項目。然后下載對應于相關測試服務的Docker圖片就可以建立Docker容器(服務)了。當所有的容器都啟動并運行,就開始進行測試了。最后,測試結果和元數據存儲在Elasticsearch(ES)和S3中。我們用內部代理服務Apollo寫入ES。
如果你生活在一個分布式系統的世界,你一定無法避免主機故障。Seagull對任何實例故障都具有容錯功能。
例如,假設一個調度器要運行兩個包。Mesos會提供一個代理(A1)的資源給調度器。假設調度器認為資源充足,那么這兩個包會被安排在A1上。假如出于某種原因,A1出故障了,那么Mesos會通知到調度器。調度器的任務管理器決定重試或者丟棄那兩個包。如果重試,當Mesos下一次提供充分的資源(這種情況下,是提供代理A2的資源)時,那兩個包會被重新安排到時間計劃表里。為了防止包被丟棄,調度器會將那兩個包測試標記為“未執行”。
6. Seagull UI用Apollo從ES獲取結果,并將結果加載到UI供開發人員查看。如果結果全部通過,就可以準備配置了。
規模化挑戰
為了確保測試包的及時性,尤其是在高峰期,Seagull集群需要保證大量實例一直處于可用狀態。之前我們使用AWS的按需實例AWS ASGs,但對我們來說,要想達到這個容量成本太高了。
為了降低成本,我們開始使用一個叫FleetMiser的內部工具去維持Seagull集群。FleetMiser是一個用來測量基于不同信號的集群的自動縮放引擎,這些信號包括當前集群使用率、管道內的流量數等等。FleeMiser主要由以下兩部分組成:
- AWS Spot Fleet:AWS有現場實例,現場實例比按需實例成本低很多,Spot Fleet能提供交互界面更簡單的現場實例。
- 自動縮放:集群的使用時間不穩定,使用高峰主要集中在10:00-19:00(太平洋標準時間),此時,開發人員工作強度最大。為了動態調整范圍,FleetMiser使用集群在不同優先級別下的當前和歷史使用數據。Seagull集群每天的波動范圍大概介于1500CPU內核和10000CPU內核之間。
FleeMiser為我們的集群節省了大約80%的成本。在使用FleetMiser之前,我們是使用AWS按需實例,沒有自動縮放功能。
總結
Seagull使測試結果時長從2天縮短到了30分鐘,同時也大大降低了執行成本。從今以后,開發人員可以安心敲代碼,不用再花大量時間等待驗證變化卻束手無策。
來自:http://www.iteye.com/news/32424