Categories
程式開發

服務治理在貓眼的演進之路(二):Service Mesh


首先看一下,微服務架構的演進歷程中我們更關注的是什麼?一般微服務業務應用架構的演進歷程無非都是單體應用 -> 服務化 -> 雲原生 -> Serverless。這個過程中,每一次的進化,大體上可以認為都是通過拆分和抽象,伴隨犧牲一定的性能,來達到最終提升人效的目的。所以可以得出一個結論:在演進式的架構中,越來越關注人效,而非絕對的性能。

那麼問題來了,既然人效如此重要,那如何提升服務治理的人效呢?

服務治理在貓眼的演進之路(二):Service Mesh 1

服務治理的包含了非常多的能力,比如服務通訊、服務註冊發現、負載均衡、路由、失敗重試等等。在整個服務治理的歷程中,我認為湧現出三種思潮。

  • 第一類,中心式的治理。利用集中式的集群來完成治理。比如HAProxy、Nginx、Tengine、Codis、Mycat 都可以認為是這類治理手段,他的好處就是能夠跨語言,問題就是會有性能損耗以及鏈路單點問題。那如何解決這些問題呢?於是有了第二種思潮如下:

  • 第二類,融合式的分散治理。採用和業務進程通過 SDK 方式徹底融合,來達到去中心化的分散治理的目的。這也是目前基本最主流的服務治理的樣貌。比如 Dubbo、Ring-pop、Thrift、gRPC、Motan 的主流樣貌。這樣的好處即能達到性能的最優和最短鏈路,但是問題即與業務強耦合帶來的跨語言成本和運維升級的高昂代價。到了這邊,感覺有點無解,既然中心式部署不行,融合式的部署也不行,那怎麼辦呢?於是出現了第三種思潮:

  • 第三類,貼合式分散治理。貼合相比於前面談到的融合的區別,就在於離業務很近,但是不要作為一個SDK 直接融入業務進程,既能最大程度降低對性能和穩定性影響的同時,同時解決業務耦合的問題。這種架構理念其實很早就被提出了,在 LinkedIn、Airbnb 的 smartstack 治理體系、攜程OSP 以及各種雲原生服務治理方案 K8s、Marathon 中都可見到踪影。我們統稱之為 Service Mesh — 服務網格。

服務網格在貓眼的落地理論架構

當然,Service Mesh 本身是有一些不成熟或者待商榷的地方。接下來,介紹下服務網格在貓眼的一個實際落地的理論架構。

服務治理在貓眼的演進之路(二):Service Mesh 2

首先,先明確終極目的:能夠在犧牲一小部分絕對性能的前提下實現人效的大幅提升。

眾所周知,要提升人效,最好的方式就是一切都自動化,這樣就無需人工介入了。

而要實現自動化的前提則是完成標準化,非標的產品要實現自動化的成本、代價是非常之高的,往往即便實現了也容易出現這樣那樣的邏輯和非邏輯的坑。

而要完成標準化需要做什麼呢?就需要進行職責分離,類似於前後端分離的理念,我們需要將業務和服務治理中間件能力分離,否則異構的業務必然會帶來服務治理能力的非標準化。

所以進一步推演,可以發現 Service Mesh 其實就是處在推演起點的核心解決方案。在我們看來,Service Mesh 核心理念是兩個:

  • 其一,治理與應用分離,業務應用和治理能力的就近物理切割。通過部署本地代理的方式加上 Iptables 攔截的方式來完成這個切割。
  • 其二,強調執行和控制的分離,也即他非常著名的控制平面和數據平面的切分。

