远程缓存

报告问题 查看源代码

本页面介绍如何远程缓存、设置服务器以托管缓存以及如何使用远程缓存运行构建。

远程缓存供开发团队和/或持续集成 (CI) 系统共享构建输出。如果您的 build 可重现,可以在一台机器上安全地重复使用另一台机器的输出,从而加快构建速度。

概览

Bazel 将构建拆分为独立的步骤,这些步骤称为操作。每项操作都有输入、输出名称、命令行和环境变量。系统会为每个操作明确声明所需的输入和预期输出。

您可以将服务器设置为构建输出(即这些操作输出)的远程缓存。这些输出由输出文件名列表及其内容的哈希值组成。借助远程缓存,您可以重复使用其他用户的 build 中的构建输出,而不是在本地构建每个新输出。

如需使用远程缓存,请执行以下操作:

  • 将服务器设置为缓存后端
  • 将 Bazel 构建配置为使用远程缓存
  • 使用 Bazel 0.10.0 版或更高版本

远程缓存存储两种类型的数据:

  • 操作缓存,即操作哈希与操作结果元数据的映射。
  • 输出文件的内容可寻址存储区 (CAS)。

请注意,远程缓存还会为每个操作存储 stdout 和 stderr。因此,检查 Bazel 的 stdout/stderr 并不能很好地估算缓存命中

构建如何使用远程缓存

将服务器设置为远程缓存后,您可以通过多种方式使用该缓存:

  • 读取和写入远程缓存
  • 读取和/或写入远程缓存(特定目标除外)
  • 仅从远程缓存中读取
  • 完全不使用远程缓存

当您运行可以对远程缓存执行读写操作的 Bazel 构建时,该构建会执行以下步骤:

  1. Bazel 会创建需要构建的目标图,然后创建所需操作的列表。其中每项操作都声明了输入和输出文件名。
  2. Bazel 会检查本地机器上的现有构建输出并重复使用其找到的任何代码。
  3. Bazel 会检查缓存中是否有现有的构建输出。如果找到输出,Bazel 会检索输出。这是一个缓存命中。
  4. 对于找不到输出的必需操作,Bazel 会在本地执行这些操作,并创建所需的构建输出。
  5. 新的构建输出将上传到远程缓存。

将服务器设置为缓存后端

您需要设置一台服务器作为缓存后端。HTTP/1.1 服务器可以将 Bazel 的数据视为不透明字节,因此许多现有服务器可以用作远程缓存后端。Bazel 的 HTTP 缓存协议支持远程缓存。

您负责选择、设置和维护用于存储缓存输出的后端服务器。选择服务器时,请考虑以下因素:

  • 网络速度。例如,如果您的团队在同一办公室,则可能需要运行自己的本地服务器。
  • 安全性。远程缓存将包含您的二进制文件,因此需要保证安全。
  • 便于管理。例如,Google Cloud Storage 是一项全代管式服务。

有许多后端可用于远程缓存。选项包括:

nginx

nginx 是一种开源网络服务器。在其 [WebDAV 模块] 中,可将其用作 Bazel 的远程缓存。在 Debian 和 Ubuntu 上,您可以安装 nginx-extras 软件包。在 macOS 上,您可以通过 Homerew 使用 nginx:

brew tap denji/nginx
brew install nginx-full --with-webdav

以下是 nginx 的配置示例。请注意,您需要将 /path/to/cache/dir 更改为 nginx 有权写入和读取的有效目录。如果输出文件较大,您可能需要将 client_max_body_size 选项更改为更大的值。服务器将需要进行其他配置,例如身份验证。

nginx.confserver 区段的配置示例:

location /cache/ {
  # The path to the directory where nginx should store the cache contents.
  root /path/to/cache/dir;
  # Allow PUT
  dav_methods PUT;
  # Allow nginx to create the /ac and /cas subdirectories.
  create_full_put_path on;
  # The maximum size of a single file.
  client_max_body_size 1G;
  allow all;
}

