☰
Seldon Core 2 多版本模型工件部署实战:用 artifactVersion 精确加载 Triton 模型版本
2026/10/14 1:52:32 网站建设 项目流程
  • 人工智能
  • 大模型
  • MLOps
  • LLMOps
  • 模型推理服务
  • 云原生
  • 后端

【免费下载链接】seldon-core

An MLOps framework to package, deploy, monitor and manage thousands of production machine learning models

项目地址:https://gitcode.com/gh_mirrors/se/seldon-core
点击查看免费下载

导读

本文基于 Seldon Core 2(当前仓库mlops.seldon.io/v1alpha1API)的官方示例,讲解如何利用 Model 自定义资源的artifactVersion字段,从同一storageUri指向的 Triton 模型仓库中按数字版本目录精确加载并切换模型。完成阅读后,你将掌握多版本工件仓库的标准目录结构、kubectl与 Seldon CLI 两种推理验证方式,以及版本目录被选中的底层实现原理。示例全文可参考仓库中的 samples/k8s-multi-version.md。

1. 多版本工件:背景与核心思路

在 MLOps 实践中,模型工件(artifact)往往会随训练迭代产生多个版本。Triton Inference Server 约定:一个模型仓库(model repository)下,用纯数字命名的子目录表示模型的不同版本(如1/、2/、3/),每个版本目录内放置该版本对应的模型文件与配置。

Seldon Core 2 在此基础上提供了更灵活的加载策略:通过 Model 资源中的artifactVersion字段,指定要加载的版本目录;若不指定,则由 Agent 自动选择仓库内的最高版本。本示例用一个 "math" 模型演示这一能力:

  • 版本 1:对输入执行加 10(1/model.py);
  • 版本 2:对输入执行乘 10(2/model.py)。

工件仓库结构如下:

config.pbtxt 1/model.py <add 10> 2/model.py <mul 10>

该结构的完整定义见本文第 6 节,其背后的目录查找逻辑由 Triton 仓库处理器(TritonRepositoryHandler)实现(scheduler/pkg/agent/repository/triton/triton.go)。

2. 环境准备与入口地址获取

本示例来自 samples/k8s-multi-version.md,请在samples目录下执行相关命令。

2.1 前置条件

  • 已安装 Seldon Core 2,并部署在命名空间seldon-mesh中;
  • 已下载仓库的samples目录(其中包含示例 Model 配置与配套 notebook);
  • 集群入口可通过LoadBalancer类型的seldon-meshService 访问。

2.2 设置命名空间环境变量

import os os.environ["NAMESPACE"] = "seldon-mesh"

2.3 获取 Mesh 入口 IP

MESH_IP=!kubectl get svc seldon-mesh -n ${NAMESPACE} -o jsonpath='{.status.loadBalancer.ingress[0].ip}' MESH_IP=MESH_IP[0] import os os.environ['MESH_IP'] = MESH_IP MESH_IP

典型输出:

'172.19.255.1'

MESH_IP将用于后续seldon model infer --inference-host ${MESH_IP}:80推理命令。若你的集群使用 Istio 等服务网格作为入口,也可按 docs-gb/getting-started/cli.md 中的方式获取入口 IP 并替换。

3. Model 资源与 artifactVersion 字段

3.1 两个版本的定义

版本 1 的 Model 定义(仓库中的 samples/models/multi-version-1.yaml):

apiVersion: mlops.seldon.io/v1alpha1 kind: Model metadata: name: math spec: storageUri: "gs://seldon-models/scv2/samples/triton_23-03/multi-version" artifactVersion: 1 requirements: - triton - python

版本 2 的 Model 定义(仓库中的 samples/models/multi-version-2.yaml):

apiVersion: mlops.seldon.io/v1alpha1 kind: Model metadata: name: math spec: storageUri: "gs://seldon-models/scv2/samples/triton_23-03/multi-version" artifactVersion: 2 requirements: - triton - python

关键点:两份配置的metadata.name都是math,storageUri也完全相同,唯一差异是artifactVersion(1与2)。这意味着可以原地更新同一个 Model 资源来切换版本,而不是为每个版本创建不同的模型。

3.2 字段语义与 CRD 定义

从源码看,artifactVersion属于InferenceArtifactSpec(operator/apis/mlops/v1alpha1/model_types.go):

