Seldon 内置(Prepack)支持了一些推理服务类型,包括 Tensorflow serving(简称 TFServing),Triton inference server, MLflow, sklearn, xgboost。本文重点介绍 Tensorflow serving,由点带面地理解内置(Prepack)推理服务如何使用以及实现细节。
1 TFServing 服务简介
TFServing 是 Tensorflow 官方提供的模型在线推理服务,开箱即用,具备高性能,低延迟,灵活易扩展,可同时支持多模型多版本,支持 GRPC 和 HTTP。
TFServing 服务架构如下图所示,这里摘抄一段官方的处理流程介绍,一个典型的模型加载及调用过程
当 Source 模块检测到新的模型权重(weights),它会创建一个 Loader(指向该模型数据)
Source 模块通知 DynamicManager,告诉它这有一个可用的模型版本(Aspired version)
DynamicManager 根据 VersionPolicy 配置,判断是否加载这个新版本
DynamicManager 调用 Loader 加载并初始化这个新的模型版本
客户端(client)向 DynamicManager 请求该模型的 handle,DynamicManager 返回该模型的最新 handle
如需更详细的资料,可查看https://github.com/tensorflow/serving/blob/master/tensorflow_serving/g3doc/architecture.md
2 如何创建和部署 TFServing
定义 SeldonDeployment 文件 tfserving-sdep.yaml,如下:
apiVersion: machinelearning.seldon.io/v1alpha2
kind: SeldonDeployment
metadata:
name: tfserving
spec:
name: mnist
predictors:
- graph:
children: []
implementation: TENSORFLOW_SERVER
modelUri: gs://seldon-models/tfserving/mnist-model
name: mnist-model
parameters:
- name: signature_name
type: STRING
value: predict_images
- name: model_name
type: STRING
value: mnist-model
name: default
replicas: 1
复制代码
说明:
部署该 sdep,执行如下命令
kubectl create -f tfserving-sdep.yaml
复制代码
创建完成后,可通过如下命令,查看到 pod 及 service 被创建
# kubectl get sdep
NAME AGE
tfserving 1m
# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
tfserving-default-mnist-model ClusterIP 10.254.1.2 <none> 9000/TCP 1m
# kubectl get pod
NAME READY STATUS RESTARTS AGE
tfserving-default-0-mnist-model-2e8e-y1ae7 3/3 Running 0 1m
复制代码
2.1 如何调用推理服务
从上面的 service 和 pod 信息,我们可以使用 service 的 cluster IP 或者 pod 的 IP,调用这个 mnist 推理服务。
下面我们通过 seldon 的 API 接口,调用推理服务。我们可以构造如下请求:
import json
import random
import sys
import requests
url = "http://%s:9000/api/v1.0/predictions" % sys.argv[1]
data = [
[random.random() for i in range(784)]
]
# seldon protocol
payload = {"data":{"ndarray": data}}
resp = requests.post(url, json=payload)
print(resp.content)
复制代码
返回结果如下:
{
"data":{
"names":["t:0","t:1","t:2","t:3","t:4","t:5","t:6","t:7","t:8","t:9"],
"ndarray":[[2.31042499e-20,4.70782478e-30,0.017343564,0.903749764,1.29772914e-33,0.0789065957,9.34007e-14,4.42152e-19,1.50050141e-07,9.49472516e-17]]
},
"meta":{}
}
复制代码
3 Seldon 如何实现内置推理服务
seldon 的 manager 服务,即 seldon-controller-manager,这是一个 k8s 的 Operator 服务,负责管理所有 SeldonDeployment(简称 sdep)资源的生命周期,包括创建,修改及删除。每当新的 sdep 资源被创建时,manager 服务会为其创建相关的 deployment 及 service。
从上面的 pod 信息,我们可以看到,当 sdep 资源创建后,manager 服务为其创建了一个 pod。这个 pod 包含了四个 container,具体如下内容:
containers:
- name: mnist-model
env:
- name: PREDICTIVE_UNIT_PARAMETERS
value: '[{"name":"signature_name","value":"predict_images","type":"STRING"},{"name":"model_name","value":"mnist-model","type":"STRING"},{"name":"rest_endpoint","value":"http://0.0.0.0:2001","type":"STRING"},{"name":"model_name","value":"mnist-model","type":"STRING"}]'
image: seldonio/tfserving-proxy_rest:1.2.1
- name: tfserving
args:
- /usr/bin/tensorflow_model_server
- --port=2000
- --rest_api_port=2001
- --model_name=mnist-model
- --model_base_path=/mnt/models
image: tensorflow-serving:2.1.0
volumeMounts:
- mountPath: /mnt/models
name: tfserving-provision-location
- name: seldon-container-engine
args:
- --sdep
- tfserving
- --namespace
- kubeflow
- --predictor
- default
- --port
- "8000"
- --protocol
- seldon
- --prometheus_path
- /prometheus
image: seldon/seldon-core-executor:1.2.1
initContainers:
- args:
- gs://seldon-models/tfserving/mnist-model
- /mnt/models
image: gcr.io/kfserving/storage-initializer:0.2.2
volumeMounts:
- mountPath: /mnt/models
name: tfserving-provision-location
复制代码
这四个 container 的作用和关系,分别如下:
init 容器:根据 sdep 的参数 modelUri,从 gs 获取模型文件,并保存到/mnt/models
tfserving 容器:从/mnt/models 加载模型,并通过 2000/2001 端口提供服务
mnist-model 容器:从其使用的 image 可知,这是一个 proxy 服务,主要实现协议转换,即 seldon 协议请求,转换成 tensorflow 协议请求
engine 容器:主要用于支持 sdep 资源的 graph 功能,即可以将多个模型服务分别进行打分及组合。将在后续文章继续展开介绍
最后,再从代码的角度看看,manager 服务处理 sdep 的主要过程。如下所示,如同其他的 operator 一样,manager 实现了 Reconcile()接口方法:
func (r *SeldonDeploymentReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
// 获取sdep资源实例的信息
instance := &machinelearningv1.SeldonDeployment{}
err := r.Get(ctx, req.NamespacedName, instance)
// 设置默认值
instance.Default()
// 创建所需组件:主要是container
components, err := r.createComponents(ctx, instance, podSecurityContext, log)
// 创建服务网络:主要是service
servicesReady, err := r.createServices(components, instance, false, log)
// 创建deployment:即将container分配到deployment,并创建
deploymentsReady, err := r.createDeployments(components, instance, log)
if deploymentsReady {
// 完成最后的创建
err := r.completeServiceCreation(instance, components, log)
}
}
复制代码
代码逻辑比较直观,就是分别创建相关的 k8s 资源。这里主要说一下 Default()和 createComponents()。在 sdep 的声明中,我们只需要配置 tensorflow 模型的名称和文件路径,但 manager 服务创建了上述四个容器,分别实现了不同的功能:
Default()方法,检查 sdep 的 componentSpecs(该示例未使用)是否有定义 mnist-model 容器,如果没有则创建一个,使用 tfserving-proxy 镜像。换句话讲,我们可以在 componentSpec 中自定义该 proxy 容器,而不使用默认的
createComponents()方法,调用 addTFServerContainer()分别创建 tfserving 容器和 init 容器(同样,我们可以在 componentSpecs 中自定义)。其中,init 容器会使用 sdep 的 modelUri 参数,用以下载模型文件。最后,调用 addEngineToDeployment()创建 engine 容器。(关于 engine 容器,将在后续文章介绍)
tfserving 容器监听端口 2001,proxy 容器实现协议转换将请求发送到该端口。上文的调用示例,访问的是 proxy 服务的端口,使用的是 seldon 协议。同样,可以直接访问 tfserving 端口,使用 tensorflow 协议。如下:
curl -s -X POST http://$endpoint:2001/v1/models/mnist-model:predict \
-H "Content-Type: application/json" \
-d '{"instances": [[0.7663934193992182, 0.0880671623857957, 0.0775122634595451,..., 0.4126315414418871, 0.42650235187504826]], "signature_name": "predict_images"}'
复制代码
返回结果如下:
{
"predictions": [[1.04614508e-17, 3.39505514e-32, 0.489694506, 0.476404935, 2.44480641e-25, 0.0338525251, 3.64945924e-15, 6.72624967e-15, 4.80203671e-05, 7.55260474e-16]
]
}
复制代码
4 总结
seldon 内置的 TFServing 服务,大大简化了模型推理服务的部署,用户只需要配置模型名称及文件路径即可,而不需要考虑模型文件如何下载,如何做协议转换等服务部署相关问题。通过使用默认配置简化用户输入,同时也提供了可自定义的方式(componentSpecs),以满足特定场景需求,这是很好地设计。
评论