博客 / 詳情

返回

istio流量分發實戰:從配置到踩坑全解析

前言

上一小節,istio成功的安裝,並且還解決了常見的426的問題,本節內容主要探討一下istio關於流量轉發的問題

按比例分發

配置

需要創建一個backend-v1,它與backend的selector都是app: backend,backend-v1部署完成之後,它會立即分走50%的流量,為了測試istio流控,我們需要在不改變任何配置的情況下實現9:1分流,也就是90%進入原backend,10%進入新的backend-v1

watermarked-istio_functions_1

  • 標記2個deployment,追加標籤,backend為version: v0,backend-v1為version: v1

    kubectl patch deployment backend -p '{"spec":{"template":{"metadata":{"labels":{"version":"v0"}}}}}'
    kubectl patch deployment backend-v1 -p '{"spec":{"template":{"metadata":{"labels":{"version":"v1"}}}}}'
    
  • 創建istio資源:DestinationRule,該資源主要用來標記istio要往哪個地方轉發

    apiVersion: networking.istio.io/v1
    kind: DestinationRule
    metadata:
      name: backend-dr
      namespace: default
    spec:
      host: backend-service
      subsets:
      - labels:
          version: v0
        name: v0
      - labels:
          version: v1
        name: v1
    
    
  • 創建istio資源:VirtualService,該資源用來確定轉發的權重

    apiVersion: networking.istio.io/v1
    kind: VirtualService
    metadata:
      name: backend-vs
      namespace: default
    spec:
      hosts:
      - backend-service
      http:
      - route:
        - destination:
            host: backend-service
            subset: v0
          weight: 90
        - destination:
            host: backend-service
            subset: v1
          weight: 10
    

調試

  • 測試命令: for i in {1..10}; do curl -s 10.22.12.178:30785/test > /dev/null ; done

  • 登錄到k8s的istio-proxy控制枱查看: kubectl logs -f -l app=backend -c istio-proxy

    [2026-01-28T08:24:55.670Z] "GET /test HTTP/1.1" 200 - upstream=10.244.0.55:10000 duration=0ms route=default
    [2026-01-28T08:24:55.687Z] "GET /test HTTP/1.1" 200 - upstream=10.244.0.55:10000 duration=0ms route=default
    [2026-01-28T08:24:55.706Z] "GET /test HTTP/1.1" 200 - upstream=10.244.0.53:10000 duration=0ms route=default
    [2026-01-28T08:24:55.741Z] "GET /test HTTP/1.1" 200 - upstream=10.244.0.53:10000 duration=1ms route=default
    [2026-01-28T08:24:55.751Z] "GET /test HTTP/1.1" 200 - upstream=10.244.0.53:10000 duration=0ms route=default
    [2026-01-28T08:24:55.759Z] "GET /test HTTP/1.1" 200 - upstream=10.244.0.53:10000 duration=0ms route=default
    [2026-01-28T08:24:55.696Z] "GET /test HTTP/1.1" 200 - upstream=10.244.0.55:10000 duration=0ms route=default
    [2026-01-28T08:24:55.716Z] "GET /test HTTP/1.1" 200 - upstream=10.244.0.55:10000 duration=0ms route=default
    [2026-01-28T08:24:55.725Z] "GET /test HTTP/1.1" 200 - upstream=10.244.0.55:10000 duration=0ms route=default
    [2026-01-28T08:24:55.734Z] "GET /test HTTP/1.1" 200 - upstream=10.244.0.55:10000 duration=0ms route=default
    
    
    ▶ kubectl get pod -owide
    NAME                          READY   STATUS    RESTARTS   AGE     IP            NODE     NOMINATED NODE   READINESS GATES
    backend-86b958bdc-5zjgn       2/2     Running   0          21m     10.244.0.53   wilson   <none>           <none>
    backend-v1-75ccff86dc-sl6bt   2/2     Running   0          119s    10.244.0.55   wilson   <none>           <none>
    nginx-test-7d87875694-8vsrp   2/2     Running   0          30m     10.244.0.61   wilson   <none>           <none>
    
  • 明顯不對,10.244.0.55與10.244.0.53的比例並沒有呈現9:1,轉發到backend要backend-v1還是5:5

修復

可以直接修改nginx的配置

server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    location /test {
        proxy_http_version 1.1;
        # proxy_set_header Host $host; # 原配置
        proxy_set_header Host backend-service.default.svc.cluster.local; # 新配置
        proxy_pass http://backend-service:10000;
    }
}

重啓之後再次測試:

[2026-01-28T08:30:59.968Z] "GET /test HTTP/1.1" 200 - upstream=10.244.0.53:10000 duration=0ms route=default
[2026-01-28T08:30:59.988Z] "GET /test HTTP/1.1" 200 - upstream=10.244.0.53:10000 duration=1ms route=default
[2026-01-28T08:31:00.027Z] "GET /test HTTP/1.1" 200 - upstream=10.244.0.53:10000 duration=1ms route=default
[2026-01-28T08:31:00.037Z] "GET /test HTTP/1.1" 200 - upstream=10.244.0.53:10000 duration=0ms route=default
[2026-01-28T08:31:00.048Z] "GET /test HTTP/1.1" 200 - upstream=10.244.0.53:10000 duration=0ms route=default
[2026-01-28T08:31:00.056Z] "GET /test HTTP/1.1" 200 - upstream=10.244.0.53:10000 duration=0ms route=default
[2026-01-28T08:31:00.008Z] "GET /test HTTP/1.1" 200 - upstream=10.244.0.55:10000 duration=0ms route=default
[2026-01-28T08:31:00.066Z] "GET /test HTTP/1.1" 200 - upstream=10.244.0.53:10000 duration=0ms route=default
[2026-01-28T08:31:00.074Z] "GET /test HTTP/1.1" 200 - upstream=10.244.0.53:10000 duration=0ms route=default
[2026-01-28T08:31:00.083Z] "GET /test HTTP/1.1" 200 - upstream=10.244.0.53:10000 duration=0ms route=default

