← 首页 · 边缘工程 边缘工程

边缘 AI:模型签名仓 + Mender Artifact 实操手册

日期:约 2026-10-25(Asia/Shanghai)
定位:系统 OTA 与模型 OTA 分轨的可执行手册 —— Mender(或等价)管根文件系统/容器;自建签名模型仓管 .rknn / .bmodel / .engine / .hef / .onnx + manifest(model.ver = 内容哈希)。
衔接:edge-ai-gstreamer-ota-cheatsheet.md(目录与选型)、edge-ai-event-schema-acceptance.md(事件与 model.ver)、edge-ai-license-ledger-template.md(AGPL/Balena 红线)、edge-ai-ppe-smoke-fire-model-shortlist.md(样例 PPE)、edge-ai-power-dev-roadmap.md(D0–30 / W4)。
实证:Mender 官方 Create a custom Update Module、Artifact 概览、Sign and verify;Update Modules v3 File API(/usr/share/mender/modules/v3)。不确定处标 unknown,勿当固化版本号。


1. 架构一句话

轨道 管什么 制品 回滚手段
系统轨(Mender) 根文件系统、内核/BSP、推理 Agent、GStreamer 插件、模型公钥 .mender rootfs-image 或容器 Update Module A/B 分区;启动/报到失败自动回旧分区
模型轨(自建签名仓) 权重与 meta:.rknn/.bmodel/.engine/.hef/.onnx + manifest.json 内容寻址包;可选再包一层 自定义 Update Module Artifact current/previous 软链;健康检查失败切回上一 model.ver

硬规则:未签名权重 禁止塞进 rootfs;百 MB 级引擎进 rootfs 会拖垮 delta、放大变砖面、并把日更绑死在整镜像发布上(见 OTA cheatsheet B.2 / B.3)。数据分区(如 /data)应独立于 A/B rootfs,保证系统回滚时不误删已激活模型;反之,模型回滚也绝不改写只读根分区。公钥随系统镜像预置,权重永不进镜像层。

┌─────────────┐   HTTPS/mTLS    ┌──────────────────┐
│ Mender 服务  │ ──────────────► │ 设备:Mender Client│──► A/B rootfs
└─────────────┘                 └──────────────────┘
┌─────────────┐   签名包+index  ┌──────────────────┐
│ 模型签名仓   │ ──────────────► │ Model Agent      │──► /data/models/
│ (MinIO等)   │                 │ 验签→store→软链  │    current/previous
└─────────────┘                 └──────────────────┘
                                         │ SIGHUP / 本地 socket
                                         ▼
                                  GStreamer / 推理进程

与 Balena 一句对比:系列更推 Mender + 自建仓 —— Mender 客户端/服务端以 Apache-2.0 为主、合规清晰;openBalena 含 AGPL-3.0 风险(许可台账 TL-003),闭源站端盒子标书勿默认「可闭源 OTA」。全容器团队若已沉没 Balena,可让其管应用容器,模型仍走数据分区签名仓,避免每次改 PPE/烟火权重都打整镜像。许可与选型细节以台账及 OTA cheatsheet B.1 为准,本文不重复展开 AGPL 传染边界。


2. 目录与 Manifest

2.1 设备落盘约定(与 cheatsheet 对齐)

/data/models/
  store/<sha256>/          # 不可变:权重 + manifest.json + labels
  current -> store/<sha>/  # 推理只认此软链
  previous -> store/<sha>/ # 一键回滚
  keys/model-verify.pub    # 仅公钥;私钥永不落设备

2.2 manifest.json 示例(可复制)

model.ver 必须等于 主权重文件的内容哈希(SHA-256 hex),与事件 Schema 验收否决项一致。

{
  "name": "ppe-yolox-s-int8",
  "task": "ppe",
  "schema_ver": "1.0.0",
  "model.ver": "a3f1c9e8b2d0471a6e5f90c4d8b7a216e3f0c1d2a4b5e67890abcdef12345678",
  "backend": "rknn",
  "files": [
    {
      "path": "model.rknn",
      "sha256": "a3f1c9e8b2d0471a6e5f90c4d8b7a216e3f0c1d2a4b5e67890abcdef12345678",
      "bytes": 12456789
    },
    {
      "path": "labels.txt",
      "sha256": "11aa22bb33cc44dd55ee66ff77889900aabbccddeeff00112233445566778899",
      "bytes": 128
    }
  ],
  "sig": {
    "alg": "ed25519",
    "key_id": "model-prod-2026q4",
    "value_b64": "<DetachedSignatureBase64>"
  },
  "compat": {
    "board": ["rk3588-evb", "rk3588-power-box-v2"],
    "soc": "rk3588",
    "runtime": {
      "rknn_toolkit2": "2.x.x",
      "librknnrt": ">=2.x",
      "jetpack": null,
      "tensorrt": null,
      "hailort": null
    },
    "input": { "w": 640, "h": 640, "fmt": "RGB", "mean": [0,0,0], "std": [255,255,255] }
  },
  "rollback": {
    "keep_n": 3,
    "healthcheck": "http://127.0.0.1:9100/readyz",
    "fail_threshold": 3,
    "window_sec": 120,
    "on_fail": "switch_previous_and_reload"
  },
  "license_ledger_id": "WT-00x",
  "channel": "prod",
  "built_at": "2026-10-25T08:00:00+08:00"
}
task 建议值 说明
ppe / smoke_fire 对齐 PPE/烟火短名单与 D0–30
meter / thermal / oil_level 表计/红外/油位
vib / us 振动/超声;事件码见 SOP

