☰
Seldon Core V2 多命名空间部署实战:clusterwide 控制器、命名空间隔离运行时与 Kafka 主题前缀
2026/10/14 15:04:59 网站建设 项目流程
  • 人工智能
  • 大模型
  • 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 V2 的多命名空间(Multi-Namespace)部署示例,完整演示如何将控制平面集中安装在seldon-mesh命名空间、通过controller.clusterwide=true开启跨命名空间调谐,并配合kafka.topicPrefix/kafka.consumerGroupIdPrefix在共享 Kafka 集群中实现主题与消费组的租户级隔离。读完本文,你可以独立在 Kubernetes 上搭建“一套控制平面 + 多个业务命名空间运行时”的 Seldon V2 集群,并通过 REST/gRPC 对模型与 Pipeline 发起推理、验证 Kafka 隔离效果,最后按顺序完成拆除。

部署拓扑与前置条件

该示例的拓扑分为两层:

  • 控制平面(集中式):CRD、调度器(scheduler)、operator(controller)安装在seldon-mesh命名空间,负责监听并调谐集群中各业务命名空间的Model、Pipeline等 Seldon 自定义资源;
  • 数据面运行时(分布式):在每个业务命名空间(示例中的ns1、ns2)分别安装 runtime(envoy、dataflow、modelgateway、pipelinegateway、hodometer 等)与模型服务器(mlserver、triton)。

前置条件包括:

  • 一个可用的 Kubernetes 集群,且节点具备 LoadBalancer 能力(示例通过kubectl get svc ... -o jsonpath='{.status.loadBalancer.ingress[0].ip}'获取入口 IP);
  • 一个 Kafka 集群。若通过仓库中的 Ansible playbook setup-ecosystem.yaml 安装,Kafka 会部署在seldon-mesh命名空间(Pod 名为seldon-kafka-0),默认 bootstrap 地址即 Helm values 中的seldon-kafka-bootstrap.seldon-mesh:9092(见 values.yaml 中kafka.bootstrap);
  • 示例中的模型来自公共对象存储gs://seldon-models/...,需要能够访问该存储(runtime 侧默认通过 rclone 拉取,参考 runtime 模板中的 seldon-rclone-gs-public)。

说明:文中所有helm命令的 chart 路径(如k8s/helm-charts/...)均指仓库根目录下的相对路径。

安装 CRD 与控制平面

1. 安装 CRD

helm upgrade --install seldon-core-v2-crds k8s/helm-charts/seldon-core-v2-crds -n seldon-mesh

输出示例:

Release "seldon-core-v2-crds" does not exist. Installing it now. NAME: seldon-core-v2-crds LAST DEPLOYED: Tue Aug 15 11:01:03 2023 NAMESPACE: seldon-mesh STATUS: deployed REVISION: 1 TEST SUITE: None

2. 安装控制平面(seldon-v2 setup chart)

helm upgrade --install seldon-v2 k8s/helm-charts/seldon-core-v2-setup/ -n seldon-mesh \ --set controller.clusterwide=true \ --set kafka.topicPrefix=myorg \ --set kafka.consumerGroupIdPrefix=myorg

输出示例:

Release "seldon-v2" does not exist. Installing it now. NAME: seldon-v2 LAST DEPLOYED: Tue Aug 15 11:01:07 2023 NAMESPACE: seldon-mesh STATUS: deployed REVISION: 1 TEST SUITE: None

这条 setup 命令的三个--set参数是跨命名空间部署的核心,它们在 seldon-core-v2-setup/values.yaml 中均有对应默认值:

参数默认值作用
controller.clusterwidefalse开启后 operator 调谐所有命名空间的 Seldon CR,并创建集群级 RBAC(ClusterRole + ClusterRoleBinding)
kafka.topicPrefixseldon所有 Kafka 主题名的全局前缀,用于共享 Kafka 集群时的命名空间
kafka.consumerGroupIdPrefix空所有 Kafka consumer group 的前缀,用于隔离不同组织/环境的消费位点

