使用 Dockerfile 建置 LLVM 指南¶
簡介¶
您可以在 llvm/utils/docker
中找到許多用於使用 LLVM 元件建置 docker 映像的資源。任何想要建置 docker 映像供自己使用的人,或想要撰寫自己的 Dockerfile 的人,都可以使用這些資源作為起點。
我們目前提供具有 debian10
和 nvidia-cuda
基礎映像的 Dockerfile。我們還提供一個 範例
映像,其中包含需要填寫的預留位置,以便為新的 docker 映像產生 Dockerfile。
為什麼?¶
Docker 映像提供了一種在受控環境中產生軟體二進位發行版的方法。在 LLVM 儲存庫中使用 Dockerfile 建置 docker 映像,比將它們放置在任何其他地方更容易被發現。
Docker 基礎¶
如果您以前從未聽說過 Docker,您可能會發現本節有助於您對其進行非常基本的了解。Docker 是一種在隔離且可重現的環境中執行程式的熱門解決方案,特別是用於維護部署到大型分散式叢集的軟體版本。它使用 Linux 核心命名空間和 cgroup 在當前執行的 Linux 核心內提供輕量級隔離。Docker 化環境的單一活動執行個體稱為 *docker 容器*。docker 容器檔案系統的快照稱為 *docker 映像*。您可以從預先建置的 docker 映像啟動容器。
Docker 映像是由所謂的 *Dockerfile* 建置而成,這是一個以專用語言撰寫的原始程式碼檔案,用於定義建置 docker 映像時要使用的指令(如需更多詳細資訊,請參閱官方文件)。最小的 Dockerfile 通常包含一個基礎映像和一些必須執行的 RUN 命令來建置映像。建置新映像時,docker 會先下載您的基礎映像,將其檔案系統掛載為唯讀,然後在其之上新增一個可寫入的覆蓋層,以追蹤建置映像時執行的所有檔案系統修改。當建置過程完成後,映像最終檔案系統狀態與基礎映像檔案系統之間的差異將儲存在產生的映像中。
概覽¶
llvm/utils/docker
資料夾包含 Dockerfile 和簡單的 bash 腳本,可作為任何想要使用從原始程式碼編譯的 LLVM 元件建立自己的 Docker 映像的基礎。建置映像時,會從上游 git 儲存庫簽出原始程式碼。
產生的映像僅包含請求的 LLVM 元件和一些額外的套件,以使映像在 C++ 開發中至少可用,例如 libstdc++ 和 binutils。
用來建置的介面是 build_docker_image.sh
腳本。 它接受要提取的 LLVM 儲存庫列表和 CMake 呼叫的參數。
如果您想編寫自己的 Docker 映像檔,請從 example/
子資料夾開始。 它提供了一個不完整的 Dockerfile,其中包含(非常少)的 FIXME,說明您需要採取的步驟才能使您的 Dockerfile 發揮作用。
用法¶
llvm/utils/build_docker_image.sh
腳本提供了相當高的控制程度,可以控制如何執行建置。 它允許您指定要從 git 中提取的專案,並提供在 Docker 容器內建置 LLVM 時要使用的 CMake 參數列表。
以下是一個非常簡單的範例,說明如何取得一個 Docker 映像檔,其中包含由 debian10 映像檔中的系統編譯器編譯的 clang 二進制檔
./llvm/utils/docker/build_docker_image.sh \
--source debian10 \
--docker-repository clang-debian10 --docker-tag "staging" \
-p clang -i install-clang -i install-clang-resource-headers \
-- \
-DCMAKE_BUILD_TYPE=Release
請注意,這樣的建置沒有使用您可能想要用於 clang 的 2 階段建置過程。 執行 2 階段建置稍微複雜一些,以下指令可以做到這一點
# Run a 2-stage build.
# LLVM_TARGETS_TO_BUILD=Native is to reduce stage1 compile time.
# Options, starting with BOOTSTRAP_* are passed to stage2 cmake invocation.
./build_docker_image.sh \
--source debian10 \
--docker-repository clang-debian10 --docker-tag "staging" \
-p clang -i stage2-install-clang -i stage2-install-clang-resource-headers \
-- \
-DLLVM_TARGETS_TO_BUILD=Native -DCMAKE_BUILD_TYPE=Release \
-DBOOTSTRAP_CMAKE_BUILD_TYPE=Release \
-DCLANG_ENABLE_BOOTSTRAP=ON -DCLANG_BOOTSTRAP_TARGETS="install-clang;install-clang-resource-headers"
這將從最新的上游版本產生一個新的映像檔 clang-debian10:staging
。 建置映像檔後,您可以像這樣在基於您的映像檔的容器內執行 bash
docker run -ti clang-debian10:staging bash
現在您可以像往常一樣執行 bash 指令了
root@80f351b51825:/# clang -v
clang version 5.0.0 (trunk 305064)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /bin
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/4.8
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/4.8.4
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/4.9
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/4.9.2
Selected GCC installation: /usr/lib/gcc/x86_64-linux-gnu/4.9
Candidate multilib: .;@m64
Selected multilib: .;@m64
我應該選擇哪個映像檔?¶
我們目前提供兩種映像檔:基於 Debian10 的映像檔和基於 nvidia-cuda 的映像檔。 它們使用的基礎映像檔不同,也就是說,它們預先安裝的二進制檔集不同。 Debian8 非常精簡,nvidia-cuda 較大,但預先安裝了 CUDA 函式庫,並且可以存取安裝在您機器上的 GPU。
如果您需要一個只包含 clang 和 libstdc++ 的最小化 Linux 發行版,則應該嘗試使用基於 Debian10 的映像檔。
如果您想使用 CUDA 函式庫並存取您機器上的 GPU,則應該選擇基於 nvidia-cuda 的映像檔,並使用 nvidia-docker 來執行您的 Docker 容器。 請注意,您不需要 nvidia-docker 來建置映像檔,但您需要它才能從執行已建置映像檔的 Docker 容器存取 GPU。
如果您有不同的使用案例,則可以根據 example/
資料夾建立自己的映像檔。
任何 Docker 映像檔都可以僅使用 docker 二進制檔來建置和執行,也就是說,您可以在 Fedora 或任何其他 Linux 發行版上執行 debian10 建置。 您不需要安裝 CMake、編譯器或任何其他 clang 相依項。 這一切都是在 Docker 隔離環境中的建置過程中處理的。
穩定建置¶
如果您想要一個有點新且有點穩定的建置,請使用 branches/google/stable
分支,也就是說,以下指令將使用最新的 google/stable
來源為您產生一個基於 Debian10 的映像檔
./llvm/utils/docker/build_docker_image.sh \
-s debian10 --d clang-debian10 -t "staging" \
--branch branches/google/stable \
-p clang -i install-clang -i install-clang-resource-headers \
-- \
-DCMAKE_BUILD_TYPE=Release
最小化 Docker 映像檔大小¶
由於 Docker 文件系統的運作方式,所有中間寫入操作都會保留在最終映像檔中,即使它們在後續命令中被移除。為了最小化最終映像檔的大小,我們使用 多階段 Docker 建置。Docker 內部會建置兩個映像檔。第一個映像檔會執行所有工作:安裝建置相依性、簽出 LLVM 原始碼、編譯 LLVM 等等。第一個映像檔僅在建置期間使用,並且沒有描述性名稱,亦即它只能在建置完成後透過雜湊值存取。第二個映像檔是我們的結果映像檔。它只包含建置的二進制文件,而不包含任何建置相依性。它也可以透過描述性名稱存取(由 -d 和 -t 旗標指定)。