伴鱼技术团队

Technology changes the world

伴鱼开放平台 上线了! 源于实践的解决方案,助力企业成就未来!

使用 Seldon Core 部署 LightGBM 模型

在伴鱼,我们使用 Seldon Core 部署机器学习模型,本文将一步步地带着大家使用 Seldon Core 这个开源的机器学习模型部署方案,部署 LightGBM 模型。

首先,让我们了解几个有关 Seldon Core 的核心概念。

核心概念

Seldon Core

Seldon Core 目前是机器学习模型部署领域最受欢迎的方案之一,由 Seldon 公司开源和维护。Seldon 公司同时还是 KFServing 项目的深度参与者。Seldon Core 旨在为不同框架下训练出来的模型(Tensorflow,Pytorch,SKLearn,XGBoost)提供一套相对统一的部署方式,而 Inference Server 是实现这一目的核心机制。

Inference Server

Inference Server 本质就是一个镜像,它可以加载模型,并按照 SeldonKFServing 预测协议向外提供模型预测接口。预测接口的实现通常委托给打包在其中的开源 ModelServer 后端。例如,Tensorflow Server 使用了 Tensorflow Serving 作为后端,Triton Inference Server 使用了 NIVIDIA Triton Inference Server 作为后端。

Seldon Core 预置了一批 Inference Server,方便用户直接调用。此外,Seldon 还支持自定义 Inference Server,用户可以自己构建一个能加载模型并对外提供预测接口的镜像,妥善配置即可。在这篇文章中,我们会使用 MLServer 作为后端,构建 Inference Server 镜像。MLServer 是 Seldon 官方实现的 ModelServer,支持各种树模型(LightGBM,SKLearn,XGBoost)并支持 KFServing v2 协议。你可能会问,为什么 Seldon 没有自己基于 MLServer 实现并发布 LightGBM Inference Server?大概是因为 MLServer 还在频繁迭代吧。

预测协议

预测协议是独立于任何特定 ML/DL 框架和模型服务器的预测/推理 API。Seldon Core 支持 Seldon 和 KFServing 两种预测协议。Seldon 协议是 Seldon Core 项目自己开发的;KFServing v2 协议是 kubeflow 的子项目 KFServing 发起的。目前 KFServing v2 协议使用比较广泛,包括 TorchServeNIVIDIA Triton Inference Server 等 ModelServer 都支持该协议。

安装 Istio

Seldon Core 使用 Isito 实现服务发现,路由管理,金丝雀发布,A/B 测试等功能。此外,Seldon Core 会为每个模型自动创建 Isitio VirtualService 并将其绑定到 Istio Gateway 上,用户不用管理 k8s 集群内的路由。这对我们稍后使用 grpcurl 在 k8s 集群外通过 Istio Gateway 访问 Seldon Core 上的模型来说非常方便,所以在这里我们首先安装 Istio。

⚠️ 如果你的场景只是在 k8s 集群内通过 k8s service object 访问模型服务,并且不需要 Istio 提供的高级功能则可以不安装 Istio。

可以去 istio release 下载 Istio 并解压安装包:

1
2
3
$ wget https://github.com/istio/istio/releases/download/1.10.3/istio-1.10.3-osx.tar.gz
$ mv -xzvf istio-1.10.3-osx.tar.gz ~/softwares
$ tar -xzvf istio-1.10.3-osx.tar.gz .

下载好 Istio 后,将 istioctl 放到环境变量 PATH 中:

1
$ export PATH=$PATH:~/softwares/istio-1.10.3/bin

使用 istioctl 安装 istio:

1
2
3
4
5
$ istioctl install --set profile=demo -y
$ kubectl -n istio-system get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-egressgateway ClusterIP 10.98.232.177 <none> 80/TCP,443/TCP 10s
istio-ingressgateway LoadBalancer 10.107.4.139 localhost 15021:30041/TCP,80:32273/TCP,443:30014/TCP,31400:31481/TCP,15443:31473/TCP 10s

以上就安装成功了。

安装 Seldon Core

首先创建 Seldon Core 需要用到的 namespace:

1
2
$ kubectl create namespace seldon-system
$ kubectl create namespace seldon

然后使用 helm 安装 Seldon Core,这里安装的是最新的 1.10.0 版本。helm 的安装见这里