然而,這樣就足夠了嗎,理想照進現實的模樣是什麼樣的呢?
服務治理在貓眼的演進之路(二):Service Mesh 3Service Mesh 在生產環境應用中面臨的難點和挑戰部分如下:

  • 控制平面的邊界在哪?業內最出名的 Istio 的 Mixer 的 check 方法會帶來很嚴重的性能挑戰,即便加了本地 cache 仍然有嚴重的性能問題,而且可能會帶來代理資源開銷的指數級提升。 Report 方法則會帶來2倍的網絡開銷。這種非常暴力的一刀切的方式其實並不為業內所接納,包括 Istio 本身也正在將 Mixer 能力整合到控制面板中。

  • 新的單體應用困局如何破局?我們是否注意到一點,即我們把各種服務治理甚至其他中間件能力下沉,必然會帶來在 PaaS 中間件領域新的單體應用問題。你的配置管理、限流、熔斷、混沌工程、服務註冊發現,以及各種存儲、日誌、監控報警的中間件能力都會集中到這個單體代理上,這根本是不可能 Work 的一個架構。針對這種問題,如何解決呢?

  • 零侵入業務真的現實嗎?Service Mesh 也很強調對業務進程的零侵入,希望將服務治理能力看待為協議棧的一部分。而我們實際使用中,我們需要或者已經有現成的RPC 書寫和調用的方式,這種方式可以讓我們的業務之間的調用更規範、易於上手、不易出錯。徹底抹殺掉這部分現實嗎?

  • 其他的就是 Service Mesh 本質上是寄生在業務資源中的一個大規模部署的形態,如何保障交付質量、如何降低性能和資源開銷,如何最大程度保障可用性,都是需要探索的問題。

基於這些難點與挑戰,貓眼摸索出的最終落地理論架構是:有節制、可插拔、半貼合式的無中心治理。

  • 有節制指對於控制平面和控制平面的切割,不追求一刀切。
  • 可插拔是為了應對新單體應用的問題,數據面板的各種能力應該是可以服務化,各種能力是可插拔的。
  • 半貼合式即我們為了保護業務實際使用中的體驗,不追求對業務進程完全無侵入,而採取盡可能低侵入的方式進行。

為了確保這個治理的高質量交付,技術團隊在可用性、性能、交付質量上都做了充分的工作。下面會具體展開介紹。

在具體介紹之前,大家可能都會有這樣一個疑問,“你們為什麼不採用開源方案呢?” 國內開源最為活躍的即螞蟻的 Sofa-Mosn,國外開源最主流的即為 Google 牽頭的 Istio。我們主要對標的是這兩款產品,沒有直接使用主要出於這麼幾個原因:

  • 在2018年啟動的時候,國內主流開源方案的 Sofa-Mosn 也處於一個快速迭代,不穩定的狀態。

  • 由業務特性決定,不希望上來就和雲原生綁定,這個和業內的主流解決方案不太相符。

  • Istio 本身開啟 Mixer 之後存在較為嚴重的性能問題。而我們本身也希望在性能優化上能夠有更多探索的空間。我們的一些性能優化的措施如自研的 IPC 框架和 Istio 本身架構整合成本就很大。

  • 貓眼存在較多已經存在的服務治理中間件,比如前面的高可用治理中心就是其中之一。他們的功能其實和開源的內容存在很大Gap,無法直接套用,而如果進行整合的話,成本非常之高。基本無法接受。

  • 最後一個則是更加現實的問題,貓眼並沒有專業的 C/C++的團隊,所以我們不存在基於 Envoy 去開發的基本條件。

基於以上原因考慮,最終沒有直接採用開源的產品,而是導向自研。但技術團隊在整體自研過程中也充分借鑒了一些開源的優秀設計實現。

服務治理在貓眼的演進之路(二):Service Mesh 4

基於以上的推演,貓眼的下一代微服務治理體係也就呼之欲出了,代號盤古,為貓眼的服務治理中心。他是貓眼下一代的服務連接、註冊、發現、中間件管理的一站式解決方案。

服務治理在貓眼的演進之路(二):Service Mesh 5

如圖是盤古服務治理中心的系統架構。可以看到整體上,仍然是劃分為上下兩層,即控制平台和數據平面。有幾個模塊是被標紅的

  • Portal 是數據平面。對標 Envoy、Mosn 這些數據平面。
  • Dolphin 是輕量級的 Mesh SDK,供業務方進行實際各種服務治理能力使用。
  • Pilot 是配置型控制平面的對接適配層。對接了註冊中心、配置中心和各種元數據中心。
  • 控制平面還包括了監控、鏈路追踪、流控、地址服務、一站式治理平台等服務。

