Service Mesh在有讚的實踐與發展

前言

ServiceMesh的概念自2017年初提出之後,受到了業界的廣泛關注,作爲微服務的下一代發展架構在社區迅速發酵,並且孵化出了諸如Istio等廣受業界關注的面向於雲原生(CloudNative)的微服務架構。目前阿里、華爲雲、騰訊雲都在ServiceMesh上投入了大量精力進行研發和推廣。闡述和討論ServiceMesh架構的文章目前網絡上已經非常豐富,在此不再贅述。本文主要闡述ServiceMesh架構在有贊是如何一步步發展和落地的,期望能夠給讀者帶來一定的思考和借鑑意義,並對ServiceMesh架構能夠解決的問題和應用場景有進一步的瞭解。同時,有贊ServiceMesh架構發展的過程也正是有贊微服務架構的演進過程,期待能夠給正在進行微服務改造的團隊帶來一定的啓發和思考。

一、緣起

有贊初期,使用的是Nginx+PHP-FPM,所有的業務邏輯代碼都在一個叫做Iron的PHP代碼倉庫裏,是一個典型的單體應用(Monolith),整體架構可以簡單的表示成下圖:

該架構在有贊初期,團隊規模比較小,且業務邏輯相對比較簡單的時候,很好的支撐和承載了有讚的核心業務。但是,隨着有贊業務和團隊規模的極速發展,單體應用的缺陷愈來愈凸顯:

  • 耦合性高
  • 隔離性差
  • 團隊協作性差

一次發佈帶來的故障往往需要幾個業務團隊的人坐在一起,花費數十分鐘甚至幾個小時才能定位究竟是哪處改動引發的。對單體應用進行微服務改造,勢在必行。

綜合當時團隊和業務發展的實際情況,一方面,有贊選擇了國內非常流行且具備良好生態的 dubbo 作爲Java語言RPC框架;另一方面,考慮到團隊中有相當數量PHP開發的同學,有贊內部孵化出了 ZanPHP——使用PHP語言的純異步RPC框架,並選擇了ETCD作爲服務註冊和發現中心,開始搭建有贊服務化的整體架構。爲了解決跨語言(Java與PHP語言之間)的RPC通信問題,有贊在facebook開源的thrift協議基礎上進行了二次封裝,開發了NOVA協議用以支持跨語言RPC調用。

綜上所述,這一時期,整體的架構選型如下:

儘管將單體應用拆分成微服務能夠帶來一系列衆所周知的收益,但任何業務遷移的過程都是痛苦的,同時,在遷移過程中必定會有相當長的一段時間新舊業務架構代碼需要同時在線上運行。因而,現有承載了大量核心業務的單體PHP-FPM代碼如何調用新拆分出來的Java或者ZanPHP的微服務,就成了首當其衝的問題。

由於PHP-FPM運行模式的特殊性:單個HTTP請求處理完成之後會釋放所有的資源和內存,導致其很難實現最基本的微服務架構的需求。如,微服務架構需要調用端(consumer)長時間緩存服務發現結果,並能感知服務發現結果的變化。即使使用共享內存的方式實現,整體的實現成本也非常高,穩定性也難以得到很好的保證。在這樣的背景下,有贊PaaS團隊在16年初使用golang開發並上線了服務化代理組件Tether0.1版本。設計的主要功能爲:

  • 實現對有贊內部跨語言RPC協議NOVA的解析
  • 對接有贊內部ETCD服務發現中心,解析並緩存服務發現數據
  • 通過本地端口接受NOVARPC請求,並根據解析的NOVA請求信息和服務發現結果,將請求通過長鏈接轉發至後端服務

進行總結就是:Tether0.1版本是實現了代理、轉發NOVARPC請求至相應的服務提供方的本地Agent,簡單的架構圖如下:

從架構圖上可以看出:

  • 對於PHP-FPM(現存的單體應用),只需要實現NOVA協議的編解碼即可
  • 服務化整體架構的複雜度,包括:服務發現、負載均衡、後端服務的優雅下線等等,全部都下沉到Tether層處理。
  • Tether與有贊監控、日誌平臺對接,實現了對微服務間調用的監控和報警。

從功能和架構上可以很清晰地看出,Tether就是ServiceMesh架構中的Sidecar,只不過在有贊初期的實踐中,只有服務的調用方(consumer端)通過Sidecar發起RPC請求。