文档中特别说明:即便本示例里 Kafka 是专门给 Seldon 安装的、并非真正“共享”,也演示了这套基于前缀的隔离配置——当 Kafka 集群与其他应用共用、需要强制命名约束时,这两项就是必需的。

clusterwide在 operator 侧的实际落地可以在源码中确认:operator/main.go 中定义了命令行参数

flag.StringVar(&watchNamespaces, "watch-namespaces", "", "Comma separated list of namespaces to watch. "+ "Only used when --clusterwide is set to true. "+ "Defaults to all namespaces if not set and --clusterwide is true.") flag.BoolVar(&clusterwide, "clusterwide", false, "Allow clusterwide operations")

而 getWatchNamespaceConfig 决定了缓存(调谐范围)行为:未开启clusterwide时只 watch 自身所在命名空间;开启且watchNamespaces为空时 watch 全部命名空间;开启并传入逗号分隔的命名空间列表时,仅 watch 这些命名空间(外加 operator 自身命名空间)。Helm 模板 _components-statefulsets.tpl 将controller.clusterwide注入为容器环境变量CLUSTERWIDE,并以--clusterwide=$(CLUSTERWIDE)、--watch-namespaces=$(WATCH_NAMESPACES)启动 manager。

RBAC 层面,同一模板在clusterwide(或watchNamespaces)为真时额外渲染集群级角色:seldon-v2-manager-role ClusterRole 覆盖 configmaps、secrets、serviceaccounts、services、deployments/statefulsets 以及mlops.seldon.io全部资源组,并生成 ClusterRoleBinding 把seldon-v2-controller-managerServiceAccount 绑定到集群角色上——这正是 operator 能够跨命名空间管理ns1/ns2的权限来源。

在业务命名空间部署 runtime 与模型服务器

1. 创建业务命名空间

kubectl create namespace ns1 kubectl create namespace ns2
namespace/ns1 created namespace/ns2 created

2. 在每个命名空间安装 runtime 与 servers

ns1与ns2的安装步骤完全一致:

# ns1 helm install seldon-v2-runtime k8s/helm-charts/seldon-core-v2-runtime -n ns1 --wait helm install seldon-v2-servers k8s/helm-charts/seldon-core-v2-servers -n ns1 --wait # ns2 helm install seldon-v2-runtime k8s/helm-charts/seldon-core-v2-runtime -n ns2 --wait helm install seldon-v2-servers k8s/helm-charts/seldon-core-v2-servers -n ns2 --wait

成功时每个 release 的输出形如:

NAME: seldon-v2-runtime LAST DEPLOYED: Tue Aug 15 11:01:11 2023 NAMESPACE: ns1 STATUS: deployed REVISION: 1 TEST SUITE: None

这两个 chart 分别负责什么?

  • seldon-core-v2-runtime 渲染一个SeldonRuntime自定义资源(名为seldon),声明该命名空间中的数据面组件:hodometer、seldon-scheduler、seldon-envoy、seldon-dataflow-engine、seldon-modelgateway、seldon-pipelinegateway,并通过config.kafkaConfig.topicPrefix等字段下发 Kafka 配置。其 values.yaml 中scheduler.serviceType与envoy.serviceType默认为LoadBalancer,这正是后续各命名空间拥有独立入口 IP 的原因;
  • seldon-core-v2-servers 声明该命名空间可用的模型服务器,默认包含mlserver与triton(各 1 副本),服务器镜像与能力项(serverCapabilities)来自 setup chart 的serverConfig段。

3. 等待服务器就绪

kubectl wait --for condition=ready --timeout=300s server --all -n ns1 kubectl wait --for condition=ready --timeout=300s server --all -n ns2
server.mlops.seldon.io/mlserver condition met server.mlops.seldon.io/triton condition met

4. 获取各命名空间的 mesh 入口 IP

每个命名空间的 envoy 暴露为独立的 LoadBalancer 服务seldon-mesh,需要分别获取 IP:

