分散式版本

回報問題 查看原始碼 Nightly · 8.0 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 就能從快取中讀取輸入內容。最終產品也會緩存,讓本機電腦下載。請注意,我們也需要另外提供一種方式,讓使用者在來源樹狀結構中匯出本機變更,以便工作人員在建構前套用這些變更。

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

Google 的分散式建構作業

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

高階建構系統

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

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

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

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