流量管理最佳實踐

本節提供特定的部署或組態指南,以避免網路或流量管理問題。

設定服務的預設路由

雖然預設的 Istio 行為很方便,會將來自任何來源的流量傳送到目標服務的所有版本,而無需設定任何規則,但在 Istio 中,從一開始就為每個服務建立具有預設路由的 VirtualService 通常被認為是最佳實務。

即使您最初只有一個服務版本,一旦您決定部署第二個版本,您也需要在新版本啟動之前就設定好路由規則,以防止它立即以不受控制的方式接收流量。

當依賴 Istio 的預設循環式路由時,另一個潛在的問題是 Istio 的目標規則評估演算法中的一個微妙之處。在路由請求時,Envoy 會先評估虛擬服務中的路由規則,以判斷是否要路由到特定的子集。如果是,它才會啟用與該子集對應的任何目標規則策略。因此,只有當您明確將流量路由到對應的子集時,Istio 才會應用您為特定子集定義的策略。

例如,考慮以下目標規則,它是為 reviews 服務定義的唯一組態,也就是說,對應的 VirtualService 定義中沒有路由規則

apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: reviews
spec:
  host: reviews
  subsets:
  - name: v1
    labels:
      version: v1
    trafficPolicy:
      connectionPool:
        tcp:
          maxConnections: 100

即使 Istio 的預設循環式路由偶爾會呼叫 “v1” 實例,甚至如果 “v1” 是唯一正在執行的版本,也會總是呼叫 “v1” 實例,但上述流量策略永遠不會被調用。

您可以用以下兩種方式之一修正上面的範例。您可以將流量策略向上移動到 DestinationRule 中的較高層級,使其適用於任何版本

apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: reviews
spec:
  host: reviews
  trafficPolicy:
    connectionPool:
      tcp:
        maxConnections: 100
  subsets:
  - name: v1
    labels:
      version: v1

或者,更好的是,在 VirtualService 定義中為服務定義適當的路由規則。例如,為 “reviews:v1” 新增一個簡單的路由規則

apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v1

控制跨命名空間的組態共享

您可以在一個命名空間中定義虛擬服務、目標規則或服務條目,然後在其他命名空間中重複使用它們,前提是它們已匯出到這些命名空間。Istio 預設會將所有流量管理資源匯出到所有命名空間,但您可以使用 exportTo 欄位覆寫可見性。例如,只有來自相同命名空間中工作負載的請求才會受到以下虛擬服務的影響

apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: myservice
spec:
  hosts:
  - myservice.com
  exportTo:
  - "."
  http:
  - route:
    - destination:
        host: myservice

