在Raw Deployment部署模式下,应用的灰度发布需要基于网关实现。本文以Nginx Ingress Controller网关为例,介绍如何实现推理服务的灰度发布,并最终平稳地完成从v1到v2版本推理服务的升级。
前提条件
步骤一:部署v1版本的推理服务
执行以下命令,部署一个v1版本的推理服务,模型名称为canary。
arena serve kserve \ --name=model-v1 \ --image=kube-ai-registry.cn-shanghai.cr.aliyuncs.com/ai-sample/kserve-canary:1.0.0 \ --cpu=1 \ --memory=2Gi \ "python app.py --model_name=canary"
预期输出:
inferenceservice.serving.kserve.io/model-v1 created INFO[0002] The Job model-v1 has been submitted successfully INFO[0002] You can run `arena serve get model-v1 --type kserve -n default` to check the job status
输出结果表明v1版本的推理服务已部署成功。
创建v1版本模型的模型服务Service。
创建并拷贝以下内容到model-svc.yaml文件中,用于创建Service。
执行以下命令,创建Service。
kubectl apply -f model-svc.yaml
预期输出:
service/model-svc created
输出结果表明Service已创建成功。
通过Nginx Ingress访问一个名为model-v1的Inference Service,以验证model-v1是否正确部署。
执行以下命令,从kube-system命名空间中获取名为nginx-ingress-lb的服务(Service),并使用
jsonpath
提取负载均衡器(LoadBalancer)的IP地址。NGINX_INGRESS_IP=`kubectl -n kube-system get svc nginx-ingress-lb -ojsonpath='{.status.loadBalancer.ingress[0].ip}'`
执行以下命令,获取模型服务(Inference Service)的HOSTNAME。
SERVICE_HOSTNAME=$(kubectl get inferenceservice model-v1 -o jsonpath='{.status.url}' | cut -d "/" -f 3)
执行以下命令,向模型服务发起POST请求,以访问model-v1服务。
curl -H "Host: $SERVICE_HOSTNAME" -H "Content-Type: application/json" \ http://$NGINX_INGRESS_IP:80/v1/models/canary:predict -X POST \ -d '{"data": "test"}'
预期输出:
{"id":"bd73dcde-2dd5-4cfb-8097-8ee3531e0880","model_name":"canary","model_version":null,"outputs":[{"name":"output-0","shape":[1,1],"datatype":"STR","data":["model-v1"]}]}%
模型输出结果返回了一个JSON响应,包含预测ID、模型名称、输出等信息。表明model-v1已成功部署。
步骤二:部署v2版本的推理服务
执行以下命令,部署一个v2版本的推理服务,模型名称仍为canary。
arena serve kserve \ --name=model-v2 \ --image=kube-ai-registry.cn-shanghai.cr.aliyuncs.com/ai-sample/kserve-canary:1.0.0 \ --cpu=1 \ --memory=2Gi \ "python app-v2.py --model_name=canary"
预期输出:
inferenceservice.serving.kserve.io/model-v2 created INFO[0002] The Job model-v2 has been submitted successfully INFO[0002] You can run `arena serve get model-v2 --type kserve -n default` to check the job status
输出结果表明v2版本的推理服务已成功部署。
创建Service。
创建并拷贝以下内容到model-v2-svc.yaml文件中,用于创建Service。
执行以下命令,创建Service。
kubectl apply -f model-v2-svc.yaml
预期输出:
service/model-v2-svc created
输出结果表明Service已创建成功。
通过Nginx Ingress访问一个名为model-v2的推理服务,以验证model-v2是否已正确部署。
执行以下命令,从kube-system命名空间中获取名为nginx-ingress-lb的Service,并使用
jsonpath
提取LoadBalancer的IP地址。NGINX_INGRESS_IP=`kubectl -n kube-system get svc nginx-ingress-lb -ojsonpath='{.status.loadBalancer.ingress[0].ip}'`
执行以下命令,获取模型服务(Inference Service)的HOSTNAME。
SERVICE_HOSTNAME=$(kubectl get inferenceservice model-v2 -o jsonpath='{.status.url}' | cut -d "/" -f 3)
执行以下命令,向模型服务发起POST请求,以访问model-v2服务。
curl -H "Host: $SERVICE_HOSTNAME" -H "Content-Type: application/json" \ http://$NGINX_INGRESS_IP:80/v1/models/canary:predict -X POST \ -d '{"data": "test"}'
预期输出:
{"id":"89918594-b995-4d2d-b016-9fbe140ffc0a","model_name":"canary","model_version":null,"outputs":[{"name":"output-0","shape":[1,1],"datatype":"STR","data":["model-v2"]}]}%
模型输出结果返回了一个JSON响应,包含预测ID、模型名称、输出等信息。表明model-v2已成功部署。
步骤三:配置灰度策略
通过以下示例介绍Nginx Ingress如何基于客户端请求和服务权重来实现应用服务的灰度发布。关于通过Nginx Ingress实现灰度发布的背景信息及原理说明,请参见通过Nginx Ingress实现灰度发布和蓝绿发布。
创建Ingress,以管理进入集群的流量。
创建并拷贝以下内容到model-ingress.yaml文件中,用于创建Ingress。
执行以下命令,创建Ingress。
kubectl apply -f model-ingress.yaml
预期输出:
ingress.networking.k8s.io/model-ingress created
输出结果表明Ingress已创建成功。
配置灰度发布策略。
场景一:基于客户端请求的流量切分场景
创建并拷贝以下内容到gray-release-canary.yaml文件中,用于配置灰度发布策略。
本示例以允许特定请求(带有头部
foo: bar
)被定向到名为model-v2-svc
的新版本服务为例,介绍如何基于客户端请求的流量切分。执行以下命令,部署灰度发布策略。
kubectl apply -f gray-release-canary.yaml
预期输出:
ingress.networking.k8s.io/gray-release-canary created
输出结果表明灰度发布策略已经部署成功。
场景二:基于服务权重的流量切分场景
创建并拷贝以下内容到gray-release-canary.yaml文件中,用于配置灰度发布策略。
本示例以将50%的流量被导向名为
model-v2-svc
的新版本服务,而剩余流量则继续流向未在此配置中明确指定的其他服务或默认服务为例,介绍如何基于服务权重的流量切分场景。执行以下命令,部署灰度发布策略。
kubectl apply -f gray-release-canary.yaml
预期输出:
ingress.networking.k8s.io/gray-release-canary created
输出结果表明灰度发布策略已经部署成功。
验证灰度策略是否已生效。
场景一:基于客户端请求的流量切分场景
执行以下命令,从kube-system命名空间中获取名为nginx-ingress-lb的Service,并使用
jsonpath
提取LoadBalancer的IP地址。NGINX_INGRESS_IP=`kubectl -n kube-system get svc nginx-ingress-lb -ojsonpath='{.status.loadBalancer.ingress[0].ip}'`
执行以下命令,验证无特定请求头(默认版本访问)的服务响应。
# 以下代码的Host为Ingress中定义的业务Host。 curl -H "Host: model.example.com" -H "Content-Type: application/json" \ http://$NGINX_INGRESS_IP:80/v1/models/canary:predict -X POST \ -d '{"data": "test"}'
预期输出:
{"id":"4d8c110d-c291-4670-ad0a-1a30bf8e314c","model_name":"canary","model_version":null,"outputs":[{"name":"output-0","shape":[1,1],"datatype":"STR","data":["model-v1"]}]}%
输出结果返回了model-v1的结果,表明默认情况下服务能够正确提供model-v1的预测结果。即流量流向了model-v1。
执行以下命令,验证带有
"foo: bar"
请求头(期望访问金丝雀版本)的客户端请求。curl -H "Host: model.example.com" -H "Content-Type: application/json" \ -H "foo: bar" \ http://$NGINX_INGRESS_IP:80/v1/models/canary:predict -X POST \ -d '{"data": "test"}'
预期输出:
{"id":"4d3efc12-c8bd-40f8-898f-7983377db7bd","model_name":"canary","model_version":null,"outputs":[{"name":"output-0","shape":[1,1],"datatype":"STR","data":["model-v2"]}]}%
输出结果返回了model-v2的结果,表明带有特定请求头的流量被正确地导向了灰度版本,即灰度发布策略已生效。
场景二:基于服务权重的流量切分场景
执行以下命令,从kube-system命名空间中获取名为nginx-ingress-lb的Service,并使用
jsonpath
提取LoadBalancer的IP地址。NGINX_INGRESS_IP=`kubectl -n kube-system get svc nginx-ingress-lb -ojsonpath='{.status.loadBalancer.ingress[0].ip}'`
执行以下命令,验证流量的流向。
curl -H "Host: model.example.com" -H "Content-Type: application/json" \ http://$NGINX_INGRESS_IP:80/v1/models/canary:predict -X POST \ -d '{"data": "test"}'
重复执行以上命令,您可以观察到大约有50%的流量被路由到新版本(model-v2)的服务上,另一半流量则流向稳定版本(model-v1)的服务。即灰度策略已生效。
步骤四:删除老版本服务
系统运行一段时间后,当新版本服务已经稳定并且符合预期后,需要下线老版本的服务 ,仅保留新版本服务在线上运行。
拷贝以下内容到已创建的model-svc.yaml文件中,来修改已创建的Service,使其指向新版本model-v2服务。
执行以下命令,重新部署Service。
kubectl apply -f model-svc.yaml
预期输出:
service/model-svc configured
输出结果表明Service已成功修改。
执行以下命令,从kube-system命名空间中获取名为nginx-ingress-lb的Service,并使用
jsonpath
提取LoadBalancer的IP地址。NGINX_INGRESS_IP=`kubectl -n kube-system get svc nginx-ingress-lb -ojsonpath='{.status.loadBalancer.ingress[0].ip}'`
重复执行以下命令,查看路由访问情况。
curl -H "Host: model.example.com" -H "Content-Type: application/json" \ http://$NGINX_INGRESS_IP:80/v1/models/canary:predict -X POST \ -d '{"data": "test"}'
预期输出:
{"id":"a13f2089-73ce-41e3-989e-e58457d14fed","model_name":"canary","model_version":null,"outputs":[{"name":"output-0","shape":[1,1],"datatype":"STR","data":["model-v2"]}]}%
重复执行以上命令后,您可以观察到100%的请求(流量)被路由到新版本(model-v2)的服务上。
执行以下命令,删除旧版本服务。
# 删除灰度策略。 kubectl delete ingress gray-release-canary # 删除model-v1推理服务。 arena serve delete model-v1 # 删除model-v2的Service。 kubectl delete svc model-v2-svc
执行完以上这些命令后,表示您已完成了从旧版本到新版本过渡过程中的清理工作。