MESH_IP=!kubectl get svc seldon-mesh -n ns1 -o jsonpath='{.status.loadBalancer.ingress[0].ip}' MESH_IP_NS1=MESH_IP[0] import os os.environ['MESH_IP_NS1'] = MESH_IP_NS1 MESH_IP_NS1
'172.18.255.2'
MESH_IP=!kubectl get svc seldon-mesh -n ns2 -o jsonpath='{.status.loadBalancer.ingress[0].ip}' MESH_IP_NS2=MESH_IP[0] import os os.environ['MESH_IP_NS2'] = MESH_IP_NS2 MESH_IP_NS2
'172.18.255.4'

(具体 IP 值因集群而异,以实际输出为准。)

在不同命名空间运行模型

提示:Seldon CLI 允许你在非 Kubernetes 环境中查看底层 Seldon 资源信息并通过 scheduler 对其做变更;但在 Kubernetes 集群中它无法修改底层 manifest,因此在 Kubernetes 环境里不推荐用 CLI 做控制面操作,应直接kubectl操作 CR。详见 Seldon CLI。

1. 在 ns1 创建 iris 模型

模型清单为 samples/models/sklearn-iris-gs.yaml:

apiVersion: mlops.seldon.io/v1alpha1 kind: Model metadata: name: iris spec: storageUri: "gs://seldon-models/scv2/samples/mlserver_1.3.5/iris-sklearn" requirements: - sklearn memory: 100Ki

storageUri指向模型对象存储地址,requirements中的sklearn决定 operator 将其调度到具备该 capability 的 mlserver。

kubectl create -f samples/models/sklearn-iris-gs.yaml -n ns1
model.mlops.seldon.io/iris created
kubectl wait --for condition=ready --timeout=300s model --all -n ns1
model.mlops.seldon.io/iris condition met

2. 对 ns1 中的模型发起推理

REST(JSON)方式:

curl -k http://${MESH_IP_NS1}:80/v2/models/iris/infer \ -H "Host: seldon-mesh.inference.seldon" \ -H "Seldon-Model: iris" \ -H "Content-Type: application/json" \ -d '{ "inputs": [ { "name": "predict", "datatype": "FP32", "shape": [1,4], "data": [[1,2,3,4]] } ] }' | jq -M .

gRPC(JSON over gRPC)方式:

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

也可以用 seldon-cli 完成同样的调用:

# REST seldon model infer iris --inference-host ${MESH_IP_NS1}:80 \ '{"inputs": [{"name": "predict", "shape": [1, 4], "datatype": "FP32", "data": [[1, 2, 3, 4]]}]}' # gRPC seldon model infer iris --inference-mode grpc --inference-host ${MESH_IP_NS1}:80 \ '{"model_name":"iris","inputs":[{"name":"input","contents":{"fp32_contents":[1,2,3,4]},"datatype":"FP32","shape":[1,4]}]}' | jq -M .

REST 返回示例:

{ "model_name": "iris_1", "model_version": "1", "id": "3ca1757c-df02-4e57-87c1-38311bcc5943", "parameters": {}, "outputs": [ { "name": "predict", "shape": [1, 1], "datatype": "INT64", "parameters": { "content_type": "np" }, "data": [2] } ] }

gRPC 返回示例:

{ "modelName": "iris_1", "modelVersion": "1", "outputs": [ { "name": "predict", "datatype": "INT64", "shape": ["1", "1"], "parameters": { "content_type": { "stringParam": "np" } }, "contents": { "int64Contents": ["2"] } } ] }

注意请求头Host: seldon-mesh.inference.seldon:envoy 通过该虚拟主机名(vhost)路由到对应模型的 pipeline/model 入口,Seldon-Model头则进一步标识目标资源。

3. 在 ns2 重复相同流程

kubectl create -f samples/models/sklearn-iris-gs.yaml -n ns2
model.mlops.seldon.io/iris created
kubectl wait --for condition=ready --timeout=300s model --all -n ns2
model.mlops.seldon.io/iris condition met

之后把入口地址换成MESH_IP_NS2重复上一节的推理命令即可,例如:

