Service Mesh - Istio流量控制篇(上)

動態路由:用Virtual Service和Destination Rule設置路由規則

路由這個功能是流量控制裏面非常重要,也是最常用的一個功能。在Istio裏一般通過Virtual Service(虛擬服務)以及Destination Rule(目標規則)這兩個API資源進行動態路由的設置。

基本概念

虛擬服務(Virtual Service):

  • 定義路由規則,匹配請求
  • 描述滿足條件的請求去哪裏

目標規則(Destination Rule):

  • 定義子集、策略
  • 描述到達目標的請求怎麼處理

Service Mesh - Istio流量控制篇(上)

實踐動態路由

在上一篇Service Mesh - Istio安裝與部署文章中,我們演示了BookInfo這個Demo應用的部署,並且可以發現其中的 reviews 服務共有三個不同的版本。現在我們的需求是將請求路由到 reviews 服務的指定版本上。例如,路由到版本1上,如下圖:
Service Mesh - Istio流量控制篇(上)

實現該需求很簡單,官方已經提供了配置清單文件,我們只需執行如下命令應用路由規則即可:

[root@m1 ~]# kubectl apply -f /usr/local/istio-1.8.1/samples/bookinfo/networking/virtual-service-all-v1.yaml    # 創建虛擬服務
virtualservice.networking.istio.io/productpage created
virtualservice.networking.istio.io/reviews created
virtualservice.networking.istio.io/ratings created
virtualservice.networking.istio.io/details created
[root@m1 ~]# kubectl apply -f /usr/local/istio-1.8.1/samples/bookinfo/networking/destination-rule-all.yaml   # 創建目標規則
destinationrule.networking.istio.io/productpage created
destinationrule.networking.istio.io/reviews created
destinationrule.networking.istio.io/ratings created
destinationrule.networking.istio.io/details created
[root@m1 ~]# 

然後回到應用頁面上測試下規則是否生效,正常情況下,此時無論怎麼刷新頁面,訪問的都是版本1的 reviews 服務:
Service Mesh - Istio流量控制篇(上)

那麼配置是如何生效的呢?我們先來看看這兩個API資源它們的一些具體配置項:
Service Mesh - Istio流量控制篇(上)

  • Virtual Service
    • hosts:對應 DestinationRule 所配置的host,可配置多個
    • gateways:用來和配置的網關進行匹配使用的,如果是服務網關內部的虛擬服務就不需要配置這一項
    • http:配置http請求的路由規則與 HTTPRoute 對應
    • tls:配置tls請求的路由規則
    • tcp:配置tcp請求的路由規則
    • exportTo:給虛擬服務設置它的可見性,例如設置爲所有的Namspace都可見
  • HTTPRoute:
    • match:設置匹配滿足什麼樣條件的請求,對應 HTTPMatchRequest 對象配置項
    • route:匹配到的請求經過route配置的規則進行路由
  • HTTPRouteDestination:
    • destination:通過該字段將虛擬服務與目標規則進行綁定
  • Destination Rule
    • host:最終路由到的具體目標地址
    • subset:子集,一般主要是給服務限定版本

virtual-service-all-v1.yaml 文件中針對 reviews 服務創建的虛擬服務配置內容如下:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService  # 資源類型
metadata:
  name: reviews   # 虛擬服務的名稱
spec:
  hosts:
  - reviews   # 虛擬服務的主機名,可定義多個
  http:  # 沒有定義match代表匹配任意的請求
  - route:
    - destination:
        host: reviews   # 服務名稱,與目標規則中的host配置對應
        subset: v1      # 通過子集限定了服務版本

destination-rule-all.yaml 文件中針對 reviews 服務創建的目標規則配置內容如下:

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule  # 資源類型
metadata:
  name: reviews   # 目標規則的名稱
spec:
  host: reviews   # 指向k8s中的服務名稱,k8s平臺的dns機制可以解析出具體的服務地址
  subsets:   # 定義子集,對應該服務的三個版本,虛擬服務就是根據在這裏定義的子集來路由對應的版本的
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
  - name: v3
    labels:
      version: v3

Virtual Service 和 Destination Rule 的應用場景:

  • 按服務版本路由
  • 按比例切分流量
  • 根據匹配規則進行路由
  • 定義各種策略(負載均衡、連接池等)

