乐于分享
好东西不私藏

Bazel C++ 构建系列文档(九):远程缓存与分布式构建

Bazel C++ 构建系列文档(九):远程缓存与分布式构建

1. 远程缓存原理

Bazel 的远程缓存是其性能核心特性,通过存储构建产物(编译输出、测试结果)并复用它们来大幅加速构建。

1.1 缓存的基本概念

1.2 缓存存储的产物类型

产物类型
描述
缓存级别
Action Outputs
编译器的输出(.o、.a、.so)
细粒度缓存
Test Results
测试执行结果
测试缓存
Analysis Cache
构建分析结果
分析缓存
Build Metadata
构建元数据
会话级别

1.3 缓存策略

缓存策略:1. 按内容哈希存储 → 同一内容只存储一次2. 只存储中间产物 → 源代码不缓存3. 缓存分层 → 本地磁盘 → 远程缓存 → 重新构建4. 原子性操作 → 避免部分构建

2. 远程缓存配置

2.1 基本配置

# 配置远程缓存(gRPC)bazel build //... --remote_cache=grpc://cache.example.com:9092# 配置远程缓存(HTTP)bazel build //... --remote_cache=http://cache.example.com:8080# 配置本地磁盘缓存bazel build //... --disk_cache=~/.cache/bazel

2.2 .bazelrc 配置

# .bazelrc# 默认启用远程缓存build --remote_cache=grpc://cache.example.com:9092build --remote_timeout=60                # 超时时间(秒)# 本地缓存配置build --disk_cache=/tmp/bazel_cachebuild --remote_upload_local_results=true   # 本地结果上传到远程# 缓存大小限制build --max_idle_secs=3600                # 缓存保留时间build --remote_default_exec_properties=platform=x86_64

2.3 企业级配置

# .bazelrc# 远程执行配置build --remote_executor=grpc://executor.example.com:9091build --remote_instance_name=instance1build --remote_default_platform_properties=platform=x86_64,os=linux# 缓存配置build --remote_cache=grpc://cache.example.com:9092build --remote_timeout=300build --remote_retries=3# 证书配置build --remote_cafile=/path/to/ca.crtbuild --remote_cacert_pin="sha256=..."

3. 本地磁盘缓存

3.1 磁盘缓存结构

~/.cache/bazel/├── execroot/          # 执行根目录├── cache/             # 缓存数据│   ├── action_cache/  # Action 输出缓存│   ├── content_cache/ # 文件内容缓存│   └── test_logs/     # 测试日志└── tmp/              # 临时文件

3.2 配置磁盘缓存

# .bazelrc# 设置缓存大小限制build --remote_max_connections=20        # 最大并发连接build --remote_timeout=60                # 连接超时# 缓存清理策略build --max_idle_secs=86400              # 24小时不访问则清理build --experimental_remote_downloader_cache_size=100  # 100GB# Windows 特定配置build --experimental_windows_symlink_in_runfiles=false

3.3 管理磁盘缓存

# 查看缓存大小du -sh ~/.cache/bazel/# 清理缓存bazel clean --expunge                  # 完全清理rm -rf ~/.cache/bazel/cache            # 手动清理缓存# 设置缓存清理任务# 在 .bashrc 或 crontab 中添加# 每周清理一次*/5 * * * * find ~/.cache/bazel/cache -type f -mtime +7 -delete

4. 远程执行

远程执行与远程缓存协同工作,将计算任务分发到远程执行器。

4.1 远程执行配置

# 启用远程执行bazel build //... --remote_executor=grpc://executor.example.com:9091# 同时使用缓存和执行bazel build //... \    --remote_cache=grpc://cache.example.com:9092 \    --remote_executor=grpc://executor.example.com:9091# 指定执行平台bazel build //... --platform_target=@platforms//os:linux

4.2 工作区配置

# WORKSPACE 或 MODULE.bazelload("@bazel_tools//tools/build_defs/repo:http.bzl""http_archive")# 注册远程执行器http_archive(    name = "my_remote_tools",    urls = ["https://example.com/tools.tar.gz"],    patch_cmds = [        "echo 'my_executable' > remote_executor",        "chmod +x remote_executor",    ],)# BUILD 文件中使用toolchain(    name = "remote_toolchain",    toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",    toolchain = ":remote_toolchain_impl",)

4.3 安全配置

# .bazelrc# 启用认证build --remote_timeout=60build --remote_downloader_cafile=/path/to/ca.crtbuild --remote_downloader_cacert_pin="sha256=..."build --remote_executor_cafile=/path/to/ca.crt# 令牌认证build --remote_downloader_header="Authorization: Bearer token"build --remote_executor_header="Authorization: Bearer token"

5. 缓存优化策略

5.1 缓存命中率优化