curl -k http://${MESH_IP_NS2}:80/v2/models/iris/infer \ -H "Host: seldon-mesh.inference.seldon" \ -H "Seldon-Model: iris" \ -H "Content-Type: application/json" \ -d '{ "inputs": [ { "name": "predict", "datatype": "FP32", "shape": [1,4], "data": [[1,2,3,4]] } ] }' | jq -M .
{ "model_name": "iris_1", "model_version": "1", "id": "f706a23e-775f-4765-bd18-2e98d83bf7d5", "parameters": {}, "outputs": [ { "name": "predict", "shape": [1, 1], "datatype": "INT64", "parameters": { "content_type": "np" }, "data": [2] } ] }

gRPC 方式同理:

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

返回结构与 ns1 的 gRPC 响应一致(modelName: iris_1,输出INT64类别预测值2,仅id不同)。

4. 清理模型

kubectl delete -f samples/models/sklearn-iris-gs.yaml -n ns1 kubectl delete -f samples/models/sklearn-iris-gs.yaml -n ns2
model.mlops.seldon.io "iris" deleted model.mlops.seldon.io "iris" deleted

跨命名空间的 Pipeline

Pipeline 场景使用 TensorFlow 简单加法模型tfsimple1/tfsimple2(模型清单见 tfsimple1.yaml、tfsimple2.yaml),以及由两个步骤组成的 Pipeline tfsimples.yaml:

apiVersion: mlops.seldon.io/v1alpha1 kind: Pipeline metadata: name: tfsimples spec: steps: - name: tfsimple1 - name: tfsimple2 inputs: - tfsimple1 tensorMap: tfsimple1.outputs.OUTPUT0: INPUT0 tfsimple1.outputs.OUTPUT1: INPUT1 output: steps: - tfsimple2

即在ns1、ns2中各部署两个模型与同一条 pipeline:tfsimple2的输入来自tfsimple1的输出(通过tensorMap映射OUTPUT0/OUTPUT1→INPUT0/INPUT1),pipeline 的输出取自tfsimple2。

1. 创建模型并等待就绪

kubectl create -f samples/models/tfsimple1.yaml -n ns1 kubectl create -f samples/models/tfsimple2.yaml -n ns1 kubectl create -f samples/models/tfsimple1.yaml -n ns2 kubectl create -f samples/models/tfsimple2.yaml -n ns2
model.mlops.seldon.io/tfsimple1 created model.mlops.seldon.io/tfsimple2 created model.mlops.seldon.io/tfsimple1 created model.mlops.seldon.io/tfsimple2 created
kubectl wait --for condition=ready --timeout=300s model --all -n ns1 kubectl wait --for condition=ready --timeout=300s model --all -n ns2
model.mlops.seldon.io/tfsimple1 condition met model.mlops.seldon.io/tfsimple2 condition met model.mlops.seldon.io/tfsimple1 condition met model.mlops.seldon.io/tfsimple2 condition met

2. 创建 pipeline 并等待就绪

kubectl create -f samples/pipelines/tfsimples.yaml -n ns1 kubectl create -f samples/pipelines/tfsimples.yaml -n ns2
pipeline.mlops.seldon.io/tfsimples created pipeline.mlops.seldon.io/tfsimples created
kubectl wait --for condition=ready --timeout=300s pipeline --all -n ns1 kubectl wait --for condition=ready --timeout=300s pipeline --all -n ns2
pipeline.mlops.seldon.io/tfsimples condition met pipeline.mlops.seldon.io/tfsimples condition met

3. 对两个命名空间的 pipeline 发起推理

注意 pipeline 请求的Seldon-Model头值为tfsimples.pipeline(带.pipeline后缀),与 model 资源的iris不同;输入张量名为INPUT0/INPUT1,数据类型INT32,shape[1,16]。

ns1:

