[译] 用 Git Bisect 排查 Bazel 构建问题

原文:Troubleshooting Bazel with Git Bisect 作者:Son Luong Ngoc · 原文发表于 2025-02-18。 译注:文中的“我们”指原作者或 BuildBuddy 团队,时效性表述以原文发布日期为准。Bazel 专有名词保留英文,代码和命令未作翻译。 译注:本文保留 2025 年 2 月使用 Bazel 8.0.1、8.1.0 和 rules_go 排查回归的案例。版本号、commit 和日志均是当时的记录,用来演示二分定位方法。 升级 Bazel 和相关依赖项有时会导致意外问题。这些问题的范围从 构建 故障到运行时错误,而且通常很难排除故障。 今天我们来看看,依赖升级后出现 构建 失败,如何用 git bisect 找出根因。 升级 Bazel 后 构建 失败 如果您像我一样,您会喜欢在项目中拥有最新、最好的工具。而我使用最多的工具是Bazel,所以随着最近8.1.0版本的发布,我决定升级我们的BuildBuddy repository来使用它。 $ echo '8.1.0' > .bazelversion $ bazel test --config=remote-minimal //... ... ERROR: /root/workspace/output-base/external/bazel_tools/tools/build_defs/repo/http.bzl:137:45: An error occurred during the fetch of repository 'rules_cc+': Traceback (most recent call last): File "/root/workspace/output-base/external/bazel_tools/tools/build_defs/repo/http.bzl", line 137, column 45, in _http_archive_impl download_info = ctx.download_and_extract( Error in download_and_extract: com.google.devtools.build.lib.remote.common.CacheNotFoundException: Missing digest: abc605dd850f813bb37004b77db20106a19311a96b2da1c92b789da529d28fe1/178823 ... 哎呀!将 Bazel 升级到 8.1.0 后,构建 似乎失败了。返回到 8.0.1 问题就消失了,这意味着问题是在 Bazel 8.1.0 中引入的。 ...

2026-11-09 · 7 min · 1436 words · Son Luong Ngoc

[译] 通过分布式调度加快构建

原文:Distributed Scheduling for Faster Builds 作者:Tyler Williams · 原文发表于 2022-04-07。 译注:文中的“我们”指原作者或 BuildBuddy 团队,时效性表述以原文发布日期为准。Bazel 专有名词保留英文,代码和命令未作翻译。 先向还不了解的读者介绍一下 BuildBuddy。我们为 Bazel 构建提供 UI、分布式缓存和 remote execution 平台,安全地编译代码、缓存 artifacts,并展示结果。使用这套服务,可以在笔记本上用不到 5 分钟构建 Tensorflow 等项目,而原先需要 90 分钟。 显然,要做到这一切,我们必须应对一些棘手的工程挑战,其中之一就是安排远程执行。为此,我们有一个调度程序。调度程序只是将我们的 API 接收到的 actions(基本上是作业)与实际执行工作的远程 workers 相匹配。如果您将 Tensorflow 之类的完整 构建 视为 10 道菜的餐食,那么单个 action 就相当于该餐食一小部分的食谱。为了更容易可视化,这里有一个来自构建 BuildBuddy 的真实 action: # this action just uses gcc to compile zlib/infback.c into an object file, zlib/infback.o /usr/bin/gcc -U_FORTIFY_SOURCE -fstack-protector -Wall -Wunused-but-set-parameter \ -Wno-free-nonheap-object -fno-omit-frame-pointer -g0 -O2 -D_FORTIFY_SOURCE=1 \ -DNDEBUG -ffunction-sections -fdata-sections -MD -MF \ bazel-out/k8-opt-exec-34F00540/bin/external/zlib/_objs/zlib/infback.d \ -frandom-seed=bazel-out/k8-opt-exec-34F00540/bin/external/zlib/_objs/zlib/infback.o \ -iquote external/zlib -iquote bazel-out/k8-opt-exec-34F00540/bin/external/zlib \ -isystem external/zlib/zlib/include \ -isystem bazel-out/k8-opt-exec-34F00540/bin/external/zlib/zlib/include -g0 \ -Wno-unused-variable -Wno-implicit-function-declaration -fno-canonical-system-headers \ -Wno-builtin-macro-redefined -D__DATE__="redacted" -D__TIMESTAMP__="redacted" \ -D__TIME__="redacted" -c external/zlib/infback.c -o \ bazel-out/k8-opt-exec-34F00540/bin/external/zlib/_objs/zlib/infback.o 这是另一个: ...