在特定命名空間中設定目標規則的可見性並不能保證該規則會被使用。將目標規則匯出到其他命名空間可讓您在這些命名空間中使用它,但要在請求期間實際應用它,命名空間也需要位於目標規則查詢路徑上

  1. 用戶端命名空間
  2. 服務命名空間
  3. 已組態的 meshconfig.rootNamespace 命名空間(預設為 istio-system

例如,考慮以下目標規則

apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: myservice
spec:
  host: myservice.default.svc.cluster.local
  trafficPolicy:
    connectionPool:
      tcp:
        maxConnections: 100

假設您在命名空間 ns1 中建立此目標規則。

如果您從 ns1 中的用戶端向 myservice 服務發送請求,則會應用該目標規則,因為它位於查詢路徑上的第一個命名空間中,也就是在用戶端命名空間中。

如果您現在從不同的命名空間(例如 ns2)發送請求,則用戶端不再與目標規則 ns1 位於同一命名空間中。因為對應的服務 myservice.default.svc.cluster.local 也不是在 ns1 中,而是在 default 命名空間中,因此在查詢路徑的第二個命名空間(服務命名空間)中也找不到該目標規則。

即使 myservice 服務已匯出到所有命名空間,因此在 ns2 中可見,並且目標規則也已匯出到包括 ns2 在內的所有命名空間,但它在來自 ns2 的請求期間不會被應用,因為它不位於查詢路徑上的任何命名空間中。

您可以透過在與對應服務相同的命名空間中建立目標規則來避免此問題,在此範例中為 default。然後,它將被應用於來自任何命名空間中用戶端的請求。您也可以將目標規則移動到 istio-system 命名空間(查詢路徑上的第三個命名空間),除非該目標規則確實是適用於所有命名空間的全域組態,並且它需要管理員權限,否則不建議這樣做。

Istio 使用此受限的目標規則查詢路徑有兩個原因

  1. 防止定義可覆寫完全不相關命名空間中服務行為的目標規則。
  2. 在同一主機有多個目標規則的情況下,有一個明確的查詢順序。

將大型虛擬服務和目標規則分割為多個資源

在單一 VirtualServiceDestinationRule 資源中,為特定主機定義完整路由規則或策略集不方便的情況下,最好以遞增方式在多個資源中指定主機的組態。如果這些目標規則和虛擬服務繫結到閘道,控制平面將會合併它們。

考慮繫結到一個入口閘道的 VirtualService 的情況,該閘道公開一個應用程式主機,該應用程式主機使用基於路徑的委派到多個實作服務,如下所示

apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: myapp
spec:
  hosts:
  - myapp.com
  gateways:
  - myapp-gateway
  http:
  - match:
    - uri:
        prefix: /service1
    route:
    - destination:
        host: service1.default.svc.cluster.local
  - match:
    - uri:
        prefix: /service2
    route:
    - destination:
        host: service2.default.svc.cluster.local
  - match:
    ...

這種組態的缺點是,任何基礎微服務的其他組態(例如路由規則)也需要包含在此單一組態檔案中,而不是在與個別服務團隊相關聯且可能由其擁有的個別資源中。有關詳細資訊,請參閱 路由規則對入口閘道請求沒有作用

為了避免此問題,最好將 myapp.com 的組態分解為數個 VirtualService 片段,每個後端服務一個。例如

apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: myapp-service1
spec:
  hosts:
  - myapp.com
  gateways:
  - myapp-gateway
  http:
  - match:
    - uri:
        prefix: /service1
    route:
    - destination:
        host: service1.default.svc.cluster.local
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: myapp-service2
spec:
  hosts:
  - myapp.com
  gateways:
  - myapp-gateway
  http:
  - match:
    - uri:
        prefix: /service2
    route:
    - destination:
        host: service2.default.svc.cluster.local
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: myapp-...

當套用現有主機的第二個和後續 VirtualService 時,istiod 會將其他路由規則合併到主機的現有組態中。但是,此功能有一些注意事項,使用時必須仔細考慮。

  1. 雖然任何給定來源 VirtualService 中規則的評估順序將會保留,但跨資源的順序是未定義的。換句話說,跨片段組態的規則沒有保證的評估順序,因此只有當片段之間沒有衝突的規則或規則之間的順序相依性時,它才會有可預測的行為。
  2. 片段中應該只有一個「萬用」規則(即沒有 match 欄位的規則)。所有此類「萬用」規則都將移動到合併組態中清單的末尾,但由於它們捕獲所有請求,因此無論哪個先應用,都會實質上覆寫並停用任何其他規則。
  3. 只有當 VirtualService 繫結到閘道時,才能以這種方式將其片段化。Sidecar 不支援主機合併。

DestinationRule 也可以片段化,具有類似的合併語意和限制。

  1. 對於同一主機的多個目標規則,應該只有一個給定子集的定義。如果有多個具有相同名稱的子集,則會使用第一個定義,並捨棄任何後續的重複項。不支援合併子集內容。
  2. 同一主機應只有一個最上層 trafficPolicy。當在多個目標規則中定義最上層流量策略時,將會使用第一個處理的策略。任何後續的最上層 trafficPolicy 組態都會被捨棄。
  3. 與虛擬服務合併不同,目標規則合併在 Sidecar 和閘道中都有效。

重新設定服務路由時避免 503 錯誤

當設定路由規則以將流量導向到服務的特定版本(子集)時,必須小心確保子集在路由中使用之前可用。否則,在重新組態期間,對服務的呼叫可能會傳回 503 錯誤。

使用單個 kubectl 呼叫(例如,kubectl apply -f myVirtualServiceAndDestinationRule.yaml)建立定義對應子集的 VirtualServicesDestinationRules 是不夠的,因為資源會以最終一致的方式(從組態伺服器,即 Kubernetes API 伺服器)傳播到 istiod 實例。如果使用子集的 VirtualService 比定義子集的 DestinationRule 先到達,istiod 產生的 Envoy 組態將會參照不存在的上游池。這會導致 HTTP 503 錯誤,直到所有組態物件都可供 istiod 使用為止。

為了確保服務在組態包含子集的路由時具有零停機時間,請遵循如下所述的「先建立,後中斷」流程

  • 新增新的子集時

    1. 先更新 DestinationRules 以新增新的子集,再更新任何使用它的 VirtualServices。使用 kubectl 或任何平台特定的工具套用規則。

    2. 等待幾秒鐘,讓 DestinationRule 組態傳播到 Envoy Sidecar

    3. 更新 VirtualService 以參照新新增的子集。

  • 移除子集時

    1. 在從 DestinationRule 移除子集之前,先更新 VirtualServices 以移除對子集的任何參照。

    2. 等待幾秒鐘,讓 VirtualService 組態傳播到 Envoy Sidecar。

    3. 更新 DestinationRule 以移除未使用的子集。

此資訊對您是否有幫助?
您有任何改進建議嗎?

感謝您的回饋!