服務治理在貓眼的演進之路(二):Service Mesh 6

這個是 Service Mesh 的數據平面架構。整體架構上抽像出 Server、Transport、Stream、Router、Cluster、Resource 幾個核心層。

  • Server 層負責服務啟動、可用性保障、指標收集以及XDS交互。
  • Transport 層負責底層通訊鏈路。
  • Stream 層負責協議解析、會話綁定/銷毀,以及對 Transport 的連接和讀寫能力的封裝。
  • Router 就是負責將請求路由至對應的 cluster 上。
  • Cluster 負責進行負載均衡、目標機器篩選、失敗重試、連接管理。
  • Resource 負責對 Mesh 的資源進行管理,包括各類協程池、對象/字節池,以及 SPI 框架。

整體上,他和 Envoy 以及 Sofa-Mosn 的整體架構是非常類似的。

服務治理在貓眼的演進之路(二):Service Mesh 7

接下來看一下貓眼的 Service Mesh 的一些建設思路。首先,是對控制平面基進行有節制的切割和優化。

最先去做的架構選擇,就是將遙測下沉,限流接入自建流控,取消Mxier,解決性能問題。其次,將服務註冊與發現整合,提供完整解決方案。改變了 Service Mesh 只關注服務發現而不關注​​服務註冊的問題。

控制平面可以分為兩類:

  • 配置型的控制平面,主要用以下髮指令和配置。如註冊中心、配置中心、各種中間件的元數據中心。
  • 數據型的控制平面,會有大量的實時數據的存儲或分析,比如分佈式鏈路追踪、比如監控報警、比如日誌。

配置型平面以MMCP(Maoyan-MCP)協議快速接入,協議提供Watch、UnWatch、Push、PassThrough 四個通用能力接口,新的配置型平面只要按照這個協議來接入,就可以實現快速接入,整個過程Pilot零改造成本。

數據型控制平面和普通應用一致性對待,我們認為他們就是一個普通應用。也需要接入我們的Sidecar 來做流量管控,所以我們這裡也是對這類控制平面踐行Pet&Cow 理論,即你應該盡量少養寵物,多養奶牛,寵物生病需要治等等的特殊照顧,而奶牛生病直接殺了就行一視同仁。我們希望盡可能地對我們分佈式拓撲下的節點一視同仁,這樣能降低系統複雜度。

服務治理在貓眼的演進之路(二):Service Mesh 8

我們對於 Service Mesh 也進行了較多的性能優化的嘗試。

  • 採用了基於 Reactor+ 多級協程池的異步通訊模型
  • 將協程進行多級的池化,對於字節和對象資源進行池化。來降低調度和內存分配所帶來的資源開銷
  • 採用 Copy-On-Write 的方式來對核心配置進行無鎖化替換。也採用了CAS 的方式來對一些核心請求狀態進行原子修改。整個過程實現了無鎖化的設計。
  • 在協議層面,將 Payload 後置到協議尾,同時反序列化時將 Header 和 Body的byte內容進行緩存。以此來達到加速請求傳輸的性能。
  • 我們有很多地方都存在著心跳,比如從調用方Sidecar 到服務提供方的Sidecar,當調用方依賴多個服務提供方,且服務提供方具有較多實例的時候,我們將不得不建立大量的心跳協程來檢測健康狀態,這很明顯是不OK的。所以我們採用了時間輪的方式來進行心跳維持邏輯的優化。降低了資源開銷。
  • 同時也提供類似於 Java 的 SPI 機制,對可以單例化的一些對象,比如各種 Filter 進行了單例化處理。
  • 最後, Service Mesh 為了保障一些高流量應用以及後續可能會延伸到的基礎設施層的服務的性能,也進行了高流量下IPC 優化的探索,基於uds和mmap 自研了一個RingBuffer,以mmap 傳遞數據,以uds 進行事件通知,進行了內存對齊、無鎖化等等的優化。最後可以看到在高 QPS 下,其相比於 tcp/uds,最大性能可提升30%。