sig 覆盖对象:对「规范化后的 manifest 去掉 sig 字段 + 按路径排序的各文件 sha256」做摘要再签(实现自定,但设备与 CI 必须同一规范)。


3. 签名流程(两层密钥,勿混用)

3.1 模型仓签名(日更)——推荐 Ed25519

设备只持公钥;私钥离线或 HSM/隔离 CI 签名机。

# 一次性:离线生成(示例用 openssl ed25519;亦可用 minisign/cosign,择一并写入台账)
openssl genpkey -algorithm ED25519 -out model-sign.key
openssl pkey -in model-sign.key -pubout -out model-verify.pub

# CI:对包目录算哈希、写 manifest、分离签名
sha256sum model.rknn labels.txt > HASHES
# 将 HASHES + manifest 规范字节串签名 → 写入 manifest.sig.value_b64
# 发布:store/<model.ver>/ + 更新 index.json

校验失败:拒绝加载、不切 current,上送事件(对齐 Schema):

{
  "schema_ver": "1.0.0",
  "ts": "2026-10-25T08:30:00+08:00",
  "device_id": "rk3588-cabinet-01",
  "event": {
    "code": "MODEL_VERIFY_FAIL",
    "quality": "confirmed",
    "detail": "sig_mismatch_or_sha256"
  },
  "model": { "ver": "offered-but-rejected", "path": "/data/models/store/..." }
}

等价码可映射为 MODEL_SIG_FAIL / MODEL_COMPAT_REJECT,但台账与 MQTT topic 须统一。

3.2 Mender Artifact 签名(系统轨)——官方支持 RSA≥3072 或 ECDSA P-256

官方 Sign and verify(核验日约 2026-10):不支持 Ed25519 Artifact 签名(社区讨论亦确认暂无计划)。模型轨可用 Ed25519;系统轨跟官方能力。

# RSA 3072(官方推荐下限)
openssl genpkey -algorithm RSA -out mender-private.key -pkeyopt rsa_keygen_bits:3072
openssl rsa -in mender-private.key -out artifact-verify-key.pem -pubout

# 签名 / 校验
mender-artifact sign artifact.mender -k mender-private.key -o artifact-signed.mender
mender-artifact validate artifact-signed.mender -k artifact-verify-key.pem

设备 mender.conf 配置 ArtifactVerifyKey 或 ArtifactVerifyKeys(轮换窗口可多钥);开启后拒收未签名 Artifact。


4. Mender Artifact:模型包如何「可选」进 Update Module

4.1 官方概念(核验摘要)

