華爲雲應用服務網格最佳實踐之從Spring Cloud 到 Istio

摘要:在全球首屆社區峯會IstioCon 2021中,華爲雲應用服務網格首席架構師張超盟發表了《Best practice:from Spring Cloud to Istio》主題演講,分享了Istio在生產中使用的實際案例。

點擊鏈接觀看演講:https://events.istio.io/istiocon-2021/sessions/best-practice%EF%BC%9Afrom-spring-cloud-to-istio/

以下爲演講全文

大家好,我是來自華爲雲的工程師。很榮幸有機會和大家分享Istio在生產中使用的實際案例。

華爲雲應用服務網格從2018年在公有云上線,作爲全球最早的幾個網格服務之一,經歷和見證了從早期對網格的瞭解、嘗試到當前大規模使用的過程。服務的客戶越來越多,場景也越來越複雜。這其中的通用功能作爲feature大都貢獻到Istio社區,解決方案層面的實踐也希望通過這樣的機會和大家交流。

本次我選取的主題是Spring Cloud to Istio。來自我們客戶的Spring cloud的項目和Istio的結合與遷移案例。

演講主要包含四部分的內容:

1)背景介紹

2)使用Spring cloud微服務框架遇到的問題

3)解決方案

4)通過示例來描述方案的實踐細節

背景介紹

還是以微服務爲切入點,微服務的諸多優勢非常明顯,但相應給整個系統帶來的複雜度也非常顯著。單體的系統變成了分佈式後,網絡問題,服務如何找到並訪問到對端的服務發現問題,網絡訪問的容錯保護問題等。連當年最簡單的通過日誌中的調用棧就能實現的問題定位,微服務化後必須要通過分佈式調用鏈才能支持。怎樣解決微服務帶來的這些挑戰?

微服務SDK曾經是一個常用的解決方案。將微服務化後通用的能力封裝在一個開發框架中,開發者使用這個框架開發寫自己的業務代碼,生成的微服務自然就內置了這些能力。在很長的一段時間內,這種形態是微服務治理的標配,以至於初學者以爲只有這些SDK纔是微服務。

服務網格則通過另一種形態提供治理能力。不同於SDK方式,服務治理的能力在一個獨立的代理進程中提供,完全和開發解耦。雖然從圖上看兩者差異非常小,後面我們將會從架構和實際案例來分析兩者在設計理念上的差異,來體會前者是一個開發框架,而後者是一個基礎設施。

SDK形態中Spring cloud是最有影響力的代表項目。Spring cloud提供了構建分佈式應用的開發工具集,如列表所示。其中被大部分開發者熟知的是微服務相關項目,如:服務註冊發現eureka、配置管理 config、負載均衡ribbon、熔斷容錯Hystrix、調用鏈埋點sleuth、網關zuul或Spring cloud gateway等項目。在本次分享中提到的Spring cloud也特指Spring cloud的微服務開發套件。

而網格形態中,最有影響力的項目當屬Istio。Istio的這張架構圖在這次演講中會高頻出現。作爲本次分享的背景,我們只要知道架構上由控制面和數據面組成,控制面管理網格里面的服務和對服務配置的各種規則。數據面上每個服務間的出流量和入流量都會被和服務同POD的數據面代理攔截和執行流量管理的動作。

除了架構外,作爲背景的另外一個部分,我們挑兩個基礎功能稍微打開看下兩者的設計和實現上的相同和不同。首先是服務發現和負載均衡。

左邊是Spring cloud,所有的微服務都會先註冊中心,一般是Eureka進行服務註冊,然後在服務訪問時,consumer去註冊中心進行服務發現得到待訪問的目標服務的實例列表,使用客戶端負載均衡ribbon選擇一個服務實例發起訪問。

