本文會介紹 Bazel 中的沙箱機制、安裝 sandboxfs
,以及對沙箱環境進行偵錯。
「沙箱」是一種權限限制策略,可將程序彼此獨立或與系統中的資源分開。對 Bazel 來說,這代表限制檔案系統的存取權。
Bazel 的檔案系統沙箱會在僅包含已知輸入內容的工作目錄中執行程序,因此,除非編譯器和其他工具知道其絕對路徑,否則不會看見不應存取的來源檔案。
沙箱機制無法以任何方式隱藏主機環境。處理程序可自由存取檔案系統中的所有檔案。不過,在支援使用者命名空間的平台上,程序無法修改工作目錄以外的任何檔案。這可確保建構圖表沒有隱藏依附元件,也不會對建構作業的可重現性造成影響。
具體來說,Bazel 會為每個動作建構 execroot/
目錄,並在執行時做為動作的工作目錄。execroot/
包含動作的所有輸入檔案,且可做為任何產生的輸出的容器。Bazel 接著會使用作業系統提供的技術 (Linux 上的容器和 macOS 上的 sandbox-exec
) 來限制 execroot/
中的動作。
採用沙箱機制的原因
如未採用動作沙箱機制,Bazel 無法得知工具是否使用未宣告的輸入檔案 (未明確列在動作依附元件中的檔案)。當其中一個未宣告的輸入檔案變更時,Bazel 仍會認為建構作業是最新版本,不會重新建構動作。這可能會導致漸進式建構作業出錯。
若是以不正確的方式重複使用快取項目,將會在遠端快取期間發生問題。共用快取中的錯誤快取項目會影響專案的所有開發人員,因此清除整個遠端快取並不是可行的解決方案。
沙箱機制會模仿遠端執行的行為,如果建構作業能與沙箱完美搭配運作,也許也能與遠端執行搭配運作。透過讓遠端執行上傳所有必要檔案 (包括本機工具),您就能大幅減少編譯叢集的維護成本,比起在每次嘗試新編譯器或變更現有工具時,在叢集中的每個機器上安裝工具。
採用的沙箱策略
您可以利用策略旗標,選擇要使用哪一種沙箱功能 (如果有的話)。使用 sandboxed
策略可讓 Bazel 選擇下列其中一種沙箱實作項目,優先使用 OS 專屬的沙箱來取代語意較雜的通用沙箱。如果您傳遞 --worker_sandboxing
標記,永久工作站會在一般沙箱中執行。
local
(又稱為 standalone
) 策略不執行任何類型的沙箱作業。它只會使用設為工作區執行根目錄的工作目錄來執行動作的指令列。
processwrapper-sandbox
是一種不需要任何「進階」功能的沙箱策略,應可在任何立即可用的 POSIX 系統中運作。這項工具會建構沙箱目錄,由指向原始來源檔案的符號連結組成,然後透過設為這個目錄 (而非 execroot) 的工作目錄執行動作的指令列,然後將已知的輸出構件從沙箱移至 execroot 中,並刪除沙箱。這樣可防止動作意外使用任何未宣告的輸入檔案,避免輸出具有不明輸出檔案的 execroot。
linux-sandbox
進一步以 processwrapper-sandbox
為基礎建構而成。與 Docker 實際上的功能類似,它會使用 Linux 命名空間 (User、Mount、PID、網路和 IPC 命名空間) 將動作與主機隔離,也就是說,除了沙箱目錄以外,整個檔案系統會設為唯讀,因此動作不會意外修改主機檔案系統上的任何內容。這可避免像是錯誤測試誤將「rm-rf」設為 $HOME 目錄的情形。或者,您也可以防止動作存取網路。linux-sandbox
會使用 PID 命名空間來避免動作看到任何其他程序,並在最後穩定終止所有程序 (包含動作產生的 Daemon)。
darwin-sandbox
很類似,但適用於 macOS。此設定使用 Apple 的 sandbox-exec
工具執行,其作業與 Linux 沙箱大致相同。
由於作業系統提供的機制有所限制,linux-sandbox
和 darwin-sandbox
都無法在「巢狀」情境中運作。由於 Docker 也會使用 Linux 命名空間來發揮容器神奇的功用,因此除非您使用 docker run --privileged
,否則可能無法在 Docker 容器內輕鬆執行 linux-sandbox
。在 macOS 上,您無法在已採用沙箱機制的程序中執行 sandbox-exec
。因此在這類情況下,Bazel 會自動改回使用 processwrapper-sandbox
。
如果您比較想要發生建構錯誤 (例如不會使用較嚴格的執行策略意外進行建構),請明確修改 Bazel 嘗試使用的執行策略清單 (例如 bazel build
--spawn_strategy=worker,linux-sandbox
)。
動態執行通常需要在本機執行時採用沙箱機制。如要停用,請傳遞 --experimental_local_lockfree_output
旗標。動態執行會自動採用沙箱機制,無須監控永久工作站。
採用沙箱機制的缺點
沙箱作業需要額外的設定及拆解成本。這筆費用有多大取決於許多因素,包括建構型態和主機 OS 的效能。對 Linux 來說,採用沙箱機制的建構作業,速度幾乎不會超過百分之少。設定
--reuse_sandbox_directories
可能會減少設定及卸除費用。沙箱機制實際上會有效停用工具的所有快取。您可以使用永久工作站來減輕這種情形,但沙箱保證的成本較低。
「多重工作站」需要明確的工作站支援才能採用沙箱機制。如果工作站不支援多工沙箱機制,就能在動態執行下以單面工作站的形式執行,因此可能會耗用額外的記憶體。
沙盒
sandboxfs
是 FUSE 檔案系統,可公開對基礎檔案系統的任意檢視畫面,而不會有時間懲處。Bazel 會使用 sandboxfs
即時為每個動作產生 execroot/
,避免發出數千次系統呼叫的費用。請注意,由於 FUSE 負擔,在 execroot/
中進一步的 I/O 速度可能會變慢。
安裝 Sandboxf
請按照下列步驟安裝 sandboxfs
,並使用該指令執行 Bazel 建構作業:
下載
下載並安裝
sandboxfs
,讓 sandboxfs
二進位檔最終位於 PATH
。
執行 sandboxfs
- (僅限 macOS) 安裝 OSXFUSE。
(僅限 macOS) 執行:
sudo sysctl -w vfs.generic.osxfuse.tunables.allow_other=1
您需要在安裝後和每次重新啟動後執行這項作業,確保核心 macOS 系統服務可透過 Sandboxfs 正常運作。
使用
--experimental_use_sandboxfs
執行 Bazel 建構作業。bazel build target --experimental_use_sandboxfs
疑難排解
如果看到 local
而不是 darwin-sandbox
或 linux-sandbox
做為執行動作的註解,可能表示沙箱功能已停用。傳遞 --genrule_strategy=sandboxed --spawn_strategy=sandboxed
即可啟用。
偵錯
請按照下列策略對沙箱機制問題進行偵錯。
已停用的命名空間
在某些平台上 (例如 Google Kubernetes Engine 叢集節點或 Debian),基於安全考量,使用者命名空間會依預設停用。如果 /proc/sys/kernel/unprivileged_userns_clone
檔案存在且包含 0,您可以執行以下指令啟用使用者命名空間:
sudo sysctl kernel.unprivileged_userns_clone=1
規則執行失敗
由於系統設定,沙箱可能會無法執行規則。如果您看到類似 namespace-sandbox.c:633: execvp(argv[0], argv): No such file or
directory
的訊息,請嘗試使用 --strategy=Genrule=local
停用沙箱,針對 Genrules 問題,使用 --spawn_strategy=local
對其他規則停用。
建構失敗的詳細偵錯資訊
如果建構失敗,請使用 --verbose_failures
和 --sandbox_debug
,讓 Bazel 顯示建構失敗時的確切執行指令,包括設定沙箱的部分。
錯誤訊息範例:
ERROR: path/to/your/project/BUILD:1:1: compilation of rule
'//path/to/your/project:all' failed:
Sandboxed execution failed, which may be legitimate (such as a compiler error),
or due to missing dependencies. To enter the sandbox environment for easier
debugging, run the following command in parentheses. On command failure, a bash
shell running inside the sandbox will then automatically be spawned
namespace-sandbox failed: error executing command
(cd /some/path && \
exec env - \
LANG=en_US \
PATH=/some/path/bin:/bin:/usr/bin \
PYTHONPATH=/usr/local/some/path \
/some/path/namespace-sandbox @/sandbox/root/path/this-sandbox-name.params --
/some/path/to/your/some-compiler --some-params some-target)
您現在可以檢查產生的沙箱目錄,看看 Bazel 建立了哪些檔案,然後再次執行指令,看看運作方式。
請注意,使用 --sandbox_debug
時,Bazel 不會刪除沙箱目錄。除非您主動偵錯,否則應停用 --sandbox_debug
,因為這樣會隨時間填滿磁碟。