curl -k http://${MESH_IP_NS1}:80/v2/models/tfsimples/infer \ -H "Host: seldon-mesh.inference.seldon" \ -H "Seldon-Model: tfsimples.pipeline" \ -H "Content-Type: application/json" \ -d '{ "model_name": "simple", "inputs": [ { "name": "INPUT0", "datatype": "INT32", "shape": [1,16], "data": [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16] }, { "name": "INPUT1", "datatype": "INT32", "shape": [1,16], "data": [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16] } ] }' | jq -M .

或 gRPC + CLI:

seldon pipeline infer tfsimples --inference-mode grpc --inference-host ${MESH_IP_NS1}:80 \ '{"model_name":"simple","inputs":[{"name":"INPUT0","contents":{"int_contents":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]},"datatype":"INT32","shape":[1,16]},{"name":"INPUT1","contents":{"int_contents":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]},"datatype":"INT32","shape":[1,16]}]}' | jq -M .

ns2:将上面命令中的${MESH_IP_NS1}替换为${MESH_IP_NS2}后执行,命令体与 ns1 完全一致。

两个命名空间均返回相同的计算结果(每路输出为输入逐元素翻倍后的 1~16 与 1~16 之和,即 2~32 的偶数序列):

{ "outputs": [ { "name": "OUTPUT0", "datatype": "INT32", "shape": ["1", "16"], "contents": { "intContents": [2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32] } }, { "name": "OUTPUT1", "datatype": "INT32", "shape": ["1", "16"], "contents": { "intContents": [2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32] } } ] }

4. 删除 pipeline 与模型

kubectl delete -f samples/pipelines/tfsimples.yaml -n ns1 kubectl delete -f samples/pipelines/tfsimples.yaml -n ns2
pipeline.mlops.seldon.io "tfsimples" deleted pipeline.mlops.seldon.io "tfsimples" deleted
kubectl delete -f samples/models/tfsimple1.yaml -n ns1 kubectl delete -f samples/models/tfsimple2.yaml -n ns1 kubectl delete -f samples/models/tfsimple1.yaml -n ns2 kubectl delete -f samples/models/tfsimple2.yaml -n ns2
model.mlops.seldon.io "tfsimple1" deleted model.mlops.seldon.io "tfsimple2" deleted model.mlops.seldon.io "tfsimple1" deleted model.mlops.seldon.io "tfsimple2" deleted

验证 Kafka 隔离:consumer group 与 topic 命名

如果 Kafka 是通过 Ansible playbooksetup-ecosystem安装的,可以直接在 Kafka Pod 内查看 consumer group 与 topic 列表,验证安装时设置的myorg前缀已经生效:

kubectl exec seldon-kafka-0 -n seldon-mesh -- bin/kafka-consumer-groups.sh --list --bootstrap-server localhost:9092
myorg-ns2-seldon-pipelinegateway-dfd61b49-4bb9-4684-adce-0b7cc215d3af myorg-ns2-seldon-modelgateway-17 myorg-ns1-seldon-pipelinegateway-d4fc83e6-29cb-442e-90cd-92a389961cfe myorg-ns2-seldon-modelgateway-60 myorg-ns2-seldon-dataflow-73d465744b7b1b5be20e88d6245e50bd myorg-ns1-seldon-modelgateway-60 myorg-ns1-seldon-modelgateway-17 myorg-ns1-seldon-dataflow-f563e04e093caa20c03e6eced084331b
kubectl exec seldon-kafka-0 -n seldon-mesh -- bin/kafka-topics.sh --bootstrap-server=localhost:9092 --list
__consumer_offsets myorg.ns1.errors.errors myorg.ns1.model.iris.inputs myorg.ns1.model.iris.outputs myorg.ns1.model.tfsimple1.inputs myorg.ns1.model.tfsimple1.outputs myorg.ns1.model.tfsimple2.inputs myorg.ns1.model.tfsimple2.outputs myorg.ns1.pipeline.tfsimples.inputs myorg.ns1.pipeline.tfsimples.outputs myorg.ns2.errors.errors myorg.ns2.model.iris.inputs myorg.ns2.model.iris.outputs myorg.ns2.model.tfsimple1.inputs myorg.ns2.model.tfsimple1.outputs myorg.ns2.model.tfsimple2.inputs myorg.ns2.model.tfsimple2.outputs myorg.ns2.pipeline.tfsimples.inputs myorg.ns2.pipeline.tfsimples.outputs