右邊Istio不需要服務註冊的過程,只需要從運行平臺k8s中獲取服務和實例的關係,在服務訪問時,數據面代理Envoy攔截到流量,選擇一個目標實例發送請求。可以看到都是基於服務發現數據進行客戶端負載均衡,差別是服務發現數據來源不同,負載均衡的執行體不同。

下面比較下熔斷:

左邊爲經典的Hystrix的狀態遷移圖。一段時間內實例連續的錯誤次數超過閾值則進入熔斷開啓狀態,不接受請求;隔離一段時間後,會從熔斷狀態遷移到半熔斷狀態,如果正常則進入熔斷關閉狀態,可以接收請求;如果不正常則還是進入熔斷開啓狀態。

Istio中雖然沒有顯示的提供這樣一個狀態圖,但是大家熟悉Istio規則和行爲應該會發現,Istio中OutlierDection的閾值規則也都是這樣設計的。兩者的不同是Spring cloud的熔斷是在SDK中Hystrix執行,Istio中是數據面proxy執行。Hystrix因爲在業務代碼中,允許用戶通過編程做一些控制。

以上分析可以看到服務發現、負載均衡和熔斷,能力和機制都是類似的。如果忽略圖上的某些細節,粗的看框圖模型都是完全一樣的,對比表格中也一般只有一項就是執行位置不同,這一點不同在實際應用中帶來非常大的差異。

使用Spring cloud微服務框架遇到的問題

本次演講的重點是實踐。以下是我們客戶找到我們TOP的幾個的問題,剖析下用戶使用傳統微服務框架碰到了哪些問題,這些大部分也是他們選擇網格的最大動力。

1)多語言問題

在企業應用開發下,一個業務使用統一的開發框架是非常合理常見的,很多開發團隊爲了提升效率,經常還會維護有自己公司或者團隊的通用開發框架。當然因爲大部分業務系統都是基於Java開發,所以Spring cloud開發框架,或者衍生於Spring cloud的各種開發框架使用的尤其廣泛。

但是在雲原生場景下,業務一般更加複雜多樣,特別是涉及到很多即存的老系統。我們不能要求爲了微服務化將在用的一組成熟服務用Spring cloud重寫下。用戶非常希望有一種方式不重寫原來的系統也能對其進行應用層服務訪問管理。

2)將Spring cloud的微服務運行在K8s上會有很大的概率出現服務發現不及時

前面介紹過Spring cloud服務發現是基於各個微服務先向註冊中心進行服務註冊的數據來實現的,在傳統Spring cloud場景下,當微服務部署在VM上,服務動態變化要求沒有那麼高,頂多個別實例運行不正常,通過服務發現的健康檢查就足夠了。但是在k8s場景下,服務實例動態遷移是非常正常場景。如圖示,producer的某個Pod已經從一個節點遷移到另外一個節點了,這時需要新的pod2的producer實例向eureka註冊,老實例Pod1要去註冊。

如果該情況頻繁發生,會出現註冊中心數據維護不及時,導致服務發現和負載均衡到舊的實例pod1上,從而引起訪問失敗的情況。

3)升級所有應用以應對服務管理需求變化

第三個問題是一個比較典型的問題。客戶有一個公共團隊專門維護了一套基於Spring cloud的自有開發框架,在每次升級開發框架時,不得不求着業務團隊來升級自己的服務。經常會SDK自身修改測試工作量不大,但卻要制定很長週期的升級計劃,來對上千個基於這個SDK開發的服務分組重新編譯,打包,升級,而且經常要陪着業務團隊在夜間變更。業務團隊因爲自身沒有什麼改動,考慮到這個升級帶來的工作量和線上風險,一般也沒有什麼動力。

4)從單體式架構向微服務架構遷移