雖然在此階段引入Tether作爲Sidecar的出發點是爲了解決PHP-FPM調用後端服務的問題,但是,ServiceMesh架構帶來的優勢很快就體現了出來:整個微服務架構的複雜度都對應用隱藏了,架構的功能迭代和升級對業務應用完全透明,只需由運維升級Sidecar,業務應用便具備了新的微服務功能特性。

以此爲基礎,有贊開始逐步將單體的PHP-FPM應用拆分成邏輯和業務上相對獨立的微服務,逐步縮小PHP-FPM應用上所承載的業務邏輯,開啓了有贊微服務架構的演進之路。

二、發展

時間來到2017年年中,隨着技術團隊和業務的進一步發展,有讚的核心業務中臺基本上都選擇了Java與Dubbo的組合,爲跨語言調用設計的NOVA協議以及ZanPHP框架逐漸式微。在這樣的趨勢和技術發展背景之下,有贊技術架構確定了新的發展方向:

  • 後端業務中臺全部遷移至Java
  • PHP-FPM中與頁面拼裝和渲染相關的邏輯全部遷移至Node.js

隨着Node.js的引入,同樣的問題再次出現:Node.js作爲業務編排和模版渲染層,如何調用部署在複雜服務化架構中的Java中臺應用?

首先,是RPC協議和編碼的選擇問題。對於跨語言調用業界一般選擇使用IDL來描述接口定義,並來通過工具自動生成的樁代碼序列化、反序列化數據和編碼、解析RPC請求包,grpc和thrift都是通過這種方式實現的多語言支持。這是筆者比較推崇的方式,IDL通常僅保留各個編程語言公共的特性,而避免引入與特定語言相關的特性,進而對跨語言調用有着非常好的支持。同時IDL本身就是良好的接口描述文檔,能夠在相當程度上減少溝通、協作成本,也便於開發者養成先設計接口再進行開發的習慣。

雖然有贊初期就設計了NOVA協議,通過編寫IDL生成樁代碼的方式實現跨語言RPC,但是使用dubbo框架的Java開發同學已經習慣了直接編寫接口就能實現Java服務之間相互調用的開發模式,導致在推廣NOVA協議的時候遇到了不少的阻力——存在更便捷的調用方式時,爲何還要學習具有一定上手門檻的IDL?擺在面前的困境:使用Java的後端開發同學已經不情願、甚至牴觸編寫IDL,向Node.js的開發同學推廣IDL也可能遇到同樣的問題。

緊接着是Node.js框架與有贊服務化架構的整合問題。我們調研了業界已有的開源方案 dubbo2.js。雖然Node.js具有實現請求編解碼、服務發現、長鏈接保持、請求負載均衡的能力;但是,業務前臺是否有必要引入如此的複雜度?服務化調用的監控、路由策略、限流、熔斷等特性,Node.js是否需要全部都實現一遍?若有贊後續業務需要使用新的編程語言:C#、Python等,那是否這些編程語言又要再實現一遍這些特性?

dubbo2.js對我們面臨的第一個問題提供了一定的思路:通過顯示的指定調用的後端Java接口的參數類型,dubbo2.js實現了dubbo協議的編解碼,達到了不通過IDL實現跨語言調用的目的。但是,讓Node.js的開發同學去感知後端的Java類型系統真的合理嗎?

基於以上的思考,我們想到了早已接入有贊微服務框架的Sidecar產品:Tether。同時,在傳統的Sidecar上進行了創新:爲了貼近Node.js同學的開發模式和習慣,並最大程度的隱藏後端服務化架構的複雜度,我們設計了簡單的HTTP+Json的接口用以Node.js與Tether之間的調用,由Tether實現HTTP協議與微服務調用的dubbo協議之間的相互轉換。整體架構如下:

值得注意的是,圖中的泛化調用並不是開源版本dubbo的“泛化調用”,而是有贊內部仿照開源版本的“泛化調用”針對性實現的參數使用json編碼(有贊內使用dubbo默認的hessian2序列化方式編碼參數)的“跨語言泛化調用”,該接口調用返回的也是hessian編碼的json串。

至此,一勞永逸地解決了多語言接入有贊服務化架構的問題,由於HTTP+Json協議的通用性,任何其他語言都可以很方便地通過Tether調用核心Java服務的dubbo接口,而不用關心服務發現、監控等複雜問題。