網關:用Gateway管理進入網格的流量

什麼是網關(Gateway)

  • 一個運行在網格邊緣的負載均衡器
  • 接收外部請求,轉發給網格內的服務
  • 配置對外的端口、協議與內部服務的映射關係
  • Istio中的Ingress網關控制入口流量,Egress網關控制出口流量,在網關只定義入口點不定義具體的路由
  • 與k8s中的Ingress一樣,Istio中的Gateway也只是一種資源,需要配合一個真正工作的組件使用,在k8s中通常是ingress-nginx,在Istio中則是基於envoy的istio-ingressgateway / istio-egressgateway
    Service Mesh - Istio流量控制篇(上)
  • 官方文檔

實踐創建網關

我們來創建一個入口網關,配合虛擬服務對外暴露一些服務接口:
Service Mesh - Istio流量控制篇(上)

Gateway資源的一些配置選項
Service Mesh - Istio流量控制篇(上)

  • Gateway:
    • servers:定義入口點列表
    • selector:選擇器,用於通過label選擇集羣中Istio網關的Pod
  • Server:
    • port:暴露給外部訪問的端口信息,包括端口號、名稱、協議
    • hosts:暴露給外部可訪問的host列表
  • VirtualService:
    • gateways:用於配置關聯哪些Gateway資源,通過名稱指定

創建 test-gateway.yaml 文件,內容如下:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway   # 資源類型爲網關
metadata:
  name: test-gateway   # 網關的名稱
spec:
  selector:   # 配置選擇器,指向istio-ingressgateway的pod
    istio: ingressgateway
  servers:  # 定義對外暴露的入口點,主要配置哪些host和端口允許訪問
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"

---

# 由於在網關只定義入口點不定義具體的路由,所以我們需要定義虛擬服務來配置路由規則
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService   # 資源類型爲虛擬服務
metadata:
  name: test-gateway    # 虛擬服務的名稱
spec:
  hosts:
  - "*"
  gateways:   # 配置gateway的名稱,用於關聯具體的gateway
  - test-gateway
  http:
  - match:   # 定義路由匹配規則,即暴露哪些接口
    - uri:
        prefix: /details
    - uri:
        exact: /health
    route:
    - destination:   # 將請求轉發到哪些目標
        host: details
        port:
          number: 9080

在應用該配置文件之前,我們先到瀏覽器上訪問 details 接口,此時返回的狀態碼將會是404:
Service Mesh - Istio流量控制篇(上)

然後應用配置文件,創建我們所定義的網關和虛擬服務:

[root@m1 ~]# kubectl apply -f service-mash/test-gateway.yaml
gateway.networking.istio.io/test-gateway created
virtualservice.networking.istio.io/test-gateway created
[root@m1 ~]# 

通過網關和虛擬服務暴露接口後,再重新訪問一下該接口,可以看到能正常訪問了:
Service Mesh - Istio流量控制篇(上)

同樣,health 接口也可以被訪問到:
Service Mesh - Istio流量控制篇(上)

Gateway 的應用場景:

  • 暴露網格內服務給外界訪問
  • 訪問安全(HTTPS、mTLS 等)
  • 統一應用入口,API 聚合

服務入口:用Service Entry擴展你的網格服務

官方文檔:

什麼是服務入口(ServiceEntry):

  • 服務入口與網關正好是相反的概念,服務入口是用於添加外部服務到網格內
  • 管理到外部服務的請求,對外部服務進行抽象,使得可以像訪問內部服務一樣訪問外部服務
  • 擴展網格,例如需要給多個集羣共享同一個Mesh的場景
    Service Mesh - Istio流量控制篇(上)

接下來我們實踐一下,將 httpbin.org 註冊爲網格內部的服務,並配置流控策略。httpbin.org 這個網站能測試 HTTP 請求和響應的各種信息,比如 cookie、ip、headers 和登錄驗證等,且支持 GET、POST 等多種方法,對 web 開發和測試很有幫助,我們可以將它作爲一個外部的服務進行測試。

由於我們之前部署的Bookinfo應用的所有服務中都沒有 curl 命令,因此我們想要模擬內部服務調用外部服務,就需要額外添加一個 sleep 服務。官方已經給我們準備好了配置文件,使用如下命令應用一下即可:

