本ガイドでは、Raw Deployment モードで推論サービスに対してカナリアリリースを実装する手順について説明します。ゲートウェイとして NGINX Ingress コントローラーを使用し、v1 と v2 の 2 つのバージョンの推論サービスを並列してデプロイした後、トラフィックを段階的に v1 から v2 へシフトさせ、v2 の検証完了後に完全な切り替えを実行します。
前提条件
開始する前に、以下の環境が整っていることを確認してください。
仕組み
本ガイドでは、同一モデル(canary)からデプロイされた 2 つの推論サービス(v1 および v2)を使用します。NGINX Ingress コントローラーは、カナリアアノテーションを用いて両サービス間のトラフィックをルーティングします。以下に、2 つのトラフィック分割戦略を紹介します。
-
ヘッダーに基づくルーティング:特定のリクエストヘッダー(
foo: bar)を持つリクエストは v2 へ、それ以外のリクエストは v1 へルーティングされます。 -
重みに基づくルーティング:20 % のリクエストを v2 へ、残りの 80 % を v1 へルーティングします。
v2 の検証が完了したら、バックエンドの Service を v2 に完全に指向するよう更新し、その後 v1 のリソースをクリーンアップします。
NGINX Ingress を使用したカナリアリリースおよびブルーグリーンリリースの背景情報については、「NGINX Ingress コントローラーを使用してカナリアリリースおよびブルーグリーンリリースを実装する」をご参照ください。
手順 1:推論サービスのデプロイと検証
v1 および v2 の両方の推論サービスをデプロイし、それぞれに対応する Kubernetes Service を作成します。
v1 のデプロイ
-
v1 推論サービスをデプロイします。
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" -
以下の内容で
model-svc.yamlというファイルを作成します。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 -
model-v1 が正しく実行されていることを検証します。
curl -H "Host: $(kubectl get inferenceservice model-v1 -o jsonpath='{.status.url}' | cut -d "/" -f 3)" \ -H "Content-Type: application/json" \ http://$(kubectl -n kube-system get svc nginx-ingress-lb -ojsonpath='{.status.loadBalancer.ingress[0].ip}'):80/v1/models/canary:predict -X POST \ -d '{"data": "test"}'
v2 のデプロイ
-
v2 推論サービスをデプロイします。
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" -
以下の内容で
model-v2-svc.yamlというファイルを作成します。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 -
model-v2 が正しく実行されていることを検証します。
curl -H "Host: $(kubectl get inferenceservice model-v2 -o jsonpath='{.status.url}' | cut -d "/" -f 3)" \ -H "Content-Type: application/json" \ http://$(kubectl -n kube-system get svc nginx-ingress-lb -ojsonpath='{.status.loadBalancer.ingress[0].ip}'):80/v1/models/canary:predict -X POST \ -d '{"data": "test"}'
手順 2:Ingress の作成
デフォルトですべてのトラフィックを v1 Service へルーティングする基本的な Ingress を作成します。
-
以下の内容で
model-ingress.yamlというファイルを作成します。apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: model-ingress spec: rules: - host: model.example.com # ホスト名を実際のものに置き換えてください。 http: paths: - path: / backend: service: name: model-svc # v1 用の Service。 port: number: 80 pathType: ImplementationSpecific -
Ingress を作成します。
kubectl apply -f model-ingress.yaml
手順 3:カナリアリリースポリシーの作成と検証
v2 へのトラフィック制御方法に応じて、以下のいずれかの戦略を選択してください。
シナリオ 1:ヘッダーに基づくトラフィック分割
特定のリクエストヘッダーを持つリクエストを v2 へルーティングします。その他のリクエストはデフォルトで v1 へルーティングされます。この戦略は、特定のクライアントまたはチームによるターゲットテストに適しています。
-
以下の内容で
gray-release-canary.yamlというファイルを作成します。apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: gray-release-canary annotations: nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-weight: "20" # トラフィックの 20 % を v2 にルーティングします。デフォルトの合計重みは 100 です。 spec: rules: - host: model.example.com http: paths: - path: / backend: service: name: model-v2-svc # v2 の Service。 port: number: 80 pathType: ImplementationSpecific -
カナリアリリースポリシーをデプロイします。
kubectl apply -f gray-release-canary.yaml -
カナリアヘッダーを持たないリクエストが v1 へルーティングされることを検証します。
# ホスト名を Ingress で指定した値に置き換えてください。 curl -H "Host: model.example.com" -H "Content-Type: application/json" \ http://$(kubectl -n kube-system get svc nginx-ingress-lb -ojsonpath='{.status.loadBalancer.ingress[0].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 からの応答により、デフォルトのトラフィックが引き続き v1 へ到達していることが確認できます。
-
foo: barを含むリクエストが v2 へルーティングされることを検証します。curl -H "Host: model.example.com" -H "Content-Type: application/json" \ -H "foo: bar" \ http://$(kubectl -n kube-system get svc nginx-ingress-lb -ojsonpath='{.status.loadBalancer.ingress[0].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 からの応答により、カナリアリリースポリシーが正常に機能していることが確認できます。
シナリオ 2:重みに基づくトラフィック分割
リクエストヘッダーに関係なく、固定割合のトラフィックを v2 へルーティングします。この戦略は、全ユーザーを対象とした広範囲のカナリア検証に適しています。
-
以下の内容で
gray-release-canary.yamlというファイルを作成します。apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: gray-release-canary annotations: nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-weight: "20" # トラフィックの 20 % を v2 へ(デフォルトの合計重みは 100)。 spec: rules: - host: model.example.com http: paths: - path: / backend: service: name: model-v2-svc # v2 用の Service。 port: number: 80 pathType: ImplementationSpecific -
カナリアリリースポリシーをデプロイします。
kubectl apply -f gray-release-canary.yaml -
複数回のリクエスト送信により、トラフィックの配分を検証します。
curl -H "Host: model.example.com" -H "Content-Type: application/json" \ http://$(kubectl -n kube-system get svc nginx-ingress-lb -ojsonpath='{.status.loadBalancer.ingress[0].ip}'):80/v1/models/canary:predict -X POST \ -d '{"data": "test"}'コマンドを複数回実行してください。約 20 % の応答に
"data":["model-v2"]、残りの約 80 % に"data":["model-v1"]が含まれ、カナリアリリースポリシーが正常に機能していることが確認できます。
手順 4:新バージョンへのトラフィック切り替え
v2 が所定通りに動作していることを確認した後、すべてのトラフィックを v2 へリダイレクトし、v1 を削除します。
-
model-svc.yamlを更新し、model-svcService を 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 -
すべてのトラフィックが v2 へ到達していることを検証します。
curl -H "Host: model.example.com" -H "Content-Type: application/json" \ http://$(kubectl -n kube-system get svc nginx-ingress-lb -ojsonpath='{.status.loadBalancer.ingress[0].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"]}]}コマンドを複数回実行し、すべての応答が model-v2 から返されることを確認してください。
-
カナリア Ingress および v1 のリソースを削除します。
kubectl delete ingress gray-release-canary arena serve delete model-v1 kubectl delete svc model-v2-svc