沙盒

报告问题 查看来源 每晚 · 7.2。 · 7.1敬上 · 7.0。 · 6.5 · 6.4

本文介绍了在 Bazel 中沙盒化、安装 sandboxfs 和进行调试 测试环境

沙盒是一种权限限制策略,用于将进程与 或来自系统中的资源。对于 Bazel,这意味着 系统访问权限。

Bazel 的文件系统沙盒在工作目录中运行 包含已知输入,因此编译器和其他工具不会看到源代码 文件,除非知道这些文件的绝对路径。

沙盒不会以任何方式隐藏托管环境。进程可以自由地 访问文件系统上的所有文件。然而,在支持用户的平台上 命名空间,因此进程无法修改其工作目录之外的任何文件。 这可以确保构建图没有 会影响构建的可再现性。

更具体地说,Bazel 会为每个操作构建一个 execroot/ 目录, 在执行时充当相应操作的工作目录。execroot/ 包含操作的所有输入文件,并充当 生成输出。然后,Bazel 会使用操作系统提供的方法, 容器在 Linux 上以及 macOS 上为 sandbox-exec,用于将操作限制在 execroot/

采用沙盒的原因

  • 如果没有操作沙盒,Bazel 就不知道工具是否使用了未声明的 输入文件(未明确列在 操作)。当其中一个未声明的输入文件发生更改时,Bazel 仍然 认为构建是最新的,且不会重新构建操作。这可以 会导致错误的增量构建。

  • 错误重用缓存条目会导致远程缓存期间出现问题。答 共享缓存中有错误的缓存条目会影响项目的每位开发者, 并擦除整个远程缓存并不是可行的解决方案。

  • 沙盒可以模拟远程执行的行为(如果构建运行良好的话) 以及远程执行。通过使 远程执行上传所有必要的文件(包括本地工具)后, 与 每次在集群内的每台机器上都安装这些工具 想要试用新编译器或更改现有工具。

应使用哪种沙盒策略

您可以选择要使用的沙盒(如果有的话),通过 策略标志。使用 sandboxed 策略会让 Bazel 选择下面列出的沙盒实现之一 首选操作系统专用的沙盒,而不是不那么封闭的通用沙盒。 永久性工作器在通用沙盒中运行,如果您通过 --worker_sandboxing 标志。

local(也称为 standalone)策略不会执行任何类型的沙盒化。 它只是执行操作的命令行,并将工作目录设置为 工作区的执行根。

processwrapper-sandbox 是一种沙盒策略,不需要任何 “高级”它应该可以在任何开箱即用的 POSIX 系统上运行。它 用于构建沙盒目录,该目录由指向原始版本的符号链接组成 源文件,使用工作目录集执行操作的命令行 移到此目录(而非 execroot),然后移动已知的输出制品 到 execroot 并删除沙盒。这样可以防止 操作意外使用任何未声明的输入文件, 丢弃包含未知输出文件的 execroot。

更进一步,linux-sandbox 基于 processwrapper-sandbox。与 Docker 在后台执行的操作类似,它使用 Linux 命名空间(用户、装载、PID、网络和 IPC 命名空间),用于隔离 来自主机的操作。也就是说,它会将整个文件系统设为只读, 因此该操作不会意外修改 主机文件系统。这样可以防止错误测试意外发生故障 -rf 您的 $HOME 目录。您也可以选择 访问网络的情况linux-sandbox 会使用 PID 命名空间来阻止该操作 查看任何其他进程并可靠地终止所有进程(甚至守护程序) 由操作衍生)。

darwin-sandbox 与之类似,但适用于 macOS。它使用 Apple 的 sandbox-exec 工具 其实现方式与 Linux 沙盒大致相同

linux-sandboxdarwin-sandbox 在“嵌套”中均不起作用 (由运营服务提供商提供的机制限制) 系统。Docker 还使用 Linux 命名空间实现其容器魔法, 无法在 Docker 容器内轻松运行 linux-sandbox,除非您使用 docker run --privileged。在 macOS 上,您无法在 sandbox-exec 已经被沙盒化的进程因此,在这些情况下,Bazel 自动回退到使用 processwrapper-sandbox

如果您想遇到构建错误 - 例如不想意外使用 不太严格的执行策略 - 明确修改执行列表 Bazel 尝试使用的策略(例如 bazel build --spawn_strategy=worker,linux-sandbox)。

动态执行通常需要采用沙盒机制才能在本地执行。如需停用,请按以下步骤操作: 传递 --experimental_local_lockfree_output 标志。以静默方式动态执行 沙盒永久性工作器

沙盒的缺点

  • 沙盒会产生额外的设置和拆解费用。这笔费用有多大 取决于很多因素,包括作品的形状和 主机操作系统的性能。对于 Linux 而言,沙盒化 build 很少会超过 慢了百分之几。设置 --reuse_sandbox_directories 可以 降低设置和拆解费用。

  • 沙盒可有效地停用该工具可能具有的任何缓存。您可以 为缓解此类问题,您可以使用永久性工作器,位于 降低较弱沙盒保证的代价

  • 多路复用工作器需要显式 worker 支持 进行沙盒化处理不支持多路复用沙盒的 worker 会以如下身份运行: 动态执行下的单工工作器,这可能会产生额外的内存。

sandboxfs

sandboxfs 是一个 FUSE 文件系统,公开任意视图来公开 底层文件系统(没有时间损失)。Bazel 使用 sandboxfs 来 针对每项操作即时生成 execroot/,从而避免 发出数千个系统调用。请注意,execroot/ 中的进一步 I/O 操作 可能会因为 FUSE 开销而变慢。

安装 sandboxfs

请按照以下步骤安装 sandboxfs 并使用以下命令执行 Bazel 构建: :

下载

下载并安装 sandboxfs,以使 sandboxfs 二进制文件最终在您的 PATH 中。

运行 sandboxfs

  1. (仅限 macOS)安装 OSXFUSE
  2. (仅限 macOS)运行以下命令:

    sudo sysctl -w vfs.generic.osxfuse.tunables.allow_other=1
    

    您需要在安装后和每次重新启动后执行此操作,以确保 核心 macOS 系统服务通过 sandboxfs 运行。

  3. 使用 --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 停用沙盒 genrule,--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 然后再次运行该命令,看看其行为方式。

请注意,在使用时,Bazel 不会删除沙盒目录, --sandbox_debug。除非您正在主动调试,否则应停用 --sandbox_debug,因为它会随着时间的推移占用您的磁盘空间。