Skip to content

代码明明修了,板端为什么还在跑旧版本

“源码已经改了”与“板端正在运行修复后的程序”之间,隔着构建、打包、上传、安装、服务重启和进程替换。任何一层发生漂移,日志看起来就像新版本,真实执行文件却仍然来自旧 release。

这次发布链排障里,真正的问题并不在算法代码,而在资产路径、包内容和版本身份没有形成闭环。

现场:每一层单独看都像是对的

源码里已经替换了配置和模型路径,CI 也能运行,服务安装后还是 active。但检查最终 package 和板端进程时,陆续发现:

  • 运行时读取的配置实际位于 /etc,打包脚本却只放进 install tree;
  • 模型文件名改了,CI 或 health check 仍引用旧名称;
  • payload 脚本的默认路径依赖调用时 cwd;
  • 宿主机只完成 staging,没有生成真实 .deb
  • 板端降级后,旧进程没有完全停止,仍然持有相机设备。

这类问题最难的地方是:每个阶段都可能给出局部成功。

第一个根因:运行时路径与打包路径不是一回事

构建系统把默认配置安装到 share/...,不代表运行时就从那里读取。源码若固定读取 /etc/camera.json,factory package 必须显式把文件 stage 到 package root 的 /etc

排查时不能从打包脚本猜运行时路径,应该从当前源码的 open/read 入口反查权威位置,再检查最终包内容。

第二个根因:模型改名没有全链更新

运行时资产路径通常同时出现在:

  • service 或 launch 默认值;
  • payload 脚本;
  • CI 检查;
  • health check;
  • 文档与示例命令。

只替换源码中的模型名,会让 package、CI 和 runtime 各自持有不同答案。正确做法是把它们当成一份 release contract 一起搜索、修改和测试,并删除无意保留的 legacy fallback。

第三个根因:脚本依赖当前目录

脚本在 repo root 执行时正常,从 CI 或其他目录调用就找不到默认模型。这说明资源路径是相对 shell cwd,而不是相对脚本或仓库根目录。

修复后的原则是:先稳定解析 repo_root,再构造默认资源路径。用户显式传入的路径可以覆盖默认值,但默认行为不能依赖“大家刚好都从同一目录运行”。

第四个根因:Staging 不是 Package 证据

宿主机缺少 dpkg-deb 时,脚本可以把文件摆进临时 package tree,让目录检查全部通过,却没有生成真正可安装的包。

最终验证必须回到可信容器或目标架构构建环境:真实生成 .deb,列出包内容,安装到干净环境,并检查 post-install/service 行为。目录看起来正确,只能算中间证据。

建立 Source Commit 追踪

每个 production package 应携带:

  • 人可读的 app version;
  • 完整 source_commit
  • 构建时间和目标架构;
  • 关键资产版本或 hash;
  • 构建 profile。

版本可以类似:

text
prod-YYYYmmdd-HHMMSS-g<shortsha>

同时在运行时版本文件中保存完整 commit。这样 deb 文件名、板端状态接口和源码提交可以互相核对,而不是依赖 CI job 名或聊天记录。

板端验收不能停在安装成功

部署后按以下顺序确认:

  1. 包管理器显示目标版本已安装;
  2. 运行时版本文件中的 source_commit 正确;
  3. systemd 的 MainPID 已变化;
  4. /proc/<pid>/exe 指向目标 release;
  5. 旧进程与旧 container 已停止;
  6. 设备没有被旧 owner 占用;
  7. topic、rate、socket 和 health 输出符合新版本合同。

一次 A/B 测量中,版本切换后旧相机进程仍然存活,导致设备占用和基线异常。这个问题说明,package version 对了也不能代替进程身份验证。

结论

发布可追踪性不是在版本页面多显示一个 commit id,而是把源码、资产、package、安装状态和真实进程连成一条证据链。

当板端行为与预期不一致时,第一步不该继续改代码,而应该先证明正在运行的到底是哪一份代码、哪一套配置和哪个模型。