這是一個比較普遍的問題,就是漸進的微服務化。馬丁福勒在著名的文章單體到微服務的拆分中(https://martinfowler.com/articles/break-monolith-into-microservices.html )也提到了對漸進微服務化的倡議,如何能從業務上將一個大的業務分割,解耦,然後逐步微服務化。馬丁福勒強調 “解耦的是業務能力不是代碼” ,大神將代碼的解耦留給了開發者。

但是站在開發者的角度講漸進的微服務不是一個容易的事情。以基於Spring cloud框架進行微服務開發爲例,爲了所有的微服務間進行統一的服務發現、負載均衡,消費和執行同樣的治理策略,必須要求所有的微服務基於同樣的,甚至是統一版本的SDK來開發。

當然我們客戶在這種情況下也有基於API層面做適配,將原有的未微服務化的和已微服務化的並存,使用這種類似於灰度方式,實際操作非常麻煩。

曾經有客戶問過有沒有不用費勁搞兩套,是否可以直接有些大的單體微服務化,另外一些單體很長時間內完全不動,直到有時間或者認爲安全想動它的時候去動。

解決方案

對於客戶實際碰到的4個典型的微服務框架的問題,我們推薦的解決方案都是服務網格。下面我們分別看下Istio如何解決上面的幾個問題。

首先,多語言問題。基於服務網格,業務和治理的數據面無需運行在同一個進程裏,也無需一起編譯,因此也沒有語言和框架上的綁定。無論什麼語言開發的服務,只要有一個對外可以被訪問和管理的一定應用協議上的端口,都可以被網格進行管理。通過統一的網格控制面,下發統一的治理規則給統一的網格數據面執行,進行統一的治理動作,包括前面介紹到的灰度、流量、安全、可觀察性等等。

關於Spring cloud服務在Kubernetes運行時,關於原有的服務註冊和發現不及時的問題。根本原因是兩套服務發現導致的不一致問題,那麼解決辦法也比較簡單,統一服務發現即可。既然K8s已經在Pod調度的同時維護有服務和實例間的數據,那麼就沒有必要再單獨搞一套名字服務的機制,還要費勁的進行服務註冊,然後再發現。

比較之前Spring cloud註冊發現那張圖,註冊中心沒了,服務基於註冊中心的服務註冊和服務發現的動作也不需要了,Istio直接使用k8s的服務發現數據,但從架構上看也簡潔很多。

我們也總結過,大部分碰到這個問題的場景,都是將微服務框架從VM遷移到k8s時候碰到的,有點把容器當作之前的VM使用,只使用了k8s作爲容器部署運行的平臺,並沒有用到k8s的service。

對於SDK自身升級導致業務全部重新升級的問題,解決辦法就是把服務治理的公共能力和業務解耦。在網格中,將治理能力下沉到基礎設施後,業務的開發、部署、升級都和服務治理的基礎設施解耦了。業務開發者專注自己的業務部分。只要沒有修改業務代碼,就無需重新編譯和上線變更。

當治理能力升級只需基礎設施升級即可,基礎設施的升級對用戶業務完全沒有影響。像華爲雲ASM這樣大部分網格服務的服務提供商都能做到一鍵升級,用戶完全感知不到這個過程。

關於漸進微服務化的問題,使用Isito服務網格可以非常完美的解決。Istio治理的是服務間的訪問,只要一個服務被其他服務訪問,就可以通過Istio來進行管理,不管是微服務還是單體。Istio接管了服務的流量後,單體和微服務都可以接收統一的規則進行統一的管理。

如圖中,在微服務化的過程中,可以對某個單體應用svc1根據業務拆分優先進行微服務化,拆分成三個微服務svc11、svc12和svc13,svc1服務依賴的另外一個單體應用svc2不用做任何變更,在網格中運行起來就可以和另外三個微服務一樣的被管理。同樣在運行一段時間後,svc2服務可以根據自身的業務需要再進行微服務化。從而儘量避免一次大的重構帶來的工作量和業務遷移的風險,真正做到馬丁富勒倡導的漸進微服務化的實踐。

實踐

以上是對實際工作中客戶的幾個典型問題的解決方案。在實踐中,怎麼把這些解決方案落地呢?下面基於實際客戶案例總結,分享具體的實踐。

我們的主要是思路是解耦和卸載。卸載原有SDK中非開發的功能,SDK只提供代碼框架、應用協議等開發功能。涉及到微服務治理的內容都卸載到基礎設施去做。

從圖上可以看到開發人員接觸到開發框架變薄了,開發人員的學習、使用和維護成本也相應的降低了。而基礎設施變得厚重了,除了完成之前需要做的服務運行的基礎能力外,還包括非侵入的服務治理能力。即將越來越多的之前認爲是業務的能力提煉成通用能力,交給基礎設施去做,交給雲廠商去做,客戶擺脫這些繁瑣的非業務的事務,更多的時間和精力投入到業務的創新和開發上。在這種分工下,SDK才真的迴歸到開發框架的根本職能。

要使用網格的能力,前提是微服務出來的流量能走到網格的數據面來。主要的遷移工作在微服務的服務調用方。我們推薦3個步驟:

第一步:廢棄原有的微服務註冊中心,使用K8S的Service。

第二步:短路SDK中服務發現和負載均衡等邏輯,直接使用k8s的服務名和端口訪問目標服務;

第三步:結合自身項目需要,逐步使用網格中的治理能力替換原有SDK中提供的對應功能,當然這步是可選的,如調用鏈埋點等原有功能使用滿足要求,也可以作爲應用自身功能保留。

爲了達成以上遷移,我們有兩種方式,供不同的用戶場景採用。

一種是隻修改配置的方式:Spring cloud本身除了支持基於Eureka的服務端的服務發現外,還可以給Ribbon配置靜態服務實例地址。利用這種機制給對應微服務的後端實例地址中配置服務的Kubernetes服務名和端口。

當Spring cloud框架中還是訪問原有的服務端微服務名時,會將請求轉發到k8s的服務和端口上。這樣訪問會被網格數據面攔截,從而走到網格數據面上來。服務發現負載均衡和各種流量規則都可以應用網格的能力。

這種方式其實是用最小的修改將SDK的訪問鏈路和網格數據面的訪問鏈路串接起來。在平臺中使用時,可以藉助流水線工具輔助,減少直接修改配置文件的工作量和操作錯誤。可以看到我這個實際項目中,只是修改了項目的application.yaml配置文件,其他代碼都是0修改。當然對於基於annotation的方式的配置也是同樣的語義修改即可。

前面一種方式對原有項目的修改比較少,但是Spring cloud的項目依賴都還在。

我們有些客戶選擇了另外一種更簡單直接的方式,既然原有SDK中服務發現負載均衡包括各種服務治理能力都不需要了,乾脆這些依賴也全部幹掉。從最終的鏡像大小看,整個項目的體量也得到了極大的瘦身。這種方式客戶根據自己的實際使用方式,進行各種裁剪,最終大部分是把Spring cloud退化成Spring boot。

遷移中還有另外一部分比較特殊,就是微服務外部訪問的Gateway。

Spring cloud 有兩種功能類似的Gateway,Zuul和Spring cloud Gateway。基於Eureka的服務發現,將內部微服務映射成外部服務,並且在入口處提供安全、分流等能力。在切換到k8s和Istio上來時,和內部服務一樣,將入口各個服務的服務發現遷移到k8s上來。

差別在於對於用戶如果在Gateway上開發了很多私有的業務強相關的filter時,這時候Gateway其實是微服務的門面服務,爲了業務延續性,方案上可以直接將其當成普通的微服務部署在網格中進行管理。

但是大多數情況下我們推薦使用Istio的Ingress Gateway直接替換這個微服務網關,以非侵入的方式提供外部TLS終止、限流、流量切分等能力。

經過以上的簡單改造,各種不同語言、各種不同開發框架開發的服務,只要業務協議相通,彼此可以互相訪問,訪問協議可以被網格管理,就都可以通過Istio進行統一的管理。

控制面上可以配置統一的服務管理規則。數據面上,統一使用Envoy進行服務發現、負載均衡和其他流量、安全、可觀察性等相關能力。數據面上的服務即可以運行在容器裏,也可以運行在虛機上。並且可以運行在多個k8s集羣中。

當然在遷移過程中間,我們也支持階段性的保留原有微服務框架的註冊中心,使Istio和其他的服務發現結合使用的中間狀態,讓網格中的服務可以訪問到微服務註冊中心的服務。

這裏是一個Spring cloud開發的服務運行在Istio服務網格上進行灰度發佈的示例。上面的日誌是服務調用方Sidecar的日誌,可以看到網格將流量分發到不同的服務後端上。下面的截圖是使用了華爲雲ASM服務的灰度功能,可以看到這個Spring cloud服務通過網格配置的分流策略,將30%的流量分發到灰度版本上。

下面這個示例是Spring cloud開發的服務使用Istio的熔斷功能。這個過程就是就是前面原理一節Hystrix的狀態遷移圖的實踐,不同在於這個實現是基於Istio來實現的。基於服務網格不管這裏的服務是什麼語言或者框架開發的,都可以對訪問進行熔斷保護。

這裏的效果截圖是來自華爲雲應用服務網格ASM的應用拓撲,可以非常清新的看到服務級別、服務實例級別流量變化情況,服務和服務實例的健康狀態,從而展示故障的Spring cloud實例被隔離的全過程。從拓撲圖上可以看到有個實例異常滿足熔斷閾值,觸發了熔斷,網格數據面向這個故障實例上分發的流量逐漸減少,直到完全沒有流量,即故障實例被隔離。通過這種熔斷保護保障服務整體訪問的成功率。

下面三個流量拓撲演示了故障恢復的過程。

可以看到:

  • 初始狀態這個故障實例被隔離中,沒有流量;
  • 當實例自身正常後,網格數據面在將其隔離配置的間隔後,重新嘗試給其分配流量,當滿足閾值要求則該實例會被認爲是正常實例,可以和其他兩個實例一樣接收請求。
  • 最終可以看到三個實例上均衡的處理請求。
  • 即實現了故障恢復。

最後,通過微服務、容器、k8s和Istio的關係圖來總結今天的內容:

  1. 微服務和容器都有輕量和敏捷的共同特點,容器是微服務非常適合的一個運行環境;
  2. 在雲原生場景下,在微服務場景下,容器從來都不是獨立存在的,使用k8s來編排容器已經是一個事實標準;
  3. Istio和k8s在架構和應用場景上的緊密結合,一起提供了一個端到端的微服務運行和治理的平臺。
  4. 也是我們推薦的方案,使用Istio進行微服務治理正在成爲越來越多用戶的技術選擇。

以上四個關係順時針結合在一起爲我們的解決方案構造一個完整的閉環。

華爲雲早在2018年就率先發布全球首個Istio商用服務——應用服務網格(Application Service Mesh,簡稱ASM),華爲雲應用服務網格是一種高性能、高可靠性和易用性的全託管的服務網格,支持虛擬機、容器等多種基礎設施,支持跨區域多雲多集羣服務的統一治理。以基礎設施的方式爲用戶提供服務流量管理、服務運行監控、服務訪問安全以及服務發佈能力。

目前,華爲雲應用服務網格已服務於互聯網、汽車、製造、物流、政府等多個行業的近千家客戶,滿足不同行業客戶的業務訴求。華爲雲將在此過程中積累的豐富經驗,轉化爲代碼貢獻給Istio社區,極大的推動了Istio技術的成熟和社區的快速發展。同時,華爲雲還大力投入服務網格的技術佈道,通過社區論壇、技術會議、視頻直播、專業書籍等各種方式,推動服務網格技術傳播和普及。

 

點擊關注,第一時間瞭解華爲雲新鮮技術~

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