分散式版本

回報問題 查看原始碼 Nightly · 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

如果您擁有龐大的程式碼集,依附元件鏈可能會變得非常深。即使是簡單的二進位檔,也可能會依賴數萬個建構目標。如此規模龐大,簡直是無法在單一機器上合理時間內完成建構:沒有任何建構系統能規避機器硬體所施加的物理法則。要讓這項作業順利進行,唯一的方法就是使用支援分散式建構的建構系統,在系統執行的工作單位中,將工作單位分散到任意且可調整的機器數量。假設我們將系統的工作拆分為足夠小的單元 (稍後會進一步說明),這樣就能以我們願意付費的速度,完成任何大小的任何版本。我們一直致力於定義以構件為基礎的建構系統,以實現這項可擴充性。

遠端快取

最簡單的類型是只使用遠端快取的類型,如圖 1 所示。

使用遠端快取進行分散式建構

圖 1. 顯示遠端快取的散發式建構

每個執行建構作業的系統 (包括開發人員工作站和持續整合系統) 都會共用常見遠端快取服務的參照。這項服務可能是快速本機的短期儲存系統 (例如 Redis),或類似 Google Cloud Storage 的雲端服務。每當使用者需要直接建構成果或做為依附元件時,系統都會先使用遠端快取檢查該成果是否已存在。如果是,則可以下載構件,而非建構構件。如果沒有,系統會建構成果本身,然後將結果上傳回快取。也就是說,不會經常變動的低層級依附元件可建構一次並提供給所有使用者,不必讓每位使用者重新建構。在 Google 中,許多構件都是從快取中提供,而不是從頭建構,因此大幅降低了執行建構系統的成本。

為了讓遠端快取系統能夠順利運作,建構系統必須保證能完全重現建構作業。也就是說,對於任何建構目標,都必須能夠判斷該目標的輸入組合,以便在任何機器上產生完全相同的輸出內容。這是確保下載構件結果與自行建構構件結果相同的唯一方法。請注意,如要這麼做,快取中的每個構件都必須對其目標和輸入內容的雜湊值進行索引鍵,這樣不同的工程師就能同時對同一個目標進行不同的修改,而遠端快取則會儲存所有產生的構件,並在不衝突的情況下適當提供這些構件。

當然,如果要從遠端快取中獲得任何好處,下載構件必須比建構構件更快。但這並非總是如此,尤其是當快取伺服器與進行建構的機器相距甚遠時。Google 的網路和建構系統經過精心調整,可快速分享建構結果。

遠端執行

遠端快取並非真正的分散式建構作業。如果快取遺失,或是您進行低層級變更,需要重新建構所有內容,您仍需要在本機上執行整個建構作業。真正的目標是支援遠端執行作業,讓建構作業的實際工作可分散至任意數量的 worker。圖 2 為遠端執行系統。

遠端執行系統

圖 2. 遠端執行系統

在每位使用者電腦上執行的建構工具 (使用者可能是人類工程師或自動化建構系統) 會將要求傳送至集中式建構主機。建構主控台會將要求分割成元件動作,並在可調整大小的工作站集區中排定執行這些動作的時間。每個 worker 都會根據使用者指定的輸入內容執行要求的動作,並寫出產生的構件。這些構件會在執行需要這些構件的動作的其他機器之間共用,直到產生最終輸出內容並傳送給使用者為止。

實作這類系統最困難的部分,就是管理 worker、master 和使用者本機之間的通訊。worker 可能會依賴其他 worker 產生的中繼構件,且最終輸出內容必須傳回使用者的本機電腦。為此,我們可以讓每個工作站寫入其結果,並從快取讀取其依附元件,以上述描述的分散式快取為基礎。主工作會阻止 worker 繼續執行,直到 worker 所依賴的所有項目都完成為止,這樣 worker 就能從快取中讀取輸入內容。最終產品也會緩存,讓本機電腦下載。請注意,我們也需要另外的方法,才能匯出使用者來源樹狀結構中的本機變更,讓 worker 在建構前套用這些變更。

為使這項功能運作,您必須將先前所述的所有以構件為基礎的建構系統部分整合在一起。建構環境必須完全自述,以便我們在沒有人為介入的情況下啟動工作站。由於每個步驟都可能在不同的機器上執行,因此建構程序本身必須完全獨立。輸出內容必須完全確定,讓每個工作站都能信任從其他工作站收到的結果。這類保證對以工作為基礎的系統來說極為困難,因此幾乎不可能在這樣的系統上建構可靠的遠端執行系統。

Google 的分散式版本

自 2008 年起,Google 就一直使用分散式建構系統,同時採用遠端快取和遠端執行功能,如圖 3 所示。

高階建構系統

圖 3. Google 的分散式建構系統

Google 的遠端快取稱為 ObjFS。這個架構包含後端,可將建構輸出內容儲存在 Bigtable 中,並分散至我們的一系列實際工作機器;此外,這個架構也包含前端 FUSE 精靈,名為 objfsd,可在每位開發人員的機器上執行。FUSE 守護程序可讓工程師瀏覽建構輸出內容,就像是工作站上儲存的一般檔案一樣,但只有使用者直接要求的少數檔案會根據需求下載檔案內容。隨需提供檔案內容會大幅降低網路和磁碟用量。比起將所有建構輸出內容儲存在開發人員本機磁碟中,系統的建構速度快了兩倍。

Google 的遠端執行系統稱為 Forge。Blaze 中的 Forge 用戶端 (Bazel 內部等同項目) 稱為「Distributor」,會將每個動作的要求傳送至在資料中心執行的工作,稱為「Scheduler」。Scheduler 會維護動作結果快取,如果系統的任何其他使用者已建立動作,則可立即傳回回應。如果沒有,則會將動作放入佇列。大型的執行器工作集區會持續讀取這個佇列中的動作、執行這些工作,並將結果直接儲存在 ObjFS Bigtables 中。執行者可將這些結果用於日後的動作,或供使用者透過 objfsd 下載。

最終結果是系統可擴充,以便有效支援 Google 執行的所有建構作業。而 Google 的建構作業規模非常龐大:Google 每天執行數百萬個建構作業,執行數百萬個測試案例,並從數十億行原始程式碼產生千兆位元組的建構輸出內容。這類系統不僅可讓工程師快速建構複雜的程式碼庫,還能讓我們導入大量的自動化工具和系統,這些工具和系統都會依賴我們的建構作業。