[root@m1 ~]# kubectl apply -f /usr/local/istio-1.8.1/samples/sleep/sleep.yaml
serviceaccount/sleep created
service/sleep created
deployment.apps/sleep created
[root@m1 ~]# 

通過 sleep 服務來訪問一下 httpbin 這個外部服務:

[root@m1 ~]# kubectl exec -it sleep-854565cb79-gm6hj -c sleep -- curl http://httpbin.org/headers
{
  "headers": {
    "Accept": "*/*", 
    "Content-Length": "0", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/7.69.1", 
    "X-Amzn-Trace-Id": "Root=1-5fe0b2c6-0940d09657bb9faf42e9f49e", 
    "X-B3-Sampled": "1", 
    "X-B3-Spanid": "c8f80494e0358f1f", 
    "X-B3-Traceid": "46b7447441931824c8f80494e0358f1f", 
    "X-Envoy-Attempt-Count": "1", 
    "X-Envoy-Peer-Metadata": "ChkKDkFQUF9DT05UQUlORVJTEgcaBXNsZWVwChoKCkNMVVNURVJfSUQSDBoKS3ViZXJuZXRlcwoYCg1JU1RJT19WRVJTSU9OEgcaBTEuOC4xCt8BCgZMQUJFTFMS1AEq0QEKDgoDYXBwEgcaBXNsZWVwChkKDGlzdGlvLmlvL3JldhIJGgdkZWZhdWx0CiEKEXBvZC10ZW1wbGF0ZS1oYXNoEgwaCjg1NDU2NWNiNzkKJAoZc2VjdXJpdHkuaXN0aW8uaW8vdGxzTW9kZRIHGgVpc3RpbwoqCh9zZXJ2aWNlLmlzdGlvLmlvL2Nhbm9uaWNhbC1uYW1lEgcaBXNsZWVwCi8KI3NlcnZpY2UuaXN0aW8uaW8vY2Fub25pY2FsLXJldmlzaW9uEggaBmxhdGVzdAoaCgdNRVNIX0lEEg8aDWNsdXN0ZXIubG9jYWwKIAoETkFNRRIYGhZzbGVlcC04NTQ1NjVjYjc5LWdtNmhqChYKCU5BTUVTUEFDRRIJGgdkZWZhdWx0CkkKBU9XTkVSEkAaPmt1YmVybmV0ZXM6Ly9hcGlzL2FwcHMvdjEvbmFtZXNwYWNlcy9kZWZhdWx0L2RlcGxveW1lbnRzL3NsZWVwChcKEVBMQVRGT1JNX01FVEFEQVRBEgIqAAoYCg1XT1JLTE9BRF9OQU1FEgcaBXNsZWVw", 
    "X-Envoy-Peer-Metadata-Id": "sidecar~172.22.152.249~sleep-854565cb79-gm6hj.default~default.svc.cluster.local"
  }
}
[root@m1 ~]# 

從輸出結果可以看到,從 sleep 服務的內部能夠直接訪問外部的 httpbin 服務,原因是Istio裏默認允許所有的網格內的服務直接訪問外部服務。所以爲了測試服務入口這個API資源,我們需要把這種允許訪問外部服務的行爲給關掉,使得只有註冊過的服務才能訪問外部服務。

使用如下命令可關閉出流量可訪問權限(outboundTrafficPolicy = REGISTRY_ONLY):

[root@m1 ~]# istioctl install --set profile=demo -y --set meshConfig.outboundTrafficPolicy.mode=REGISTRY_ONLY

此時再通過 sleep 訪問 httpbin 就訪問不到了,沒有任何輸出:

[root@m1 ~]# kubectl exec -it sleep-854565cb79-gm6hj -c sleep -- curl http://httpbin.org/headers
[root@m1 ~]# 

現在我們來爲外部服務(httpbin)配置 ServiceEntry ,讓 sleep 服務通過服務入口來訪問外部服務。具體命令如下:

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry   # 資源類型爲服務入口
metadata:
  name: httpbin-ext   # 服務入口的名稱 
spec:
  hosts:   # 外部服務的訪問地址列表,域名或ip
  - httpbin.org
  ports:   # 配置外部服務的訪問端口信息
  - number: 80
    name: http
    protocol: HTTP
  resolution: DNS   # 指定服務發現機制
  location: MESH_EXTERNAL  # 定義該服務位於網格內部還是網格外部
