边缘 AI:模型金丝雀发布、灰度与回滚手册(可执行)
调研日期:约 2026-10-25(Asia/Shanghai)
落盘:/workspace/research/edge-ai-model-canary-rollback.md
定位:在签名仓store/current/previous之上,把 canary 探针、站内灰度、自动回滚 写成可操作状态机;与 Mender 系统轨分家、INT8 门禁、主动学习新权重、opsMODEL_*事件、D60/D90 验收衔接。
衔接:edge-ai-model-signstore-mender-cookbook.md、edge-ai-secure-boot-runtime-integrity.md、edge-ai-int8-quant-calibration.md、edge-ai-active-learning-relabel-loop.md、edge-ai-observability-logging-metrics.md、edge-ai-event-schema-acceptance.md、edge-ai-d60-d90-acceptance-checklist.md、edge-ai-gstreamer-ota-cheatsheet.md。
声明:本文只管模型轨发布节奏;系统镜像 canary 仍走 Mender A/B。不替代二次安规。不确定处标 unknown。
0. 一句话结论与红线
结论:新权重必须 Ed25519 验签通过 → 落入不可变 store/<sha> → 金丝雀探针通过 → 才允许 ln -sfn 切 current;金丝雀或灰度窗内健康失败 自动切回 previous 并 reload;系统轨(Mender)与模型轨(签名仓)分家回滚,禁止未验签热切、禁止「改软链冒充激活」。
| 红线 ID | 禁止项 | 说明 |
|---|---|---|
| C1 | 禁止未验签上线 | 无 sig / 错签 / model.ver≠主权重 SHA → 拒入 canary;对齐 D60-06、MODEL_VERIFY_FAIL |
| C2 | 禁止未验签热切 | 禁止 cp/mv 覆盖 current 目标、禁止跳过 Agent 直接 ln -sfn、禁止调试口手工塞权重当 prod |
| C3 | 金丝雀失败必须自动回 previous | 探针失败达阈 → 原子切 previous + reload;上报 MODEL_CANARY_FAIL / ack=rolled_back;禁止「人眼看一眼继续推」 |
| C4 | 系统轨与模型轨分家 | 坏模型不触发 Mender A/B;坏 rootfs 不删 /data/models;对齐 D60-07、D90-05、V7 |
| C5 | 禁止 INT8/许可门禁未过就进 canary | 无精度报告、校准 SHA 缺失、台账红灯 → 仓侧拒发 channel=canary(Q2/Q4/Q5、D60-05/11) |
1. 发布状态机(store → canary → current / previous)
1.1 目录与角色(对齐签名仓)
/data/models/
store/<sha256>/ # 不可变:权重 + manifest.json(+sig) + labels
canary -> store/<sha>/ # 金丝雀窗独占;推理可双载或旁路进程读此链
current -> store/<sha>/ # 正式推理只认此软链
previous-> store/<sha>/ # 上一稳定版;回滚目标
keys/model-verify.pub
| 指针 | 谁写 | 谁读 | 生命周期 |
|---|---|---|---|
store/<sha> |
Agent 下载+验签后写入 | 永不改内容 | GC:保留 current/previous/canary + keep_n |
canary |
Agent 在 verified 后指向候选 |
金丝雀推理 / 旁路评估器 | 晋升或失败后拆除 |
current |
仅 canary 通过或人工紧急回滚后原子切换 | 主推理进程 | 生产唯一入口 |
previous |
每次成功切 current 前,把旧 current 记为 previous |
回滚目标 | 至少保留 1 个稳定版 |
1.2 状态序(必须逐步,不可跳)
offered → downloaded → verified → staged(store)
→ canary_armed → canary_running → health_ok
→ promoted(current) | rolled_back(previous)
任一步失败:保持原 current;previous 不变(除非本次曾成功 promote)
| 状态 | 入口条件 | 出口成功 | 出口失败 / 事件 |
|---|---|---|---|
offered |
ota/model/offer 或仓 index |
开始下载 | rejected(channel/compat) |
downloaded |
字节完整 | 进验签 | MODEL_DOWNLOAD_FAIL |
verified |
Ed25519 + 文件 SHA + model.ver |
写 store/<sha> |
MODEL_VERIFY_FAIL(不激活) |
staged |
store 落盘 + compat 过 | 挂 canary |
MODEL_COMPAT_REJECT |
canary_armed |
canary 软链就绪;主 current 未动 |
启动探针窗 | 装载失败 → MODEL_LOAD_FAIL |
canary_running |
探针采样中 | health_ok |
见 §1.3 → MODEL_CANARY_FAIL → 回 previous(若曾试切)或拆除 canary |
promoted |
探针通过 +(可选)人工 ack | current=候选;旧 current→previous;拆 canary |
— |
rolled_back |
探针失败或人工 | current=previous;reload |
ack=rolled_back |
原子切换:一律 ln -sfn store/<sha> current(或先写 current.tmp 再 mv -T);推理只读软链,收 SIGHUP / Unix socket reload 后重开权重。禁止先删旧目录再拷新目录的非原子路径。
1.3 健康探针(金丝雀窗默认建议)
窗长默认 30~120 min(单盒)或 24~48 h(站级晋升前金样板);fail_threshold 与 window_sec 写入 manifest rollback 段。
| 探针 | 默认判失败(可配) | 采样 | 说明 |
|---|---|---|---|
| FPS / 就绪 | readyz 连续失败 ≥3;或有效推理 FPS 相对基线跌 >15% 持续 ≥5 min |
15~30 s | 对齐 MQTT 断线「跌落 ≤5%」思想;模型侧放宽到 15% 防瞬时抖动 |
| 错检 / 误报率 | 相对 previous 同验证切片:关键类 FP 升幅 >2× 或召回跌 >协议阈 | 窗末或滚动 15 min | 须有冻结验证切片或影子标注;无标签站用「人工驳回率」代理 |
| Crash / OOM | 推理进程退出码≠0、systemd 重启 ≥2 次/窗、或 RSS 超红线 | 即时 | 任一即失败,不等窗满 |
| 温度 / 降频 | SoC/NPU 触热降频且 FPS 连带跌超阈;壳温超 BOM 否决线 | 1 min | 与功耗散热手册联调;Zoo TDP 不作阈 |
| 事件完整性 | canary 期 model.ver 未写入业务事件;或 Schema 校验失败率突增 |
5 min | 防「能跑但不带版本」 |
伪代码(与签名仓手册一致并扩展):
# canary 窗内
fail=0
while within_window; do
ok=true
curl -fsS "$HEALTHCHECK" || ok=false
fps_ok || ok=false
temp_ok || ok=false
$ok || fail=$((fail+1))
[ "$fail" -ge "$FAIL_THRESHOLD" ] && {
model-agent rollback --reason canary_health --emit MODEL_CANARY_FAIL
exit 1
}
sleep "$SAMPLE_SEC"
done
# 窗末:错检门禁 +(可选)人工 ack → promote
model-agent promote --from canary --emit MODEL_PROMOTE_OK
2. 灰度策略:单盒 → 单间隔 → 整站
2.1 三级推进
| 级 | 范围 | 停留建议 | 通过条件 | 失败动作 |
|---|---|---|---|---|
| G0 单盒 | 金样板 1 台;channel=canary |
24~48 h | 探针全绿;无自动回滚;ops 可抽检 | 停发;修权重/校准后重新 store |
| G1 单间隔 | 同站 1 个间隔全部 AI 盒(或该间隔全部 cam 任务) | ≥48 h 含一次夜间/高峰 | 业务误报未恶化;值班无升级工单 | 该间隔全部回 previous;其他间隔不动 |
| G2 整站 | channel=prod 推全站 |
首周加强值班 | D60/D90 相关项仍绿 | 按盒并行回滚;可保留 1 台金样板继续观察 |
硬规则:未完成 G0 不得开 G1;未完成 G1 不得开 G2。跨站同步发布 unknown 是否允许——默认禁止,按站独立 canary。
2.2 失败判据与自动回滚(汇总)
| 判据 ID | 条件 | 动作 |
|---|---|---|
| F1 | MODEL_VERIFY_FAIL / MODEL_LOAD_FAIL |
不切 current;拆除 canary |
| F2 | canary 窗探针失败达阈 | current←previous(若已试切)或仅拆 canary;MODEL_CANARY_FAIL |
| F3 | G1 间隔内 ≥2 台同因回滚,或人工驳回率相对基线 >2× | 间隔级回滚 + 冻结该 model.ver |
| F4 | Crash 环(重启风暴) | 即时回滚;禁止自动再试同一 ver(需人工解冻) |
| F5 | INT8 站内复测事后发现超阈 | 禁止继续灰度;强制回 previous;重出报告 |
回滚后:推理不得中断超过 reload 时限(建议 ≤30 s);证据仓与 MQTT 缓冲不停;model.ver 在事件中立即反映 previous。
2.3 与 Mender 系统轨并行时的纪律
| 场景 | 允许? | 说明 |
|---|---|---|
| 同周先系统轨、后模型轨 | ✅ | runtime/Toolkit 升级后再发依赖新 runtime 的引擎 |
| 同窗双 canary(rootfs + 模型) | ❌ 默认禁止 | 故障归因混乱;必须错开 ≥48 h |
| 系统回滚期待「带走」坏模型 | ❌ | /data 独立;模型另走软链回滚 |
| 模型回滚修复内核 panic | ❌ | 走 Mender A/B |
3. 与主动学习、INT8、ops 事件衔接
3.1 主动学习新权重入口
闭环(见复标手册)终点必须是本状态机,不得「标注完直接覆盖 current」:
难例→标注→A轨微调→INT8导出→精度报告+台账绿灯
→ 签名 manifest(含 calib_sha / toolchain)
→ channel=canary offer → §1 状态机 → G0→G1→G2
| 检查 | 未过则 |
|---|---|
| 许可台账 / A 轨骨干 | 拒 prod/canary 发布 |
| 验证集 v1 哈希未变或变更已书面 | 拒;防「偷偷换集虚高」 |
| 新旧 A/B 摘要(主动学习周报) | 无摘要不得 G1 |
3.2 INT8 精度报告作发布前门禁
对齐 edge-ai-int8-quant-calibration.md:
- 报告含:相对 FP/基线跌幅、关键类分项、校准集 SHA、工具链版本、目标板 e2e 复测。
- 超协议阈 → 不得写入仓
channel=canary。 manifest缺calib_sha256/ 工具链字段 → Agentrejected(等同 Q5)。
3.3 ops / MQTT 事件码(与可观测性对齐)
业务事件仍走 evt/edge/#;发布运维走 ota/model/ack 与 ops:
| 事件 / ack | 何时 | 级别 |
|---|---|---|
MODEL_VERIFY_FAIL |
验签/哈希失败 | ERROR;对齐 D60-06 |
MODEL_COMPAT_REJECT |
board/runtime 不符 | WARN |
MODEL_LOAD_FAIL |
装载/初始化异常 | ERROR |
MODEL_CANARY_FAIL |
探针失败触发回滚或拒晋升 | ERROR |
MODEL_PROMOTE_OK |
canary→current 成功 | INFO |
MODEL_ROLLBACK_OK |
已切 previous 且 reload 成功 | WARN |
ack verified / activated / rolled_back / rejected |
端→云状态机 | 与签名仓 §7.1 一致 |
日志禁止落私钥与长时效预签名 URL;model.ver 必现于激活后业务事件。
4. 两周落地清单
| 日 | 动作 | 完成标准 |
|---|---|---|
| D1–2 | Agent 扩展状态:canary 软链 + promote/rollback/promote CLI;事件码表写入配置 |
单测:错签不切;原子 ln |
| D3–4 | 探针实现:readyz + FPS + crash 计数 + 温度只读接口 | 故意杀推理进程 → 自动回 previous |
| D5–6 | 金样板收 channel=canary;灌入已知好权重走通 promote |
MODEL_PROMOTE_OK;current/previous 正确 |
| D7–8 | 注入坏签名、坏兼容、降 FPS 桩 → 三类失败路径 | 日志三份归档 |
| D9–10 | 与 INT8 报告门禁、台账钩子对接仓侧发布 API | 无报告拒发 canary |
| D11–12 | G1 演练:同间隔 2 盒;一台注入 canary 失败 | 仅失败盒回滚;邻盒不变 |
| D13–14 | 与 Mender 错开演练:先坏模型回滚,再坏 rootfs 回滚;对照 D90-05 | 书面:分轨证据 + 本手册勾选 |
5. 验收门禁(嵌入 D60/D90)
| 门禁 | 级别 | 证据 |
|---|---|---|
D60-06 验签与 MODEL_VERIFY_FAIL |
否决 | 坏签演练 |
| D60-07 / D90-05 系统·模型分轨 | 否决 | 两份回滚演练 |
| D60-11 INT8 跌幅报告 | 警告→超标否决 | 报告绑定 model.ver |
| 本手册 CAN-01 未验签无法热切 | 否决 | 企图跳过 Agent 的失败日志 |
| CAN-02 金丝雀失败自动回 previous | 否决 | MODEL_CANARY_FAIL + 软链差 |
| CAN-03 G0→G1→G2 有停留记录 | 警告(D90 升否决) | 灰度工单时间线 |
| CAN-04 双轨同窗 canary 未发生 | 警告 | 发布日历 |
| CAN-05 主动学习权重均经 canary | 否决(对齐 D90-08) | 闭环周报引用 model.ver |
绝对否决(贴墙):未签名权重进 current;金丝雀失败仍推全站;用 Mender 回滚「顶替」模型回滚或反之。
6. 失败模式速查
| 现象 | 倾向根因 | 处置 |
|---|---|---|
| canary 绿但 promote 后全站误报 | 金样板场景不代表全站;验证切片过窄 | 拉回 previous;扩 G1 覆盖夜间/逆光后再推 |
频繁 MODEL_CANARY_FAIL 因温度 |
散热/满载路数超预算 | 先硬件/调度,勿连发新权重 |
promote 后 model.ver 事件仍旧 |
reload 未接到或多进程未统一 | 探针增加「事件 ver 一致性」检查 |
| 磁盘满导致 staged 失败 | keep_n/GC 未跑 |
保留 current/previous/canary,删更旧 store |
| 人用 SSH 改了 current | 违 C2 | 审计告警;强制 Agent reconcile;收紧调试口(安全启动手册) |
7. 与已有文档的接法
- 签名仓手册:本文件展开其
canary通道、健康检查与rolled_back,不改目录与验签算法。 - 安全启动:RoT→验签根→Agent 验签;本手册的「热切」禁令落在 L3。
- INT8 / 主动学习:发布前门禁与飞轮出口。
- 可观测性:探针指标进同一 metrics;
MODEL_*进 ops。 - D60/D90:CAN-* 作为分轨与飞轮演练的操作细则。
本文为工程手册草稿;站端变更窗与人工 ack 是否强制,以业主运维规程为准(unknown 勿写死)。密钥与法务条款须安全/法务复核。