帶你玩轉Istio-第5篇---非侵入的流量治理

本章介紹Istio提供的流量治理相關內容,涉及Istio流量治理解決的問題和實現原理,解析 Istio 提供的路由管理、熔斷、負載均衡、故障注入等流量治理能力,以及如何通過Istio中的VirtualService、DestinationRule、Gateway、ServiceEntry等重要的服務管理配置來實現以上流量治理能力。在內容安排上,每節在講解治理規則前都會從一個基礎配置入手,再詳細解析用法,並輔以典型應用案例來呈現其使用方法和應用場景。通過對本章的學習,可基於Istio的這些配置在不修改代碼的情況下實現各種流量治理。

Istio流量治理原理

流量治理是一個非常寬泛的話題,例如:
◎ 動態修改服務間訪問的負載均衡策略,比如根據某個請求特徵做會話保持;
◎ 同一個服務有兩個版本在線,將一部分流量切到某個版本上;
◎ 對服務進行保護,例如限制併發連接數、限制請求數、隔離故障服務實例等;
◎ 動態修改服務中的內容,或者模擬一個服務運行故障等。

在Istio中實現這些服務治理功能時無須修改任何應用的代碼。較之微服務的SDK方式,Istio以一種更輕便、透明的方式向用戶提供了這些功能。用戶可以用自己喜歡的任意語言和框架進行開發,專注於自己的業務,完全不用嵌入任何治理邏輯。只要應用運行在Istio的基礎設施上,就可以使用這些治理能力。

一句話總結 Istio 流量治理的目標:以基礎設施的方式提供給用戶非侵入的流量治理能力,用戶只需關注自己的業務邏輯開發,無須關注服務訪問管理。

 

                                            圖3-1 Istio流量治理的概要流程

在控制面會經過如下流程:
(1)管理員通過命令行或者API創建流量規則;
(2)Pilot將流量規則轉換爲Envoy的標準格式;
(3)Pilot將規則下發給Envoy。
在數據面會經過如下流程:
(1)Envoy攔截Pod上本地容器的Inbound流量和Outbound流量;
(2)在流量經過Envoy時執行對應的流量規則,對流量進行治理。

下面具體看看Istio提供了哪些流量治理功能。因爲Istio提供的流量治理功能非常多,所以這裏僅從業務場景上列舉出典型和常用的功能。

負載均衡

負載均衡從嚴格意義上講不應該算治理能力,因爲它只做了服務間互訪的基礎工作,在服務調用方使用一個服務名發起訪問的時候能找到一個合適的後端,把流量導過去。

如圖3-2所示,傳統的負載均衡一般是在服務端提供的,例如用瀏覽器或者手機訪問一個 We b 網站時,一般在網站入口處有一個負載均衡器來做請求的匯聚和轉發。服務的虛擬 IP 和後端實例一般是通過靜態配置文件維護的,負載均衡器通過健康檢查保證客戶端的請求被路由到健康的後端實例上。

                                                                                  圖3-2 服務端的負載均衡器

在微服務場景下,負載均衡一般和服務發現配合使用,每個服務都有多個對等的服務實例,需要有一種機制將請求的服務名解析到服務實例地址上。服務發現負責從服務名中解析一組服務實例的列表,負載均衡負責從中選擇一個實例。

如圖 3-3 所示爲服務發現和負載均衡的工作流程。不管是 SDK 的微服務架構,還是Istio這樣的Service Mesh架構,服務發現和負載均衡的工作流程都是類似的,如下所述。

(1)服務註冊。各服務將服務名和服務實例的對應信息註冊到服務註冊中心。
(2)服務發現。在客戶端發起服務訪問時,以同步或者異步的方式從服務註冊中心獲取服務對應的實例列表。
(3)負載均衡。根據配置的負載均衡算法從實例列表中選擇一個服務實例。

 

 

                                                 圖3-3 服務發現和負載均衡的工作流程

Istio的負載均衡正是其中的一個具體應用。在Istio中,Pilot負責維護服務發現數據。如圖 3-4 所示爲 Istio 負載均衡的流程,Pilot將服務發現數據通過 Envoy的標準接口下發給數據面Envoy,Envoy則根據配置的負載均衡策略選擇一個實例轉發請求。Istio當前支持的主要負載均衡算法包括:輪詢、隨機和最小連接數算法。

 

                                                                圖3-4 Istio負載均衡的流程

在Kubernetes上支持Service的重要組件Kube-proxy,實際上也是運行在工作節點的一個網絡代理和負載均衡器,它實現了Service模型,默認通過輪詢等方式把Service訪問轉發到後端實例Pod上,如圖3-5所示。

                                             圖3-5 Kubernetes的負載均衡

服務熔斷

熔斷器在生活中一般指可以自動操作的電氣開關,用來保護電路不會因爲電流過載或者短路而受損,典型的動作是在檢測到故障後馬上中斷電流。