bazel-remote

bazel-remote 是一个开源远程构建缓存,您可以在基础架构上使用。自 2018 年初以来,它已被多家公司成功采用。请注意,Bazel 项目不为 bazel-remote 提供技术支持。

此缓存会将内容存储在磁盘上,并且会强制执行垃圾回收以强制执行存储空间上限并清理未使用的工件。缓存以 [docker 映像] 的形式提供,其代码在 GitHub 上提供。 支持 REST 和 gRPC 远程缓存 API。

如需查看使用说明,请参阅 GitHub 页面。

Google Cloud Storage

[Google Cloud Storage] 是一种全代管式对象存储,它提供与 Bazel 的远程缓存协议兼容的 HTTP API。您需要拥有一个启用了结算功能的 Google Cloud 帐号。

如需使用 Cloud Storage 作为缓存,请执行以下操作:

  1. 创建存储分区。由于网络带宽对远程缓存而言非常重要,因此请确保选择离您最近的存储分区位置。

  2. 为 Bazel 创建一个服务帐号,用于向 Cloud Storage 进行身份验证。请参阅创建服务帐号

  3. 生成一个 Secret JSON 密钥,然后将其传递给 Bazel 以进行身份验证。您可以安全地存储密钥,因为任何拥有该密钥的人都可以在 GCS 存储分区中读写任意数据。

  4. 将以下标志添加到 Bazel 命令,以连接到 Cloud Storage:

    • 使用此标志将以下网址传递给 Bazel:--remote_cache=https://storage.googleapis.com/bucket-name,其中 bucket-name 是存储分区的名称。
    • 使用以下标志传递身份验证密钥:--google_credentials=/path/to/your/secret-key.json--google_default_credentials,以使用应用身份验证
  5. 您可以将 Cloud Storage 配置为自动删除旧文件。为此,请参阅管理对象生命周期

其他服务器

您可以将任何支持 PUT 和 GET 的 HTTP/1.1 服务器设置为缓存后端。用户已经报告已成功缓存后端,例如 HazelcastApache httpdAWS S3

身份验证

从 0.11.0 版开始,Bazel 开始支持 HTTP 基本身份验证。您可以通过远程缓存网址将用户名和密码传递给 Bazel。语法为 https://username:password@hostname.com:port/path。请注意,HTTP 基本身份验证通过网络以明文形式传输用户名和密码,因此务必要始终通过 HTTPS 使用用户名和密码。

HTTP 缓存协议

Bazel 支持通过 HTTP/1.1 进行远程缓存。该协议的概念很简单:二进制数据 (BLOB) 通过 PUT 请求上传,并通过 GET 请求下载。 操作结果元数据存储在路径 /ac/ 下,输出文件存储在路径 /cas/ 下。

例如,假设某个远程缓存在 http://localhost:8080/cache 下运行。使用 SHA256 哈希 01ba4719... 的操作下载操作结果元数据的 Bazel 请求将如下所示:

GET /cache/ac/01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b HTTP/1.1
Host: localhost:8080
Accept: */*
Connection: Keep-Alive

使用 SHA256 哈希 15e2b0d3... 将输出文件上传到 CAS 的 Bazel 请求如下所示:

PUT /cache/cas/15e2b0d3c33891ebb0f1ef609ec419420c20e320ce94c65fbc8c3312448eb225 HTTP/1.1
Host: localhost:8080
Accept: */*
Content-Length: 9
Connection: Keep-Alive

0x310x320x330x340x350x360x370x380x39

使用远程缓存运行 Bazel

将服务器设置为远程缓存后,要使用远程缓存,您需要在 Bazel 命令中添加标志。请参阅下面的配置及其标志列表。

您可能还需要选择所选服务器的身份验证。