1
2
3
$ helm repo add seldonio https://storage.googleapis.com/seldon-charts
$ helm repo update
$ helm install seldon-core-operator seldonio/seldon-core-operator --namespace seldon-system --set istio.enabled=true

我们选择在 seldon 命名空间下部署模型,所以这里先给 seldon 打个 Istio 自动注入的标签。这使得在 seldon 命名空间下创建的 k8s object 都会被 Istio 自动注入,纳入到 Istio 的管理。这里是为了使用 Istio 的路由管理功能:

1
$ kubectl label namespace seldon istio-injection=enabled

然后手动创建 Seldon Core 的 Istio Gateway。注意这是为了在集群外通过 Istio Gateway 访问部署的模型,如果是在集群内部访问该模型则不需要:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ kubectl -n istio-system apply -f - << END
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: seldon-gateway
namespace: istio-system
spec:
selector:
istio: ingressgateway # use istio default controller
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
END

构建 LightGBM Inference Server 镜像

如前文所说,我们使用 MLServer 项目构建 Inference Server 的镜像。镜像总共分为两部分:一是 LightGBM 模型的依赖,这里拷贝了该项目的 Dockerfile。二是 MLServer 本身。下面我编写的 Dockerfile 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
FROM ubuntu:16.04

ARG CONDA_DIR=/opt/conda
ENV PATH $CONDA_DIR/bin:$PATH