type InferenceArtifactSpec struct { // Model type ModelType *string `json:"modelType,omitempty"` // Storage URI for the model repository StorageURI string `json:"storageUri"` // Artifact Version // A v2 version folder to select from storage bucket // +optional ArtifactVersion *uint32 `json:"artifactVersion,omitempty"` // Schema URI SchemaURI *string `json:"schemaUri,omitempty"` // Secret name SecretName *string `json:"secretName,omitempty"` }

其字段含义为:

字段类型说明
storageUristring(必填)模型仓库的存储 URI(如 GCS、S3 等),指向包含数字版本目录的仓库根
artifactVersionuint32(可选)要加载的版本目录号,即从存储桶中选择的 v2 版本文件夹
requirementsstring[]加载该模型所需的能力(本例为triton与python),用于调度到兼容的 Server
secretNamestring(可选)访问存储所需的 Secret

对应 CRD(operator/config/crd/bases/mlops.seldon.io_models.yaml)中同样标注:"A v2 version folder to select from storage bucket",类型为integer(int32)。

当 Model 被创建/更新后,Operator 会通过AsSchedulerModel()方法(operator/apis/mlops/v1alpha1/model_types.go)把资源转换为 Scheduler 的 proto 消息,其中ArtifactVersion: m.Spec.ArtifactVersion会被原样传递,驱动后续的版本目录解析。

4. 部署版本 1 并验证推理

4.1 创建 Model 并等待就绪

kubectl apply -f ./models/multi-version-1.yaml -n ${NAMESPACE}

输出:

model.mlops.seldon.io/math created

等待模型变为 ready 状态:

kubectl wait --for condition=ready --timeout=300s model --all -n ${NAMESPACE}

输出:

model.mlops.seldon.io/math condition met

4.2 通过 Seldon CLI 发起推理

使用 gRPC 推理模式,向 Mesh 入口发送一条形状为[4]的 FP32 输入[1,2,3,4]:

seldon model infer math --inference-mode grpc --inference-host ${MESH_IP}:80 \ '{"model_name":"math","inputs":[{"name":"INPUT","contents":{"fp32_contents":[1,2,3,4]},"datatype":"FP32","shape":[4]}]}' | jq -M .

返回结果(版本 1 执行"加 10"):

{ "modelName": "math_1", "modelVersion": "1", "outputs": [ { "name": "OUTPUT", "datatype": "FP32", "shape": [ "4" ], "contents": { "fp32Contents": [ 11, 12, 13, 14 ] } } ] }

注意响应中的modelName为math_1、modelVersion为1,说明当前路由到的是版本 1;输入[1,2,3,4]经加 10 后得到[11,12,13,14]。

4.3 通过 curl 发起推理(HTTP 方式)

若使用 REST 推理接口,等价请求为(入口 IP 需替换为你的实际值,并携带虚拟主机头):

curl -k http://<INGRESS_IP>:80/v2/models/math/infer \ -H "Host: seldon-mesh.inference.seldon" \ -H "Content-Type: application/json" \ -H "Seldon-Model: math" \ -d '{ "model_name": "math", "inputs": [ { "name": "INPUT", "datatype": "FP32", "shape": [4], "data": [1, 2, 3, 4] } ] }' | jq

返回:

{ "model_name": "math_1", "model_version": "1", "outputs": [ { "name": "OUTPUT", "datatype": "FP32", "shape": [4], "data": [11.0, 12.0, 13.0, 14.0] } ] }

seldon model infer支持的常用选项可查阅 docs-gb/cli/seldon_model_infer.md,包括--inference-mode(rest/grpc,默认 rest)、--inference-host(默认0.0.0.0:9000)、--iterations、--seconds、--show-request/--show-response等。

5. 原地切换:部署版本 2

5.1 更新 Model 并等待就绪

kubectl apply -f ./models/multi-version-2.yaml -n ${NAMESPACE}

输出:

model.mlops.seldon.io/math configured

注意此处输出为configured而非created,印证了"同一 Model 资源原地更新"的语义。

kubectl wait --for condition=ready --timeout=300s model --all -n ${NAMESPACE}
model.mlops.seldon.io/math condition met

5.2 验证版本 2 的推理结果

同样使用 Seldon CLI:

seldon model infer math --inference-mode grpc --inference-host ${MESH_IP}:80 \ '{"model_name":"math","inputs":[{"name":"INPUT","contents":{"fp32_contents":[1,2,3,4]},"datatype":"FP32","shape":[4]}]}' | jq -M .

返回结果(版本 2 执行"乘 10"):

{ "modelName": "math_2", "modelVersion": "1", "outputs": [ { "name": "OUTPUT", "datatype": "FP32", "shape": [ "4" ], "contents": { "fp32Contents": [ 10, 20, 30, 40 ] } } ] }

REST 等价请求:

curl -k http://<INGRESS_IP>:80/v2/models/math/infer \ -H "Host: seldon-mesh.inference.seldon" \ -H "Content-Type: application/json" \ -H "Seldon-Model: math" \ -d '{ "model_name": "math", "inputs": [ { "name": "INPUT", "datatype": "FP32", "shape": [4], "data": [1, 2, 3, 4] } ] }' | jq
{ "model_name": "math_2", "model_version": "1", "outputs": [ { "name": "OUTPUT", "datatype": "FP32", "shape": [4], "data": [10.0, 20.0, 30.0, 40.0] } ] }

输入仍为[1,2,3,4],输出变为[10,20,30,40],且modelName变为math_2——模型行为随artifactVersion的切换而改变,验证了版本选择生效。

6. 源码原理:版本目录是如何被选中的

6.1 下载与版本解析的整体流程

当 Scheduler 下发加载指令后,Agent 的V2ModelRepository.DownloadModelVersion()(scheduler/pkg/agent/repository/model_repository.go)负责执行:

  1. 通过 rclone 将modelSpec.Uri指向的仓库同步到本地(带指数退避重试);
  2. 调用FindModelVersionFolder(modelName, artifactVersion, rclonePath)解析要加载的版本目录;
  3. 将该目录复制到推理服务器模型仓库中的<modelName>/<version>路径;
  4. 调用UpdateModelRepository()等回调,更新config.pbtxt中的模型名与仓库配置。

其中"选择哪个版本目录"完全由第二步决定。

6.2 指定版本与"最高版本"两种模式

Triton 仓库处理器的入口(scheduler/pkg/agent/repository/triton/triton.go):

func (t *TritonRepositoryHandler) FindModelVersionFolder(_ string, version *uint32, path string) (string, bool, error) { if version != nil { return t.findModelVersionInPath(path, *version) } else { return t.findHighestVersionInPath(path) } }

可见存在两条路径:

  • 显式指定版本:findModelVersionInPath()在仓库根目录下查找与版本号完全同名的第一层子目录;找不到时报错Failed to find requested version %d in path %s,找到多个同名目录时报Found multiple folders with version %d;
  • 未指定版本(artifactVersion为空):findHighestVersionInPath()遍历所有纯数字命名的第一层子目录,取数值最大者;若仓库根目录下没有任何数字版本目录(如工件直接放在顶层),则回退为把仓库根目录本身当作模型路径(found=false)。

测试用例(scheduler/pkg/agent/repository/triton/triton_test.go)覆盖了Simple(精确命中)、MidVersion(中间版本)、HighestVersion(默认取最高)、NoVersionFolders(无版本目录时回退根目录)以及NoVersionFoldersArtifactVersion(指定了版本但仓库中没有对应目录时报错)等场景。

6.3 推理响应中的 math_1 / math_2 命名从何而来

响应中modelName为math_1、math_2,来源于模型命名工具函数(scheduler/pkg/util/model.go):

func GetVersionedModelName(model string, version uint32) string { return fmt.Sprintf("%s%s%d", model, separator, version) }

即以_拼接模型名与加载版本号,形成带版本的内部模型名;反过来GetOrignalModelNameAndVersion()可以从math_2还原出原始模型名math与版本2。因此math这个 Model 资源实际承载了math_1与math_2两个可寻址版本。

7. 清理示例资源

删除版本 1 的 Model 定义即可移除该模型:

kubectl delete -f ./models/multi-version-1.yaml -n ${NAMESPACE}

输出:

model.mlops.seldon.io "math" deleted

由于multi-version-1.yaml与multi-version-2.yaml操作的是同一个名为math的资源,删除任一都会将该 Model 从集群中移除。若你希望保留部署,可跳过此步。

8. 注意事项与最佳实践

  • Kubernetes 环境下请用 kubectl 管理资源:Seldon CLI 允许在非 Kubernetes 环境中查看底层 Seldon 资源并通过 Scheduler 执行变更,但它无法修改 Kubernetes 集群内的底层 manifest。因此在 Kubernetes 环境中进行控制面操作时,建议直接使用kubectl apply/delete(详见 docs-gb/getting-started/cli.md)。
  • 版本号约定:目录必须是纯数字命名(Triton 约定版本号大于 0);若显式指定了artifactVersion但仓库中不存在该目录,Agent 会返回错误,模型无法就绪。
  • 未指定版本时的行为:artifactVersion为空时默认加载最高版本,这与 Triton 的版本策略一致,适合"始终使用最新"的场景;显式指定则适合灰度、回滚等需要精确控制版本的场景。
  • 原地切换的成本:更新同一 Model 的artifactVersion会触发重新加载流程,Agent 会为新版本重建<modelName>/<version>目录并更新config.pbtxt,期间请通过kubectl wait --for condition=ready等待就绪后再发起推理。
  • 可继续深入的材料:仓库中的 notebook docs-gb/examples/notebooks/k8s-multi-version.ipynb 提供了可逐步执行的交互式版本;Model 资源的完整 spec 定义见 operator/apis/mlops/v1alpha1/model_types.go 与 CRD operator/config/crd/bases/mlops.seldon.io_models.yaml。
  • 人工智能
  • 大模型
  • MLOps
  • LLMOps
  • 模型推理服务
  • 云原生
  • 后端

【免费下载链接】seldon-core

An MLOps framework to package, deploy, monitor and manage thousands of production machine learning models

项目地址:https://gitcode.com/gh_mirrors/se/seldon-core
点击查看免费下载

相关推荐

上一篇:Awesome Codex Skills中的抽奖获奖者选择器:公平随机选择的审计友好工具
下一篇:5分钟上手OpenCodex:macOS/Linux/Windows安装、启动与配置保姆级教程

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询