您可能需要在 .bazelrc 文件中添加这些标志,这样就无需在每次运行 Bazel 时指定这些标志。您可以向项目和 .bazelrc 文件添加标志,具体取决于项目和团队动态,即:

  • 在本地计算机上
  • 在项目的工作区中,与团队共享
  • 在 CI 系统上

对远程缓存执行读写操作

请留意谁能写入远程缓存。您可能只需要 CI 系统能够写入远程缓存。

使用以下标志可对远程缓存执行读写操作:

build --remote_cache=http://your.host:port

除了 HTTP 之外,还支持以下协议:HTTPSgrpcgrpcs

除上述标记外,请使用以下标记,以便仅从远程缓存中读取:

build --remote_upload_local_results=false

从远程缓存中排除特定目标

如需排除特定目标,使其无法使用远程缓存,请使用 no-remote-cache 标记目标。例如:

java_library(
    name = "target",
    tags = ["no-remote-cache"],
)

删除远程缓存中的内容

从远程缓存中删除内容是管理服务器的步骤之一。如何删除远程缓存中的内容取决于您已设置为缓存的服务器。删除输出时,请删除整个缓存或删除旧输出。

缓存的输出会存储为一组名称和哈希。删除内容时,无法区分哪个输出属于特定 build。

您可能需要从缓存中删除内容,以便:

  • 在缓存投毒后创建干净缓存
  • 通过删除旧输出来减少存储空间用量

Unix 套接字

远程 HTTP 缓存支持通过 Unix 网域套接字进行连接。其行为类似于 curl 的 --unix-socket 标志。使用以下命令配置 Unix 网域套接字:

   build --remote_cache=http://your.host:port
   build --remote_cache_proxy=unix:/path/to/socket

Windows 不支持此功能。

磁盘缓存

Bazel 可以将文件系统上的目录用作远程缓存。这在切换分支和/或处理同一项目的多个工作区(例如多个检出)时有助于共享 build 工件。由于 Bazel 不会对目录进行垃圾回收,因此您可能需要自动清理此目录。按如下方式启用磁盘缓存:

build --disk_cache=path/to/build/cache

您可以使用 ~ 别名将特定于用户的路径传递给 --disk_cache 标志(Bazel 会替换当前用户的主目录)。通过在 .bazelrc 文件中签入项目的所有开发者为其启用磁盘缓存时,这非常方便。

已知问题

构建期间的输入文件修改

在构建期间修改输入文件时,Bazel 可能会将无效结果上传到远程缓存。您可以使用 --experimental_guard_against_concurrent_changes 标志启用更改检测。没有已知问题,在未来版本中,该功能将默认处于启用状态。请参阅 [问题 3360],了解最新动态。通常,避免在构建期间修改源文件。

环境变量泄露给操作

操作定义包含环境变量。在机器之间共享远程缓存命中时,这可能是一个问题。例如,具有不同 $PATH 变量的环境不会共享缓存命中。只有通过 --action_env 明确列入白名单的环境变量才会包含在操作定义中。Bazel 的 Debian/Ubuntu 软件包,用于通过环境变量(包括 $PATH)安装 /etc/bazel.bazelrc。如果您的缓存命中数量低于预期,请检查您的环境没有旧的 /etc/bazel.bazelrc 文件。

Bazel 不会跟踪工作区之外的工具

Bazel 目前不跟踪工作区之外的工具。例如,如果操作使用 /usr/bin/ 中的编译器,这可能会造成问题。然后,安装了不同编译器的两个用户将错误地共享缓存命中,因为输出不同,但操作哈希值相同。请参阅问题 4558,了解最新动态。

在 Docker 容器内运行构建时,增量内存状态会丢失 即使运行在单个 Docker 容器中,Bazel 也会使用服务器/客户端架构。在服务器端,Bazel 会维护内存状态,从而加快构建速度。在 Docker 容器(例如 CI)中运行构建时,内存状态会丢失,Bazel 必须重新构建,然后才能使用远程缓存。