Kong crate_locks 详解:Bazel + rules_rust 下 Rust 依赖锁文件的作用与 Repin 操作流程
本文基于 Kong 仓库中的 crate_locks/README.md 及其配套源码,讲解 Kong 在 Bazel 构建体系中如何管理 Rust 依赖(atc-router)的锁文件:xxx.Cargo.lock 与 xxx.lock 各自由谁生成、各自承担什么职责,以及如何使用 CARGO_BAZEL_REPIN 系列环境变量对指定 crate 索引执行 repin(重新锁定依赖版本),读完后可在 Kong 的 Bazel 构建环境中正确升级和审计 Rust 依赖。
背景:Kong 为什么要为 Rust 依赖单独维护锁文件目录
Kong 的核心路由模块 atc-router 是用 Rust 编写的(当前锁定版本为 1.7.1,可查 crate_locks/atc_router.Cargo.lock 中 [[package]] name = "atc-router" 条目)。Kong 的发布构建已经迁移到 Bazel:Rust 代码通过 rules_rust 构建为 rust_shared_library,最终产物被 OpenResty/Lua 侧加载。
Bazel 的构建模型要求所有外部依赖的解析结果在构建开始之前就完全确定,因此 Rust 生态中“运行时由 cargo 在线解析”的惯例在 Bazel 世界行不通。crate_locks/ 目录就是为此而设:crate_locks/README.md 明确写道,该目录存放 Rust 依赖的锁文件(lock files),目前包含两个文件:
- crate_locks/atc_router.Cargo.lock:cargo 标准格式锁文件;
- crate_locks/atc_router.lock:rules_rust 专用的 JSON 格式锁文件。
两种锁文件的分工:Cargo.lock 与 xxx.lock
crate_locks/README.md 对两类文件给出了简短但关键的定义:
xxx.Cargo.lock:由cargo生成的标准锁文件(记录每个依赖的精确版本与 checksum),但“为了让rules_rust满意,我们是用 Bazel 来生成它的”——即它由 Bazel 的 repin 流程驱动 cargo 重新计算,而不是开发者手动运行cargo update;xxx.lock:由 Bazel(rules_rust 的 crate_universe 机制)生成,直接用于 rules_rust 的依赖解析,是可复现构建(reproducibility)的关键输入。
从仓库中两个锁文件的实际内容可以印证这种分工:
- atc_router.Cargo.lock 是标准 TOML 格式,头部注释
# This file is automatically @generated by Cargo、version = 3,每个[[package]]段包含name、version、source(crates.io 索引地址)与checksum,例如aho-corasick 1.1.3的 sha256 为8e60d3...。atc-router 自身的直接依赖包括bitflags、cidr、criterion、fnv、lazy_static、pest、pest_derive、regex、serde、serde_regex、uuid; - atc_router.lock 是 JSON 格式(约 5591 行),顶层有一个
"checksum"字段(整个文件的完整性校验),"crates"字段下每个键(如"aho-corasick 1.1.3")记录了下载 URL(https://static.crates.io/crates/...)、逐文件的sha256、crate 的构建 targets、edition、feature 集、依赖关系以及 license 信息。rules_rust 正是基于这份 JSON 在 Bazel 的 repository rule 阶段把每个 crate 物化为外部仓库。
Bazel 侧如何消费这两个文件
锁文件在 Bazel 侧的消费点在 build/kong_crate/deps.bzl。kong_crate_repositories() 函数通过 rules_rust 的 crates_repository 规则声明了名为 atc_router_crate_index 的 crate 索引:
crates_repository(
name = "atc_router_crate_index",
cargo_lockfile = "//:crate_locks/atc_router.Cargo.lock",
isolated = cargo_home_isolated,
lockfile = "//:crate_locks/atc_router.lock",
manifests = [
"@atc_router//:Cargo.toml",
],
)
几个值得注意的细节:
- 两个锁文件路径都以
//:crate_locks/...显式指向仓库根下的crate_locks/目录,这正是 README 中 repin 命令与文件命名能对应上的原因——repin 的对象名atc_router_crate_index就是这里的name字段; isolated = cargo_home_isolated:默认使用隔离的 CARGO_HOME(首次 bootstrap 约需 2 分钟),避免污染开发者系统的 cargo 缓存;- 该函数同时通过
rust_register_toolchains将 Rust 工具链钉死在 1.82.0(edition 2021,附 x86_64 与 aarch64 两套 tarball 的 sha256),见 build/kong_crate/deps.bzl,进一步保证跨机器构建一致。
crate 索引生成后会暴露一个 defs.bzl,由 build/kong_crate/crates.bzl 加载:
load("@atc_router_crate_index//:defs.bzl", atc_router_crate_repositories = "crate_repositories")
def kong_crates():
atc_router_crate_repositories()
而真正编译 atc-router 的 build/openresty/atc_router/BUILD.atc_router.bazel 同样从该索引加载 aliases 与 all_crate_deps,用 rust_shared_library 规则以 default + ffi 两个 feature 构建共享库——所有 deps 全部来自 all_crate_deps(normal = True),即完全由锁文件解析出的依赖集合,构建时不会再去访问网络解析新版本。
如何 Repin:Bash 与 Fish 两种操作方式
Repin 的含义是:让 cargo 按当前 Cargo.toml 重新求解依赖版本,并同步刷新 crate 索引及其衍生的两个锁文件。crate_locks/README.md 给出的核心命令(Bash 版)为:
crates="atc_router_crate_index"
CARGO_BAZEL_REPIN=1 CARGO_BAZEL_REPIN_ONLY=$crates bazel sync --only=$crates
unset crates
Fish 版等价写法:
set -l crates \
atc_router_crate_index
CARGO_BAZEL_REPIN=1 CARGO_BAZEL_REPIN_ONLY=$(string join ',' $crates) bazel sync --only=$(string join ',' $crates)
set -e crates
逐段拆解这条命令:
| 元素 | 作用 |
|---|---|
crates="atc_router_crate_index" |
指定要 repin 的 crate 索引名,即 deps.bzl 中 crates_repository 的 name;若仓库将来新增其它 crate 索引,只需在列表中追加 |
CARGO_BAZEL_REPIN=1 |
rules_rust 约定的开关,告诉 crate_universe 的 repository rule:本次运行允许调用 cargo 重新计算并写回锁文件(默认情况下该值是只读的) |
CARGO_BAZEL_REPIN_ONLY=$crates |
限定只刷新指定索引的锁文件,不影响其它索引 |
bazel sync --only=$crates |
只触发该外部仓库的 fetch/sync,而不是全量同步,缩短操作时间 |
README 同时提示:更完整的 repin 说明请参考 rules_rust 项目文档(此处不再展开外部链接)。实际操作时的注意点:
- 由于
crates_repository默认isolated = True,首次 repin 会 bootstrap 一个隔离的 CARGO_HOME,耗时约 2 分钟; - repin 完成后应检查
crate_locks/下两个文件是否成对更新——atc_router.Cargo.lock(版本/checksum 变化)与atc_router.lock(JSON,含顶层checksum与每个 crate 的下载 sha256),两者必须一致地提交,否则 Bazel 构建与可复现性保证会被破坏; - repin 是“有写权限”的特殊操作:日常构建时这些锁文件是只读输入,任何人在本地手动改
Cargo.lock都不会被构建采纳,只有通过CARGO_BAZEL_REPIN=1流程重新生成才有效。
锁文件内容速览:以 aho-corasick 为例
对比同一个依赖在两份锁文件中的记录,可以直观理解两者格式差异:
atc_router.Cargo.lock(cargo 视角):
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
atc_router.lock(rules_rust 视角,节选):
"aho-corasick 1.1.3": {
"name": "aho-corasick",
"version": "1.1.3",
"repository": {
"Http": {
"url": "https://static.crates.io/crates/aho-corasick/1.1.3/download",
"sha256": "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
}
},
"targets": [ { "Library": { "crate_name": "aho_corasick", "crate_root": "src/lib.rs", ... } } ]
}
两份文件中的 sha256 完全一致,说明 .lock(JSON)是以 Cargo.lock 的版本解析结果为基础、再补充 Bazel 物化所需的元数据(下载 URL、构建 targets、edition、license 等)。这正是 README 中“Cargo.lock 让 cargo 生态认账、xxx.lock 让 rules_rust 可复现构建”这句分工描述的具体体现。
小结
crate_locks/是 Kong Bazel 构建中 Rust 依赖的可复现性锚点:atc_router.Cargo.lock记录版本与校验和,atc_router.lock记录 rules_rust 物化每个 crate 所需的全部元数据;- 两者的消费入口是 build/kong_crate/deps.bzl 的
crates_repository,产物经 build/kong_crate/crates.bzl 注入到 build/openresty/atc_router/BUILD.atc_router.bazel 的编译依赖中; - 升级 Rust 依赖时,按 crate_locks/README.md 的 repin 流程执行
CARGO_BAZEL_REPIN=1 CARGO_BAZEL_REPIN_ONLY=atc_router_crate_index bazel sync --only=atc_router_crate_index,并成对提交更新后的两个锁文件。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00