本文作者:邵俊雄,螞蟻金服中介軟體團隊高階技術專家,目前主要負責 SOFAMesh 的開發工作。
本文是基於作者在 Service Mesh Meetup #3 深圳的主題分享《SOFAMesh的通用協議擴充套件》部分內容所整理,完整內容以及 PPT 見文末。
SOFAMesh :http://github.com/alipay/sofa-mesh
本次分享主要介紹螞蟻金服在 SOFAMesh 上開發對 SOFARPC 與 HSF 這兩個RPC框架的支援過程中總結出來的通用協議擴充套件方案
1. SOFAMesh 介紹
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。
2、Service Mesh 落地中的問題
第二部分是這次分享的重點,主要介紹螞蟻金服在整合 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 原生的方向發展,在生產環境中落地打磨,並和社群一起解決碰到的問題。
UC 的 UAE 2.0 平臺
充分利用 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 模組。
3、SOFAMesh 的統一解決方案
考慮到支援不同 RPC框架的大量重複工作和實現過程中的效能保障,我們希望能提供一個統一的解決方案,以高效能和外掛化做為重點來支援,並允許使用者在效能和功能之間做平衡。
這個方案是基於 Kubernetes Native 的方式來做的,使用 interface 來定址服務,因此需要對客戶端做輕量化,以做到不侵入使用者的業務程式碼。
輕量化客戶端是要解決客戶端 Loadbalance 引起的問題。
4、DNS 服務定址方案
我們會在 Kubernetes 的 DNS 之外額外做一層域名抽象,不受 Kubernetes 的規則的限制,比如,允許使用者直接使用 interface 作為域名或者按照組織結構來規劃域名的層級關係。Kubernetes 的 namespace 往往被用來作為多租戶的解決方案,並不適合用來作為企業內不同部門的邏輯劃分。
有些微服務應用本身沒有版本,版本反應在應用中的服務介面上,往往每個介面服務都有其獨立的版本,比如 SOFA 應用,其版本體現在服務介面的例項上(參考 SOFA 應用註冊中心結構)。
螞蟻主站內部在做藍綠部署和灰度的時候,往往一次藍綠髮佈會有多個應用參與,為了保證引流的準確性,我們會要求流量在整個呼叫的鏈路裡面全部落到藍或者綠的例項上,不允許出現交叉呼叫的情況。所以對於單應用多服務的場景,我們通過POD label 把介面區分開來,從而做到流量在 POD 間調撥的粘性。
服務將會被按照介面維度建立,介面的版本和名字會反應在 POD 的 Label 上,這樣做會增加運維的工作量,但是可以通過 PasS 平臺提供的工具解決這個痛點。這裡面一個隱含的要求是,一個 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 中的域名記錄。
5、X-PROTOCOL 通用協議
七層代理的效能瓶頸往往是出現在協議資料包的解析上,由於 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 落地的時候,能夠有個參考。
相關閱讀:
開源 | Service Mesh 資料平面 SOFAMosn 深層揭祕
Cilium 1.2釋出—DNS安全性策略、EKS支援、ClusterMesh、kube-router整合等
使用Cilium增強Istio|通過Socket感知BPF程式
視訊回放:http://www.itdks.com/eventlist/detail/2549
PPT 下載地址:https://github.com/servicemesher/meetup-slides
連結活動資訊歸檔:http://www.servicemesher.com/activity
Meetup 現場圖片,SOFA 團隊期待下次與大家的見面
長按關注,獲取分散式架構乾貨
歡迎大家共同打造 SOFAStack https://github.com/alipay