2026-11-09 · 2 min · 227 words · Tyler Williams

[译] Bazel 的 strategies 是什么?

原文:What are Bazel’s strategies? · Julio Merino · 2019 年 12 月 14 日。译文保留原文当时的技术描述。 “strategies?你要聊 Bazel 称霸世界的战略 🙀?”呃……不完全是。 过去几个月,dynamic execution 一直是我工作中的热门话题,我也正准备写一系列文章来介绍它。不过,在此之前,得先回顾一下 Bazel 的 execution strategies。要理解 dynamic execution 是什么、如何实现,execution strategies 很重要。 简单来说,strategies 是 Bazel 对一套子进程运行机制的抽象,用于执行 action 的一部分工作,比如调用编译器。它们本质上就是加强版的 Unix exec() 系统调用或 Windows CreateProcess,能让子进程在差别很大的环境中运行。 不过,有一条约束始终不变:strategy 不影响执行语义。也就是说,同一条命令行交给 strategy A 和 strategy B 执行,必须生成相同的输出文件。因此,切换 strategy 不会让 analysis graph 失效。 有了这个保证,就可以在运行时混用不同的 strategy,而不影响输出行为。Bazel 通过可重复指定的 --strategy 参数提供这项能力,参数值的形式是 [mnemonic]=strategy。你可以用它指定某一类 action 使用哪个 strategy。例如,--strategy=remote --strategy=Javac=worker 会把默认 strategy 设为 remote,只有 Java 编译使用 worker。另外,--strategy_regexp 可以根据 action message 选择 strategy,不过这里先不谈这套“魔法”。Bazel 也有一些挺巧妙的机制,支持自动选择 strategy。 ...

2026-11-09 · 2 min · 272 words · 翟志军 Jack Zhai

使用 BLA 进行反 REA AI 逆向——以 Electron 应用为例

English | 简体中文 AI 逆向带来的变化 REA 让 AI agent 借助逆向工具分析没有源码的应用,并辅助重建功能。软件公司还能保护自己的实现吗? BLA 如何应对 AI 逆向 魔高一尺,道高一丈。BLA 会向发布制品加入干扰分析的内容。诱饵模块和调用关系让 agent 面对更多待核对的线索,扩大的元数据则可能触及解析器的资源上限。 本例使用的 ASAR 方案会改写 JavaScript,并扩充归档目录。目标是保持应用可用,同时增加自动分析的开销或让特定入口受限。真实效果取决于目标格式与工具版本,需要后续评估。 以 Electron 为例接入 CI 示例仓库是 zacker330/bla-ci,包含简单的报价计算器和三平台 CI。在构建 job 生成原始 app.asar 之后加入 BLA,处理完成后再打包、生成完整性记录和签名。 加固会改写交付文件,后续打包若重新生成文件,就可能覆盖加固结果。把 BLA 接进构建,可以固定处理顺序,让每次发布都执行,并在失败时阻止交付。 原始制品 → 对照包 → 回归测试 └→ BLA → 加固制品 → 打包与回归 → 按渠道签名、封装及发布验证 两份制品 → 按时机评估反 REA AI 逆向效果 构建脚本保留原始 ASAR,再生成加固版本: npx --no-install bla audit dist/asar/original/app.asar --json npx --no-install bla protect dist/asar/original/app.asar \ -o dist/asar/hardened/app.asar --intensity bankrupt \ --seed bla-ci-demo-v1 --json npx --no-install bla verify dist/asar/hardened/app.asar \ dist/asar/original/app.asar --json 锁定工具版本和依赖,每次从原始制品重新加固。固定 seed 用于复现,不承担保密功能。命令或校验失败就停止构建,并保存报告。dist/build.json 记录版本、配置、产物哈希和耗时,让后续测试与评估能核对输入。 ...

2026-11-07 · 2 min · 279 words · 翟志军 Jack Zhai

[译] Skylark 设计一瞥

