4. 模型仓库结构

Triton Inference Server 之所以能够同时服务多个模型、多个版本,并支持动态批处理、模型集成等高级特性,其根基在于一套严格且灵活的模型仓库(Model Repository)规范。模型仓库是 Triton 启动时加载模型、运行时管理模型版本、执行推理请求的“文件系统契约”。如果仓库结构不符合规范,Triton 会直接拒绝加载模型;如果 config.pbtxt 配置有误,模型可能无法启动或推理结果错误。本章将深入拆解模型仓库的目录组织、版本管理机制,以及 config.pbtxt 的核心字段与语法,帮助你从“能跑通”进阶到“设计合理、可运维”的部署架构。

Triton 模型仓库结构与配置加载链路 模型仓库根目录 (model_repo/) 模型A (sentiment_model/) 模型B (resnet50/) 模型C (ensemble/) config.pbtxt 1/ (版本) 2/ (版本) model.pt config.pbtxt 1/ (版本) model.onnx config.pbtxt 1/ (版本) 子模型引用 config.pbtxt 核心字段映射 name / platform / backend 模型标识与运行后端 max_batch_size 动态批处理上限 input / output 张量名称、形状、类型 instance_group GPU/CPU 实例分配 版本目录命名必须为纯数字,Triton 按数字大小决定默认版本(数值最大为默认)

上图展示了模型仓库的整体组织逻辑:根目录下每个子目录代表一个模型,每个模型目录内必须包含 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 会认为该模型没有可用版本,加载失败。
风险提示:切勿在版本目录中存放无关文件(如 README、临时文件)。Triton 会尝试加载版本目录下的所有文件,如果遇到无法识别的文件格式,可能导致模型加载失败或推理异常。建议每个版本目录只存放模型文件及其必要的附属文件(如 tokenizer 词典)。

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 { } 调试、对比多个版本输出 内存占用高,加载时间长
实战建议:在 Kubernetes 环境中,推荐使用 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 流水线中的产物命名。

实际操作步骤:

  1. 创建目录结构:bert_sentiment/1/ 和 bert_sentiment/2/,分别放入 v1 和 v2 的模型文件。
  2. 编写 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]
  }
]
  1. 启动 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),以便追踪配置变更历史。

核心记忆点:模型仓库 = 目录结构(模型名/版本号/模型文件) + 配置契约(config.pbtxt)。版本号永远是无符号整数,默认版本是数值最大的已加载版本。配置中的 name 必须与目录名一致,dims 不包含 batch 维度。