Bazel Docker 容器使用指南:两种模式构建 Abseil 项目与容器环境详解
【免费下载链接】bazela fast, scalable, multi-language and extensible build system项目地址: https://gitcode.com/GitHub_Trending/ba/bazel
本指南围绕 Bazel 官方发布的 Docker 容器(gcr.io/bazel-public/bazel)展开,讲解其内置工具链内容,并给出两条可落地的构建路径:在宿主机上通过目录挂载复用容器构建,以及在容器内部完成源码检出与构建。读完本文,你将掌握 Bazel Docker 容器的启动参数、--output_user_root构建缓存复用机制、ASan/TSan/MSan 消毒器构建方式,并能独立排查容器内外的权限与路径问题。
本文内容对应仓库中的 docs/install/docker-container.mdx,并补充了 docs/docs/user-manual.mdx 中关于输出根目录的权威说明。
Bazel Docker 容器是什么
Bazel 官方提供预构建的 Docker 镜像,镜像中打包了 Bazel 本身及其构建所需的运行时环境(GCC 编译器、OpenJDK、Python 等)。它的价值在于:你无需在宿主机上安装 Bazel 或配置任何工具链,只需一个 Docker 运行时,就能以完全一致的环境执行构建——这对 CI、团队协作、复现历史构建尤其有用。
容器内的典型环境(从文档记录的实际输出可确认):
- gcc:Ubuntu 9.4.0 工具链
- java:OpenJDK 1.8.0_362
- python:Python 3.8.10
- bazel:内置 Bazel 二进制,文档示例中为 Build label
6.2.1
这套组合意味着容器开箱即可构建 C++、Java、Python 等多种语言的 Bazel 项目,无需任何额外安装步骤。
方式一:宿主机源码 + 目录挂载构建(推荐用于本地开发)
这种方式的核心思路是:源码保留在宿主机,容器只为每次构建命令临时启动,构建产物和缓存落在宿主机挂载的目录中。这样你可以在宿主机上使用自己熟悉的编辑器修改代码,构建完成后产物直接可见,且缓存跨构建复用,不必反复重编。
第一步:检出项目源码
在宿主机上克隆 abseil-cpp 项目(指定深度为 1 的浅克隆与固定标签,保证环境可复现):
git clone --depth 1 --branch 20220623.1 https://github.com/abseil/abseil-cpp.git /src/workspace第二步:创建构建缓存目录
mkdir -p /tmp/build_output/该目录将成为 Bazel 的输出根目录(--output_user_root),用于存放安装基目录与输出基目录,从而在多次构建之间共享缓存。
第三步:启动容器执行构建
docker run \ -e USER="$(id -u)" \ -u="$(id -u)" \ -v /src/workspace:/src/workspace \ -v /tmp/build_output:/tmp/build_output \ -w /src/workspace \ gcr.io/bazel-public/bazel:latest \ --output_user_root=/tmp/build_output \ build //absl/...逐项拆解这条命令的参数含义:
| 参数 | 作用 |
|---|---|
-e USER="$(id -u)" | 以宿主机当前用户 UID 设置容器内USER环境变量 |
-u="$(id -u)" | 以宿主机当前 UID 运行容器进程,避免生成 root 拥有的构建产物 |
-v /src/workspace:/src/workspace | 将宿主机源码目录挂载进容器,路径保持一致 |
-v /tmp/build_output:/tmp/build_output | 将宿主机缓存目录挂载进容器 |
-w /src/workspace | 设置容器工作目录为源码根目录 |
gcr.io/bazel-public/bazel:latest | 使用的官方镜像(latest标签) |
--output_user_root=/tmp/build_output | Bazel 启动选项,指定输出根目录 |
build //absl/... | 构建 absl 包下全部目标 |
为什么必须指定--output_user_root
容器文件系统是临时的,若不指定,Bazel 默认会把输出写到容器内的~/.cache/bazel,容器退出即丢失。通过--output_user_root将输出根目录指向宿主机挂载目录后,构建缓存得以持久化。
关于该启动选项的权威定义,可参考仓库 docs/docs/user-manual.mdx:
- 它指向安装基目录(install base)和输出基目录(output base)共同所在的根目录;安装基目录的位置由
--output_user_root加上 Bazel 内嵌二进制的 MD5 标识计算得出。 - 目录必须不存在或归属于当前调用用户——这正是命令中使用
-u="$(id -u)"以非 root 身份运行的原因;历史上允许多用户共享该目录,现已不允许(见 issue #11100 的讨论)。 - 若同时指定
--output_base,则后者优先生效。 - 官方建议不要在 NFS 等网络文件系统上使用该目录,访问延迟会明显拖慢构建。
在 Windows 上也有同样的实践:通过startup --output_user_root=C:/tmp规避长路径问题,参见 docs/configure/windows.mdx。
使用消毒器(Sanitizer)构建
通过追加--config=asan、--config=tsan或--config=msan构建标志,可分别启用 AddressSanitizer、ThreadSanitizer、MemorySanitizer:
docker run \ -e USER="$(id -u)" \ -u="$(id -u)" \ -v /src/workspace:/src/workspace \ -v /tmp/build_output:/tmp/build_output \ -w /src/workspace \ gcr.io/bazel-public/bazel:latest \ --output_user_root=/tmp/build_output \ build --config={asan | tsan | msan} -- //absl/... -//absl/types:variant_test注意此命令的两个细节:
--config={asan | tsan | msan}是花括号展开语法,实际执行时替换为三者之一(如--config=asan);-- //absl/... -//absl/types:variant_test中--之后是目标模式列表,-前缀表示排除//absl/types:variant_test目标——该测试在部分消毒器配置下已知存在问题,故从构建集合中剔除。
方式二:容器内部检出并构建(适合隔离式工作流)
如果你希望整个开发闭环都发生在容器内(检出源码、修改、构建),可以在开发会话开始时启动一个常驻容器,构建缓存保存在容器文件系统内持续复用。
启动容器内的交互式 shell:
docker run --interactive --entrypoint=/bin/bash gcr.io/bazel-public/bazel:latest每个容器都有唯一的容器 ID,下文示例以5a99103747c6为例。在容器内克隆项目并进入目录:
ubuntu@5a99103747c6:~$ git clone --depth 1 --branch 20220623.1 https://github.com/abseil/abseil-cpp.git && cd abseil-cpp/执行常规构建:
ubuntu@5a99103747c6:~/abseil-cpp$ bazel build //absl/...执行消毒器构建(排除已知有问题的测试目标):
ubuntu@5a99103747c6:~/abseil-cpp$ bazel build --config={asan | tsan | msan} -- //absl/... -//absl/types:variant_test对比两种方式:方式一每个构建命令都会拉起新容器,但缓存经由挂载目录保留在宿主机;方式二容器常驻,工作区与缓存都在容器内,适合不希望源码离开隔离环境的场景。两者在消毒器构建、目标选择语法上完全一致。
探索容器内部环境
若尚未启动,先进入容器交互式 shell:
docker run -it --entrypoint=/bin/bash gcr.io/bazel-public/bazel:latest ubuntu@5a99103747c6:~$依次验证各工具版本:
ubuntu@5a99103747c6:~$ gcc --version gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0 Copyright (C) 2019 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ubuntu@5a99103747c6:~$ java -version openjdk version "1.8.0_362" OpenJDK Runtime Environment (build 1.8.0_362-8u372-ga~us1-0ubuntu1~20.04-b09) OpenJDK 64-Bit Server VM (build 25.362-b09, mixed mode) ubuntu@5a99103747c6:~$ python -V Python 3.8.10 ubuntu@5a99103747c6:~$ bazel version WARNING: Invoking Bazel in batch mode since it is not invoked from within a workspace (below a directory having a WORKSPACE file). Extracting Bazel installation... Build label: 6.2.1 Build target: bazel-out/k8-opt/bin/src/main/java/com/google/devtools/build/lib/bazel/BazelServer_deploy.jar Build time: Fri Jun 2 16:59:58 2023 (1685725198) Build timestamp: 1685725198 Build timestamp as int: 1685725198几个值得注意的输出细节:
Extracting Bazel installation...:首次运行会在安装基目录解压 Bazel 内嵌二进制,这正是前文所述安装基目录由--output_user_root决定的体现;WARNING: Invoking Bazel in batch mode...:在WORKSPACE文件所在的目录之外运行 Bazel 时,它会以批处理模式执行(每次命令独立启动,不常驻 server),这里只是提示当前不在工作区内,无碍验证版本;Build target指向bazel-out/k8-opt/...,可以推断该镜像面向 x86_64 Linux 构建。
深入:镜像的 Dockerfile 与容器化构建的进阶场景
查看镜像构建方式
想知道 Bazel Docker 镜像本身是如何构建出来的,可以查看其 Dockerfile 定义(维护于 bazelbuild/continuous-integration 仓库的bazel/oci目录)。镜像内容(GCC 9.4、OpenJDK 8、Python 3.8 的组合)与文档示例记录完全一致,说明这是一个面向 C++/Java/Python 多语言构建的通用镜像。
容器化构建在 Bazel 内的另一种形态:Docker sandbox
除了"用容器跑整个 Bazel",Bazel 还支持让每个构建动作在独立的容器内执行的 docker sandbox 模式,二者的实践可以相互印证。在 docs/remote/sandbox.mdx 中可以看到,构建工具链容器同样强调"输出用户为 root 或与宿主机路径一致",以保证 Bazel 运行所在的主容器、spawn 出的动作容器、宿主机三方能够访问同一绝对路径:
bazel --output_user_root=/tmp/bazel_docker_root --bazelrc=.bazelrc \ build --config=docker-sandbox <var>target</var>这与本文方式一中"宿主机与容器保持相同挂载路径、以-u统一用户身份"的设计动机完全一致:容器化构建的核心约束是文件系统路径与属主权限的对齐。若本地二进制泄漏进工具链容器,会导致构建错误;磁盘空间不足时则可通过--memory=XX提升限制(实验性选项,可能产生不可预期行为)。
常见问题与最佳实践小结
- 产物属主混乱:务必同时使用
-u="$(id -u)"与-e USER="$(id -u)",让容器内进程以宿主机 UID 运行,否则挂载目录中会出现 root 属主的产物文件; - 缓存丢失:容器默认文件系统是临时的,必须将
--output_user_root指向挂载目录(方式一),或在常驻容器内持续构建(方式二); - 消毒器构建失败:个别测试目标与特定消毒器不兼容,使用
-- //absl/... -//absl/types:variant_test的排除语法处理; - 网络文件系统:
--output_user_root指向 NFS 等网络存储会显著降低构建速度,应使用本地磁盘; - 多用户共享输出目录:现代 Bazel 已禁止将输出根目录共享给多个用户,请为每个用户/每次任务使用独立目录。
结合 docs/install/index.mdx 可知,Docker 容器是 Bazel 官方安装途径之外的便捷分发形态:它不需要在宿主机安装任何 Bazel 二进制,与 Bazelisk、系统包管理器等方式互为补充,尤其适合需要可复现构建环境与临时环境的场景。
【免费下载链接】bazela fast, scalable, multi-language and extensible build system项目地址: https://gitcode.com/GitHub_Trending/ba/bazel
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考