本页介绍了远程缓存、设置用于托管缓存的服务器,以及使用远程缓存运行 build。
开发者团队和/或持续集成 (CI) 系统使用远程缓存来共享构建输出。如果您的 build 可重现,则一台机器的输出可安全地在另一台机器上重复使用,这可以显著加快构建速度。
概览
Bazel 会将构建拆分为多个独立的步骤,称为操作。每个操作都有输入、输出名称、命令行和环境变量。系统会为每个操作明确声明必需的输入和预期输出。
您可以将服务器设置为构建输出(即这些操作输出)的远程缓存。这些输出包括输出文件名称列表及其内容的哈希值。借助远程缓存,您可以重复使用由另一位用户的构建生成的构建输出,而不是每次都在本地构建新输出。
如需使用远程缓存,请执行以下操作:
- 将服务器设置为缓存的后端
- 配置 Bazel build 以使用远程缓存
- 使用 Bazel 0.10.0 或更高版本
远程缓存存储两种类型的数据:
- 操作缓存,即操作哈希与操作结果元数据的映射。
- 输出文件的内容可寻址存储 (CAS)。
请注意,远程缓存还会存储每个操作的标准输出和标准错误输出。因此,检查 Bazel 的标准输出/标准错误输出并不能很好地估算缓存命中率。
build 如何使用远程缓存
将服务器设置为远程缓存后,您可以通过多种方式使用该缓存:
- 读取和写入远程缓存
- 对远程缓存执行读取和/或写入操作(特定目标除外)
- 仅从远程缓存读取
- 完全不使用远程缓存
当您运行可读取和写入远程缓存的 Bazel build 时,该 build 会按照以下步骤操作:
- Bazel 会创建需要构建的目标的图表,然后创建所需操作的列表。其中每项操作都声明了输入和输出文件名。
- Bazel 会检查本地机器上的现有构建输出,并重复使用找到的任何构建输出。
- Bazel 会检查缓存中是否有现有构建输出。如果找到输出,Bazel 会检索输出。这属于缓存命中。
- 对于未找到输出的必要操作,Bazel 会在本地执行操作并创建所需的构建输出。
- 新的构建输出会上传到远程缓存。
将服务器设置为缓存的后端
您需要设置一台服务器来充当缓存的后端。HTTP/1.1 服务器可以将 Bazel 的数据视为不透明字节,因此许多现有服务器可以用作远程缓存后端。Bazel 的 HTTP 缓存协议支持远程缓存。
您负责选择、设置和维护用于存储缓存输出的后端服务器。选择服务器时,请考虑以下因素:
- 网速。例如,如果您的团队位于同一办公室,您可能希望运行自己的本地服务器。
- 安全性。远程缓存将包含您的二进制文件,因此需要是安全的。
- 易于管理。例如,Google Cloud Storage 是一项全代管式服务。
可用于远程缓存的后端有很多。选项包括:
nginx
nginx 是一种开源 Web 服务器,凭借其 [WebDAV 模块],它可用作 Bazel 的远程缓存。在 Debian 和 Ubuntu 上,您可以安装 nginx-extras
软件包。在 macOS 上,您可以通过 Homebrew 安装 nginx:
brew tap denji/nginx
brew 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
下运行的远程缓存。用于下载 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 命令添加标志。请参阅下文,查看配置及其 flag 的列表。
您可能还需要配置身份验证(特定于您选择的服务器)。
您可能需要在 .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"],
)
从远程缓存中删除内容
从远程缓存中删除内容是管理服务器的一部分。从远程缓存中删除内容的方式取决于您设置为缓存的服务器。删除输出时,请删除整个缓存或删除旧输出。
缓存的输出以一组名称和哈希的形式存储。删除内容时,无法区分哪些输出属于特定 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 工件非常有用。按如下方式启用磁盘缓存:
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 容器内运行构建时会丢失内存中增量状态。即使在单个 Docker 容器中运行,Bazel 会使用服务器/客户端架构。在服务器端,Bazel 会维护一个内存中状态,以加快构建速度。在 Docker 容器内(例如在 CI 中)运行构建时,内存中的状态将丢失,Bazel 必须在使用远程缓存之前重新构建。