1. 程式人生 > >Istio的流量管理(概念)(istio 系列二)

Istio的流量管理(概念)(istio 系列二)

## Istio的流量管理(概念) [TOC] 涵蓋istio官方文章的[Traffic Management](https://istio.io/docs/concepts/traffic-management/)章節 ### 概述 istio的流量路由規則可以簡單地控制不同服務間的流量以及API呼叫。Istio在服務層面提供了斷路器,超時,重試等功能,通過這些功能可以簡單地實現A/B測試,金絲雀釋出,基於百分比的流量分割等,此外還提供了開箱即用的故障恢復功能,用於增加應用的健壯性,以應對服務故障或網路故障。 istio的流量管理依賴Envoy代理,該代理作為sidecar與服務容器部署在同一個pod內,服務傳送或接收的流量都會經過Envoy,這樣就可以在不改變服務的情況下實現網格中的流量管理。 為了直接管理網格中的流量,istio需要了解所有的endpoints,以及哪些service對應哪些endpoints,為了將這些資訊推送到它的服務登錄檔(service reistry)中,istio需要連線到一個服務發現系統,例如,當istio安裝到一個kubernetes叢集時,istio會自動探測叢集的services和endpoints。 > service reistry:istio內部的服務登錄檔,包含了service的集合以及service對應的endpoint資訊。istio使用服務登錄檔生成Envoy的配置資訊。Pilot可以通過底層平臺提供的服務發現機制實現自動註冊service,也可以通過[ServiceEntry](https://istio.io/docs/concepts/traffic-management/#service-entries)手動註冊service。 使用服務登錄檔,Envoy代理可以將流量定向到相關的服務中。大多數基於微服務的應用會有多個例項,且每個例項處理一部分服務流量,這些例項有時也被稱為負載均衡池。預設情況下,Envoy代理會使用輪詢模式,通過服務的負載均衡池分發流量,此時會按照順序將請求傳送到負載均衡池中的各個成員。 雖然istio的基本服務發現和負載均衡提供了一個可執行的服務網格,但istio的功能遠非如此。在大多數場景下,使用者可能想更好地控制網格的流量,如在A/B測試中按照百分比將流量匯入一個新版本的服務,或對某些服務例項應用不同的負載均衡策略,對進出網格的流量應用特殊的規則,或將網格的外部依賴項新增到服務登錄檔中等。這些功能都可以通過istio的流量管理API,在istio中新增流量配置來實現。 跟其他istio配置一樣,流量管理API也使用CRD指定。下面介紹各個流量管理API資源,以及這些API的功能。API資源包括: - [Virtual services](https://istio.io/docs/concepts/traffic-management/#virtual-services) - [Destination rules](https://istio.io/docs/concepts/traffic-management/#destination-rules) - [Gateways](https://istio.io/docs/concepts/traffic-management/#gateways) - [Service entries](https://istio.io/docs/concepts/traffic-management/#service-entries) - [Sidecars](https://istio.io/docs/concepts/traffic-management/#sidecars) ### [Virtual services](https://istio.io/docs/reference/config/networking/virtual-service/#VirtualService) [Virtual services](https://istio.io/docs/reference/config/networking/virtual-service/#VirtualService)和[destination rules](https://istio.io/docs/concepts/traffic-management/#destination-rules)是istio流量路由功能的核心部分。virtual service規定了(在使用者平臺提供的基本連線和服務發現的基礎上)如何將一個請求路由到一個istio的服務網格中。每個virtual service包含一個按順序處理的路由規則集,istio會通過這些規則將每個匹配virtual service的請求分發到網格中一個特定的目的地。根據需求可以使用多個virtual service或不使用virtual service。 #### 為什麼使用virtual service virtual service將客戶端請求與目標負載進行解耦,通過這種方式,大大提升了istio流量管理的靈活性,並增強了流量管理的功能。virtual service還提供了大量方式來指定不同的流量路由規則,用於將流量傳送到目標負載中。 如果沒有virtual service,Envoy會像前面介紹的那樣,在所有服務例項中採用輪詢負載均衡方式進行流量分發。使用者需要了解具體的負載才能做進一步動作。例如,某些負載可能代表不同版本的服務,這樣就可以用於A/B測試,將不同百分比的流量分發到不同版本的服務中,或直接將流量分發到特定的服務例項。 使用virtual service後,就可以為一個或多個主機名指定流量行為,使用virtual service中的路由規則告訴Envoy如何將virtual service的流量傳送到合適的目的地。路由目的地可能對應相同版本的服務或完全不同版本的服務。 一個典型的場景是將流量傳送到不同版本的服務。客戶端將virtual service視為一個獨立的實體,並將請求傳送到virtual service,Envoy會根據virtual service中的規則將流量路由到不同的版本:例如,"20%的呼叫分發到新版本",或"將來自某些使用者的呼叫分發到版本2"。通過這種方式可以實現金絲雀釋出。流量路由完全獨立於例項的deployment,這意味著新版本的服務例項的數量可以根據流量負載擴大或縮小,而不必參考流量路由。相比之下,像Kubernetes這樣的容器編排平臺只支援基於例項縮放的流量分佈,這樣很快就會變得複雜。 Virtual services允許: - 通過一個virtual service處理多個應用服務。如果網格使用了kubernetes,可以通過一個virtual service處理特定名稱空間下的所有服務。通過將一個virtual service對映到多個"真實的"服務,可以方便將單一應用程式轉換為由不同的微服務組成的複合服務,而無需服務的使用者去適應這種轉變(*可以看作是kubernetes service之上的service*)。 - 將配置的流量規則與[gataways](https://istio.io/docs/concepts/traffic-management/#gateways)進行結合來控制ingress和egress的流量。 在一些場景下還需要配置destination rule來使用這些特性,此時需要指定服務子集(`subset`)。在一個獨立的物件中指定服務子集和其他destination指定的策略(*如負載均衡*),可以在不同的virtual service中重用這些配置。 #### Virtual services舉例 下面是virtual service的一個例子,它根據路由規則將請求分發到不同版本的服務 ```yaml apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: reviews spec: hosts: - reviews http: - match: - headers: end-user: exact: jason route: - destination: host: reviews subset: v2 #對應destination rule中的服務子集 - route: - destination: host: reviews subset: v3 ``` ##### hosts欄位 `hosts`欄位列出了virtual service的主機,即使用者可定位的目的地或這些路由規則應用的目的地。它是客戶端向服務傳送請求時使用的一個或多個地址(*該欄位的作用與kubernetes的clusterIP類似,是個虛擬服務地址的概念*)。 ```yaml hosts: - reviews ``` virtual service的主機名可以是IP地址、DNS名稱,也可以是短名稱(例如Kubernetes服務短名稱),該名稱隱式或顯式解析為完全限定的域名(FQDN),具體取決於istio依賴的平臺。可以使用字首萬用字元(“*”)為所有匹配的服務建立一組路由規則。virtual service的`host`不作為Istio服務登錄檔的一部分,它們只是**虛擬目的地**,允許使用者為網格無法路由到的虛擬主機建立流量模型。 #### 路由規則 `http`部分包含了virtual service的路由規則,描述了匹配條件,以及將HTTP/1.1,HTTP2和gRPC流量傳送到`hosts`欄位指定的目的地的相關動作。一個路由規則包含流量的目的地以及0個或多個條件,具體取決於實際的場景。 ##### match 第一個路由規則的例子如下,它有一個以`match`欄位開頭的條件,這種情況下希望路由來自使用者"jason"的所有請求,使用`headers`, `end-user`和`exact` 欄位來挑選合適的請求。 ```yaml - match: - headers: end-user: exact: jason ``` ##### destination 路由部分的`destination`欄位指定了匹配條件的流量的實際地址。與virtual service的主機不同,該host必須是存在於istio的服務登錄檔中的真實的destination,否則Envoy不知道應該將流量傳送到哪裡。它可以是一個帶代理的網格服務或使用service entry新增的非網格服務。在kubernetes作為平臺的情況下,`host`表示名為kubernetes的service名稱: ```yaml route: - destination: host: reviews subset: v2 ``` 注意在本章的其他例子中使用kubernetes的短名稱作為`destination`的主機。當使用這種規則時,istio會根據包含路由規則的virtual service的名稱空間新增域字尾,在這些例子使用短名稱意味著可以在任意名稱空間中應用這些規則。 Istio根據包含路由規則的虛擬服務的名稱空間新增域字尾來獲取主機的完整的名稱。 > 只有當destination主機和virtual service在相同的kubernetes名稱空間下面才會正常工作。由於kubernetes的短名稱可能會導致誤解,因此建議在生成環境中使用完整的主機名。 `destination` 部分也指定了期望請求的kubernetes的服務子集,上面例子中定義了名為v2的子集。 ##### 路由規則優先順序 路由規則按從上到下的順序執行, virtual service定義的第一個規則具有最高的優先順序。這種情況下當不匹配第一條規則時,會進行第二條規則的匹配,當第二條規則不匹配時,會進行第三條規則的匹配。 ```shell - route: - destination: host: reviews subset: v3 ``` 建議提供一個無需匹配的預設規則作為每個virtual service的最後一條規則,確保virtual service的流量能夠至少匹配到一條路由。 ##### 路由規則的更多介紹 正如上面描述的,可以通過設定路由規則將特定的流量分發到特定的目的地。例如,以下virtual service可以讓使用者將流量分發到兩個獨立的service(reviews和ratings)上,就像這兩個service是virtual service http://bookinfo.com/的一部分(*即virtual service作為kubernetes的service的service,可以方便後者進行擴充套件*)。virtual service的路由匹配規則基於請求的URLs,然後將請求分發到正確的service上。 ```yaml apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: bookinfo #virtual service的名稱 spec: hosts: - bookinfo.com #virtual service的地址 http: - match: #匹配條件一 - uri: prefix: /reviews route: ##如果匹配條件一,則路由到真實的reviews服務上 - destination: host: reviews - match: #匹配條件二 - uri: prefix: /ratings route: #如果匹配條件二,則路由到真實的ratings服務上 - destination: host: ratings ``` 對於一些匹配條件,使用者可以使用確切的值,字首或正則表示式。 可以在相同的`match`塊中新增多個匹配條件,多個匹配條件之間的關係為AND;或在相同的規則中新增多個`match`塊,多個`match`之間的關係為OR。對於一個給定的virtual service,可以配置多個路由規則,者使得一個virtual serivce中的路由條件變得更加複雜或更加簡單。可以在[HTTPMatchRequest reference](https://istio.io/docs/reference/config/networking/virtual-service/#HTTPMatchRequest)中找到完整的`match`欄位以及欄位值。 除了使用匹配條件,還可以使用百分比的"權重"來分發流量,通常用於A/B測試和金絲雀釋出。 ```yaml spec: hosts: - reviews http: - route: - destination: host: reviews subset: v1 weight: 75 - destination: host: reviews subset: v2 weight: 25 ``` 此外還可以使用路由規則對流量採取某些動作,如: - 追加或移除首部 - 重寫URL - 對本目的地的呼叫設定重試策略 更多參見[HTTPRoute reference](https://istio.io/docs/reference/config/networking/virtual-service/#HTTPRoute) ### [Destination Rule](https://istio.io/docs/reference/config/networking/destination-rule/) destination rule是istio流量路由功能的重要組成部分。一個virtual service可以看作是如何將流量分發的給定的目的地,然後呼叫destination rule來配置分發到該目的地的流量。destination rule在virtual service的路由規則之後起作用(*即在virtual service的math->route-destination之後起作用,此時流量已經分發到真實的service上*),應用於真實的目的地。 特別地,可以使用destination rule來指定命名的服務子集,例如根據版本對服務的例項進行分組,然後通過virtual service的路由規則中的服務子集將控制流量分發到不同服務的例項中。 destination rule允許在呼叫完整的目標服務或特定的服務子集(如傾向使用的負載均衡模型,TLS安全模型或斷路器)時自定義Envoy流量策略。更多參見[Destination Rule reference](https://istio.io/docs/reference/config/networking/destination-rule/)。 #### 負載均衡選項 istio預設會使用輪詢策略,此外istio也支援如下負載均衡模型,可以在destination rule中使用這些模型,將請求分發的特定的服務或服務子集。 - Random:將請求轉發到一個隨機的例項上 - Weighted:按照指定的百分比將請求轉發到例項上 - Least requests:將請求轉發到具有最少請求數目的例項上 更多參見[Envoy load balancing documentation](https://www.envoyproxy.io/docs/envoy/v1.5.0/intro/arch_overview/load_balancing) #### Destination rule例子 下面的Destination rule使用不同的負載均衡策略為my-svc目的服務配置了3個不同的子集(`subset`)。 ```yaml apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: my-destination-rule spec: host: my-svc trafficPolicy: #預設的負載均衡策略模型為隨機 loadBalancer: simple: RANDOM subsets: - name: v1 #subset1,將流量轉發到具有標籤version:v1的deployment對應的服務上 labels: version: v1 - name: v2 #subset2,將流量轉發到具有標籤version:v2的deployment對應的服務上,指定負載均衡為輪詢 labels: version: v2 trafficPolicy: loadBalancer: simple: ROUND_ROBIN - name: v3 #subset3,將流量轉發到具有標籤version:v3的deployment對應的服務上 labels: version: v3 ``` 每個子集由一個或多個`labels`定義,對應kubernetes中的物件(如pod)的key/value對。這些標籤定義在kubernetes服務的deployment的`metadata` 中,用於標識不同的版本。 除了定義子集外,destination rule還定義了該目的地中所有子集的預設流量策略,以及僅覆蓋該子集的特定策略。預設的策略定義在`subset`欄位之上,為`v1`和`v3`子集設定了隨機負載均衡策略,在`v2`策略中使用了輪詢負載均衡。 ### [Gateway](https://istio.io/docs/reference/config/networking/gateway/#Gateway) gateway用於管理進出網格的流量,指定可以進入或離開網格的流量。gateway配置應用於網格邊緣的獨立的Envoy代理上,而不是服務負載的Envoy代理上。 與其他控制進入系統的流量的機制(如kubernetes ingress API)不同,istio gateway允許利用istio的流量路由的強大功能和靈活性。istio的gateway資源僅允許配置4-6層的負載屬性,如暴露的埠,TLS配置等等,但結合istio的virtual service,就可以像管理istio網格中的其他資料面流量一樣管理gateway的流量。 gateway主要用於管理ingress流量,但也可以配置egress gateway。通過egress gateway可以配置流量離開網格的特定的節點,限制哪些服務可以訪問外部網路,或通過[egress安全控制](https://istio.io/blog/2019/egress-traffic-control-in-istio-part-1/)來提高網格的安全性。gateway可以用於配置為一個純粹的內部代理。 istio提供了一些預配置的gateway代理(通過`istio-ingressgateway`和`istio-egressgateway`),default profile下僅會部署ingress gateway。gateway可以通過部署檔案進行部署,也可以單獨部署。 下面是一個gateway的例子,用於配置外部HTTPS的ingress流量: ```yaml apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: ext-host-gwy spec: selector: #指定gateway配置下發的代理,如具有標籤app: my-gateway-controller的pod app: my-gateway-controller servers: - port: #暴露的埠資訊 number: 443 name: https protocol: HTTPS hosts: #外部流量 - ext-host.example.com tls: mode: SIMPLE serverCertificate: /tmp/tls.crt privateKey: /tmp/tls.key ``` 上述gateway配置允許來自`ext-host.example.com` 流量進入網格的443埠,但沒有指定該流量的路由。(*此時流量只能進入網格,但沒有指定處理該流量的服務,因此需要與virtual service進行繫結*) 為了為gateway指定路由,需要通過virtual service的`gateway`欄位,將gateway繫結到一個virtual service上, ```yaml apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: virtual-svc spec: hosts: - ext-host.example.com gateways: #將gateway "ext-host-gwy"繫結到virtual service "virtual-svc"上 - ext-host-gwy ``` ### [Service entries](https://istio.io/docs/reference/config/networking/service-entry/#ServiceEntry) *可以看作是一個網格外部的virtual service* 使用service entry可以在istio內部維護的service registry中註冊一個表項。在新增service entry後,Envoy代理就可以將流量轉發到該服務上(就像服務是網格中的服務一樣)。配置service entry允許管理網格外的服務的流量,包括: - 重定向和轉發到達外部目的地的流量,例如web使用的api,或者使用遺留基礎設施提供的服務。 - 為外部目的地定義重試,超時和故障注入策略 - 提供將[vm新增到網格](https://istio.io/docs/examples/virtual-machines/)中,在VM中執行網格服務 - 在邏輯上將一個不同的叢集新增到網格中,來在kubernetes上配置[多叢集istio網格](https://istio.io/docs/setup/install/multicluster/gateways/#configure-the-example-services)。 無需為每個網格服務使用的外部服務新增service entry。預設下,istio僅會配置Envoy代理來轉發請求到無法識別的服務。如果一個目的地沒有註冊到網格中,則不能利用istio的特性來控制到該目的地的流量。 下面是一個通過service entry將外部服務`ext-svc.example.com` 新增到istio的service registry的例子: ```yaml apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: svc-entry spec: hosts: - ext-svc.example.com #外部服務 ports: #外部服務對應的埠 - number: 443 name: https protocol: HTTPS location: MESH_EXTERNAL #flag,表示該服務是網格外的服務 resolution: DNS #主機服務的發現模型 ``` 使用`hosts`欄位指定外部資源,該欄位可以是一個完整的域名,也可以是使用字首萬用字元的域名。 可以使用virtual service和destination rule來控制到達一個service entry的流量。例如,下面destination rule配置了流量路由使用mutual TLS來加密到達外部服務`ext-svc.example.com`間的連線。 ```yaml apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: ext-res-dr spec: host: ext-svc.example.com trafficPolicy: tls: mode: MUTUAL clientCertificate: /etc/certs/myclientcert.pem privateKey: /etc/certs/client_private_key.pem caCertificates: /etc/certs/rootcacerts.pem ``` ### [Sidecars](https://istio.io/docs/reference/config/networking/sidecar/#Sidecar) 預設情況下,istio會配置每個Envory代理來接收與其關聯的負載的(所有埠上的)流量,以及在轉發流量時將流量分發到網格中的每個負載。使用sidecar可以實現如下功能: - 對Envoy代理接受的埠和協議集進行調優 - 限制Envoy代理可以訪問的服務集 在大型應用中,如果在每個服務都經過sidecar代理,可能會因為記憶體過高而影響網格的效能,這種情況下,可能需要限制sidecar的可達性。 可以將sidecar配置到某個特定的名稱空間中,或通過`workloadSelector`選擇特定的負載。例如,下面的sidecar配置將bookinfo名稱空間中的所有服務配置為,僅訪問在同一名稱空間和Istio控制平面中的服務。 *sidecar配置僅能影響到該配置所在的名稱空間* ```yaml apiVersion: networking.istio.io/v1alpha3 kind: Sidecar metadata: name: default namespace: bookinfo #sidecar所在的名稱空間 spec: egress: #限制bookinfo能夠訪問的egress - hosts: #使用namespace/dnsName格式的監聽器,參見https://istio.io/docs/reference/config/networking/sidecar/#IstioEgressListener - "./*" - "istio-system/*" ``` ### 網路彈性和測試 除了可以幫助引導網格周圍的流量,Istio還提供可選的故障恢復和故障注入功能。可以在執行時動態配置這些功能。使用這些特性可以幫助增強應用的可靠性,保證服務網格能夠容忍節點失敗,以及防止本地化故障級聯到其他節點。 #### 超時 timeout是Envoy代理在等待給定服務響應前的時間,保證不會無限等待服務的響應,最後返回成功會超時後返回失敗。預設的HTTP請求超時時間為15s,即如果服務無法在15秒內響應,則呼叫失敗。 對於一些應用和服務,istio的預設超時可能不大合適。例如,超時過長可能會在有失敗的服務下出現大量延時,而超時過短可能會導致不必要的呼叫失敗(如呼叫鏈比較長)。為了優化超時設定,istio允許使用virtual service動態調整每個服務的超時,而無需修改服務程式碼。下面virtual service將`ratings` 服務的`v1`服務子集的超時設定為10s。 ```yaml apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: ratings spec: hosts: - ratings http: - route: - destination: host: ratings subset: v1 timeout: 10s ``` #### 重試 與timeout類似,可以在virtual service中設定單個服務的重試配置。下面例子中配置的最大重試次數為3,每次超時時間為2s。 ```yaml apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: ratings spec: hosts: - ratings http: - route: - destination: host: ratings subset: v1 retries: attempts: 3 perTryTimeout: 2s ``` #### [斷路器](https://istio.io/docs/tasks/traffic-management/circuit-breaking/) 斷路器是istio提供的另一種構建彈性微服務的機制。在斷路器中,可以設定對服務中單個主機的呼叫限制,如限制到一臺主機的併發連線數,或限制到一臺主機的呼叫失敗的次數,一旦達到限制值,斷路器或發出告警並停止連線這臺主機。 由於斷路器會作用到負載均衡池中的真實網格目標,可以在`destination rules`中配置斷路器閾值,該設定作用到服務中的每個主機。下面例子限制了reviews服務負載的v1服務子集的最大併發連線數為100。 ```yaml apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: reviews spec: host: reviews subsets: - name: v1 labels: version: v1 trafficPolicy: connectionPool: tcp: maxConnections: 100 ``` #### [故障注入](https://istio.io/docs/tasks/traffic-management/fault-injection/) 在配置完網路,包括故障恢復策略後,可以使用istio的故障注入機制來測試應用的故障恢復能力。故障注入是一個測試機制,將錯誤引入到一個系統來保證系統能夠承受該錯誤支援錯誤恢復。使用故障注入可以特別有助於確保不會因為故障恢復策略的相容性和限制性而導致關鍵服務不可用。 與在網路層面引入延遲報文或殺死pod不同,istio允許在應用層注入故障,如通過注入HTTP錯誤碼來獲取相關的結果。 使用virtual service可以注入兩種型別的故障: - 延遲:時間故障。用於模擬增加的網路延遲或超載的上游服務 - 中斷:崩潰故障。用於模擬上游服務故障,通常以HTTP錯誤程式碼或TCP連線失敗的形式出現。 下面例子中,使用virtual service注入故障,對`ratings`服務的訪問請求中,每1000個請求中就有一個5s的延遲。 ```yaml apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: ratings spec: hosts: - ratings http: - fault: delay: percentage: value: 0.1 fixedDelay: 5s route: - destination: host: ratings subset: v1 ``` ### 與應用的配合 istio的故障恢復功能對應用來說是完全透明的。一個應用無法知道一個Envoy sidecar代理是否為一個被呼叫的服務配置了失敗處理功能。這意味著如果在應用代理中設定了故障恢復策略,則需要注意這兩個策略是相互獨立的,有可能傳送衝突。例如,假設有兩個超時設定,一個配置在virtual service中,一個配置在應用中。應用為呼叫某個服務的API設定的超時為2s;而virtual service中設定的超時為3s,重試次數為1。這種情況下,應用首先會發送超時,Envoy的超時和重試將失效。 雖然istio的故障恢復功能提升了網格中服務的可靠性和可用性,但應用仍然需要處理故障或錯誤,並做出相應動作。例如,當一個負載均衡池中的所有例項都失敗後,Envoy會返回`HTTP 503`錯誤,應用必須實現對HTTP 503錯誤碼做出反饋。