原文:A glimpse of the design of Skylark 作者:Laurent Le Brun。原文发布日期:2017 年 3 月 21 日。 译注:本文保留原文的 Skylark 名称和 2017 年的技术语境,文中的“当前”等时间表述均以原文发表时为准。原文来自 Bazel 官方博客,其源码仓库采用 Apache License 2.0。本文为中文翻译。 本文介绍 Skylark 的设计。Skylark 是 Bazel 中用于描述构建的语言。 简史 很多年前,Google 使用 Makefile 构建代码。正如其他人也曾指出的那样,Makefile 很难应对大型代码库。一个临时方案是用 Python 脚本生成 Makefile:构建描述保存在 BUILD 文件中,以调用 Python 函数的形式表达。但这个方案实在太慢了,瓶颈就在 Make。 Blaze 项目于 2006 年启动,后来以 Bazel 的名字开源。它用一个简单的解析器读取 BUILD 文件,只支持函数调用、列表推导式和变量赋值。当 Blaze 无法直接解析某个 BUILD 文件时,就会先执行一个预处理步骤:用 Python 解释器运行用户编写的 BUILD 文件,生成一个简化版的 BUILD 文件,再交给 Blaze 使用。 这种方法很简单,也允许开发者创建自己的宏。但它又带来了许多维护、性能和安全方面的问题。由于 Blaze 本身无法解析这些 BUILD 文件,各类工具的开发也变得更加复杂。 在当前版本的 Bazel 中,我们移除了 Python 预处理步骤,让系统变得更加合理。不过,为了迁移现有代码库,我们保留了 Python 语法。无论如何,这似乎都是个好主意:许多人喜欢 BUILD 文件的语法,其他构建工具,例如 Buck、Pants 和 Please,也采用了这种语法。 ...

2026-11-05 · 1 min · 134 words · 翟志军 Jack Zhai

我有一个 AI 同事

我很少在博客里写小说。但前段时间,我注意到一个现象:IM 上的技术讨论越来越有条理,越来越专业,也越来越不像人话。 于是,有了下面这个故事。故事纯属虚构。如有雷同,纯属正常。 晚上十一点四十七分,Slack 响了一声。 陈默正在盯着一块已经红了两个小时的 Grafana 面板。线上 API 的 P99 延迟从 180ms 爬到了 2.7 秒,重启 Pod 没用,扩容没用,数据库连接池看起来也正常。 消息来自他的同事老周。 我分析了一下,目前最可能有三个原因: 数据库慢查询导致连接池排队; Redis 出现热点 Key; 下游支付服务响应时间异常。 建议按照以下顺序排查…… 陈默看了两眼。 很有道理。 但他懒得想。 他切到另一个窗口,把老周的消息复制进去。 “这是我同事对线上故障的分析。你觉得他说得对吗?还有没有遗漏?” 几秒后,屏幕上开始吐字。 你同事的分析总体是合理的,但还可以进一步考虑以下几个方向…… 陈默快速扫了一遍,删掉最后一句“如果你愿意,我可以帮你整理一份完整的排查 checklist”,然后复制到 Slack。 我觉得除了这三个方向,还应该检查: Node CPU throttling; CoreDNS 延迟; Service Mesh sidecar 是否出现连接堆积; 最近是否有配置变更。 我建议先从最近变更入手,因为问题发生得比较突然。 不到一分钟,老周回复: 同意。从时间点来看,配置变更确实值得优先排查。另外我补充一点:如果是 Istio sidecar 导致的问题,可以对比应用容器和 Envoy 的 upstream request time…… 陈默盯着这段话。 不知道为什么,他觉得老周今天说话特别有条理。 他顺手复制。 “我同事这么回复。下一步我应该怎么回答?” AI 说: 可以这样回复: “同意。我们先查最近一小时的 deployment 和 config change。我来检查 Kubernetes event 和 ArgoCD,你帮忙看 Istio metrics,这样可以并行推进。” ...

2026-08-27 · 3 min · 500 words · 翟志军 Jack Zhai

不知道运维在忙什么