# 查看缓存命中率bazel build //... --remote_verbose# 分析构建缓存效果bazel analyze-profile --build \    --output_html=cache_analysis.html# 使用 bazel analyze-action-inputsbazel analyze-action-inputs //... > action_inputs.log

5.2 Action 合并

# 合并相似的 Actioncc_library(    name = "optimized_lib",    srcs = ["a.cc""b.cc""c.cc"],    copts = ["-O2"],  # 相同编译选项)# 而不是分开编译# cc_library(name = "a", srcs = ["a.cc"], copts = ["-O2"])# cc_library(name = "b", srcs = ["b.cc"], copts = ["-O2"])

5.3 内容感知的缓存

# 使用文件的哈希值而不是文件名cc_library(    name = "versioned_lib",    srcs = ["lib.cc"],    copts = ["-DVERSION_SHA=@"EOF

6.3 监控与报警

# 监控脚本(Python)import requestsimport timedef monitor_cache_health():    # 检查缓存服务器状态    try:        response = requests.get("https://cache.example.com/health", timeout=5)        if response.status_code != 200:            alert("Cache server unhealthy")        else:            stats = response.json()            if stats["cache_hit_ratio"] < 0.8:                alert("Low cache hit ratio")    except Exception as e:        alert(f"Cache server error: {e}")# 定期执行while True:    monitor_cache_health()    time.sleep(60)

7. 故障排查

7.1 常见问题

问题
可能原因
解决方案
缓存命中率低
Action 过于细碎
合并相似的 Action
缓存缓慢
网络带宽不足
优化网络或使用本地缓存
构建失败
远程服务器不可用
切换到本地构建
测试缓存失效
测试用例依赖时间
使用固定时间戳

7.2 调试命令

# 启用详细日志bazel build //... --remote_verbose# 测试缓存连接bazel build //:hello --remote_cache=grpc://cache.example.com:9092# 查看缓存的 Actionbazel query "buildfiles(//...)" --output=graph# 分析构建缓存bazel analyze-profile --build --output_html=analysis.html

7.3 缓存统计

# 缓存使用统计bazel info --disk_cache_usagebazel info --remote_cache_usage# 查看缓存中的 Actionbazel cquery "kind(cc_compile, //...)" --output=label | wc -l

8. 性能基准测试

8.1 本地 vs 远程构建对比

#!/bin/bash# build_benchmark.sh# 确保构建目录干净bazel clean --expunge# 构建时间测试echo "=== 本地构建 ==="time bazel build //...echo "=== 本地缓存构建 ==="time bazel build //...echo "=== 远程缓存构建 ==="time bazel build //... --remote_cache=grpc://cache.example.com:9092echo "=== 远程执行构建 ==="time bazel build //... --remote_executor=grpc://executor.example.com:9091

8.2 缓存命中测试

# 测试不同场景的缓存命中率import subprocessimport jsondef test_cache_hit_ratio():    # 记录初始状态    result = subprocess.run(["bazel""info""disk_cache_usage"], capture_output=True)    initial_cache = int(result.stdout.decode().strip())    # 构建并测量    subprocess.run(["bazel""build""//..."], check=True)    result = subprocess.run(["bazel""info""disk_cache_usage"], capture_output=True)    final_cache = int(result.stdout.decode().strip())    cache_growth = final_cache - initial_cache    return cache_growth# 测试不同配置print("Action-level cache size:", test_cache_hit_ratio())

9. 成本优化

9.1 缓存压缩

# .bazelrc# 启用压缩build --remote_compression=truebuild --remote_timeout=60# 压缩级别build --remote_compression_level=6

9.2 智能缓存策略

# 自动清理旧缓存import osimport timedef cleanup_old_cache():    cache_dir = os.path.expanduser("~/.cache/bazel/cache")    max_age = 30 * 24 * 60 * 60  # 30天    for root, dirs, files in os.walk(cache_dir):        for file in files:            file_path = os.path.join(root, file)            if time.time() - os.path.getmtime(file_path) > max_age:                os.remove(file_path)                print(f"Removed: {file_path}")

9.3 冷热数据分离

存储策略:1. 热数据(最近7天)→ 存在高速存储2. 温数据(30天)→ 存在标准存储3. 冷数据(30天以上)→ 存在低成本存储或删除// .bazelrc 配置build --remote_cache_cleanup_age=2592000  # 30天build --remote_cache_retention_multiplier=1.5

10. 小结

本篇详细介绍了 Bazel 的远程缓存与分布式构建:

  • ✅ 远程缓存原理与基本概念
  • ✅ 本地磁盘缓存配置与管理
  • ✅ 远程执行与缓存协同工作
  • ✅ 缓存优化策略与技巧
  • ✅ 企业级部署方案
  • ✅ 故障排查与监控
  • ✅ 性能基准测试
  • ✅ 成本优化策略