要点 官方口径
Update Module 位置 /usr/share/mender/modules/v3/<type>,可执行文件
调用约定 两参数:STATE、FILES(File API 根路径)
必做状态 实践上至少实现 ArtifactInstall
回滚 SupportsRollback 打印 Yes;ArtifactInstall 先备份,ArtifactRollback 恢复
应用类 Artifact mender-artifact write module-image -T <type> -n <name> -c <device_type> -f ...
载荷位置 $FILES/files/*
是否重启 NeedsArtifactReboot 可答 No(模型更新通常不必整机重启)

推荐默认路径:不经 Mender 传大权重 —— 模型 Agent 直连签名仓(带宽、差分、日变更顺)。
可选路径:变电站弱网/离线柜,用自定义 Module(如 edge-model)把已签名整包写入 /data/models/store/,再由同一 Agent 验签激活。即便走 Module,验签逻辑仍在 Agent,Module 只负责落盘与触发。

4.2 最小 Update Module 骨架(示意,须幂等)

#!/bin/bash
# /usr/share/mender/modules/v3/edge-model
set -euo pipefail
STATE="$1"; FILES="$2"
STORE=/data/models/store
case "$STATE" in
  NeedsArtifactReboot) echo "No" ;;
  SupportsRollback) echo "Yes" ;;
  ArtifactInstall)
    # 期望 payload:manifest.json + 权重;先拷到 staging,再调 model-agent activate
    STAGE=$(mktemp -d /data/models/.stage.XXXX)
    cp -a "$FILES"/files/. "$STAGE"/
    /usr/bin/model-agent activate --from-dir "$STAGE" --mender-context "$FILES"
    ;;
  ArtifactRollback)
    /usr/bin/model-agent rollback --to previous
    ;;
esac
exit 0
# 工作站打包(勿把未签名裸权重当最终产物)
DEVICE_TYPE=rk3588-power-box-v2
mender-artifact write module-image \
  -T edge-model \
  -n ppe-yolox-s-int8-20261025 \
  -c "$DEVICE_TYPE" \
  -o ppe-model.mender \
  -f dist/manifest.json \
  -f dist/model.rknn \
  -f dist/labels.txt \
  -f dist/manifest.sig

# 再对 .mender 做官方 Artifact 签名(RSA/ECDSA)
mender-artifact sign ppe-model.mender -k mender-private.key -o ppe-model-signed.mender

Standalone / 离线:mender-update install /path/ppe-model-signed.mender(mender-client 4.0.0 之前命令名为 mender,以现场版本为准;不确定标 unknown)。


5. 回滚:模型层 vs Mender A/B

故障 谁处理 动作
新 rootfs 起不来 / 报到失败 Mender A/B 自动回旧分区;模型软链原样保留在数据分区
新模型验签失败 Model Agent 不激活;事件 MODEL_VERIFY_FAIL
激活后 ready 探针失败 N 次 Model Agent ln -sfn previous current → 通知推理重载;ack=rolled_back
模型好、但 BSP/runtime 不兼容 Agent compat 检查 rejected;需先发系统轨 Artifact 升 Toolkit/JetPack 再发模型
电源中断在 Module 安装中 Mender 状态机 Module 须幂等;ArtifactRollback 可恢复上一 store

关系写清:A/B 保命(系统),软链保业务(模型)。禁止指望「回滚 rootfs」顺带走模型版本,也禁止「只回滚模型」修复内核崩溃。验收时分别出具两份演练记录:① 拔电或故意坏 rootfs Artifact → 回到旧分区且 current 模型仍可推理;② 投放坏签名/坏兼容模型 → MODEL_VERIFY_FAIL 或自动 rolled_back,rootfs 版本号不变。这与路线图 W4「系统镜像 / 模型包分离」门禁一致。

健康检查伪代码:

for i in $(seq 1 "$FAIL_THRESHOLD"); do
  curl -fsS "$HEALTHCHECK" && exit 0
  sleep $((WINDOW_SEC / FAIL_THRESHOLD))
done
model-agent rollback --reason healthcheck
# MQTT: ota/model/ack {"status":"rolled_back","model.ver":"<previous>"}

6. 安全边界

  1. 模型仓传输:HTTPS;优先 证书钉扎(SPKI pin)或设备 mTLS 客户端证;禁止「仅明文 HTTP + 信任任意 CA」。
  2. 网络分区:模型仓与 MQTT Broker 放站端/地市保护网或跳板区;禁止推理进程直连公网任意 URL。
  3. 权限:Agent 以专用用户跑;只写 /data/models;禁止 Update Module / Agent 执行远程下发的任意脚本(payload 白名单:权重 + json + 文本标签)。
  4. 密钥:模型私钥与 Mender 私钥分离;轮换用双公钥窗口;私钥不出签名机。
  5. 许可:发布前过许可台账;AGPL/NC 资产不得入 channel=prod(见短名单与台账红线)。
  6. 日志:不落私钥、不落完整预签名 URL 长时效链接;抓拍合规仍按 Schema 文档。

7. 两周落地清单(对齐路线图 W4)

日 动作 完成标准
D1–2 离线生成 模型 Ed25519 + Mender RSA/ECDSA 密钥;设备预置两公钥 私钥不进 Git;台账登记 key_id
D3–4 搭 MinIO(或等价)+ index.json;内网 DNS/证书 一台开发机可 curl 下载并验签
D5–6 实现 model-agent:下载→sha256→验签→compat→store→烟测→软链→SIGHUP 单测覆盖错签/错板型
D7–8 从 PPE 短名单 A 轨导出样例 → 打 manifest → 发布 model.ver==文件哈希;事件可带该 ver
D9–10 一台 RK3588 或 Orin 试点:直连仓激活成功;可选再打 edge-model Artifact 推理读 /data/models/current
D11–12 断网:本地/U 盘安装签名包;错签:改一字节权重 → 必须 MODEL_VERIFY_FAIL 且不切换 录像/日志归档
D13–14 健康检查失败自动回 previous;与 Mender rootfs canary 各演练一次 书面:系统轨/模型轨分离证据

7.1 Agent 状态机与 MQTT(与 cheatsheet B.3 对齐)

业务遥测与 OTA 控制建议分 topic 前缀,避免环路(见 GStreamer OTA cheatsheet A.6)。

Topic 方向 载荷要点
ota/model/offer 云→端 url、model.ver、sha256、sig、channel、compat 摘要
ota/model/ack 端→云 verified / activated / rolled_back / rejected + 原因码
evt/edge/# 端→云 业务事件;校验失败另发 MODEL_VERIFY_FAIL

推荐状态序:offered → downloaded → verified → staged → health_ok → activated;任一步失败保持原 current,写 previous 不变。激活用 ln -sfn 原子切换,推理进程只读软链,收到 SIGHUP 或本地 Unix socket「reload」后重新打开权重。

index.json 仓侧最小字段:channel、hw、model.ver、url、sha256、sig、min_agent_ver、schema_ver。设备可缓存上一份 index;断网时允许 U 盘放入同等目录结构后执行 model-agent activate --from-dir /media/usb/store/<sha>。


7.2 RK3588 / Orin 试点差异(compat 怎么填)

平台 backend compat 必填 常见坑
RK3588 rknn rknn_toolkit2 / librknnrt 大版本 Toolkit 升级后旧 .rknn 可能拒载 → 先系统轨再模型轨
Jetson Orin tensorrt / engine jetpack、tensorrt、CUDA 能力 JetPack 小版本也可能导致 .engine 不兼容;必须同环境重建
Hailo / BM1684X 等 hef / bmodel 对应 runtime 版本 同理:runtime 进系统轨,权重进模型轨

试点机柜建议:固定一块「金样板」只收 channel=canary,观察 24–48h 无 MODEL_VERIFY_FAIL、无自动回滚后再推 prod。电力站端若无法 MQTT 出站,用 Mender standalone + U 盘签名包,仍走同一 Agent 验签路径。


7.3 失败模式速查

现象 根因倾向 处置
OTA「成功」但全是错检 compat 未拦、预处理不一致 Agent 强制核对输入尺寸/mean/std;拒切
模型激活后管道卡死 引擎崩溃拖死主进程 systemd Restart= + 健康检查回滚;勿整机 A/B
签名公钥轮换后全拒 只换了仓侧钥、设备未双钥窗口 ArtifactVerifyKeys / 模型双公钥并存一个版本周期
磁盘占满 keep_n 未执行 GC:保留 current/previous + 最近 N 个 store
把训练代码打进包 许可与攻击面双爆 包内白名单仅权重+json+labels;AGPL 训练栈见台账

7.4 为何「模型 Module」仍要二次验签

即便 Mender 已对 .mender 做 RSA/ECDSA 校验,模型内容仍应保留独立 Ed25519(或 RSA)分离签名:

  1. 直连仓路径不经过 Mender,必须自保;
  2. 同一 model.ver 可能被 USB、内网 mirror、Module 三条通道投递,内容信任锚应是 manifest 签名,而非投递通道;
  3. 便于审计:许可台账 ID、schema_ver、板型列表与哈希绑在同一 signed blob。

系统轨负责「谁有权改机器」;模型轨负责「谁有权改检测行为」。二者私钥分离,泄露面互不传染。

8. 与已有文档的接法

  • GStreamer OTA cheatsheet:本手册展开其 B.3「自建签名仓」与「Mender 勿塞大模型」。
  • 事件 Schema:激活成功后遥测必带 model.ver;校验失败用 MODEL_VERIFY_FAIL。
  • PPE 短名单:试点权重只允许 legal_status 达 eng_ok 且非红灯。
  • 许可台账:密钥算法、MinIO、Mender、训练栈 SPDX 分行;Balena/Ultralytics AGPL 保持黄/红直至合同。
  • 开发路线图:本清单位于 D0–30 的 W4 门禁(model.ver 与哈希一致、OTA 分轨记录)。

9. 参考链接(官方)

  • https://docs.mender.io/artifact-creation/create-a-custom-update-module
  • https://docs.mender.io/client-installation/use-an-updatemodule
  • https://docs.mender.io/artifact-creation/create-an-artifact
  • https://docs.mender.io/overview/artifact
  • https://docs.mender.io/artifact-creation/sign-and-verify
  • https://github.com/mendersoftware/mender/blob/master/Documentation/update-modules-v3-file-api.md

本文为工程手册草稿;密钥管理与法务条款须安全/法务复核。Mender 小版本命令差异以现场 mender-artifact --version / 客户端版本文档为准(unknown 勿写死)。

← 边缘 AI:MCU / TinyML 电力传感特征提取与上送手册(可执行) 首页 边缘 AI:模型金丝雀发布、灰度与回滚手册(可执行) →