从输出可以看出隔离的两个维度:

  • topic 命名:<topicPrefix>.<namespace>.<model|pipeline>.<资源名>.<inputs|outputs>,例如myorg.ns1.model.iris.inputs;错误 topic 为<topicPrefix>.<namespace>.errors.errors;
  • consumer group 命名:<consumerGroupIdPrefix>-<namespace>-<组件名>-<实例后缀>,例如myorg-ns1-seldon-modelgateway-17。

源码层面可以直接印证这套命名规则。scheduler/pkg/kafka/topics.go 中定义了分隔符与常量:

const ( defaultSeldonTopicPrefix = "seldon" modelTopic = "model" pipelineTopic = "pipeline" errorsTopic = "errors" inputsSuffix = "inputs" outputsSuffix = "outputs" errorsSuffix = "errors" TopicSeparator = "." )

TopicNamer 按topicPrefix + namespace组合生成主题名,并提供正则校验:前缀只允许a-z、A-Z、0-9、.、_、-,不合法会直接报错;GetModelTopicInputs/GetPipelineTopicOutputs等方法即对应上面看到的...inputs/...outputs主题。前缀为空时回退到默认值seldon——这解释了 setup chart 中kafka.topicPrefix默认值为seldon的原因。consumer group 前缀的拼接逻辑在 scheduler/pkg/util/hash.go 的GetKafkaConsumerName中:当consumerGroupIdPrefix非空时,将其以-分隔写入消费者名称,最终形成myorg-ns1-seldon-modelgateway-...这样的组名。Helm 模板侧,kafka.topicPrefix/kafka.consumerGroupIdPrefix分别写入 SeldonRuntime 的kafkaConfig.topicPrefix/consumerGroupIdPrefix以及 dataflow 引擎的环境变量SELDON_KAFKA_CONSUMER_PREFIX(见 _components-statefulsets.tpl),从而保证同一控制平面下不同命名空间、不同组织的前缀不会互相冲突。

拆除(TearDown)

按“servers → runtime → 控制平面 → CRD → 命名空间”的顺序删除:

helm delete seldon-v2-servers -n ns1 --wait helm delete seldon-v2-servers -n ns2 --wait
release "seldon-v2-servers" uninstalled release "seldon-v2-servers" uninstalled
helm delete seldon-v2-runtime -n ns1 --wait helm delete seldon-v2-runtime -n ns2 --wait
release "seldon-v2-runtime" uninstalled release "seldon-v2-runtime" uninstalled
helm delete seldon-v2 -n seldon-mesh --wait
release "seldon-v2" uninstalled
helm delete seldon-core-v2-crds -n seldon-mesh
release "seldon-core-v2-crds" uninstalled

最后删除业务命名空间:

kubectl delete namespace ns1 kubectl delete namespace ns2
namespace "ns1" deleted namespace "ns2" deleted

小结

本文演示的 Seldon Core V2 多命名空间部署可归纳为三点:其一,seldon-core-v2-setup安装在seldon-mesh并设置controller.clusterwide=true后,operator 通过集群级 RBAC 与--clusterwide参数获得跨命名空间调谐能力,业务命名空间只需安装 runtime 与 servers 两个 chart 即可接入统一控制平面;其二,各命名空间的 envoy 独立暴露 LoadBalancer,请求经Host: seldon-mesh.inference.seldon与Seldon-Model头路由到指定 Model/Pipeline,支持 REST 与 gRPC 两种推理协议;其三,kafka.topicPrefix与kafka.consumerGroupIdPrefix通过 scheduler 的TopicNamer与消费者命名逻辑落到每个 topic / consumer group 上,实现myorg.ns1.*、myorg-ns1-*形式的租户隔离,适合多个团队共享同一 Kafka 集群的场景。

  • 人工智能
  • 大模型
  • 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
点击查看免费下载

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

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

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

立即咨询