在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。
apiVersion: v1 kind: Service metadata: name: model-svc spec: ports: - port: 80 protocol: TCP targetPort: 8080 selector: serving.kserve.io/inferenceservice: model-v1 type: ClusterIP
執行以下命令,創建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。
apiVersion: v1 kind: Service metadata: name: model-v2-svc spec: ports: - port: 80 protocol: TCP targetPort: 8080 selector: serving.kserve.io/inferenceservice: model-v2 type: ClusterIP
執行以下命令,創建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。
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: model-ingress spec: rules: - host: model.example.com # 替換為您實際業務的Host。 http: paths: # 老版本服務。 - path: / backend: service: name: model-svc port: number: 80 pathType: ImplementationSpecific
執行以下命令,創建Ingress。
kubectl apply -f model-ingress.yaml
預期輸出:
ingress.networking.k8s.io/model-ingress created
輸出結果表明Ingress已創建成功。
配置灰度發布策略。
場景一:基于客戶端請求的流量切分場景
創建并拷貝以下內容到gray-release-canary.yaml文件中,用于配置灰度發布策略。
本示例以允許特定請求(帶有頭部
foo: bar
)被定向到名為model-v2-svc
的新版本服務為例,介紹如何基于客戶端請求的流量切分。apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: gray-release-canary annotations: # 開啟Canary。 nginx.ingress.kubernetes.io/canary: "true" # 請求頭為foo。 nginx.ingress.kubernetes.io/canary-by-header: "foo" # 請求頭foo的值為bar時,請求才會被路由到新版本服務model-v2中。 nginx.ingress.kubernetes.io/canary-by-header-value: "bar" spec: rules: - host: model.example.com http: paths: # 新版本服務。 - path: / backend: service: name: model-v2-svc port: number: 80 pathType: ImplementationSpecific
執行以下命令,部署灰度發布策略。
kubectl apply -f gray-release-canary.yaml
預期輸出:
ingress.networking.k8s.io/gray-release-canary created
輸出結果表明灰度發布策略已經部署成功。
場景二:基于服務權重的流量切分場景
創建并拷貝以下內容到gray-release-canary.yaml文件中,用于配置灰度發布策略。
本示例以將50%的流量被導向名為
model-v2-svc
的新版本服務,而剩余流量則繼續流向未在此配置中明確指定的其他服務或默認服務為例,介紹如何基于服務權重的流量切分場景。apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: gray-release-canary annotations: # 開啟Canary。 nginx.ingress.kubernetes.io/canary: "true" # 僅允許50%的流量會被路由到新版本服務new-nginx中。 # 默認總值為100。 nginx.ingress.kubernetes.io/canary-weight: "50" spec: rules: - host: model.example.com http: paths: # 新版本服務。 - path: / backend: service: name: model-v2-svc port: number: 80 pathType: ImplementationSpecific
執行以下命令,部署灰度發布策略。
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服務。
apiVersion: v1 kind: Service metadata: name: model-svc spec: ports: - port: 80 protocol: TCP targetPort: 8080 selector: serving.kserve.io/inferenceservice: model-v2 # 將model-v1修改為model-v2。 type: ClusterIP
執行以下命令,重新部署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
執行完以上這些命令后,表示您已完成了從舊版本到新版本過渡過程中的清理工作。