作者:黄驰琳、露营、车漾
KServe 是 Kubernetes 上的标准模型推理平台,专为高度可扩展的场景而构建,支持现代 Serverless 推理工作负载,用于在任意框架上提供机器学习(ML)模型服务。它提供高性能、高度抽象的接口,以支持常见的 ML 框架(如Tensorflow、XGBoost、Scikit-Learn、PyTorch 和 ONNX)来解决生产模型服务场景。此外,KServe 封装了自动缩放、网络、健康检查和服务配置的复杂性,支持GPU 自动缩放、归零缩放和金丝雀发布等。为生产级别机器学习服务提供了一个简单、可插拔且完整的支持,包括预测、预处理、后处理和可解释性的能力。
人工智能生成内容(AIGC)和大型语言模型(LLM)在近一年内方兴未艾,进一步提升了了大众对 AI 的期望值。为了能够产生新的业务价值,越来越多的公司开始使用 KServe 来部署它们,主要原因是:
但是在生产实践中,KServe 对于大型语言模型(LLMs)的支持依然有不小的挑战。主要问题在于:
KServe 在 Kubecon 2023 就提到了 Fluid 有可能帮助解决其在弹性上遇到的问题。Fluid 是一个开源的 Kubernetes 原生的分布式数据集编排和加速引擎,主要服务于云原生场景下的数据密集型应用,例如大数据应用、AI 应用等。参见数据加速 Fluid 概述 [ 1] 。
阿里云容器服务团队和 KServe,Fluid 社区的小伙伴一起探索在阿里云 Serverless Kubernetes 平台上简单,方便,高性能,生产级别的支持大型语言模型(LLMs):
一切就绪,我们一起开启阿里云 Kubernetes(ACK) 中 KServe 推理大模型之旅:
本演示使用的 ACK 集群包含 3 个 ecs.g7.xlarge 规格的 ecs 和 1 个 ecs.g7.2xlarge 规格的 ecs。您可以在创建时选择添加 3 个 ecs.g7.xlarge 规格的 ecs,待集群创建完成后新建一个节点池以增加一个 1 个 ecs.g7.2xlarge 规格的 ecs,关于创建节点池的具体操作请参考创建节点池 [ 3] 。
您可以使用上述配置来创建阿里云服务网格 ASM 实例,其中:
说明:KServe on ASM 功能依赖 cert-manager 组件的部署与使用。cert-manager 是一个证书生命周期管理系统,支持证书的申请、部署等功能。如果您没有在集群中安装 cert-manager,您需要在 KServe on ASM 页面开启 在集群中自动安装 CertManager 组件,以自动为您安装 cert-manager;否则您需要关闭此项,再单击启用 KServe on ASM。
说明:如果您的数据面集群为 ACK 集群,您需要在 ACK 集群中安装云原生AI套件并部署 ack-fluid 组件。参考:加速在线应用数据访问 [ 10] 。
如果您的数据面集群为 ASK 集群,您需要在 ASK 集群中部署 ack-fluid 组件。参考:加速 Job 应用数据访问 [1****1] 。
a. 准备训练好的 AI 模型保存数据。本文以基于 pytorch 的开源 transformer 大语言模型 bloom 为例,可以在 hugging face 社区中获取模型数据:https://huggingface.co/bigscience/bloom-560m/tree/main
b. 将下载的模型数据文件上传至 OSS Bucket,并记录模型数据文件的保存位置。模型数据文件的保存位置格式为 oss://{bucket}/{path}。例如,如果您创建了名为 fluid-demo 的 bucket,并在 bucket 中的 models/bloom 目录中上传了所有的模型数据文件,则模型数据文件的保存位置为 oss://fluid-demo/models/bloom
说明:您可以通过 OSS 提供的客户端工具 ossutil 上传数据。具体操作,请参见安装 ossutil [ 12] 。
a. 使用 kubectl 连接到数据面 ACK/ASK 集群。具体操作,参考:通过 kubectl 连接 Kubernetes 集群 [ 13] 。
b. 使用 kubectl 创建命名空间,以部署 Fluid 及 KServe AI 服务。本文以 kserve-fluid-demo 命名空间为例。
kubectl create ns kserve-fluid-demo
c. 使用 kubectl 为命名空间添加 eci 标签,以将命名空间中的 Pod 调度到虚拟节点上。
kubectl label namespace kserve-fluid-demo alibabacloud.com/eci=true
d. 使用以下内容,创建 oss-secret.yaml 文件。其中 fs.oss.accessKeyId 和 fs.oss.accessKeySecret 分别代表可以访问 OSS 的 accessKeyId 和 accessKeySecret。
apiVersion: v1
kind: Secret
metadata:
name: access-key
stringData:
fs.oss.accessKeyId: xxx #替换为可以访问OSS的阿里云accessKeyId
fs.oss.accessKeySecret: xxx #替换为可以访问OSSd的阿里云accessKeySecret
e. 执行以下命令,部署 Secret,配置 OSS 访问秘钥。
kubectl apply -f oss-secret.yaml -n kserve-fluid-demo
a. 使用以下内容,保存为 oss-jindo.yaml 文件。
apiVersion: data.fluid.io/v1alpha1
kind: Dataset
metadata:
name: oss-data
spec:
mounts:
- mountPoint: "oss://{bucket}/{path}" # 需要替换为模型数据文件的保存位置
name: bloom-560m
path: /bloom-560m
options:
fs.oss.endpoint: "{endpoint}" # 需要替换为实际的OSS endpoint地址
encryptOptions:
- name: fs.oss.accessKeyId
valueFrom:
secretKeyRef:
name: access-key
key: fs.oss.accessKeyId
- name: fs.oss.accessKeySecret
valueFrom:
secretKeyRef:
name: access-key
key: fs.oss.accessKeySecret
accessModes:
- ReadOnlyMany
---
apiVersion: data.fluid.io/v1alpha1
kind: JindoRuntime
metadata:
name: oss-data
spec:
replicas: 2
tieredstore:
levels:
- mediumtype: SSD
volumeType: emptyDir
path: /mnt/ssd0/cache
quota: 50Gi
high: "0.95"
low: "0.7"
fuse:
args:
- -ometrics_port=-1
master:
nodeSelector:
node.kubernetes.io/instance-type: ecs.g7.xlarge
worker:
nodeSelector:
node.kubernetes.io/instance-type: ecs.g7.xlarge
说明:您需要将 Dataset CR 中的 oss://{bucket}/{path} 替换为上文中记录的模型数据文件的保存位置。将 Dataset CR 中的 {endpoint}替换为 OSS 的访问域名。如何获取不同地域 OSS 的访问域名,参考:访问域名和数据中心 [ 14] 。
a. 执行以下命令,部署 Dataset 和 JindoRuntime CR。
kubectl create -f oss-jindo.yaml -n kserve-fluid-demo
b. 执行以下命令,查看 Dataset 和 JindoRuntime 的部署情况。
kubectl get jindoruntime,dataset -n kserve-fluid-demo
预期输出:
NAME MASTER PHASE WORKER PHASE FUSE PHASE AGE
jindoruntime.data.fluid.io/oss-data Ready Ready Ready 3m
NAME UFS TOTAL SIZE CACHED CACHE CAPACITY CACHED PERCENTAGE PHASE AGE
dataset.data.fluid.io/oss-data 3.14GiB 0.00B 100.00GiB 0.0% Bound 3m
由预期输出中得到,Dataset 的 PHASE为Bound,JindoRuntime 的 FUSE PHASE 为 Ready,代表 Dataset 和 JindoRuntime 都部署成功。
a. 使用以下内容,保存为 oss-dataload.yaml。
apiVersion: data.fluid.io/v1alpha1
kind: DataLoad
metadata:
name: oss-dataload
spec:
dataset:
name: oss-data
namespace: kserve-fluid-demo
target:
- path: /bloom-560m
replicas: 2
b. 执行以下命令,部署 Dataload 以预热数据。
kubectl create -f oss-dataload.yaml -n kserve-fluid-demo
c. 执行以下命令,查看数据预热的进度。
kubectl get dataload -n kserve-fluid-demo
预期输出:
NAME DATASET PHASE AGE DURATIONoss-dataload
oss-data Complete 1m 45s
由预期输出得到,数据预热耗时约 45s。需要等待一段时间才能使得数据预热完成。
apiVersion: "serving.kserve.io/v1beta1"
kind: "InferenceService"
metadata:
name: "fluid-bloom"
spec:
predictor:
timeout: 600
minReplicas: 0
nodeSelector:
node.kubernetes.io/instance-type: ecs.g7.2xlarge
containers:
- name: kserve-container
image: cheyang/kserve-fluid:bloom-gpu
resources:
limits:
cpu: "3"
memory: 8Gi
requests:
cpu: "3"
memory: 8Gi
env:
- name: STORAGE_URI
value: "pvc://oss-data/bloom-560m"
- name: MODEL_NAME
value: "bloom"
# 如果使用GPU则设置成True,否则设置为False
- name: GPU_ENABLED
value: "False"
说明:本例中的 InferenceService 配置中在 image 字段使用了 cheyang/kserve-fluid:bloom-gpu 示例镜像,该镜像提供加载模型及推理服务的接口,您可以在 KServe 开源社区中找到此示例镜像的代码,并进行镜像自定义:https://github.com/kserve/kserve/tree/master/docs/samples/fluid/docker
kubectl create -f oss-fluid-isvc.yaml -n kserve-fluid-demo
kubectl get inferenceservice -n kserve-fluid-demo
预期输出:
NAME URL
READY PREV LATEST PREVROLLEDOUTREVISION LATESTREADYREVISION AGEfluid-bloom http://fluid-bloom.kserve-fluid-demo.example.com True 100 fluid-bloom-predictor-00001 2d
由预期输出可以看到 READY 字段为 True,证明 AI 模型推理服务已经部署成功。
a. 登录 ASM 控制台,在左侧导航栏,选择服务网格 > 网格管理。
b. 在网格管理页面,单击目标实例名称,然后在左侧导航栏,选择 ASM 网关 > 入口网关。
c. 在入口网关页面找到名为 ingressgateway 的 ASM 入口网关,在服务地址区域,查看并获取 ASM 网关服务地址。
执行以下命令,访问示例 AI 模型推理服务 bloom,将其中的 ASM 网关服务地址替换为上文中获取的 ASM 入口网关地址。
curl -v -H "Content-Type: application/json" -H "Host: fluid-bloom.kserve-fluid-demo.example.com" "http://{ASM网关服务地址}:80/v1/models/bloom:predict" -d '{"prompt": "It was a dark and stormy night", "result_length": 50}'
预期输出:
* Trying xxx.xx.xx.xx :80...
* Connected to xxx.xx.xx.xx (xxx.xx.xx.xx ) port 80 (#0)
> POST /v1/models/bloom:predict HTTP/1.1
> Host: fluid-bloom-predictor.kserve-fluid-demo.example.com
> User-Agent: curl/7.84.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 65
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< content-length: 227
< content-type: application/json
< date: Thu, 20 Apr 2023 09:49:00 GMT
< server: istio-envoy
< x-envoy-upstream-service-time: 1142
<
{
"result": "It was a dark and stormy night, and the wind was blowing in the\ndirection of the west. The wind was blowing in the direction of the\nwest, and the wind was blowing in the direction of the west. The\nwind was"
}
* Connection #0 to host xxx.xx.xx.xx left intact
从预期输出中看到,AI 模型推理服务成功对示例输入进行了续写并返回推理结果。
我们的性能基准测试将比较 KServe 使用自身存储初始化器(OSS Storage Initializer)和 Fluid 的推理服务的扩容时间,测试不同规格模型情况下的扩容时间,即衡量服务从 0 扩展到 1 所需的时间。
我们所选的测试机型为:
使用的 Fluid Runtime 为:
其他前提:
性能测试命令:
# Total time includes: pod initialization and running + download model (storage initializer) + load model + inference + network
curl --connect-timeout 3600 --max-time 3600 -o /dev/null -s -w 'Total: %{time_total}s\n' -H "Content-Type: application/json" -H "Host: ${SERVICE_HOSTNAME}" "http://${INGRESS_HOST}:${INGRESS_PORT}/v1/models/bloom:predict" -d '{"prompt": "It was a dark and stormy night", "result_length": 50}'
# Total time:
Model Name | Model Size (Snapshot) | Machine Type | KServe + Storage Initializer | KServe + Fluid |
---|---|---|---|---|
bigscience/bloom-560m [ 15] | 3.14GB | ecs.g7.2xlarge(cpu: 8, memory: 32G) | total: 58.031551s(download:33.866s, load: 5.016s) | total: 8.488353s(load: 2.349s)(2 workers) |
bigscience/bloom-7b1 [ 16] | 26.35GB | ecs.g7.4xlarge(cpu: 16, memory: 64G) | total: 329.019987s(download: 228.440s, load: 71.964s) | total: 27.800123s(load: 12.084s)(3 workers) |
Total:无服务情况下的推理响应时间(从 0 开始扩容),包括容器调度,启动,模型下载,模型加载到显存,模型推理和网络延迟时间。
我们可以观察到 Fluid 在大语言模型(LLM)的场景下,
这样恰恰可以大幅提升 KServe 在容器场景下的弹性扩缩容能力。
通过现有的云原生 AI 框架支持大模型这种新物种还需要不断地改善和优化,但是道阻且长,行则将至。阿里云容器服务团队愿意和社区的小伙伴一起探索如何能够用更低的成本支持更好的 LLM 推理场景,提供标准开放的方案,产品化的能力。我们后续会提供通过计算弹性扩缩容的规律触发数据缓存的弹性扩缩容来控制成本,以及大模型的热更新方法。
相关链接:
[1] 数据加速 Fluid 概述
https://help.aliyun.com/document_detail/208335.html
[2] 创建 Kubernetes 托管版集群
https://help.aliyun.com/document_detail/95108.html?spm=a2c4g.118970.0.0.1c6953c8tAGxmn
[3] 创建节点池
https://help.aliyun.com/document_detail/160490.html?spm=a2c4g.476586.0.0.d23b4180G64qLH
[4] 创建 ASM 实例
https://help.aliyun.com/document_detail/147793.htm#task-2370657
[5] 添加集群到 ASM 实例
https://help.aliyun.com/document_detail/148231.htm#task-2372122
[6] 通过数据面集群 KubeAPI 访问 Istio 资源
https://help.aliyun.com/document_detail/431215.html
[7] 创建入口网关服务
https://help.aliyun.com/document_detail/149546.htm#task-2372970
[8] 使用 Knative on ASM 部署 Serveless 应用
https://help.aliyun.com/document_detail/611862.html
[9] ASM 控制台
https://account.aliyun.com/login/login.htm?oauth_callback=https%3A%2F%2Fservicemesh.console.aliyun.com%2F&lang=zh
[10] 加速在线应用数据访问
https://help.aliyun.com/document_detail/440265.html
[11] 加速 Job 应用数据访问
https://help.aliyun.com/document_detail/456858.html
[12] 安装 ossutil
https://help.aliyun.com/document_detail/120075.htm#concept-303829
[13] 通过 kubectl 连接 Kubernetes 集群
https://help.aliyun.com/document_detail/86378.html
[14] 访问域名和数据中心
https://help.aliyun.com/document_detail/31837.html
[15] bigscience/bloom-560m
https://huggingface.co/bigscience/bloom-560m
[16] bigscience/bloom-7b1
https://huggingface.co/bigscience/bloom-7b1