本页面介绍了远程缓存、如何设置服务器来托管缓存,以及 如何使用远程缓存运行构建。
远程缓存由开发者团队和/或持续集成 (CI) 系统用于共享构建输出。如果您的构建是可重现的,则 一台机器的输出可以安全地在另一台机器上重复使用,这可以 显著加快构建速度。
概览
Bazel 将构建分解为离散的步骤,这些步骤称为操作。每个操作 都有输入、输出名称、命令行和环境变量。每个操作都需要显式声明 输入和预期输出。
您可以设置一个服务器作为构建输出(即这些 操作输出)的远程缓存。这些输出包含输出文件名列表及其内容的 哈希。借助远程缓存,您可以重复使用其他用户的构建输出 ,而不是在本地构建每个新输出。
如需使用远程缓存,请执行以下操作:
- 将服务器设置为缓存的后端
- 将 Bazel 构建配置为使用远程缓存
- 使用 Bazel 0.10.0 或更高版本
远程缓存存储两种类型的数据:
- 操作缓存,它是操作哈希到操作结果元数据的映射。
- 输出文件的内容可寻址存储 (CAS)。
请注意,远程缓存还会存储每个 操作的 stdout 和 stderr。因此,检查 Bazel 的 stdout/stderr 并不是估计缓存命中的好方法 。
构建如何使用远程缓存
将服务器设置为远程缓存后,您可以通过多种方式使用缓存:
- 读取和写入远程缓存
- 读取和/或写入远程缓存,但特定目标除外
- 仅从远程缓存读取
- 完全不使用远程缓存
当您运行可以读取和写入远程缓存的 Bazel 构建时, 该构建会执行以下步骤:
- Bazel 会创建需要构建的目标图,然后创建 所需操作的列表。每个操作都有声明的输入 和输出文件名。
- Bazel 会检查本地机器中是否存在构建输出,并重复使用找到的任何 构建输出。
- Bazel 会检查缓存中是否存在构建输出。如果找到输出, Bazel 会检索该输出。这就是缓存命中。
- 对于未找到输出的所需操作,Bazel 会在本地执行这些 操作并创建所需的构建输出。
- 新的构建输出会上传到远程缓存。
将服务器设置为缓存的后端
您需要设置一个服务器作为缓存的后端。HTTP/1.1 服务器可以将 Bazel 的数据视为不透明字节,因此许多现有服务器 都可以用作远程缓存后端。Bazel 的 HTTP 缓存协议支持远程 缓存。
您负责选择、设置和维护将存储缓存输出的后端 服务器。选择服务器时,请考虑以下因素:
- 网络速度。例如,如果您的团队在同一办公室,您可能 需要运行自己的本地服务器。
- 安全性。远程缓存将包含您的二进制文件,因此需要确保其安全。
- 管理便捷度。例如,Google Cloud Storage 是一项全代管式服务。
有许多后端可用于远程缓存。选项 包括:
nginx
nginx 是一款开源 Web 服务器。借助其 [WebDAV 模块],它可以
作为 Bazel 的远程缓存。在 Debian 和 Ubuntu 上,您可以安装
nginx-extras 软件包。在 macOS 上,nginx 可通过 Homebrew 获取:
brew tap denji/nginxbrew install nginx-full --with-webdav
以下是 nginx 的示例配置。请注意,您需要将
更改/path/to/cache/dir为 nginx 有权
写入和读取的有效目录。如果您有较大的输出文件,可能需要将 client_max_body_size 选项更改为
更大的值。服务器将需要其他
配置,例如身份验证。
nginx.conf 中 server 部分的示例配置:
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 用作缓存,请执行以下操作:
创建存储分区。 确保选择离您最近的存储分区位置,因为网络带宽 对于远程缓存非常重要。
为 Bazel 创建一个服务账号,以便向 Cloud Storage 进行身份验证。请参阅 创建服务账号。
生成 Secret JSON 密钥,然后将其传递给 Bazel 以进行身份验证。请安全地存储 该密钥,因为拥有该密钥的任何人都可以从您的 GCS 存储分区读取和写入任意数据 。
通过将以下标志添加到 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来使用应用身份验证。
- 使用以下标志将以下网址传递给 Bazel:
您可以将 Cloud Storage 配置为自动删除旧文件。如需执行此操作,请参阅 管理对象生命周期。
其他服务器
您可以将任何支持 PUT 和 GET 的 HTTP/1.1 服务器设置为缓存的 后端。用户报告称,Hazelcast、 Apache httpd 和 AWS 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 下运行。
Bazel 请求下载 SHA256
哈希 01ba4719... 的操作的操作结果元数据,如下所示:
GET /cache/ac/01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b HTTP/1.1
Host: localhost:8080
Accept: */*
Connection: Keep-Alive
Bazel 请求将 SHA256 哈希为 15e2b0d3... 的输出文件上传到
CAS,如下所示:
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 之外,还支持以下协议:HTTPS、grpc、grpcs。
除了上述标志之外,还使用以下标志仅从 远程缓存读取:
build --remote_upload_local_results=false从使用远程缓存中排除特定目标
如需从使用远程缓存中排除特定目标,请使用
no-remote-cache标记目标。例如:
java_library(
name = "target",
tags = ["no-remote-cache"],
)
从远程缓存中删除内容
从远程缓存中删除内容是管理服务器的一部分。 从远程缓存中删除内容的方式取决于您设置为缓存的服务器。删除输出时,请删除整个缓存, 或删除旧输出。
缓存的输出存储为一组名称和哈希。删除 内容时,无法区分哪个输出属于特定 构建。
您可能需要从缓存中删除内容,以便:
- 在缓存中毒后创建干净的缓存
- 通过删除旧输出减少使用的存储空间量
Unix 套接字
远程 HTTP 缓存支持通过 Unix 域套接字进行连接。其行为
类似于 curl 的 --unix-socket 标志。使用以下命令配置 Unix
域套接字:
build --remote_cache=http://your.host:port
build --remote_proxy=unix:/path/to/socketWindows 不支持此功能。
磁盘缓存
Bazel 可以使用文件系统上的目录作为远程缓存。当切换分支和/或处理同一项目的多个工作区(例如多个检出)时,这对于共享构建工件非常有用。按如下方式启用磁盘缓存:
build --disk_cache=path/to/build/cache您可以使用 ~ 别名将用户专用路径传递给 --disk_cache 标志
(Bazel 将替换为当前用户的主目录)。当通过项目的已签入 .bazelrc 文件为项目的所有开发者启用磁盘缓存时,这非常方便。
垃圾回收
从 Bazel 7.4 开始,您可以使用 --experimental_disk_cache_gc_max_size 和
--experimental_disk_cache_gc_max_age 为磁盘缓存
或单个缓存条目的使用期限设置上限。Bazel 会在构建之间空闲时自动对磁盘缓存进行垃圾回收;空闲计时器可以使用 --experimental_disk_cache_gc_idle_delay 设置(默认为 5 分钟)。
除了自动垃圾回收之外,我们还提供了一个工具,用于按需运行 垃圾回收。
已知问题
构建期间修改输入文件
如果在构建期间修改了输入文件,Bazel 可能会将无效
结果上传到远程缓存。您可以使用
--experimental_guard_against_concurrent_changes 标志启用更改检测。目前没有已知问题,并且在未来的版本中将默认启用此标志。如需了解更新,请参阅 [问题 #3360]。一般来说,请避免在
构建期间修改源文件。
环境变量泄露到操作中
操作定义包含环境变量。这可能会导致
跨机器共享远程缓存命中出现问题。例如,具有
不同 $PATH 变量的环境将不会共享缓存命中。只有通过 --action_env 显式列入许可名单的环境变量才会包含在操作
定义中。Bazel 的 Debian/Ubuntu 软件包过去会安装 /etc/bazel.bazelrc
,其中包含环境变量(包括 $PATH)的许可名单。如果您获得的缓存命中数少于预期,请检查您的环境是否包含旧的
/etc/bazel.bazelrc 文件。
Bazel 不跟踪工作区外部的工具
Bazel 目前不跟踪工作区外部的工具。例如,如果操作使用 /usr/bin/ 中的编译器,则可能会出现
问题。然后,
安装了不同编译器的两个用户将错误地共享缓存命中
,因为输出不同,但它们具有相同的操作哈希。如需了解更新,请参阅
问题 #4558。
在 Docker 容器内运行构建时,增量内存状态会丢失 Bazel 即使在单个 Docker 容器中运行时也使用服务器/客户端架构。 在服务器端,Bazel 会维护内存状态,从而加快构建速度。 在 Docker 容器内(例如在 CI 中)运行构建时,内存状态会丢失 Bazel 必须先重建该状态,然后才能使用远程缓存。