全部產品
Search
文件中心

Container Service for Kubernetes:基於Nginx Ingress Controller網關實現推理服務的灰階發布

更新時間:Apr 30, 2025

在Raw Deployment部署模式下,應用的灰階發布需要基於網關實現。本文以Nginx Ingress Controller網關為例,介紹如何?推理服務的灰階發布,並最終平穩地完成從v1到v2版本推理服務的升級。

前提條件

操作說明

本文將部署兩個推理服務,分別為canary的v1版本和v2版本。基於這兩個服務來示範兩種灰階策略情境,並最終完成版本切換:

  • 基於用戶端請求的流量切分。

    將帶有要求標頭foo: bar的請求定向model-v2-svc的服務為例,其他請求預設指向model-svc

  • 基於服務權重的流量切分。

    將20%的流量被導向名為model-v2-svc的服務,剩餘流量繼續流向model-svc

關於通過Nginx Ingress實現灰階發布的背景資訊及原理說明,請參見通過Nginx Ingress實現灰階發布和藍綠髮布

步驟一:部署並驗證推理服務

v1版本

  1. 部署模型名稱為canary的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"
  2. 建立v1版本推理服務的Service。

    1. 建立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
    2. 建立Service。

      kubectl apply -f model-svc.yaml
  3. 通過Nginx Ingress訪問一個名為model-v1的Inference Service,以驗證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版本

  1. 部署模型名稱為canary的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"
  2. 建立v2版本推理服務的Service。

    1. 建立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
    2. 建立Service。

      kubectl apply -f model-v2-svc.yaml
  3. 通過Nginx Ingress訪問一個名為model-v2的Inference Service,以驗證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"}'

步驟二:建立Ingress

  1. 建立model-ingress.yaml。

    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
  2. 建立Ingress。

    kubectl apply -f model-ingress.yaml

步驟三:建立並驗證灰階策略

情境一:基於用戶端請求的流量切分情境

  1. 建立gray-release-canary.yaml。

    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
  2. 部署灰階發布策略。

    kubectl apply -f gray-release-canary.yaml
  3. 驗證無特定要求標頭(預設版本訪問)的服務響應。

    # 以下代碼的Host為Ingress中定義的業務Host。
    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的結果,表明預設情況下服務能夠正確提供model-v1的預測結果。即流量流向了model-v1。

  4. 執行以下命令,驗證帶有"foo: bar"要求標頭(期望訪問金絲雀版本)的用戶端請求。

    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的結果,表明帶有特定要求標頭的流量被正確地導向了灰階版本,即灰階發布策略已生效。

情境二:基於服務權重的流量切分情境

  1. 建立gray-release-canary.yaml。

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: gray-release-canary
      annotations:
        # 開啟Canary。
        nginx.ingress.kubernetes.io/canary: "true"
        # 僅允許20%的流量會被路由到新版本服務model-v2中。
        # 預設總值為100。
        nginx.ingress.kubernetes.io/canary-weight: "20"
    spec:
      rules:
      - host: model.example.com
        http:
          paths:
          # 新版本服務。
          - path: /
            backend:
              service: 
                name: model-v2-svc
                port:
                  number: 80
            pathType: ImplementationSpecific
  2. 部署灰階發布策略。

    kubectl apply -f gray-release-canary.yaml
  3. 驗證流量的流向。

    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%的流量被路由到新版本(model-v2)的服務上,其他流量則流向穩定版本(model-v1)的服務,兩種服務比例大約為1:4。即灰階策略已生效。

步驟四:版本切換

當新版本服務已經穩定並且符合預期後,需要下線老版本的服務 ,僅保留新版本服務線上上運行。

  1. 更新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
  2. 執行以下命令,重新部署Service。

    kubectl apply -f model-svc.yaml 
  3. 查看路由訪問情況。

    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"]}]}%  

    重複執行以上命令後,您可以觀察到100%的請求(流量)被路由到新版本(model-v2)的服務上。

  4. 執行以下命令,刪除舊版本服務。

    kubectl delete ingress gray-release-canary
    arena serve delete model-v1
    kubectl delete svc model-v2-svc

    至此,完成從舊版本到新版本過渡過程中的清理工作。