已經生效了,這次只有1次10.244.0.55:10000

疑問

有位大哥説了,如果這樣配置的,明顯影響了業務:

  • nginx的配置被修改了
  • 所有的host被寫死了,都成了:backend-service.default.svc.cluster.local,而後端業務是需要把客户端的host帶入過去的,改了之後後端業務收到嚴重影響

確實,固定host屬於粗暴簡單的寫法,還有更加驚喜的解決方法,調整VirtualService,添加hosts

apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: backend-vs
  namespace: default
spec:
  hosts:
  - backend-service
  - api.wilsontest.com # 新增
  http:
  - route:
    - destination:
        host: backend-service
        subset: v0
      weight: 90
    - destination:
        host: backend-service
        subset: v1
      weight: 10

客户端訪問的時候必須帶上該域名: for i in {1..10}; do curl -s -H 'host: api.wilsontest.com' 10.22.12.178:30785/test > /dev/null ; done

這樣也可以解決問題,不過坑點也來了,年久失修,從無數前人繼承的祖傳代碼,就需要好好的梳理到底有哪些host來訪問,否則漏掉host的話,就會出現配置問題。-_-!

再次凸顯了istio之中,host是非常非常重要的,Istio 的路由決策、Service 的匹配完全依賴 Host 頭

  • Istio 的 VirtualService 本質上是一個“增強版”的路由器。如果發現請求的 Host 是 backend-service,就按 90:10 分配。
  • 之前的配置是$host,由於客户端沒有傳輸host,當請求經過 Nginx 的 Sidecar時,它會檢查Host,發現為空。由於路由表裏沒有對應的記錄 ,sidecar並不認識,按普通 K8s 流量處理

按header分發

apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: backend-vs
  namespace: default
spec:
  hosts:
  - backend-service
  - api.wilsontest.com
  http:
  - match:
    - headers:
        hellotest:
          exact: "true"
    route:
    - destination:
        host: backend-service
        subset: v1
  - route:
    - destination:
        host: backend-service
        subset: v0

curl -s -H 'host: api.wilsontest.com' -H 'hellotest: true' 10.22.12.178:30785/test。只有header裏面匹配了hellotest: true才會去v1,否則全部去v0

按前綴分發

apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: backend-vs
  namespace: default
spec:
  hosts:
  - backend-service
  - api.wilsontest.com
  http:
  - match:
    - uri:
        prefix: /test/v1
    route:
    - destination:
        host: backend-service
        subset: v1
  - route:
    - destination:
        host: backend-service
        subset: v0

帶有/test/v1前綴的都會去新版本v1,滿足不了條件都會走默認的版本v0

url改寫

apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: backend-vs
  namespace: default
spec:
  hosts:
  - backend-service
  - api.wilsontest.com
  http:
  - match:
    - uri:
        prefix: /test/v1
    route:
    - destination:
        host: backend-service
        subset: v1
  - match:
    - uri:
        prefix: /test/v2
    rewrite:
      uri: /test
    route:
    - destination:
        host: backend-service
        subset: v0
  - route:
    - destination:
        host: backend-service
        subset: v0

如果是/test/v1,就訪問v1版本,/test/v2重寫成/test並且訪問v0版本,其餘的默認都會走v0版本

藍綠、金絲雀、灰度、A/B測試

關於流量分流的各種操作,大部分都集中在以下場景:

  • 藍綠:實現瞬間切換與零宕機回滾,消除發佈期間的中間狀態
  • 金絲雀:像礦工用金絲雀探測毒氣一樣,先讓一小部分用户(如1%~5%)訪問新版本,觀察系統指標(如錯誤率、延遲),若無問題再逐步擴大範圍
  • 灰度:將用户羣體按比例或特定規則(如地域、設備)逐步切換到新版本(例如10%→30%→100%),持續觀察反饋
  • A/B:同時向隨機分組的用户展示不同版本(A組用舊版,B組用新版),通過統計指標(如點擊率、轉化率)判斷哪個版本更優
藍綠髮布 金絲雀發佈 灰度發佈 A/B測試
主要目標 零停機、瞬時回滾 用真實流量快速發現技術風險 平穩、可控地逐步替換所有用户 驗證不同版本的業務效果
流量路由 全量切換(100%→0%) 極小比例引流(如1%-5%) 按比例分階段擴大(10%→50%→100%) 按規則/隨機分配(如50%/50%)
關注重點 系統可用性與回滾速度 系統穩定性指標(錯誤率、延遲) 發佈過程平穩性與綜合反饋 業務指標(轉化率、留存率)
所需資源 兩套完整環境,成本高 一套環境,新版本實例較少 一套環境,新舊版本實例共存 一套或多套環境,並行運行多個版本
用户選擇 全體用户同時切換 小部分用户隨機或按基礎設施選擇 用户按比例或屬性逐步遷移 用户隨機分組或按屬性定向分配
持續時間 極短(切換在幾分鐘內) 短(幾小時到一天) 中長(幾天到數週) 長(數週到數月)
典型場景 關鍵業務大版本升級、基礎設施更換 後端服務、中間件、數據庫變更 前端功能、用户界面更新 UI設計、文案、算法策略、定價優化

聯繫我

  • 聯繫我,做深入的交流


至此,本文結束
在下才疏學淺,有撒湯漏水的,請各位不吝賜教...

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.