前段时间,我从领导那里得知,有其他团队的人说:“不知道运维在忙什么”。我懒得打听是谁说的,所以就暂时把这个“谁”称为:说话人。 而我作为运维的 leader,很早前就预料到有人会说这句话。在我自己看来,就两种情况:有人想搞运维团队(坏),或者那个人太不懂职场(菜)。 为了避免我个人情绪,我问了一下 AI:“有人向领导说这话”是什么含义,得到的答案是: “不知道运维在忙什么”通常不是中性询问,而是在领导面前暗示: - 运维工作不透明; - 运维没有及时响应; - 运维可能没做出成果,甚至“瞎忙”。 这是一种比较隐晦的负面评价。尤其当对方没有先向运维了解情况、也没有指出具体事项,却直接在领导面前说,确实带有甩锅、质疑或贬低的意味。 不论是哪种答案,本质上并不是我预料有人会说这句话,而是预料到信息不透明、话语权不对等的情况,发生这样的事情,我觉得是必然的。 我在世界500强、初创公司、上市公司、跨国公司待过,我见过一些世面了。 因此,我管理团队时,选择通过看板管理来公开团队的所有工作,一方面管理自己的团队,一方面预防在出现“情况”时,好有数据来自证。我统计了引入 AI 工具后,最近半年我们完成的工作量提高了 40% 多。 既然,运维团队公开了所有的工作,为什么还有人说不知道运维在忙什么?有几种可能: 说话人没有看,或者没有心思主动去看我们的工作。 运维团队没有同步到位。 情况1,没有什么好说的,大家都是成年人。 关于情况2。运维团队每天都会在看板前进行站会。 当其他团队来找运维团队提需求后,运维团队要多久向其他团队的人同步呢?要运维团队每打一个字就同步吗?当然不是,而是根据不同的团队的“同步频率需求”,来同步。 但是,反过来想,站在公司整体经营成本的角度来想,与其让运维团队花成本来解决“同步”的问题,不如其他团队主动问一句运维团队,这样是不是成本更低?我懒得深究这个情况了。 最终,我开发了一个 bot。v1 版本有2个功能: 只要有人在公司群里发:@infra 在忙什么,bot 就会把运维团队看板上所有 doing 的任务都列出来。 @infra subscribe <ticket> 就可以订阅任何一张卡的状态变更通知。我们运维也不用思考要多久同步,你关心就自己订阅。 这个 bot 只是为了堵住说话人的口,也是职场上的一种正面的回应。 本来,本文就应该停止了。好奇心让我深入思考了这类事件的背后。本质就是话语权不对等的结果,是不同成员影响决策、定义问题和表达异议的能力不平等。 假设软件工程会经过以下流程: 产品经理 -> 设计 -> 开发 -> 运维 -> 测试 这个流程中,谁有向上说话的权力,谁就有权力判定下游工作。假如开发有向上级说话的权力,当开发说:目前卡在运维。 那么,说一次还好,每次开发都这么说。信息传到上级的意思就是:运维工作不行。把“运维”换成“测试”,结果是一样的。 但是,我知道的是,当上级预期是10天完成上线时,而开发占用了9天,留给运维和测试的时间只有1天,也就是最后一天才通知运维要进行上线工作。那么,我们会如何看待开发,以及开发说的“目前卡在运维”呢? 这里只是以开发角色举例,换成其它角色,同样的问题。 那么,我们应该如何解决“话语权不对等”所带来的问题呢?是让话语权平等吗?我也不知道答案。我会继续研究下去。 最后,希望世界和平。

2026-08-22 · 1 min · 59 words · 翟志军 Jack Zhai

什么是手工运维

一个夏天的午后,知了嘈杂,窗下的我 review AI 写好 IaC 代码后,敲下部署到线上环境的命令。 看着终端的日志滚动,一个问题突然蹦入我的大脑:什么是手工运维? 像我这样使用 IaC 来管理基础设施: 过去是手打代码,这算吗? 现在是 AI 写的代码了,但是 prompt 依然是手打,这算吗? 代码是 AI 写了,但是执行,还是需要手工的,这算吗? 如果不使用 IaC,改成使用 GUI,人们可以在界面上点一下“部署”按钮,就完成了整个服务的部署,这算不算手工运维呢? 像 Cloudflare 或者 Vercel 上,开发者都不用点,只需要将写好的业务代码 push 到远程的 git 仓库就可以了,部署和自动化的域名都给自动化完成了,这算不算手工运维呢? 先说结论:不论是使用 GUI 还是 IaC,如果无法做到自动编排部署任务,都是手工运维。 过去,运维人员通过 SSH 一条条命令的输入到服务器,大家都认为那是手工运维。但是,如果把这些命令写成 bash 脚本,可以一键执行,这算自动化运维了吗? 现在,人们在 GUI 界面上,一个个按钮的点击界面,最后点击“部署”按钮,我认为这与通过 SSH 一条条命令运维没有什么区别,也是手工运维。只不过 GUI 的动作的粒度比 SSH 命令大一些。 本质上,以上两种方式,都需要人工编排部署任务,也就属于手工运维。 而 Cloudflare 或者 Vercel。单服务时,不需要编排部署,所以人们觉得自动化运维。但是如果是多个服务,Cloudflare 或者 Vercel无法做到自动化编排部署任务,它依然是手工运维。 所以,使用 IaC 来管理基础设施,算手工运维吗?取决于你的 IaC 代码:当人表达意图后,它能否自动输出部署计划,并且能自动化运行。 如何做到?这是另一个话题了。 希望这篇文章给你带来启发。多谢。