目前,該架構在有贊生產環境中已經運行了一年半多,通過Tether的請求佔到有贊微服務總流量的20%+,是有贊微服務整體架構的重要組成部分。

三、More…

在ServiceMesh架構落地過程中,我們欣喜的發現,除了最初的設計意圖之外,Tether作爲Sidecar還能夠實現其他非常有價值的功能,進而在一些項目中發揮至關重要的作用。

在實際項目中,我們遇到了這樣一種場景:應用需要調用部署在另一個機房中的服務。由於調用發起方和服務提供方分屬不同的微服務集羣,服務發現無法發現部署在另一個機房中的服務提供方;同時,在跨機房調用的場景下,數據的安全性也必須得到高度的重視,跨機房調用必須經過嚴格的加密和鑑權。

有贊PaaS團隊通過“服務僞裝(ServicePretender,以下簡稱SP)”應用和Tether相結合,很巧妙的滿足了跨機房服務發現和數據加密、鑑權的需求。整體的架構圖如下:

值得注意的是,爲了便於理解,圖中只畫出了A機房Service0調用B機房Service1的調用圖示。實際上A、B機房是完全對稱的,B機房調用A機房的應用是完全一樣的。對稱的設計極大地簡化了系統架構,降低了運維難度。SP與服務註冊中心、Tether以及對端機房對稱部署的SP進行交互,主要完成以下兩個功能:

  1. 提供HTTPS服務,對端的SP應用可以通過相應的接口,獲取到本機房服務註冊中心ETCD上的應用元數據(圖示中,機房A的SP通過HTTPS協議從B機房的SP獲取到註冊在B機房ETCD上的Service1的元數據信息)
  2. 與指定的Tether保持心跳檢查,若Tether心跳正常,則將對應的Tether使用1中獲取的服務元數據信息,註冊到本機房的服務註冊中心。即將Tether僞裝成對端機房的相應應用(圖示中,機房A的SP將同機房的Tether僞裝成了B機房的Service1應用)

如此,當機房A中的Service0想要調用機房B中的Service1時,根據服務發現的結果,Service0會請求本機房的出口網關TetherA,TetherA收到請求後會直接轉發至B機房的入口網關TetherB,再由TetherB根據本機房內實際服務發現的結果,將調用請求路由至實際需要請求的Service1。同時在網關型的TetherA與TetherB之間,使用了TLS雙向驗證、加密來滿足數據加密和鑑權的需求。

在架構和功能上,SP與Tether完全解耦,可以各自獨立迭代和升級。由於使用了dubbo框架服務發現的特性,網關Tether可以方便地進行水平擴容;當需要進行版本升級之時,也可以通過調整服務發現的權重,方便的進行小流量灰度。加上Tether早已對接有贊監控系統,所有跨機房的服務化調用都得到了很好的監控,具備很強的可觀測性。所有這一切,保障了整體架構的穩定和可靠。

通過如上的設計,在無需任何業務和框架改造的前提下,有贊基礎保障部門實現了應用的跨機房拆分和服務化調用。整個過程,基本上做到了無需業務方參與和改造。

四、當下

正如上文所述,在有贊主站,Tether目前主要是作爲跨語言調用場景下Consumer端的Sidecar,以及跨機房調用時服務化流量的出入口網關。雖然在跨機房調用場景下,Tether實際上同時託管了Consumer端和Provider端的流量,但離真正Sidecar託管全部RPC請求的ServiceMesh架構還存在一定的距離。即使Tether的可靠性已經在生產環境得到了長時間的驗證,在已然非常成熟的dubbo生態中推廣使用,還存在非常多的困難和阻力。

有贊雲,是有贊面向有技術研發能力的商家和開發者提供的實現自定義拓展和需求的“雲平臺”。作爲“雲平臺”,有贊雲需要爲開發者提供一整套、包含完整功能的“微服務架構”,以便開發者快速搭建自己的應用、服務集羣。按照有贊內部的經驗,推動業務開發者升級應用框架是一個極其漫長而痛苦的過程;面對使用“有贊雲”的外部開發者,可想而知,這個過程將會變得更加不可控。ServiceMesh架構完美地解決了這個問題,通過將複雜的架構功能下沉到Sidecar,應用框架將變得非常簡單和輕薄。微服務層的功能迭代和升級,僅需靜默升級Sidecar即可,無需任何業務開發者的參與和協同,極大地提升了整體架構的靈活性和功能迭代可控性。

