本文翻译自:https://docs.bazel.build/versions/master/remote-caching.html
远程缓存
远程缓存,用于开发组内部或持续集成(CI )系统内,进行构建输出的共享。如果你的构建是需要反复执行的,一台机器的输出能够被另外一台机器安全的重用,那么这将会使构建速度显著的提升。
远程缓存概述
Bazel将整个构建拆分成独立的步骤,称之为构建动作。每个构建动作都有输入,输出名字,命令行和环境变量。必要的输入和期望的输出,在每个构建动作中都被显示的声明出来。
你可以为这些构建步骤的输出结果,搭建一套远程缓存服务器。这些输出由一系列的输出文件名和这些文件内容的hash组成。你可以通过远程缓存,重用其他用户的构建输出,而不是每次都为新的改动,在本地构建出新的输出。
怎样使用远程缓存:
- 搭建一套服务器作为后端缓存
- 配置Bazel构建,以使用远程缓存
- 使用Bazel版本0.10.0或更高版本
远程缓存服务器存储两种类型的数据:
- 构建动作缓存,一个map表,由构建动作的哈希和构建动作的结果元数据组成。
- 输出文件内容的可查找存储content-addressable store (CAS)。
构建如何使用远程缓存
一旦缓存服务器建立,可以通过如下多种方式来使用:
- 从远程缓存进行读、写操作
- 从远程缓存进行读、写操作,某些特定的目标除外
- 从远程缓存只读
- 不使用远程缓存
当你使用远程读、写缓存,进行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上可以通过Homebrew来完成:
$ brew tap denji
/nginx
$ brew
install
nginx-full --with-webdav
|
下面是一个nginx配置的例子。注意,你需要将/path/to/cache/dir替换为一个合法的目录,且nginx对该目录有读写权限。你可能还需要将选项client_max_body_size,修改为较大的值,如果你有很大的输出文件。同时,服务器也需要其他配置,如鉴权。
nginx.conf中,server section的配置示例:
location ~ /(
ac
|cas) {
# The path to the directory where nginx should store the cache contents.
root /www/cache;
# 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 Cache
Bazel远程缓存是一个开源的远程构建缓存服务,你可以在框架中使用。不过,它是实验性的,也不提供支持服务。
该缓存服务,将内容存储在磁盘上,还提供了垃圾收集功能,用于强制设置存储上限和清理无用产出。该缓存服务已被制作为一个docker镜像,它的代码也可以在GitHub上找到。
请参考GitHub上的使用说明,进行使用。
Google Cloud Storage
Google Cloud Storage是一个完全管理的对象存储,它提供了符合Bazel远程缓存的HTTP API接口。它需要你已充值的Google云账号。
使用Google Cloud Storage作为缓存:
- 创建一个存储箱。确保你选择的箱子位置是离你最近的,因为网络带宽对远程缓存很重要。
- 创建一个Bazel到Cloud Storage鉴权用的服务账号。参考创建服务账号。
- 产生一个JSON格式的密钥,给Bazel做鉴权使用。安全的保存该密钥,因为任何拿到这个密钥的人,都可以从GCS存储箱中读写数据。
- 通过如下Bazel命令行,连接上Cloud Storage:
通过参数:--remote_http_cache=https://storage.googleapis.com/bucket-name 将URL传递给Bazel。这里的bucket-name是你自己的存储箱。
通过参数:--google_credentials=/path/to/your/secret-key.json,将鉴权密钥,传递给Bazel。 - 你可以配置Cloud Storage自动删除老文件。参考:管理对象生命周期。
Other Servers
你可以搭建任意支持PUT和GET的HTTP/1.1服务器,当做缓存后端。有用户反馈他们已经使用Hazelcast, Apache httpd, and AWS S3,成功的搭建了缓存后端服务。
鉴权
自0.11.0版本开始,Bazel添加了HTTP基础鉴权功能。你可以将用户名和密码,通过远程缓存URL传递给Bazel。具体的语法是这样的,https://username:password@hostname.com:port/path。请注意,HTTP基础鉴权,会将用户名和密码,以明文的方式在网络上传输,所以务必一直使用HTTPS协议。
HTTP远程缓存协议
Bazel通过HTTP/1.1支持远程缓存。该协议是个比较简单的概念:二进制数据Binary data (BLOB)是通过PUT请求上传数据,通过GET请求下载数据。构建动作的结果元数据,存储在/ac/路径下,输出文件存储在/cas/路径下。
例如:某个远程缓存服务运行在http://localhost:8080/cache下。一个下载构建动作(SHA256哈希01ba4719...)结果元数据的Bazel请求,看起来是下面这样的:
GET /ac/01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b HTTP/1.1
Host: localhost:8080
Accept: */*
Connection: Keep-Alive
一个上传构建动作(SHA256哈希15e2b0d3...)结果元数据到CAS的Bazel请求,看起来是下面这样的:
PUT /cas/15e2b0d3c33891ebb0f1ef609ec419420c20e320ce94c65fbc8c3312448eb225 HTTP/1.1
Host: localhost:8080
Accept: */*
Content-Length: 9
Connection: Keep-Alive
0x310x320x330x340x350x360x370x380x39
使用远程缓存方式运行Bazel
远程缓存服务搭建完成后,你需要通过给Bazel命令添加参数,来使用远程缓存。参考配置清单和如下参数。
你可能需要配置鉴权,这取决于你选择的服务器。
你可以通过将这些参数,添加到.bazelrc中,这样,你就不需要每次运行Bazel的时候,都指定这些参数。根据你的工程和团队灵活性,你可以将这些参数放置在下面这些位置的.bazelrc文件里。
- 本地机器
- 工程目录下,团队内共享
- CI系统内
在远程缓存上读和写
注意控制好往远程缓存上的写权力。可能只有你的CI系统,有能力去写远程缓存。
用下面的参数进行配置:
- 从远程缓存上读数据,写数据到远程缓存
- 禁用沙箱功能
build --spawn_strategy=remote --genrule_strategy=remote
build --strategy=Javac=remote --strategy=Closure=remote
build --remote_http_cache=http:
//replace-with-your
.host:port
|
通过沙箱功能使用远程缓存是试验性的。通过如下参数,开启远程缓存的读、写沙箱功能。
build --experimental_remote_spawn_cache
build --remote_http_cache=http:
//replace-with-your
.host:port
|
只读远程缓存
通过下面的参数,设置只读远程缓存,并关闭沙箱功能。
build --spawn_strategy=remote --genrule_strategy=remote
build --strategy=Javac=remote --strategy=Closure=remote
build --remote_http_cache=http:
//replace-with-your
.host:port
build --remote_upload_local_results=
false
|
沙箱使用远程缓存是实验性的。通过下面的参数,开启沙箱远程缓存只读功能。
build --experimental_remote_spawn_cache
build --remote_http_cache=http:
//replace-with-your
.host:port
build --remote_upload_local_results=
false
|
从使用远程缓存中,剔除特定目标
将目标标记为no-cache,即可将指定的目标剔除出使用远程缓存列表。例如:
java_library(
name
=
"target"
,
tags
=
[
"no-cache"
],
)
|
从远程缓存中,删除内容
从远程缓存中,删除内容,是你服务管理的一部分。怎样删除内容,取决于你的缓存服务器。当删除输出时,要么整个输出缓存删除,要么删除老的输出缓存。
输出缓存是以一个名字和哈希的集合方式存储的。当删除内容是,没有办法区分出,某个输出属于哪个特定的构建。
你可以用如下方式删除缓存:
- 当缓存被污染后,创建一块新的缓存
- 通过删除老缓存的方式,减少存储的数量
已知问题
Bazel编译时,不能进行文件修改
在构建时,如果某输入文件有改动,Bazel会将非法的结果上传到远程缓存。我们正致力于解决此问题。关注issue #3360的更新。可以通过不修改文件的方式,规避该问题。
环境变量泄露到构建动作中
构建动作中包含环境变量。这会导致多台机器间共享远端缓存的命中问题。例如:不同环境配置了不同的$PATH环境变量,将不共享缓存命中。你可以通过--experimental_strict_action_env,确保不会出现该问题。仅允许--action_env配置的环境变量白名单,可以包含在构建动作定义里面。Bazel的Debian/Ubuntu包中,通过/etc/bazel.bazelrc,定义环境变量白名单,里面就包含了$PATH。如果你发现缓存命中率没有达到预期,检查下是不是你又一个较老的/etc/bazel.bazelrc文件。
Bazel不跟踪工作空间外的工具
Bazel目前不跟踪工作空间外的工具。这可能产生问题,例如,一个构建动作使用了/usr/bin/目录下的编译器。然后,两个不同用户装了不同的编译器,将会错误的共享缓存命中,因为他们的输出不同,但是却又相同的构建动作hash。请关注issue #4558的更新。
外部链接
- 你的构建在数据中心:Bazel团队在FOSDEM 2018提出了一个话题,关于远端缓存和执行。
- 使用远程缓存,进行更快的Bazel构建:基线测试:Nicolò Valigi写了个博客,关于他做的远程缓存Bazel构建。
Bazel远程执行(开发中)
基于gRPC协议的远程缓存和远程执行正在开发中。远程执行允许Bazel在一个独立空间执行构建动作,比如数据中心。你可以尝试在Buildfarm上尝试远程执行,这是个开源的项目,致力于提供一个分布式的远程执行平台。