使用 Docker Sandbox 解決 Bazel Remote Execution 的問題

回報問題 查看來源

如果 Bazel 建構作業成功執行,遠端執行時可能會失敗,原因是這些限制和要求不會影響本機建構作業。如要瞭解此類錯誤最常見的原因,請參閱為遠端執行調整 Bazel 規則

本頁說明如何使用 Docker 沙箱功能,找出並解決遠端執行時最常發生的問題,該功能在建構作業上設下與遠端執行相等的限制。這可讓您排解建構問題,而無需遠端執行服務。

Docker 沙箱功能會模擬遠端執行的限制,如下所示:

  • 建構動作會在工具鍊容器中執行。您可以透過支援容器化遠端執行作業的服務,使用相同的工具鍊容器在本機或遠端執行建構作業。

  • 沒有多餘的資料跨越容器邊界。系統只會在相關聯的建構動作成功完成後,才進入及離開容器,且明確宣告的輸入和輸出才會進入及離開容器。

  • 每個動作都會在新容器中執行。系統會為每個產生的建構動作建立新的不重複容器。

您可使用下列其中一種方式排解這些問題:

  • 原生疑難排解。透過這種方法,Bazel 及其建構動作會在本機電腦原生上執行。Docker 沙箱功能在建構作業上設下限制,效果相當於遠端執行的限制。不過,這個方法不會偵測本機工具、狀態和資料外洩到建構作業中的資料,這會導致遠端執行發生問題。

  • 排解 Docker 容器中的問題。採用這種方法時,Bazel 及其建構動作會在 Docker 容器中執行,讓您可以偵測從本機機器流入建構作業的工具、狀態和資料,以及對遠端執行的限制。這個方法可以深入分析建構作業,即使建構的某些部分失敗也一樣。這個方法仍在實驗階段,尚未正式支援。

必要條件

開始排解問題前,請先按照下列步驟進行:

  • 安裝 Docker 並設定執行所需的權限。
  • 安裝 Bazel 0.14.1 以上版本。較舊版本並不支援 Docker 沙箱功能。
  • 按照這裡的說明,將固定至最新發布版本的 bazel-toolchains 存放區新增至建構的 WORKSPACE 檔案。
  • .bazelrc 檔案中新增旗標以啟用這項功能。如果 Bazel 專案的根目錄不存在,請在該專案的根目錄中建立檔案。下列旗標為參考範例。請查看 bazel-toolchains 存放區中的最新 .bazelrc 檔案,並複製 docker-sandbox 設定中定義的標記值。
# Docker Sandbox Mode
build:docker-sandbox --host_javabase=<...>
build:docker-sandbox --javabase=<...>
build:docker-sandbox --crosstool_top=<...>
build:docker-sandbox --experimental_docker_image=<...>
build:docker-sandbox --spawn_strategy=docker --strategy=Javac=docker --genrule_strategy=docker
build:docker-sandbox --define=EXECUTOR=remote
build:docker-sandbox --experimental_docker_verbose
build:docker-sandbox --experimental_enable_docker_sandbox

如果您的規則需要其他工具,請執行下列操作:

  1. 使用 Dockerfile 安裝工具,並在本機建構映像檔,藉此建立自訂 Docker 容器。

  2. 將上述 --experimental_docker_image 標記的值替換為自訂容器映像檔的名稱。

原生疑難排解

這個方法會直接在本機電腦執行 Bazel 及其所有建構動作,這是一種可靠的方式,可讓您確認建構作業在遠端執行時是否成功。

不過,使用這個方法時,本機安裝的工具、二進位檔和資料可能會外洩到建構作業中,尤其是使用設定樣式的 WORKSPACE 規則時更是如此。這類外洩情形將導致遠端執行發生問題。除了原生疑難排解之外,您也可以在 Docker 容器中排解相關問題

步驟 1:執行版本

  1. --config=docker-sandbox 標記新增至要執行建構作業的 Bazel 指令。例如:

    bazel --bazelrc=.bazelrc build --config=docker-sandbox target
    
  2. 執行建構,並等待作業完成。由於 Docker 沙箱功能的關係,建構作業的執行速度會比一般慢四倍。

您可能會遇到以下錯誤:

ERROR: 'docker' is an invalid value for docker spawn strategy.

如果這樣做,請使用 --experimental_docker_verbose 旗標再次執行建構。這個標記會啟用詳細錯誤訊息。此錯誤通常是因為 Docker 安裝出錯,或缺少在目前使用者帳戶下執行該指令的權限。詳情請參閱 Docker 說明文件。如果問題持續發生,請直接跳到在 Docker 容器中排解問題一節。

步驟 2:解決系統偵測到的問題