與此同時,使用“有贊雲”的開發者們必然不會都使用Java這一種開發語言,使用ServiceMesh架構避免了爲每種支持的語言都開發微服務架構和進行後續功能迭代,在極大的減少開發工作量的同時保證了各個語言的微服務能力同步迭代。在上文中已經提及,目前有贊主站使用的dubboRPC(默認使用hessian序列化)協議與Java語言特性耦合嚴重,不適合於跨語言調用的場景。基於此,在“有贊雲”場景中,我們設計了基於HTTP1.1和HTTP/2協議的可拓展的RPC協議,用於跨語言調用的場景。其中HTTP/2協議由於其標準化、異步性、高性能、語義清晰等特性,期待其成爲未來跨語言調用的主流。

另外值得一提的是,“有贊雲”已經全面擁抱開源社區,使用Kubernetes進行服務編排和應用管理,極大的提升了多用戶場景下的運維效率,也爲未來支持更多的開源特性和拓展打下了堅實的基礎。在此基礎上,有贊PaaS團隊將Istio的服務發現組件Pilot也引入到了“有贊雲”的ServiceMesh架構中,Pilot直接對接Kubernetes爲TetherSidecar提供服務發現能力。通過Kubernetes本身的服務編排能力,業務應用不再需要進行服務註冊和服務保活;“有贊雲”業務微服務集羣也不再需要搭建獨立的服務發現集羣(ETCD,Consul等)。在簡化整體架構、降低成本的同時,向開源社區更靠近了一步。

目前,基於Kubernetes、IstioPilot、TetherSidecar的ServerMesh架構已經在“有贊雲”中全面落地,新特性和功能也在不斷迭代中。期待爲開發者提供更友好的開發、託管環境和更強大的功能支持。

五、展望

有贊主站的應用目前正在逐步向容器化和Kubernetes遷移,並且將在19年年內實現絕大多數應用的容器化。隨着業務規模的不斷增長,有贊微服務集羣規模也隨之水漲船高,有贊在服務化初期選擇的ETCDV2服務發現在18年雙十一期間已經遇到了瓶頸:應用發佈時,全集羣廣播服務發現信息造成ETCD集羣抖動。爲了支持更大規模的微服務集羣規模,同時對dubbo框架和TetherSidecar屏蔽實際服務發現數據的存儲格式和存儲系統,目前有贊內部也在將服務發現從ETCD遷移至IstioPilot組件,使用Pilot提供的ADS(AggregatedDiscoveryService,聚合發現服務)接口獲取服務發現數據。

在開源Pilot的基礎上,爲了滿足有贊內部的需求。我們對Pilot進行了一系列的優化和改造,包括服務發現適配有贊ETCD的數據結構、對一定時間窗口內的服務集羣變更事件進行聚合等。有贊內部還通過Pilot和Istio的路由規則,實現了dubbo請求的流量控制,通過具體的RPC流量控制,在上層包裝出了“灰度發佈”、“藍綠髮布”等產品。無論是Pilot的優化和改造,還是對Istio路由規則的使用,近期都會有專門的文章進行詳細的介紹,這裏不再展開。

未來,我們期望,在有贊主站應用完全容器化+Kubernetes之後,主站的整體服務化架構會向“有贊雲”發展:Pilot直接通過Kubernetes的服務編排能力獲取服務發現數據,dubbo框架不再需要進行服務註冊和服務保活。

同時,有贊內部,部分網關型的Java應用需要調用大量不同的後端服務接口,爲免去不斷的構造調用句柄之苦,已經開始接入TetherSidecar,並在生產環境進行使用;大量的其他Java應用也已經在QA環境接入和使用TetherSidecar。期待在不久的將來,通過ServiceMesh加上容器化兩把利刃,能夠讓架構升級和迭代的過程更加可控,免去業務開發者升級架構之苦的同時儘快享受到新的功能。

本文轉載自公衆號有贊coder(ID:youzan_coder)

原文鏈接

https://mp.weixin.qq.com/s/cRYYqx6zEo5icmVi1vvZZg

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