EOF

通過如下命令可查看已創建的服務入口:

[root@m1 ~]# kubectl get se
NAME          HOSTS             LOCATION        RESOLUTION   AGE
httpbin-ext   ["httpbin.org"]   MESH_EXTERNAL   DNS          97s
[root@m1 ~]# 

測試從 sleep 服務訪問 httpbin :

[root@m1 ~]# kubectl exec -it sleep-854565cb79-gm6hj -c sleep -- curl http://httpbin.org/headers
{
  "headers": {
    "Accept": "*/*", 
    "Content-Length": "0", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/7.69.1", 
    "X-Amzn-Trace-Id": "Root=1-5fe0bd82-51a06204013c7006305ac384", 
    "X-B3-Sampled": "1", 
    "X-B3-Spanid": "3cdd9036d7815b35", 
    "X-B3-Traceid": "08d1682631d190293cdd9036d7815b35", 
    "X-Envoy-Attempt-Count": "1", 
    "X-Envoy-Decorator-Operation": "httpbin.org:80/*", 
    "X-Envoy-Peer-Metadata": "ChkKDkFQUF9DT05UQUlORVJTEgcaBXNsZWVwChoKCkNMVVNURVJfSUQSDBoKS3ViZXJuZXRlcwoYCg1JU1RJT19WRVJTSU9OEgcaBTEuOC4xCt8BCgZMQUJFTFMS1AEq0QEKDgoDYXBwEgcaBXNsZWVwChkKDGlzdGlvLmlvL3JldhIJGgdkZWZhdWx0CiEKEXBvZC10ZW1wbGF0ZS1oYXNoEgwaCjg1NDU2NWNiNzkKJAoZc2VjdXJpdHkuaXN0aW8uaW8vdGxzTW9kZRIHGgVpc3RpbwoqCh9zZXJ2aWNlLmlzdGlvLmlvL2Nhbm9uaWNhbC1uYW1lEgcaBXNsZWVwCi8KI3NlcnZpY2UuaXN0aW8uaW8vY2Fub25pY2FsLXJldmlzaW9uEggaBmxhdGVzdAoaCgdNRVNIX0lEEg8aDWNsdXN0ZXIubG9jYWwKIAoETkFNRRIYGhZzbGVlcC04NTQ1NjVjYjc5LWdtNmhqChYKCU5BTUVTUEFDRRIJGgdkZWZhdWx0CkkKBU9XTkVSEkAaPmt1YmVybmV0ZXM6Ly9hcGlzL2FwcHMvdjEvbmFtZXNwYWNlcy9kZWZhdWx0L2RlcGxveW1lbnRzL3NsZWVwChcKEVBMQVRGT1JNX01FVEFEQVRBEgIqAAoYCg1XT1JLTE9BRF9OQU1FEgcaBXNsZWVw", 
    "X-Envoy-Peer-Metadata-Id": "sidecar~172.22.152.249~sleep-854565cb79-gm6hj.default~default.svc.cluster.local"
  }
}
[root@m1 ~]# 

服務入口的配置選項
Service Mesh - Istio流量控制篇(上)


流量轉移:灰度發佈是如何實現的?

官方文檔:

藍綠部署

Service Mesh - Istio流量控制篇(上)

藍綠部署簡單理解就是當需要部署新版本時,不會去動老版本的服務,而是在另一個環境部署相同數量的新服務,然後當新的服務測試確認 OK 後,把流量切到新的服務這邊來。

其實這種部署方式,就是我們說的預發環境。生產線上有兩套相同的集羣,一套是 Prod 是真實服務的,另一套是 Stage 是預發環境,發佈發 Stage,然後把流量切到 Stage 這邊,於是 Stage 就成了 Prod,而之前的 Prod 則成了 Stage。有點像換頁似的。

這種方式的優點是沒有停機,實時發佈和升級,也避免有新舊版本同時在線的問題。但這種部署的問題就是有點浪費,因爲需要使用雙倍的資源(不過,這只是在物理機時代,在雲計算時代沒事,因爲虛擬機部署完就可以釋放了)。

