4. 模型仓库结构
Triton Inference Server 之所以能够同时服务多个模型、多个版本,并支持动态批处理、模型集成等高级特性,其根基在于一套严格且灵活的模型仓库(Model Repository)规范。模型仓库是 Triton 启动时加载模型、运行时管理模型版本、执行推理请求的“文件系统契约”。如果仓库结构不符合规范,Triton 会直接拒绝加载模型;如果 config.pbtxt 配置有误,模型可能无法启动或推理结果错误。本章将深入拆解模型仓库的目录组织、版本管理机制,以及 config.pbtxt 的核心字段与语法,帮助你从“能跑通”进阶到“设计合理、可运维”的部署架构。
上图展示了模型仓库的整体组织逻辑:根目录下每个子目录代表一个模型,每个模型目录内必须包含 config.pbtxt 以及至少一个数字命名的版本子目录。Triton 启动时通过扫描该结构,构建出完整的模型索引表,并依据配置实例化推理引擎。下面我们逐一拆解每个环节的细节。
4.1 模型仓库的目录组织:从根目录到版本目录的硬性规范
核心原理:Triton 的模型仓库本质上是一个“约定优于配置”的文件系统布局。它要求每个模型必须拥有独立的子目录,且目录名必须与模型名完全一致(即 config.pbtxt 中的 name 字段)。这种设计使得 Triton 可以在不读取配置文件的情况下,仅通过目录扫描即可发现所有模型,并支持热加载(通过 --model-control-mode=explicit 或轮询目录变化)。
标准目录结构示例:
model_repo/
├── sentiment_model/
│ ├── config.pbtxt
│ ├── 1/
│ │ └── model.pt
│ └── 2/
│ └── model.pt
├── resnet50/
│ ├── config.pbtxt
│ └── 1/
│ └── model.onnx
└── ensemble_demo/
├── config.pbtxt
└── 1/
└── (无模型文件,仅配置)
关键规则清单:
- 模型目录名:必须与
config.pbtxt中的name字段完全一致,否则 Triton 会报错“model name mismatch”。 - 版本目录:必须是纯数字(如
1、2、10),不能包含小数点或字母。Triton 将数字最大的版本视为 默认版本(即不带版本号请求时使用的版本)。 - 模型文件命名:Triton 对模型文件名有约定,例如 PyTorch 模型必须命名为
model.pt,ONNX 必须为model.onnx,TensorRT 为model.plan。如果文件名不符合约定,需要在config.pbtxt中通过default_model_filename字段显式指定。 - 无版本目录:如果模型目录下没有数字版本子目录,Triton 会认为该模型没有可用版本,加载失败。
4.2 版本管理机制:多版本共存与无损回滚
核心原理:Triton 的版本管理并非简单的文件堆叠,而是通过 version_policy 字段控制哪些版本被加载、哪些版本被忽略。默认策略是 latest,即只加载数字最大的版本。但在生产环境中,我们通常需要保留多个版本用于 A/B 测试或快速回滚。
版本策略配置:
version_policy: {
specific: {
versions: [1, 3, 5]
}
}
上述配置表示只加载版本 1、3、5,其他版本即使目录存在也不会被加载。另一种策略是 latest 配合 num_versions 参数,例如只保留最近 2 个版本:
version_policy: {
latest: {
num_versions: 2
}
}
版本选择优先级:当客户端请求时,可以通过 HTTP/gRPC 的 model_version 参数指定具体版本。如果不指定,Triton 会使用 version_policy 中定义的默认版本(通常是数字最大的已加载版本)。
| 策略类型 | 配置方式 | 适用场景 | 注意事项 |
|---|---|---|---|
| latest(默认) | 不配置或 latest { num_versions: N } |
快速迭代,只保留最新模型 | 回滚困难,需要重新上传旧版本文件 |
| specific | specific { versions: [1, 2] } |
A/B 测试、金丝雀发布 | 需要手动维护版本列表 |
| all | all { } |
调试、对比多个版本输出 | 内存占用高,加载时间长 |
specific 策略配合 CI/CD 流水线。每次发布新版本时,将新版本目录推送到模型仓库,并更新 config.pbtxt 中的 version_policy,实现原子切换。避免使用 all 策略,除非你的显存非常充裕。
4.3 config.pbtxt 核心字段详解:从 name 到 instance_group
核心原理:config.pbtxt 使用 Protocol Buffers 的文本格式(proto3 语法),它告诉 Triton 如何加载模型、如何处理输入输出、如何分配计算资源。一个完整的配置包含模型元信息、输入输出张量定义、调度与批处理参数、后端特定参数等。
最小可用配置示例(以 ONNX 模型为例):
name: "resnet50"
platform: "onnxruntime_onnx"
max_batch_size: 8
input [
{
name: "input"
data_type: TYPE_FP32
dims: [3, 224, 224]
}
]
output [
{
name: "output"
data_type: TYPE_FP32
dims: [1000]
}
]
instance_group [
{
count: 2
kind: KIND_GPU
gpus: [0]
}
]
字段含义拆解:
name:模型名称,必须与目录名一致。platform或backend:指定推理后端。旧版本使用platform(如onnxruntime_onnx),新版本推荐使用backend(如backend: "onnxruntime")。两者不能同时使用。max_batch_size:模型支持的最大批处理大小。如果设置为 0,表示模型不支持批处理(即每次只处理一个请求)。Triton 会根据该值自动启用动态批处理(Dynamic Batching)。input/output:定义张量的名称、数据类型和维度。维度中的-1表示可变维度(如文本序列长度)。对于支持批处理的模型,dims不包含 batch 维度,Triton 会自动在前面加上 batch 维度。instance_group:定义模型实例的数量和运行设备。每个实例相当于模型的一个副本,可以并行处理请求。通过count控制实例数,kind指定KIND_GPU或KIND_CPU,gpus指定 GPU 编号。
高级字段说明:
| 字段名 | 作用 | 典型值/示例 |
|---|---|---|
dynamic_batching |
启用动态批处理,将多个请求合并为一个批次 | dynamic_batching { max_queue_delay_microseconds: 100 } |
optimization |
配置 TensorRT 加速、CUDA 图优化等 | optimization { cuda_graphs: {} } |
model_warmup |
模型加载后执行预热请求,避免首次推理延迟 | model_warmup { name: "warmup" batch_size: 1 } |
parameters |
传递自定义参数给后端 | parameters { key: "precision" value: { string_value: "fp16" } } |
4.4 案例拆解:从零配置一个 BERT 模型并实现多版本回滚
背景与约束:假设我们有一个基于 PyTorch 的 BERT 情感分类模型,需要部署到 Triton 上。模型输入为 input_ids(int64 类型,形状 [1, 128]),输出为 logits(float32,形状 [2])。我们要求支持动态批处理,并且保留两个版本(v1 为初始版本,v2 为优化后的量化版本),默认使用 v2。
初始现象与目标:直接使用默认配置启动 Triton 时,模型加载失败,报错信息为 unexpected model file name: model.pth。这是因为 PyTorch 模型文件必须命名为 model.pt,而我们的文件名为 model.pth。
分析判断过程:通过查看 Triton 日志,发现它无法识别 model.pth 文件。解决方案有两种:一是重命名文件为 model.pt;二是在配置中指定 default_model_filename: "model.pth"。我们选择第二种,因为这样可以避免修改 CI/CD 流水线中的产物命名。
实际操作步骤:
- 创建目录结构:
bert_sentiment/1/和bert_sentiment/2/,分别放入 v1 和 v2 的模型文件。 - 编写
config.pbtxt:
name: "bert_sentiment"
backend: "pytorch"
default_model_filename: "model.pth"
max_batch_size: 32
input [
{
name: "input_ids"
data_type: TYPE_INT64
dims: [128]
}
]
output [
{
name: "logits"
data_type: TYPE_FP32
dims: [2]
}
]
dynamic_batching {
max_queue_delay_microseconds: 200
}
version_policy: {
specific: {
versions: [1, 2]
}
}
instance_group [
{
count: 1
kind: KIND_GPU
gpus: [0]
}
]
- 启动 Triton 并验证:使用
curl请求/v2/models/bert_sentiment,确认返回的版本列表包含 1 和 2,且默认版本为 2。
结果对比与复盘:配置完成后,模型成功加载,动态批处理生效(在并发请求下,吞吐量提升约 40%)。通过 version_policy 的 specific 配置,我们可以在不删除 v1 文件的情况下,随时通过请求参数 model_version=1 回滚到旧版本。复盘经验:版本目录中的模型文件命名必须与后端约定一致,否则必须显式声明 default_model_filename;同时,max_batch_size 的设置需要结合模型的实际显存占用,过大会导致 OOM。
4.5 常见误区与总结复盘
误区一:混淆 platform 与 backend。在 Triton 2.x 版本中,platform 已被废弃,统一使用 backend。如果同时指定两者,Triton 会报错。建议新项目直接使用 backend,并确保后端名称正确(如 pytorch、onnxruntime、tensorrt)。
误区二:忽略 max_batch_size 对输入维度的影响。当 max_batch_size > 0 时,Triton 会自动在输入张量的第一维前插入 batch 维度。因此,配置中的 dims 不应包含 batch 维度。例如,模型实际输入形状为 [8, 128](8 为 batch),配置中应写 dims: [128],而不是 [8, 128]。
误区三:版本目录权限问题。Triton 进程需要读取版本目录中的所有文件。如果使用非 root 用户运行容器,必须确保模型仓库目录对 Triton 用户具有读权限,否则加载失败且日志中不会出现明确提示。
总结复盘:模型仓库是 Triton 的“文件系统 API”,其设计核心是确定性与可管理性。通过规范目录结构、合理配置 version_policy 和 instance_group,我们可以实现模型的灰度发布、快速回滚和资源隔离。建议在项目初期就制定仓库命名规范,并将 config.pbtxt 纳入版本控制(如 Git),以便追踪配置变更历史。
name 必须与目录名一致,dims 不包含 batch 维度。