概述
Google的Borg系統是一個集群管理工具,在它上面運行着成千上萬的job,這些job來自許許多多不同的應用,並且跨越多個集群,而每個集群又由大量的機器構成。
Borg通過組合准入控制,高效的任務打包,超額負載以及基於進程級別性能隔離的機器共享從而實現高利用率。它支持那些高可用的應用,它們的運行時特性能夠最小化錯誤恢復時間,它們的調度策略降低了相關錯誤發生的可能性。為了簡化用戶的使用,Borg提供了一個聲明工作規范語言,名稱服務一體化機制,實時job監控以及一系列用於分析和模擬系統行為的工具。
我們接下來將呈現對於Borg的一些總結,包括系統架構和特性,重要的設計決策,對它的一些策略的量化分析以及在對它十多年的使用中得到的經驗教訓。
1、簡介
這個我們內部叫做Borg的集群管理系統確認,調度,啟動,重啟以及監視Google運行的所有應用。這篇論文主要用來闡述Borg提供的三大優點:(1)它隱藏了資源管理以及錯誤處理,因此用戶能集中精力開發應用;(2)具有非常高的可靠性和可用性,從而能夠支持具有這些特性的應用;(3)能夠讓我們跨越數以千計的機器有效地運行負載。Borg並不是第一個解決這些問題的系統,但是它是少數能在這么大規模處理這些問題並且還能達到這樣彈性和完整性的系統之一。本篇論文圍繞着上述主題展開,並且以我們十多年的Borg使用經驗得出的一些定性觀察結果作為結尾。
2、用戶視角
Borg的主要用戶是Google的開發者以及運行Google應用和服務的系統管理員(網站可靠性工程師或者簡稱SRE)。用戶以job的形式向Borg提交工作,每個job由運行一個或多個運行相同程序的task組成。每個job運行在一個Borg cell中,並且將一組機器當做一個單元進行管理。本節的剩余部分將主要描述從用戶視角來看Borg的一些特性。
2.1、工作負載
Borg運行各種各樣的負載,這些負載主要可以分為兩類。第一類是長時間運行不能停止的服務並且要求能夠處理短暫的,延遲敏感的請求(延遲要求在幾微秒到幾百毫秒之間)。這些服務主要用於面向終端用戶的服務,例如Gmail,Google Docs,web搜索以及內部的一些基礎設施服務(例如BigTable)。第二類是通常需要幾秒到幾天來完成的批處理job,這些job對短時間的性能波動並不是非常敏感。這些負載通常在cell之間混合分布,每個cell隨着主要租戶以及時間的不同會運行各種不同的應用:批處理類型的job來了又走而許多面向終端用戶的job又期望一個能長時間使用的模式。Borg要求能很好地處理所有的情況。我們對一個公開的,具有代表性的Borg負載從2011年五月開始進行了為期一個月的追蹤並且已經對它進行了廣泛的分析。
在過去的幾年里,已經有很多應用框架部署到了Borg上面,包括我們內部的MapReduce系統,FlumeJava,Millwheel和Pregel。它們中的大多數都有一個控制器用於提交一個master job以及一個或多個worker job;其中前兩個和YARN中的應用管理器扮演的是相同的角色。我們的分布式存儲系統例如GFS以及它的后繼者CFS,Bigtable和Megastore都是運行在Borg上面的。
在本篇論文中,我們將高優先級的job稱為prod,剩下的則稱為non-prod。多數的長時間運行的服務job是prod類型的,而多數的批處理job是non-prod類型的。在一個具有代表性的cell中,prod類型的job分配了大概所有CPU資源的70%並且代表了60%的CPU使用;在內存方面,prod類型的job分配了大概所有內存資源的55%並且代表了大概85%的內存使用。至於分配和使用的區別,我們將在章節5.5再進行說明。
2.2、集群和cell
一個cell中的機器通常屬於單個集群,並且由數據中心規模的高性能網絡結構連接起來。一個集群通常存在於單個數據中心里,而多個數據中心的集合構成了一個site。一個集群通常包含一個大的cell,也許其中還有一些小規模用於測試或者其他特殊目的的cell。我們總是極力避免單點故障發生。
排除掉用於測試的cell之外,我們中型大小的cell通常包含10k台機器,當然還有更大的cell。從不同的維度:比如大小(CPU,RAM,硬盤,網絡),處理器類型,性能以及外部IP和閃存來看,cell是多種多樣的。但是Borg通常會通過決定在哪個cell運行task,為它們分配資源,為它們安裝程序以及它們的一些依賴,監視它們的健康狀況並且在它們崩潰的時候重啟它們,從而對用戶屏蔽了這些cell之間的差異。
2.3、job和task
Borg的一個job的屬性通常包括它的名字,所有者以及它擁有的task的名字。job可以存在一定的約束,從而讓它的task運行在有特定屬性的機器上,例如特定的處理器架構,操作系統版本,以及外部IP。約束可以分為硬性的或軟性的。軟性的約束更像是一種優先建議而不是要求。一個job的運行可以推遲到上一個結束之后才開始並且一個job只能運行在一個cell中。
每個task代表了運行在一個容器或者一個物理機器內的一系列Linux進程。Borg的大部分負載並不會運行在虛擬機中,因為我們不想承擔虛擬化帶來的開銷。並且我們在設計系統的時候就假設系統使用的處理器是沒有硬件虛擬化支持的。
每個task同樣擁有各自的屬性,例如資源需求以及task在job里的索引。大多數task屬性在同一個job里都是相同的,但是提供了針對具體task的命令行標志之后也能對它們進行重載。每一個資源的維度(例如CPU核數,RAM,磁盤空間,磁盤訪問速率,TCP端口等等)都能以很好的粒度被獨立地指定,我們並不會強加一個固定大小的bucket或者slot。Borg程序通常都是靜態鏈接的從而能減少對它們運行環境的依賴,並且二進制文件和數據文件都以包的形式組織起來,而它們的安裝都是由Borg策划的。
用戶通常通過向Borg發送遠程過程調用,即利用一些命令行工具,來操作job或者我們的監視系統。大多數的job描述都是用一種聲明性的配置語言BCL寫成的。BCL是GCL的一個變體,GCL會產生protobuf文件,而BCL會在它之上擴展一些Borg特有的關鍵詞。BCL提供了lambda函數用於計算,應用程序通常利用它來調整對環境的配置。很多的BCL文件都超過了1k行,我們已經積累了成千上萬行的BCL文件。Borg的job配置文件和Aurora的配置文件有很多類似之處。下圖展示了job和task運行走完它們整個生命周期的過程。
在一個正在運行的job中,用戶可以通過推送一個新的job配置文件給Borg並且命令Borg更新task到新的配置,從而改變一些或者全部task的屬性。這種輕量級的,非原子化的動作在它關閉前極有可能是不被操作的。更新通常以滾動的方式進行,我們一般會對更新引起的中斷(重新調度或搶占)數量進行限制,那些引起中斷的數量超過限制的更新會被直接跳過。
一些任務更新(例如推送一個新的二進制文件)總是需要重啟task,另一些操作(例如增加資源請求或者改變約束條件)可能讓task不再適應當前的機器,因此需要將它停止並且重新調度,而其他的操作(例如改變優先級)則不需要任何重啟或者移動task的操作就能完成。
在被SIGKILL搶占之前,task總能通過SIGTERM信號被通知,因此它們有足夠的時間去清理,保存狀態,結束當前正在執行的請求並且拒絕新的請求。如果搶占器設置了延遲邊界的話,實際的信號可能更少一點。事實上,一個通知只有80%的情況會被成功推送。
2.4、allocs
Borg的alloc(allocation的簡寫)操作是指在一台機器上預留一些資源,從而能夠在其上運行一個或者多個資源;這些資源不管是否被使用都是保持被分配狀態的。Allocs操作可以被用來保留資源用於未來task的使用,也可以用於在停止以及啟動一個task之間保存資源,還可以用於將不同job里的task收集起來,讓它們運行在同一台機器中:比如一個web服務器實例以及相關的用於將本地磁盤的服務器URL記錄拷貝到分布式文件系統的task。被alloc操作之后的資源和機器中的其他資源是被同等對待的,運行在同一個alloc操作之上的多個task共享其中的資源。如果一個alloc操作必須被重定向到另一台機器上,那么之上的任務就必須隨着alloc操作被重新調度。
一個alloc集就像是一個job:它是一系列的alloc操作用於在多台機器上預留資源。一旦一個alloc操作被創建,一個或多個job就能被提交並且運行在它之上。簡單起見,我們一般用“task”來代之一次alloc操作或者頂層的task(一個在alloc操作之外的task),而“job”指一個job或者一個alloc操作集。
2.5、優先級,配額以及准入控制
如果出現了超出處理能力的負載怎么辦?我們的解決方法是優先級和配額。
每個job都有一個優先級(priority),也就是一個小的正整數。一個高優先級的task可以以犧牲另一個較低優先級的task為代價來獲取資源,即使這種犧牲包括搶占或者殺死較低優先級的task。Borg對於不同的使用定義了一種非重疊的優先級帶,包括(優先級從高到底排列):監視,生產,批任務以及盡力而為的工作(也可以理解為測試或者免費的工作)。在本篇論文中,prod類型的job都是處於監視或者產品的優先級帶中。
通常一個被搶占的task會被重新調度到cell的其他地方,而搶占操作也有可能產生級聯影響:比如一個高優先級的task搶占了一個較低優先級的task,而后者又去搶占更低優先級的task,從而不斷級聯下去。為了防止這種級聯事件的發生,我們並不允許同處於生產優先級帶的task相互搶占。細粒度的優先級划分在其他一些情況下還是相當有用的:比如MapReduce的master類型的task的優先級要高於它控制的worker,從而提高整個系統的可靠性。
優先級用來表示在一個cell中運行或者等待運行的job的相對重要性。而配額(quota)則表示哪些job能夠被調度。配額我們可以理解為在給定優先級下的資源請求向量(CPU,RAM,磁盤等等)。資源請求是指在一段時間內,一般是一個月內,一個用戶的job能請求的最大資源數目(比如一個prod請求了20TB的RAM,時間是從現在到七月份,在XX cell中)。配額檢查也是准入控制的一部分,而不是調度:一個配額要求未被滿足的job是會被立刻拒絕的。
高優先級job的配額通常會比低優先級job的配額花費更多。比如生產優先級的配額會被限制在一個cell真實可獲取的資源數量范圍內。因此,如果一個用戶提交了一個生產優先級的job,並且配額合適,那么就能期待它運行。盡管我們建議用戶不要購買多於他們需求的配額,但是許多用戶仍然會選擇買過量的配額,因為這能保證將來它們應用的用戶增長之后不會出現資源的短缺。對於這一點,我們的回應是,處於低優先級的job能擁有更多的配額:每一個處於優先級0的用戶擁有無限的資源配額,盡管這很難被真正實施,因為資源被超額認購了。一個低優先級的job可能可以被准入但是也許會一直被掛起,因為請求的資源一直無法得到滿足。
配額的分配是在Borg之外進行的並且和我們的物理容量規划密切相關。它們通常反映了不同數據中心配額的價格和可用性。一個用戶的job只有在滿足了它所在優先級的配額之后才能被准入。配額的使用減少了對像優勢資源公平(Dominant Resource Fairness,DRF)這樣的策略的使用。
Borg還有一個容量系統,它能給予一些用戶以特殊的權限:比如允許管理員刪除或修改cell里任意的job,或者運行用戶訪問限制的內核特性或者Borg的行為,例如在他們的job中禁用資源限制。
2.6、命名以及監控
單單創造並且部署task還是遠遠不夠的,因為一個服務的客戶端以及其他系統需要能夠找到它們,即使在它們被調度到新的機器上以后。因此,Borg為每個task創造了一個叫”Borg name service“(BNS)類型的名字,這個名字中包含了cell的名字,job的名字以及task的編號。Borg會將task的主機名,端口號以及這個BNS名字寫入Chubby里面一個一致的,高可用的文件中,而這個文件通常被我們的RPC系統用於查找task。BNS名字同樣被用作task的DNS名字基礎,因此對於用戶ubar擁有的一個在叫做cc的cell中的一個叫jfoo的job中的第五十個task,我們就可以通過域名50.jfoo.ubar.cc.borg.google.com訪問到。同時Borg會在job的大小或者task的健康狀況改變時將它們寫入Chubby中,之后負載均衡器就能決定將請求路由到什么地方了。
幾乎Borg之下運行的每一個task都有一個內置的HTTP server用於發布task的健康狀況以及其他許多的性能指標(RPC延遲等等)。Borg會監視健康檢查的URL並且會重啟那些沒有即使回復的task或者直接返回一個HTTP 錯誤代碼。其他數據通過另外一些監控工具進行監控,並且對服務對象級別的違規行為進行報警。
一個叫做Sigma的服務提供了基於web的用戶界面,通過它,用戶能測試他們所有job或者一個特定cell的健康狀況。還可以深入到具體的job或者task中去測試它們的資源相關的行為,詳細的日志,執行歷史以及它們最終的命運。我們的應用會產生大量的日志:它們會自動地進行輪轉從而避免耗盡磁盤的空間,並且會在task退出之后仍然保留一定的時間用於調試。如果一個job不再運行了,Borg會提供一個“為什么被掛起”的注解,而且會附上如何修改job的資源請求以更好地適應cell的指導。我們已經發布了一個符合要求的資源請求指導,從而能夠讓調度進行地更為順利。
Borg會記錄所有的job提交情況,task事件,以及Infrastore中詳細的task執行前的資源使用情況。Infrastore是一個有着類SQL接口的可擴展只讀數據存儲。這些數據被用作基於使用的計費,調試,系統錯誤以及長期的容量計划。同時,它們也為Google的集群負載追蹤提供了數據。
所有上述的特性能夠幫助用戶更好地理解,調試Borg和它里面的job的行為,同時也能幫助我們的SRE每個人都能管理許許多多的機器中的一部分。
注:翻譯中部分內容可能比較晦澀或者並非十分流暢,歡迎指正
原文地址:http://static.googleusercontent.com/media/research.google.com/zh-CN//pubs/archive/43438.pdf