本文做者:邵俊雄(熊嘯),螞蟻金服中間件團隊高級技術專家,目前主要負責 SOFAMesh 的開發工做。git
本文是基於做者在 Service Mesh Meetup #3 深圳的主題分享《SOFAMesh的通用協議擴展》部份內容所整理,完整內容見文末的直播回放github
本次分享主要介紹螞蟻金服在 SOFAMesh 上開發對 SOFARPC 與 HSF 這兩個RPC框架的支持過程當中總結出來的通用協議擴展方案docker
SOFAMesh 是螞蟻從 ISTIO 上游克隆的開源項目,目的是在 ISTIO 的基礎上進行控制平面的發展和創新,同時保持和上游 ISTIO 的同步更新,跟隨 ISTIO 的發佈節奏,固然也會把一些有價值能力貢獻給 ISTIO 社區。後端
SOFAMesh 的一個重要目標是用螞蟻自研的 Golang 版 L4/L7 層代理服務器 SOFAMosn 做爲數據平面,取代 C++ 開發的 ENVOY。以前的 Meetup 中咱們已經探討過了一個 Golang 版本的數據平面的重要性,咱們相信統一控制平面和數據平面的開發語言能夠加快 Service Mesh 的技術創新和產品化落地的速度。安全
目前咱們已經完成了集成 SOFAMosn 的前期開發工做,打包,安裝,部署和測試的腳本都已經從 ENVOY 遷移到了 SOFAMosn,全部的鏡像也都推到了公開的鏡像倉庫。下一步 SOFAMesh 將會總體在 UC 內部基於 Kubernetes 的 PAAS 平臺中落地,在實際的生產環境中打磨。將來,SOFAMesh 還將在螞蟻主站落地,在金融級的生產環境中進一步打磨。服務器
ISTIO 目前僅能支持 TCP/REDIS/MONGO/HTTP 協議,其服務治理規則主要針對 HTTP 服務制定,對於業界目前大量在高併發、低延遲環境下使用的 RPC 框架及通訊協議,例如 DUBBO/HSF/SOFA 沒有很好的支持,SOFAMesh 把對於 RPC 通訊協議的支持做爲重點來看待,SOFAMosn 默認就提供對於 SOFA BOLT 協議的支持。微信
SOFAMesh 也是控制平面創新發生的地方,咱們已經規劃了的創新包括 Mesh Operator,RPC Service Controller 等等。將來的 Serverless 平臺也會考慮基於 SOFAMesh 打造,SOFAMesh 將爲 Serverless 平臺提供基於 Reversion 的服務治理能力。Google Cloud 最近聯合 CloudFoundry 和 IBM 發佈的 Serverless 平臺 Knative 一樣也是基於 ISTIO 打造,和咱們的想法也是不謀而合。網絡
SOFAMesh 的下一步也是要融合到 PAAS 平臺裏面去,成爲 PAAS 平臺基礎網絡能力的一部分,用於支撐上層的業務更快速的創新,咱們還會增強文檔和快速上手方面,方便你們試用 SOFAMesh。併發
第二部分是此次分享的重點,主要介紹螞蟻金服在集成 SOFA/DUBBO 和 HSF 這些框架的過程當中碰到的問題和咱們的一套通用的解決方案,但願可以加速 Service Mesh 在實際生產中的落地。負載均衡
總的來講,業界在 Service Mesh 落地的時候主要有下面四種作法,基本上每種咱們都思考和嘗試過,最後咱們也是走了一條按部就班的道路。
第一種作法,比較常見,就是不用 ISTIO 只參考它的設計理念,用 ENVOY/MOSN 或者自研的 SIDECAR 結合已經成熟而且大規模部署的註冊中心/配置中心組件,快速上線,拿到多語言,灰度發佈,安全這些紅利,好比惟品會的 OSP Local Proxy, 華爲的 Mesher 都是這個套路。其實 ENVOY 最先也是如此,但願用戶在 ENVOY 上直接擴展對 Consul, Eurkea 這些註冊中心的支持。可是社區沒有走這條路,反而對其 XDS API 進行了適配,由此誕生除了 Service Mesh 的控制平面,並進一步演化出了 ISTIO。目前看來這麼作的主要問題是沒法利用 ISTIO 社區在服務治理上的創新和工做,存在重複的建設,因此後來有了第二種思路。
第二種作法,更進一步,使用 ISTIO, 可是把 Kubernetes 剝離出去,適用於不少短時間內沒法上 Kubernetes 的企業。ISTIO 控制平面原本就提供了這個能力,ISTIO 有兩個擴展點,一個經過 Platform Adapter 對接第三方註冊中心,另外一個 經過 Config Adapter 對接不通的配置存儲。這個作法業界最典型的是 Ucloud 的輕量級 Service Mesh 方案,他們把 Pilot Discovery 模塊從 ISTIO 裏面剝離了出來,增長第三方註冊中心的 Platform Adapter,Cofig Store 直接對接 ETCD 集羣,能夠經過 docker compose 直接跑起來整個 ISTIO。好處是入門更簡單了,可是失去了 Kubernetes 提供了基礎能力,ISTIO 的武功已經廢了大半。
後來又了第三種作法,聽說也有很多公司採用,具體作法是把 Kubernetes 作一個虛擬機用,閹割其服務發現,DNS 等能力,用註冊中心/配置中心等成熟且大規模應用的產品替代。惟品會前幾天發的文章說明他們已經把這個作法在生產中落地了。這種作法通常只使用 POD 和 StatfuleSet,不建立服務和Endpoints。通常來講, ISTIO 經過 Platform Adapter 對接註冊中心,Config Adapter對應配置中心。相比前兩種作法,這個作法更加複雜,好處是成熟的配置中心和註冊中心可以快速的落地 ISTIO,不用解決 ISTIO 因爲 ETCD 存貯帶來的擴展性問題。這個作法還有個變種就是徹底不用 ISTIO,直接在 ENVOY/MOSN 上對接註冊中心和配置中心,甚至完成 MIXER 的檢查和遙測上報的能力。好比惟品會,用的是 DaemonSet,在同一個 Node 上共享 SIDECAR,其 SIDERCAR 組件 OSP Local Proxy 直接集成註冊中心/配置中心。
最後一個作法是咱們努力的方向,向 Kubernetes 原生的方向發展,在生產環境中落地打磨,並和社區一塊兒解決碰到的問題。
充分利用 Kubernetes 基礎設施的能力是將來的方向,只要路走對了,就不怕遠,好比說透明路由網絡流量是方向,IPTABLES 是一個實現手段,它的性能不夠好,那咱們就經過引入 Cilium,用 EBPF 代替 IPTABLES。因爲 BYPASS 了兩次 TCP 協議棧道穿透,轉發性能比經常使用的 loopback 地址 Workaround 方案還要好。更進一步,咱們還能把 ISTIO 數據平面的同步檢查邏輯,好比訪問控制,經過 Cilium 推到內核的虛擬機中執行,從而解決 ISTIO 的另外一的性能瓶頸。
Kubernetes 已經成爲了雲原生的事實標準,咱們應該充分利用 Kubernetes 的能力,借用社區的力量發展本身的技術。
Spring cloud kubernetes 項目給 Spring cloud 項目落地 Kubernetes 提供了支持,可是在整合 ISTIO 的時候碰到了問題,即使使用 Kubernetes 做爲註冊中心,客戶端的負載均衡和服務發現組件也會破壞 ISTIO 對請求規格的依賴,通過負載均衡以後發送給 ISTIO 數據平面的 PODIP 沒法被正確的路由的後端的集羣,既沒法匹配到 Virtual Host。咱們經過 BeanFactoryPostProcesser 在請求中帶上了 Host 頭,指向服務在 Kubernetes 中的域名,從而解決了這個問題,也所以認識到,給微服務框架的 SDK打補丁,或者說推進微服務框架輕量化多是一個實現對業務代碼無侵入性,必須的代價。
Envoy 社區目前尚未對非 HTTP 的 RPC 通訊協議提供擴展支持,SOFAMosn 目前內部已經基本完成了 DUBBO 擴展的開發工做。
因爲 ISTIO 的服務治理,路由規則都是針對 HTTP 協議定義的,當應用到基於接口,方法調用的 RPC 服務時,會有概念模型匹配的問題,比方說在定義 Content Based Routing 規則的時候。這裏,咱們選擇了把 RPC 協議映射到 HTTP 上去而不是從新定義新的 RPC 路由的 CRD。
RPC 服務的容器模型也是個麻煩問題,目前大規模使用的 RPC 框架都是從 SOA 發展過來的,基於的仍是傳統的容器模型。一個容器中每每同時存在多個服務,各自有本身的版本,ISTIO 基於版本的路由要求每一個服務都有本身的 POD 和 Service 定義,不然的話 Traffic Splitting 功能就沒法完成。
ISTIO 的控制平面抽象,頂層路由對象是 Virtual Host,Virtual Host 包含一組 Domain,經過 Domain 來選擇 Virtual Host,Rate limit 也是定義在 Virtual Host 上面。
在 Outbound,也就是客戶端的 SIDECAR 收到請求的時候,ISTIO 爲服務生成的 Virtual Host 包含了服務的域名,Cluster VIP 和 端口的多種組合形式,這個形式確保了對 Host 頭和 DNS 尋址的支持。Inbound,也就是服務端的 SIDECAR 收到請求的時候由於全部流量都去到後面的服務實例,因此域名是通配全部。
Route 上定義了超時,熔斷,錯誤注入的策略。Route 上定義的 Header Matcher, Query Parameter Matcher, Path Matcher 等等都是針對 HTTP 協議的,RPC 協議須要進行映射以支持 Content Based Routing。
Route Action 指向後端集羣,支持重定向和直接返回,集羣經過名字路由,集羣的變更受到 Destination Rule 的影響,主要是反應在 Subset 的變化上,權重信息就定義在這裏。
SOFA 的註冊中心使用 Interface 來識別服務的,服務的配置信息,消費者和提供者列表,以及超時等服務治理信息也定義在註冊中內心面,能夠認爲是一個具有必定服務治理能力的註冊中心。
咱們但願可以用 Interface 來調用服務,就是爲了適應 RPC 框架的這個基於接口名字識別服務的概念模型。體如今 Kubernetes 裏面就是用 Interface 名字當作域名,把請求頭映射到 HTTP 頭,請求參數映射到 Query Parameter,方法名映射到 Path 上。這樣,基於 RPC 請求內容的服務治理就能夠定義到方法和參數級別了,即使是螞蟻金服站內複雜路由規則,好比 LDC 單元化流量調撥,也是能夠支持的。
咱們暫不考慮非 Kubernetes 平臺的狀況,以支持 DUBBO 做爲例子
若是不適用 k8 做爲註冊中心,須要引入 ZK。
由於 ISTIO 目前還不支持 ZK,所以須要針對 DUBBO 的註冊模型,與 SOFA 相似,經過 Platform Adapter 的方式加入對 DUBBO 的支持。
如前所述,咱們還須要修改 Pilot Discovery 的代碼,正確的爲 DUBBO 服務生成 Inbound 和 Outbound 的配置,好比 Listener 和 Cluster 的配置信息。咱們還須要爲把 ISTIO 的路由規則正確的轉成 XDS 的路由配置信息。
固然,咱們還須要擴展 MOSN/ENVOY 來支持 DUBBO 協議,這裏面有比較大的重複工做,並且還須要保證代碼的執行性能。對於 MOSN 來講,須要自行實現 codec 和 stream 模塊。
考慮到支持不一樣 RPC框架的大量重複工做和實現過程當中的性能保障,咱們但願能提供一個統一的解決方案,以高性能和插件化作爲重點來支持,並容許用戶在性能和功能之間作平衡。
這個方案是基於 Kubernetes Native 的方式來作的,使用 interface 來尋址服務,所以須要對客戶端作輕量化,以作到不侵入用戶的業務代碼。
輕量化客戶端是要解決客戶端 Loadbalance 引發的問題。
咱們會在 Kubernetes 的 DNS 以外額外作一層域名抽象,不受 Kubernetes 的規則的限制,好比,容許用戶直接使用 interface 做爲域名或者按照組織結構來規劃域名的層級關係。Kubernetes 的 namespace 每每被用來做爲多租戶的解決方案,並不適合用來做爲企業內不一樣部門的邏輯劃分。
有些微服務應用自己沒有版本,版本反應在應用中的服務接口上,每每每一個接口服務都有其獨立的版本,好比 SOFA 應用,其版本體如今服務接口的實例上(參考 SOFA 應用註冊中心結構)。
螞蟻主站內部在作藍綠部署和灰度的時候,每每一次藍綠髮佈會有多個應用參與,爲了保證引流的準確性,咱們會要求流量在整個調用的鏈路裏面所有落到藍或者綠的實例上,不容許出現交叉調用的狀況。因此對於單應用多服務的場景,咱們經過 POD label 把接口區分開來,從而作到流量在 POD 間調撥的粘性。
服務將會被按照接口維度建立,接口的版本和名字會反應在 POD 的 Label 上,這樣作會增長運維的工做量,可是能夠經過 PAAS 平臺提供的工具解決這個痛點。這裏面一個隱含的要求是,一個 POD 只會提供一個接口的服務,推進業務走向 Kubernetes Native。
對於按照 Kubernetes Native 方式建立的應用,應用只暴露一個接口,無需加上 interface 的標籤。
經過 CoreDNS 的 PDSQL 插件支持,爲 Cluster VIP 額外添加一個 interface name 的記錄。
咱們經過在 Destination Rule 中同時使用 Interface 和 Version 這兩個 Label 來選擇 Subset,每個 Subset 都會在 Pilot Discovery 中造成一個可被路由的集羣,這樣經過 Subset 就能夠完成 Traffic Splitting 的功能了。這樣一來,藍綠髮布,灰度等能力均可基於這個RPC 接口和版原本作了。
客戶端向 Interface 域名發起請求,經過本地的 resolv.conf 文件指引到 CoreDNS 服務器進行域名解析,獲得服務的 Cluster VIP。
客戶端以 Cluser VIP 發起請求,通過 IPTables 轉發到 SOFAMosn 的 12220 端口。
SOFAMosn 經過 socket 拿到 original destination 後,在此端口監聽的 SOFA 協議 Listener,經過 Virtual Host 的域名找到正確的 Virtual Host。
SOFAMosn 將請求按照 Pilot Discovery 下發的 Destination Rule 按照權重轉發到不通的後端集羣。
Virtual Host 在生成的時候,其域名列表中會包含 Cluster VIP。
在尋址方案中,咱們爲 RPC Service 建立了一個新的 CRD,並建立一個 RPC Service Controller 來 Watch RPC Service。
RPC Service Controller 監聽到 RPC Service 更新後,經過關聯的 Service,按策略找到其中一個 POD,向其發起服務列表查詢。請求到達 Register Agent,Agent 經過其協議插件從 APP 實例中獲取到服務列表信息後返回給 RPC Service Controller。RPC Service Conroller 使用 RPC Service 接口和 Cluster VIP 更新 CoreDNS 中的域名記錄。
七層代理的性能瓶頸每每是出如今協議數據包的解析上,因爲 SIDECAR 的特殊性,它自己每每得不到足夠的資源,不得不運行在資源首先的環境,以免影響應用自己的運行。在實際的部署中,咱們經常會把 SIDECARE 限定在單核心上運行,而且限制它能使用的最大內存,這些都讓 SIDECAR 的轉發性能面臨極大的壓力。考慮到 ISTIO的複雜路由規則在實際的業務場景中不少時候並不會所有都用到,咱們容許用戶在性能和功能之間找到一個平衡。
這個 Listener 的配置是參考 ISTIO 的 HTTP Connection Manager 作的,咱們增長了 Downstream Protocol 和 Upstream Protocol 的配置,容許控制層面選擇 SOFAMosn 之間的長鏈接的通行協議,好比使用 HTTP2,利用 HTTP2 的頭部壓縮能力提升協議的轉發性能。x-protocol 配置項對應服務使用的真是通訊協議,下發到 SOFAMosn 以後,SOFAMosn 經過分解 x-protocol 協議來進行適配真是請求協議,正確的加載協議插件進行協議處理。
首先操做員在 Kubernetes 中建立 DUBBO 應用的服務,指定其 Port Name 爲 x-dubbo-user,這很重要,也是 ISTIO 對 POD 的基本要求。SOFAMesh 監聽到服務建立以後,開始在 Pilot 中建立 DUBBO 應用集羣的x-protocol 協議的監聽器和集羣配置,請參考上文的 x-protocol 配置。
SOFAMosn SIDECAR 啓動後,使用期靜態配置的 Pilot 集羣地址鏈接到 Pilot 並開始以 SIDECAR 模式,經過 ADS 接口監聽配置的變化。
SOFAMesh 把 Outbound / Inbound 的配置數據經過 ADS 接口發送給監聽的 SOFAMosn 實例。
Inbound 和 Outbound 的 SOFAMosn 之間創建 x-protocol/http2 協議的長鏈接,協議能夠由下發的 x-protocol 配置指定,好比 HTTP2。目前 SOFAMosn 的 HTTP2 實現並仍是 PingPong 模型,不推薦用做 SOFAMosn 之間的通訊協議,下個 Milestone 改進後,應該是個更好的選擇。
DUBBO 請求數據進入 Outbound 的 Downstream 後,SOFAMosn 會生成一個自增的 stream id,而且從插件中拿到 request id,創建兩個 id 的映射表,同時利用插件把 stream id 寫到請求數據中。請求通過路由計算,路由到集羣,到達 Upstream 後 SOFAMosn 建立一個 x-protocol 的請求對象,把整個 DUBBO 請求數據做爲 Payload,附上自定義的頭,發送給 上游 Inbound 的 SOFAMosn,並把從插件中拿到的 class name 和 method name 等信息記錄到自定義的頭中。
請求數據到達 Inbound 的 Downstream 後,MOSN 會再生成一個自增的 stream id 並經過插件取出 request id,創建映射關係,並寫入 stream id。通過路由匹配以後,請求經過 Upstream 發送給後端的服務實例。
服務實例返回響應,Inbound 的 SOFAMosn 從響應中拿出 request id,經過 ID 映射找回實際的 request id,寫回響應對象,而後把請求用 x-protocol 打包,經過 Downstream 返回給 Outbound 的 SOFAMosn。
Outbound 的 SOFAMosn 收到響應後,拿出響應對象,並經過插件拿回 request id,最後經過 ID 映射關係找回實際的 request id,寫回響應對象後,經過 Downstream 返回給應用實例。
得到不一樣層次的能力,所付出的性能開銷和接入成本也會不一樣,能夠根據實際狀況作出取捨。Golang 的接口特性容許協議插件的開發人員根據須要實現接口,還能夠進行接口的組合。
開箱即用模式做爲不解包方案,提供LabelRouting,LabelAccessControl,LabelFaultInjection,TLS,RateLimits,Metrics的能力,以高性能和低成本爲亮點。
輕度解包能夠得到更多能力,如多路複用,Accesslog,流控,熔斷等(視具體協議而定),是性能和能力間的權衡選擇。
更進一步,徹底解除協議的頭,能夠得到將能力最大化,相對的性能開銷和成本也一樣最大化。
8月底發佈的 SOFMesh 版本默認將會用 SOFAMosn 代替 ENVOY 作數據平面,ISTIO 自帶的 BookInfo 的例子能夠提供給你們試用。咱們後續還會提供 SOFA/DUBBO 應用的例子。
目前 SOFAMosn 還不能在 Gateway 模式中使用,即不能用於 Ingress,並且部分高級路由功能,以及熔斷,限流等高級治理能力目前還不支持。另外這個版本的 Mixer 節點也去除了,咱們會在 9 月份的版本中持續完善 SOFAMosn 和 SOFAMesh,加入高級服務治理能力,同時咱們也會完成 Mixer 的 Report 部分能力,放到開源版本中。
本文首先介紹螞蟻金服開源的 SOFAMesh ,而後分享在 SOFAMesh 上落地 UC 的 HSF 應用和螞蟻的 SOFA 應用碰到的問題,以及咱們總結出來的解決方案和最佳實踐。最後分別就其中有表明性的 DNS 尋址方案和 X-PROTOCOL 協議分享一下作法。但願你們內部的 DUBBO 或者其餘功能內部的 RPC 應用在 Service Mesh 落地的時候,可以有個參考。
本文轉載自:www.servicemesher.com/blog/ant-fi…
微信羣:聯繫我入羣
Slack:servicemesher.slack.com 須要邀請才能加入
Twitter: twitter.com/servicemesh…
GitHub:github.com/servicemesh…
更多Service Mesh諮詢請掃碼關注微信公衆號ServiceMesher。