另外,如果我們的服務中有狀態,比如一些緩存什麼的,停機部署和藍綠部署都會有問題。

灰度發佈(金絲雀發佈)

Service Mesh - Istio流量控制篇(上)

金絲雀部署又叫灰度部署,其得名來源於礦井中的金絲雀。17 世紀,英國礦井工人發現,金絲雀對瓦斯這種氣體十分敏感。空氣中哪怕有極其微量的瓦斯,金絲雀也會停止歌唱。而當瓦斯含量超過一定限度時,雖然魯鈍的人類毫無察覺,金絲雀卻早已毒發身亡。當時在採礦設備相對簡陋的條件下,工人們每次下井都會帶上一隻金絲雀作爲 ” 瓦斯檢測指標 “,以便在危險狀況下緊急撤離。

灰度部署是指逐漸將生產環境流量從老版本切換到新版本。通常流量是按比例分配的。例如 90% 的請求流向老版本,10% 的請求流向新版本。然後沒有發現問題,就逐步擴大新版本上的流量,減少老版本上的流量。

除了切流量外,對於多租戶的平臺,例如雲計算平臺,灰度部署也可以將一些新的版本先部署到一些用戶上,如果沒有問題,擴大部署,直到全部用戶。一般的策略是,從內部用戶開始,然後是一般用戶,最後是大客戶。

這個技術大多數用於缺少足夠測試,或者缺少可靠測試,或者對新版本的穩定性缺乏信心的情況下。把一部分用戶切到新版上來,然後看一下有沒有問題。如果沒有問題就繼續擴大升級,直到全部升級完成。

A/B 測試

Service Mesh - Istio流量控制篇(上)

AB 測試和藍綠部署或是金絲雀灰度部署完全是不一樣的。AB 測試是同時上線兩個版本,然後做相關的比較。它是用來測試應用功能表現的方法,例如可用性、受歡迎程度、可見性等。

藍綠部署是爲了不停機,灰度部署是對新版本的質量沒信心。而 AB 測試是對新版的功能沒信心。注意,一個是質量,一個是功能。

比如,網站 UI 大改版,推薦算法的更新,流程的改變,我們不知道新的版本否會得到用戶青睞或是能得到更好的用戶體驗,我們需要收集一定的用戶數據才能知道。

於是我們需要在生產線上發佈兩個版本,拉一部分用戶過來當小白鼠,然後通過科學的觀測得出來相關的結論。AB 測試旨在通過科學的實驗設計、採樣樣本代表性、流量分割與小流量測試等方式來獲得具有代表性的實驗結論,並確信該結論在推廣到全部流量時可信。

我們可以看到 AB 測試,其包含了灰度發佈的功能。也就是說,我們的觀測如果只是觀測有沒有 bug,那就是灰度發佈了。當然,如果我們複雜一點,要觀測用戶的一些數據指標,這完全也可能做成自動化的,如果新版本數據好,就自動化地切一點流量過來,如果不行,就換一批用戶(樣本)再試試。

對於灰度發佈或是 AB 測試可以使用下面的技術來選擇用戶:

  • 瀏覽器 cookie、查詢參數、地理位置。技術支持,如瀏覽器版本、屏幕尺寸、操作系統等。客戶端語言

實踐基於權重的路由

在Istio中我們可以配置基於權重的路由,將請求按比例路由到對應的服務版本來實現灰度發佈的效果。接下來我們利用 reviews 服務的多版本,模擬灰度發佈。官方demo已經提供了該配置文件,執行如下命令應用即可:

[root@m1 ~]# kubectl apply -f /usr/local/istio-1.8.1/samples/bookinfo/networking/virtual-service-reviews-50-v3.yaml
virtualservice.networking.istio.io/reviews configured
[root@m1 ~]# 

virtual-service-reviews-50-v3.yaml 文件的內容如下,就是通過 Virtual Service 配置了權重:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
    - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v1
      weight: 50   # 權重配置,表示50%的流量轉發到v1版本
    - destination:
        host: reviews
        subset: v3
      weight: 50   # 表示50%的流量轉發到v3版本

此時到應用頁面上進行一下測試,可以發現請求基本按照50%的比例轉發到v1和v3版本:
Service Mesh - Istio流量控制篇(上)
Service Mesh - Istio流量控制篇(上)


下篇:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章