4. 模型仓库与版本管理
在 TensorRT 多模型推理服务中,模型仓库不是简单的“放模型文件的目录”,而是服务启动、加载、路由和回滚所依赖的唯一事实来源。一个可运维的模型仓库需要同时回答四个问题:模型文件放在哪里、每个模型对应什么版本、服务如何知道该加载哪个版本、出问题时如何快速切回旧版本。本节围绕这四个问题展开,并给出可直接落地的目录结构、元数据字段和版本切换策略。
一、模型文件组织:按模型名与版本号分层
TensorRT 推理服务通常以“模型名 + 版本号”作为最小加载单元。推荐采用如下目录结构,每个模型一个子目录,版本号使用纯数字或语义化版本,避免使用 latest 这类浮动标签:
model_repository/
├── resnet50/
│ ├── 1/
│ │ ├── model.plan # TensorRT 序列化引擎
│ │ └── config.pbtxt # 模型配置(输入输出、batch、实例数)
│ ├── 2/
│ │ ├── model.plan
│ │ └── config.pbtxt
│ └── metadata.json # 模型级元数据(可选)
├── bert_base/
│ ├── 1/
│ │ ├── model.plan
│ │ └── config.pbtxt
│ └── metadata.json
└── ensemble_ocr/
└── 1/
├── model.plan
└── config.pbtxt
这里的关键约定是:版本目录名即版本号,服务启动时扫描模型目录下的数字子目录,按版本号排序后加载。TensorRT 的 model.plan 是序列化引擎,与 GPU 架构、TensorRT 版本、CUDA 版本强绑定,因此不同环境应生成不同的 plan 文件,不能跨环境直接复制。若使用 ONNX 作为中间格式,可在版本目录中同时保留 model.onnx 和 model.plan,但服务实际加载的是 plan。
二、元数据描述:让服务与运维都能读懂模型
仅有文件不足以支撑多模型服务。元数据的作用是描述“这个版本是什么、由谁产生、依赖什么、如何路由”。推荐在模型级放置 metadata.json,在版本目录内放置 config.pbtxt 描述推理配置。一个可用的元数据示例:
{
"model_name": "resnet50",
"active_version": 2,
"versions": {
"1": {"status": "archived", "created_at": "2025-01-10T09:00:00Z",
"precision": "fp16", "gpu_arch": "sm_86", "trt_version": "8.6.1"},
"2": {"status": "active", "created_at": "2025-02-18T14:30:00Z",
"precision": "int8", "gpu_arch": "sm_86", "trt_version": "8.6.1",
"calibration": "entropy", "accuracy_drop": 0.004}
},
"input_schema": {"name": "input", "dtype": "FP32", "dims": [3, 224, 224]},
"output_schema": {"name": "output", "dtype": "FP32", "dims": [1000]}
}
元数据中必须包含的字段包括:模型名、版本号、状态(active/archived/canary)、精度、GPU 架构、TensorRT 版本、创建时间、输入输出 schema。这些字段直接决定服务能否正确加载,以及回滚时能否快速判断旧版本是否仍兼容当前环境。例如,若当前 GPU 从 sm_86 升级到 sm_89,旧版本 plan 可能无法加载,此时回滚策略必须包含“重新生成 plan”这一分支,而不是简单切目录。
三、版本切换:从 canary 到 active 的原子操作
版本切换的核心是“先加载、后切流、再清理”。以 resnet50 从版本 2 切到版本 3 为例,推荐操作顺序如下:
- 将新引擎写入
resnet50/3/model.plan,并在metadata.json中新增版本 3,状态设为canary。 - 服务热加载版本 3,但路由权重仍为 0,此时版本 2 继续承载全部流量。
- 将版本 3 的权重逐步提升到 5%、20%、50%,同时观察 P99 延迟、错误率和输出一致性。
- 确认无异常后,将
active_version改为 3,版本 2 状态改为archived,但目录保留。
这里的关键是切换动作必须原子化:active_version 的修改应通过一次写入完成,避免服务在读取时看到中间状态。若服务支持动态加载,可通过文件监听或配置中心推送触发;若不支持,则需重启服务,此时应保证重启期间旧版本仍可服务,通常通过多实例滚动重启实现。
四、回滚策略:快速、可验证、可复现
回滚不是“把目录改回去”这么简单。一个可用的回滚策略需要满足三个条件:快速(分钟级)、可验证(回滚后能确认服务恢复)、可复现(保留现场用于定位)。具体做法是:
- 快速回滚:将
active_version从 3 改回 2,并触发服务重新加载。若服务支持热切换,整个过程可在秒级完成;若需重启,则依赖多实例滚动,单实例回滚时间控制在 30 秒内。 - 兼容性检查:回滚前检查旧版本 plan 的
gpu_arch和trt_version是否与当前环境一致。若不一致,直接切目录会导致加载失败,此时应使用旧版本 ONNX 重新生成 plan,再执行切换。 - 现场保留:回滚后不要删除新版本目录,而是将其状态标记为
failed,并保留对应的输入输出日志、精度对比数据,便于后续分析是精度问题、性能问题还是环境不兼容。
config.pbtxt 中的 max_batch_size、输入维度、实例数必须与 plan 匹配。若只回滚 plan 而忘记回滚 config,服务可能加载成功但推理结果错误。建议将 config 与 plan 放在同一版本目录内,回滚时整体切换。
五、版本管理检查清单
在将模型仓库投入生产前,建议逐项核对以下内容,确保版本切换与回滚路径畅通:
| 检查项 | 合格标准 | 常见问题 |
|---|---|---|
| 版本目录命名 | 纯数字,按升序排列 | 使用 latest、v1.0 等非数字名导致排序错误 |
| 元数据完整性 | 包含 active_version、status、gpu_arch、trt_version | 缺少 gpu_arch,跨环境回滚失败 |
| plan 与 config 同目录 | 同一版本目录内同时存在 | config 放在模型根目录,回滚时版本错配 |
| 回滚演练 | 每月至少一次,记录耗时与结果 | 从未演练,真实故障时才发现路径不通 |
| 旧版本保留策略 | 至少保留最近 2 个可用版本 | 只保留最新版,回滚无路可走 |
模型仓库与版本管理的本质,是把“模型文件”升级为“可被服务消费、可被运维切换、可被故障回滚”的工程资产。目录结构决定服务能否找到模型,元数据决定服务能否正确加载,切换与回滚策略决定故障时能否快速恢复。三者缺一不可,且必须在模型上线前就设计好,而不是等到出问题再补。