“熔斷器”這個概念延伸到計算機世界中指的是故障檢測和處理邏輯,防止臨時故障或意外導致系統整體不可用,最典型的應用場景是防止網絡和服務調用故障級聯發生,限制故障的影響範圍,防止故障蔓延導致系統整體性能下降或雪崩。

如圖 3-6 所示爲級聯故障示例,可以看出在 4 個服務間有調用關係,如果後端服務recommendation由於各種原因導致不可用,則前端服務 forecast和 frontend都會受影響。在這個過程中,若單個服務的故障蔓延到其他服務,就會影響整個系統的運行,所以需要讓故障服務快速失敗,讓調用方服務forecast和frontend知道後端服務recommendation出現問題,並立即進行故障處理。這時,非常小概率發生的事情對整個系統的影響都足夠大。

                                                     圖3-6 級聯故障示例

在Hystrix官方曾經有這樣一個推算:如果一個應用包含30個依賴的服務,每個服務都可以保證99.99%可靠性地正常運行,則從整個應用角度看,可以得到99.9930=99.7%的正常運行時間,即有0.3%的失敗率,在10億次請求中就會有3 000 000多種失敗,每個月就會有兩個小時以上的宕機。即使其他服務都是運行良好的,只要其中一個服務有這樣0.001%的故障機率,對整個系統就都會產生嚴重的影響。