2026-07-10 · 1 min · 63 words · 翟志军 Jack Zhai

技术决策的隐藏陷阱:用持续负债消除一次性成本

团队里用的 AI 服务越来越多——OpenAI、Deepseek、通义、豆包、Claude……每个平台一套 API、一套 Key、一套计费口径。配置越来越乱,调用越来越散,于是总有人会提出: 要不我们自己搭一个 AI Gateway 吧?自建,或者买个现成的。 这个想法非常自然。我也有过。 搭建一个 AI Gateway 一次解决所有的问题,多好的 KPI 叙事。 但是,仔细一想,把账一算,我决定放弃在公司里自建 AI Gateway。 先给结论 对绝大多数公司来说,自建或购买 AI Gateway 的 ROI 都很低。 注意,决定性的因素不是你用了多少种 AI 服务。哪怕你接了十几个平台,只要项目规模没到、团队能力没到,这笔投入依然不划算。 真正划算的前提只有一个组合:项目数足够多,且你本来就有一支扛得住 7×24 的运维/平台团队。 否则,你大概率是在用一个永久性的负债,去换一两天就能消化掉的麻烦。 下面展开说为什么。 一、AI Gateway 到底解决了什么 先得承认,AI Gateway 的确能解决公司的一些问题: 多平台 fallback:同一个模型要在多个供应商之间兜底。比如 Deepseek,要做 Deepseek 官方 → 火山引擎 → 阿里云百炼 的 fallback 路径,每个业务都得自己写一遍。 API Key 轮换要重启服务:换一次 Key 就得重启应用。三个服务用到了就至少轮换三次,再乘上多环境(dev / staging / prod),数字很快就上去了。 请求监控各自为政:对 AI 调用的监控,每个业务都得自己实现一套。 接口不统一:想换模型,就得重新对接一个平台的 SDK,而不是一套接口打天下。 申请 Key 流程慢:业务向运维申请 Key,走流程要时间。 成本统计粒度粗:很多 AI 平台的成本统计很弱,做不到按 Key 或按 Project 维度归集,只能在业务代码里自己埋点。 这听起来非常的美好,也很能打动老板们。 ...

2026-06-14 · 2 min · 225 words · 翟志军 Jack Zhai

从法庭到代码:谁主张谁举证的工程实践

2023年,与开发商打了不少官司。学习了不少庭审知识。发现有些实践或者原则放到软件工程中也是有效的。就比如谁主张谁举证这一基本原则。 谁主张谁举证就是当事人对自己提出的主张提供证据并加以证明。 举例来说,张三说隔壁老王侵占了自己的宅基地。张三必须自己拿证据来证明自己说的事实。而不是让老王拿证据出来证明自己没有侵占。 在软件工程中,服务A调用服务B,出问题了。服务A认为是服务B的问题,这时,根据谁主张谁举证原则,服务A的开发应该自己找出证据来证明自己的观点是正确的,而不应该让服务B来提供证据证明自己没有问题。 因为如果让服务B证明自己没有问题,怎么证明自己没有问题呢?怎么证明,证据都不够充分。而且会浪费服务B的团队人力。 所以,在团队中,让大家达成“谁主张谁举证”的共识会带来以下好处: 节约团队的人力。如果服务A能提供有效证据,那么就不需要服务B去证明自己没有问题了。 倒逼团队形成故障现场保留的习惯,比如打印良好的日志等 不过,我们需要注意的是,“谁主张谁举证”并不意味着,服务B团队不配合服务A一起排查问题。在一个公司里,基本的团队协作原则与底线是要有的。 那是不是所有的场景都是谁主张谁举证? 也有特殊情况。有些场景,是需要进行举证责任倒置的。开发商要拿证据来证明自己的转供电是合法的,而不应该让业主拿证据来证明开发商转供电不合法。 也就是证据只能是“被告方”能提供的情况下,我们就不是谁主张谁举证了。 以上只是我个人的思维实验。 在工作中,作为DevOps工程师,我经常遇到开发者质疑云厂商的Redis或者RDS出了问题。 根据我过往的经验,只是质疑,没有证据的情况,大多是开发者自己代码的问题。通常我会自己看看监控的同时,会要求他们加日志,然后想办法复现问题。 最后,一个公司一个团队里,如何低成本的达成谁主张谁举证这个共识? 我目前还没有答案。

2025-09-13 · 1 min · 17 words · 翟志军 Jack Zhai