經過了大量的性能測試,在壓測環境下 RT 小於0.1ms,在灰度場景下 RT 增加在0.5ms以內。穩定性達到了5個9,CPU 消耗在1%以下,內存佔用在30M左右。以上是採用 TCP 來進行本地通訊的結果數據,而如果採用我們自研的通訊框架,通訊 RT 可進一步最高提升30%。

從目前的情況來看,足以滿足貓眼業務的要求。當然,在性能優化上,仍然會結合業務需要在合適的時候進行進一步的探索。

服務治理在貓眼的演進之路(二):Service Mesh 9

在可用性方面,貓眼 Service Mesh 面向貓眼業務,做了充分的保障。

為了方便起見,我們稱服務調用方為C,服務調用方的 Sidecar 為CA,服務提供方為P,服務提供方的 Sidecar 為 PA,那麼來看下日常運維中可能碰到的一些主要場景:

  • 第一,Mesh 在前期灰度和迭代期間,避免不了會進行經常性的發布。這個時候需要保障業務方流量無損。在當前階段的做法是,基於狀態機的流轉,針對 PA 重啟的情況,會將鏈路從 CA->PA 切換為 CA->P。針對 CA 重啟的情況,會將鏈路由C->CA->PA->P 直接切換為 C->P。等重啟之後狀態變更回正常了,這個時候再進行回切。後續針對 CA 不可用的場景,也會進行句柄熱遷移的能力實現。

  • 第二,業務應用發布需要能夠平滑發布。採用通過對老註冊中心的狀態變更監聽,來同步新註冊中心對應的狀態,這樣就可以在不侵入老發布系統平滑發布全流程的時候完成應用的平滑發布。

  • 第三,場景是 Mesh 宕機,首先會有對應的運維 Agent 進行 mesh 的保活,以及我們也有流量防禦的機制,主動/被動探測到 mesh 不可用後會做快速的鏈路切換。最壞情況下,SDK 會自動切換為直連情況,徹底繞過 mesh。

  • 第四,在C->CA->PA->P 以及和Pilot,註冊中心的交互鏈路中,任意一個節點出故障,都有對應的被動感知和主動探測的方式來發現並進行主動的failover 。

  • 第五,是實際推動業務試用的過程,必然需要考慮灰度的問題。能夠進行服務、機器的多維度灰度,並可以在故障發生時一鍵回滾。
    -第六, 註冊中心方面,註冊中心可能會出現網絡分區的情況,這個時候可能會導致註冊中心誤判服務提供方不可用而將其剔除,進而引發業務問題。採用類似 Eureka會引入自我保護的機制,對於突發性的大批量節點下線,我們會不信任註冊中心的結果,而主要依賴主動心跳健康檢查的判斷。沒有採用 Envoy 的服務發現註冊中心和健康檢查共同決定的策略,是因為我們發現這樣的case — 業務中有出現老註冊中心顯示機器已下線但是服務仍然短時間內可聯通的情況。而這個時候如果仍然聯通則是非常危險的。

  • 第七,註冊中心如果不可用的情況下,會有 Sidecar 內存和文件的多級別緩存來保障可用性。

通過以上手段,我們的可用性一直維持在6個9左右。很好地為業務提供了各種保障。

服務治理在貓眼的演進之路(二):Service Mesh 10

在 CI/CD 環節,我們完成了整個流程的閉環建設。在 Pipeline 中,我們進行了自動化的性能測試和混沌測試。我們整個性能測試過程中,需要模擬5 * 5 * 5一共125種的的場景、調度流量、探測容量上限、對過程中的各種指標進行採集,以及產出結果報告,正常做一輪下來,需要耗費大量的時間和精力,所以我們針對這種情況進行了自動化性能測試體系的搭建,將上述環節都進行了自動化處理,並集成進了CI流中,讓我們的每一次發版都能夠對於代碼Diff 帶來的性能變化有更直觀的了解。