關於熔斷的設計,Martin Fowler 有一個經典的文章(https://martinfowler.com/bliki/CircuitBreaker.html),其中描述的熔斷主要應用於微服務場景下的分佈式調用中:在遠程調用時,請求在超時前一直掛起,會導致請求鏈路上的級聯故障和資源耗盡;熔斷器封裝了被保護的邏輯,監控調用是否失敗,當連續調用失敗的數量超過閾值時,熔斷器就會跳閘,在跳閘後的一定時間段內,所有調用遠程服務的嘗試都將立即返回失敗;同時,熔斷器設置了一個計時器,當計時到期時,允許有限數量的測試請求通過;如果這些請求成功,則熔斷器恢復正常操作;如果這些請求失敗,則維持斷路狀態。Martin把這個簡單的模型通過一個狀態機來表達,我們簡單理解下,如圖3-7所示。

                                                                圖3-7 熔斷器狀態機

圖3-7上的三個點表示熔斷器的狀態,下面分別進行解釋。
◎ 熔斷關閉:熔斷器處於關閉狀態,服務可以訪問。熔斷器維護了訪問失敗的計數器,若服務訪問失敗則加一。
◎ 熔斷開啓:熔斷器處於開啓狀態,服務不可訪問,若有服務訪問則立即出錯。
◎ 熔斷半開啓:熔斷器處於半開啓狀態,允許對服務嘗試請求,若服務訪問成功則說明故障已經得到解決,否則說明故障依然存在。

圖上狀態機上的幾條邊表示幾種狀態流轉,如表3-1所示。

                                                    表3-1 熔斷器的狀態流轉

Martin這個狀態機成爲後面很多系統實現的設計指導,包括最有名的Hystrix,當然,Istio的異常點檢測也是按照類似語義工作的。

1.Hystrix熔斷

關於熔斷,大家比較熟悉的一個落地產品就是Hystrix。Hystrix是Netflix提供的衆多服務治理工具集中的一個,在形態上是一個Java庫,在2011年出現,後來多在Spring Cloud中配合其他微服務治理工具集一起使用。

Hystrix的主要功能包括:
◎ 阻斷級聯失敗,防止雪崩;
◎ 提供延遲和失敗保護;
◎ 快速失敗並即時恢復;
◎ 對每個服務調用都進行隔離;
◎ 對每個服務都維護一個連接池,在連接池滿時直接拒絕訪問;
◎ 配置熔斷閾值,對服務訪問直接走失敗處理 Fallback 邏輯,可以定義失敗處理邏輯;
◎ 在熔斷生效後,在設定的時間後探測是否恢復,若恢復則關閉熔斷;

◎ 提供實時監控、告警和操作控制。

Hystrix的熔斷機制基本上與Martin的熔斷機制一致。在實現上,如圖3-8所示,Hystrix將要保護的過程封裝在一個 HystrixCommand 中,將熔斷功能應用到調用的方法上,並監視對該方法的失敗調用,當失敗次數達到閾值時,後續調用自動失敗並被轉到一個Fallback方法上。在 HystrixCommand 中封裝的要保護的方法並不要求是一個對遠端服務的請求,可以是任何需要保護的過程。每個 HystrixCommand都可以被設置一個 Fallback方法,用戶可以寫代碼定義Fallback方法的處理邏輯。

                                                圖3-8 HystrixCommand熔斷處理

在 Hystrix 的資源隔離方式中除了提供了熔斷,還提供了對線程池的管理,減少和限制了單個服務故障對整個系統的影響,提高了整個系統的彈性。

在使用上,不管是直接使用Netflix的工具集還是Spring Cloud中的包裝,都建議在代碼中寫熔斷處理邏輯,有針對性地進行處理,但侵入了業務代碼,這也是與 Istio 比較大的差別。

業界一直以 Hystrix 作爲熔斷的實現模板,尤其是基於 Spring Cloud。但遺憾的是,Hystrix 在 1.5.18 版本後就停止開發和代碼合入,轉爲維護狀態,其替代者是不太知名的Resilience4J。

2.Istio熔斷

雲原生場景下的服務調用關係更加複雜,前文提到的若干問題也更加嚴峻,Istio提供了一套非侵入的熔斷能力來應對這種挑戰。

與Hystrix類似,在Istio中也提供了連接池和故障實例隔離的能力,只是概念術語稍有不同:前者在 Istio 的配置中叫作連接池管理,後者叫作異常點檢測,分別對應 Envoy的熔斷和異常點檢測。

Istio在0.8版本之前使用V1alpha1接口,其中專門有個CircuitBreaker配置,包含對連接池和故障實例隔離的全部配置。在Istio 1.1的V1alpha3接口中,CircuitBreaker功能被拆分成連接池管理(ConnectionPoolSettings)和異常點檢查(OutlierDetection)這兩種配置,由用戶選擇搭配使用。

首先看看解決的問題,如下所述。

(1)在 Istio 中通過限制某個客戶端對目標服務的連接數、訪問請求數等,避免對一個服務的過量訪問,如果超過配置的閾值,則快速斷路請求。還會限制重試次數,避免重試次數過多導致系統壓力變大並加劇故障的傳播;

(2)如果某個服務實例頻繁超時或者出錯,則將該實例隔離,避免影響整個服務。

以上兩個應用場景正好對應連接池管理和異常實例隔離功能。

Istio 的連接池管理工作機制對 TCP 提供了最大連接數、連接超時時間等管理方式,對HTTP提供了最大請求數、最大等待請求數、最大重試次數、每連接最大請求數等管理方式,它控制客戶端對目標服務的連接和訪問,在超過配置時快速拒絕。

如圖3-9所示,通過Istio的連接池管理可以控制frontend服務對目標服務forecast的請求:

(1)當frontend服務對目標服務forecast的請求不超過配置的最大連接數時,放行;

(2)當 frontend服務對目標服務 forecast的請求不超過配置的最大等待請求數時,進入連接池等待;

(3)當 frontend服務對目標服務 forecast的請求超過配置的最大等待請求數時,直接拒絕。

                                                       圖3-9 Istio的連接池管理

Istio提供的異常點檢查機制動態地將異常實例從負載均衡池中移除,如圖3-10所示,當連續的錯誤數超過配置的閾值時,後端實例會被移除。異常點檢查在實現上對每個上游服務都進行跟蹤,對於HTTP服務,如果有主機返回了連續的5xx,則會被踢出服務池;而對於TCP服務,如果到目標服務的連接超時和失敗,則都會被記爲出錯。

                                                    圖3-10 Istio異常點檢查

另外,被移除的實例在一段時間之後,還會被加回來再次嘗試訪問,如果可以訪問成功,則認爲實例正常;如果訪問不成功,則實例不正常,重新被逐出,後面驅逐的時間等於一個基礎時間乘以驅逐的次數。這樣,如果一個實例經過以上過程的多次嘗試訪問一直不可用,則下次會被隔離更久的時間。可以看到,Istio 的這個流程也是基於 Martin 的熔斷模型設計和實現的,不同之處在於這裏沒有熔斷半開狀態,熔斷器要打開多長時間取決於失敗的次數。

另外,在 Istio 中可以控制驅逐比例,即有多少比例的服務實例在不滿足要求時被驅逐。當有太多實例被移除時,就會進入恐慌模式,這時會忽略負載均衡池上實例的健康標記,仍然會向所有實例發送請求,從而保證一個服務的整體可用性。

下面對Istio與Hystrix的熔斷進行簡單對比,如表3-2所示。可以看到與Hystrix相比,Istio實現的熔斷器其實是一個黑盒,和業務沒有耦合,不涉及代碼,只要是對服務訪問的保護就可以用,配置比較簡單、直接。

                                                表3-2 Istio和Hystrix熔斷的簡單對比

熔斷功能本來就是疊加上去的服務保護,並不能完全替代代碼中的異常處理。業務代碼本來也應該做好各種異常處理,在發生異常的時候通知調用方的代碼或者最終用戶,如下所示:

public void callService( String serviceName ) throws Exception {
  try {
  // call remote service
     RestTemplate restTemplate = new ResTemplate();
     String result = restTemplate.getForObject(serviceName, String.class);

  }cathch( Exception e ) {
  // exception handle
     dealException(e)
  }

}

 

Istio 的熔斷能力是對業務透明的,不影響也不關心業務代碼的寫法。當 Hystrix 開發的服務運行在Istio環境時,兩種熔斷機制疊加在一起。在故障場景下,如果Hystrix和Istio兩種規則同時存在,則嚴格的規則先生效。當然,不推薦採用這種做法,建議業務代碼處理好業務,把治理的事情交給Istio來做。

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