以下列出最常見的問題及解決方法。

  • Bazel 執行檔案樹狀結構參照的檔案、工具、二進位檔或資源遺失。確認已明確宣告受影響目標的所有依附元件。詳情請參閱「管理隱含依附元件」一文。

  • 缺少絕對路徑或 PATH 變數參照的檔案、工具、二進位檔或資源。確認已在工具鍊容器中安裝所有必要工具,並使用工具鍊規則正確宣告指向缺少資源的依附元件。詳情請參閱透過工具鍊規則叫用建構工具一文。

  • 二進位檔執行失敗。其中一項建構規則所參照的二進位檔與執行環境 (Docker 容器) 不相容。詳情請參閱管理平台相依的二進位檔。如果您無法解決問題,請聯絡 bazel-discuss@google.com 尋求協助。

  • @local-jdk」中的檔案遺失或導致錯誤。本機電腦上的 Java 二進位檔會外洩到建構中,同時與程式庫不相容。請在規則和目標中使用 java_toolchain,而非 @local_jdk。如需進一步協助,請與 bazel-discuss@google.com 聯絡。

  • 其他錯誤。如需協助,請與 bazel-discuss@google.com 聯絡。

Docker 容器中的疑難排解

使用這種方法時,Bazel 會在主機 Docker 容器中執行,而 Bazel 的建構動作會在 Docker 沙箱功能產生的個別工具鍊容器中執行。沙箱會為每個建構動作產生新的工具鍊容器,且在每個工具鍊容器中只會執行一個動作。

這個方法可更精細地控管安裝在主機環境中的工具。將建構作業的執行作業與其建構動作的執行程序分開,並將已安裝的工具保持在最低限度,您可以驗證建構作業是否在本機執行環境中有任何依附元件。

步驟 1:建構容器

  1. 建立 Dockerfile,用於建立 Docker 容器並使用一組最少的建構工具安裝 Bazel:

    FROM debian:stretch
    
    RUN apt-get update && apt-get install -y apt-transport-https curl software-properties-common git gcc gnupg2 g++ openjdk-8-jdk-headless python-dev zip wget vim
    
    RUN curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
    
    RUN add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable"
    
    RUN apt-get update && apt-get install -y docker-ce
    
    RUN wget https://releases.bazel.build/<latest Bazel version>/release/bazel-<latest Bazel version>-installer-linux-x86_64.sh -O ./bazel-installer.sh && chmod 755 ./bazel-installer.sh
    
    RUN ./bazel-installer.sh
    
  2. 將容器建構為 bazel_container

    docker build -t bazel_container - < Dockerfile
    

步驟 2:啟動容器

使用如下所示的指令啟動 Docker 容器。在指令中,將路徑替換成您要建構的主機原始碼路徑。

docker run -it \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /tmp:/tmp \
  -v your source code directory:/src \
  -w /src \
  bazel_container \
  /bin/bash

這個指令會以根層級執行容器、對應 Docker 通訊端,並掛接 /tmp 目錄。這可讓 Bazel 產生其他 Docker 容器,以及使用 /tmp 下的目錄,以便與這些容器共用檔案。您可以在容器內的 /src 中找到原始碼。

指令會刻意從 debian:stretch 基本容器啟動,此容器包含的二進位檔與做為工具鍊容器使用的 rbe-ubuntu16-04 容器不相容。如果本機環境的二進位檔外洩到工具鍊容器中,則會導致建構錯誤。

步驟 3:測試容器

從 Docker 容器內執行下列指令來進行測試:

docker ps
bazel version

步驟 4:執行版本

如下所示執行建構。輸出使用者為根目錄,因此對應至可在 Bazel 執行的主機容器內,以相同絕對路徑存取的目錄、從執行 Bazel 建構動作的 Docker 沙箱功能產生的工具鍊容器,以及執行主機和動作容器的本機電腦中存取。

bazel --output_user_root=/tmp/bazel_docker_root --bazelrc=.bazelrc \ build --config=docker-sandbox target

步驟 5:解決系統偵測到的問題

您可以按照下列方式解決建構失敗的問題:

  • 如果建構失敗並發生「磁碟空間不足」錯誤,您可以使用旗標 --memory=XX (其中 XX 是以 GB 為單位分配的磁碟空間) 啟動主機容器,藉此提高上限。這是實驗功能,可能會導致無法預期的行為。

  • 如果建構在分析或載入階段失敗,在 WORKSPACE 檔案中,宣告的一或多個建構規則與遠端執行並不相容。請參閱針對遠端執行調整 Bazel 規則,瞭解可能的原因和解決方法。

  • 如果因任何其他原因導致建構失敗,請參閱「步驟 2:解決偵測到的問題」中的疑難排解步驟。