另外,上線後的生產環境會有隨機的各種場景出現,這些場景都可能會引發系統問題,所以在 CI 環節中引入了自動化的混沌測試。針對模擬的複雜拓撲去觸發隨機概率事件,包括請求響應的包體大小和流量規模也會隨機產生,同時會去模擬服務伸縮、服務重啟、機器宕機等等的 case。通過這種混沌式的沙盒測試,對系統進行更進一步的可用性和性能的探測。

服務治理在貓眼的演進之路(二):Service Mesh 11

最後,針對於前面提及的中間件能力的下沉所帶來的新的單體應用的困局,技術團隊採用的方式就是進行數據平面的“服務化”。

從上到下進行了三層切割:

  • 第一層是中間件門面層,包括 KV、RPC、Trace、Redis 等的中間件底層都基於和 Mesh 交互的統一 SDK,上層由貓眼的腳手架工程統一封裝。

  • 第二層即有節制拆分的中間件服務化層,拆分出 RPC、監控、存儲等四個 Mesh。這時候,可能就會產生一個問題,即我RPC mesh 裡面也需要監控能力,監控Mesh 裡面也需要RPC 能力,這本身是一個相互依賴的關係,如何能夠以更優雅可控的方式來進行拆解?我們的解決方案也就是構建服務化體系第三層 — 中間件模塊化層。

  • 第三層也是中間件 Mesh 服務化的基石。我們將最為通用的能力進行下沉,收斂出Mesh Stone 這樣的底層核心上,在這之上,提供了RPC、Log、Trace 等等很多可插拔的模塊,可以通過在Mesh 初始化的時候自由組合拼裝任意模塊來快速完成一個Mesh 的底座封裝,並在這之上去實現自己獨有的業務邏輯。如此一來,我們就可以在相互不影響的情況下來實現最大程度的複用。

可以看到,提煉的關鍵詞即第一個,進行三層切割,分為門面、服務化、模塊化三層,其次進行頭尾合併,最底層有統一的模塊化底座Mesh Stone 來提供最通用的能力和整體框架。最上層有統一的門面封裝來為業務方提供一致性的使用體驗。最後,為了能夠支撐起中間件服務化層的相互獨立以及最大化復用,我們允許這種可插拔的模塊之間的自由拼裝組合。

通過這三點,可以實現數據平面的服務化。但需要特別警惕 Mesh 過渡拆分導致的 Agent 氾濫引發的運維問題,這塊需要跟隨著後期中間件的實際落地去把握裡面的度,貓眼也是在一個探索的道路上。

未來規劃與探索

服務治理在貓眼的演進之路(二):Service Mesh 12

前面提到了貓眼當前在提升系統穩定性和提升人效方面所做的一些探索。高可用治理中心目前已經在貓眼大規模鋪開落地實踐了,而貓眼基於 Service Mesh 的服務治理中心目前也已經在新業務中進行落地實踐,整體上處於在生產環境中持續探索驗證的階段。未來,貓眼的服務治理主要會朝三個方向去探索與演進。

  • 將治理能力 AIOps 化。比如基於更全面的健康度量體係來進行一些智能決策,比如治理策略無參化、智能報警、容量自動評估水位預警、故障診斷、異常探測、動態伸縮等等方面。
  • 希望藉由 Service Mesh 數據面板的服務化能力,將中間件進行網格化。以此將網格的紅利從 RPC 延伸到我們越來越多的 PaaS 設施中。
  • 建立在雲原生的基礎上進行 Serverless 的探索,目前的 Serverless其實對於簡單邏輯的應用比較友好,但是對於像基於 Java/Golang 之類的重度業務邏輯的應用來說,較難落地。所以我們也希望在未來探索 Serverless 如何真正意義上能夠解放業務方的人力。

未來貓眼的服務治理會通過這三個方向的延伸,聚焦在解決人效、穩定性以及資源利用率的提升上。