RUN apt-get update && \
apt-get install -y --no-install-recommends \
ca-certificates \
cmake \
build-essential \
gcc \
g++ \
curl \
git && \
# python environment
curl -sL https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -o conda.sh && \
/bin/bash conda.sh -f -b -p $CONDA_DIR && \
export PATH="$CONDA_DIR/bin:$PATH" && \
conda config --set always_yes yes --set changeps1 no && \
# lightgbm
conda install -q -y numpy scipy scikit-learn pandas
git clone --recursive --branch stable --depth 1 https://github.com/Microsoft/LightGBM && \
cd LightGBM/python-package && python setup.py install
# clean
apt-get autoremove -y && apt-get clean && \
conda clean -a -y && \
rm -rf /usr/local/src/* && \
# seldon mlserver
pip install mlserver mlserver-lightgbm

ENTRYPOINT cd /mnt/models && mlserver start .

使用下列命令构建镜像:

1
docker build -t 1034552569/seldon-lightgbm:latest .

⚠️ 1034552569 是我的 docker hub 账号,请替换成你自己的。镜像名称也并非必须是 seldon-LightGBM,可以替换。

下面我们将这个打包好的镜像配置到 Seldon Core 中。

将构建的镜像配置到 Seldon Core 中

Seldon Core 的配置都在 k8s seldon-system 命名空间下一个叫做 seldon-config 的 ConfigMap 中。其中字段 predictor_servers 是 Inference Server 的配置。配置项里面有每个 Inference Server 的名字,支持的协议(Seldon or Kfserving v2)以及使用了什么镜像。我们可以使用下面的命令编辑 ConfigMap:

1
kubectl -n seldon-system edit cm seldon-config

按照 Seldon Core 官方文档,我们在原有的基础上将自定义的 Inference Server 加上,如下:

1
2
3
4
5
6
7
8
9
10
11
 {
...
"LightGBM_SERVER": {
"protocols": {
"kfserving":{
"defaultImageVersion": "v0.0.3",
"image": "1034552569/seldon-lightgbm"
}
}
}
}

部署 LightGBM 模型

到了这里所有的准备工作都做完了,可以正式部署 LightGBM 模型了。我们使用 LightGBM 项目中的一个简单例子:simple

下面是部署 LightGBM 模型的模型文件和配置文件。其中 model.txt 是 simple 模型文件,可以参考文档训练出来。model-settings.json 和 settings.json 是 MLServer 项目需要 simple 模型的配置文件。关于具体的配置项可以参考我在 MLServer 提的 Issue

1
2
3
4
5
$ tree
.
├── model-settings.json
├── model.txt
└── settings.json

下面是 model-settings.json 文件的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"name": "simple-lgb",
"implementation": "mlserver_lightgbm.LightGBMModel",
"parameters": {
"uri": "./model.txt"
},
"inputs": [
{
"datatype": "FP32",
"name": "input-0",
"shape": [1,28]
}
]
}

下面是 settings.json 文件的内容,这里是开启了 debug 模式:

1
2
3
{
"debug": "true"
}

笔者已经将这三个文件放到腾讯 cos 上了。接下来让我们创建 seldondeployment 吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$ kubectl -n seldon apply -f - << END
apiVersion: machinelearning.seldon.io/v1alpha2
kind: SeldonDeployment
metadata:
name: simple-lgb
spec:
protocol: kfserving
predictors:
- graph:
children: []
implementation: LightGBM_SERVER
modelUri: s3://lianxm-seldon-model-1256448737/lightgbm/simple
envSecretRefName: seldon-init-secret
name: simple-lgb
name: simple-lgb
replicas: 1
---
apiVersion: v1
kind: Secret
metadata:
name: seldon-init-secret
type: Opaque
stringData:
RCLONE_CONFIG_S3_TYPE: s3
RCLONE_CONFIG_S3_PROVIDER: TencentCOS
RCLONE_CONFIG_S3_ENV_AUTH: "false"
RCLONE_CONFIG_S3_ENDPOINT: cos.ap-beijing.myqcloud.com
RCLONE_CONFIG_S3_ACL: default
RCLONE_CONFIG_S3_STORAGE_CLASS: standard
END

查看 simple 模型的 seldondeployment 是否创建好了:

1
2
3
$ kubectl -n seldon get seldondeployment
NAME AGE
simple-lgb 10s

再看下 deployment:

1
2
3
$ kubectl -n seldon get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
simple-lgb-simple-lgb-0-simple-lgb 0/1 1 1 20s

注意 READY 的值是 1/1 时才算启动成功。0/1 代表启动中,可能在拉取镜像。可以使用下面的命令查看详细信息:

1
$ kubectl -n seldon describe deployment simple-lgb-simple-lgb-0-simple-lgb

查看 virtualservice

seldondeployment 在启用 Isito 时会创建 virtualserver 用作流量管理,这里看下是否创建成功了:

1
2
3
4
$ kubectl -n seldon get virtualservice
NAME GATEWAYS HOSTS AGE
simple-lgb-grpc ["istio-system/seldon-gateway"] ["*"] 4m
simple-lgb-http ["istio-system/seldon-gateway"] ["*"] 4m

使用 grpcurl 调试模型预测接口

首先,看下 Istio 的入口网关 ip:

1
2
3
4
5
$  kubectl -n istio-system get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-egressgateway ClusterIP 10.98.232.177 <none> 80/TCP,443/TCP 18d
istio-ingressgateway LoadBalancer 10.107.4.139 localhost 15021:30041/TCP,80:32273/TCP,443:30014/TCP,31400:31481/TCP,15443:31473/TCP 18d
istiod ClusterIP 10.110.118.169 <none> 15010/TCP,15012/TCP,443/TCP,15014/TCP 18d

注意看 istio-ingressgateway 的 EXTERNAL-IP 那项的地址,笔者是 localhost。接下来使用该地址作为 grpcurl 的访问地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ grpcurl -d '{"model_name":"simple-lgb","inputs":[{"name":"INPUT0","contents":{"int_contents":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},"datatype":"FP32","shape":[1,28]}]}' \
-plaintext -proto ./grpc_service.proto \
-rpc-header seldon:simple-lgb -rpc-header namespace:seldon \
localhost:80 inference.GRPCInferenceService/ModelInfer

{
"modelName": "simple-lgb",
"modelVersion": "v1",
"outputs": [
{
"name": "predict",
"datatype": "FP32",
"shape": [
"1"
],
"contents": {
"fp32Contents": [
0.4641754
]
}
}
]
}

这里就完成了使用 Seldon Core 部署 LightGBM 模型了。

总结

至此,我们就把 LightGBM 模型在本地的 Seldon Core 里面部署起来了。希望本文对想要了解 Seldon Core 的读者有所帮助。

参考文献

[1] Kfserving Perdict Protocol - Version 2 https://github.com/kubeflow/kfserving/tree/master/docs/predict-api/v2
[2] Seldon Core 文档 https://docs.seldon.io/projects/seldon-core/en/latest/?#
[3] Istio 文档 https://istio.io/latest/docs/
[4] MlServer https://github.com/SeldonIO/MLServer

欢迎关注我的其它发布渠道