騰訊雲Service Mesh生產實踐及架構演進

背景介紹

Service Mesh(服務網格)是一個基礎設施層,讓服務之間的通信更安全、快速和可靠,是雲原生技術棧的關鍵組建之一。2018年是Service Mesh 高歌猛進的一年,Service Mesh數據面板百花齊放,遍地開花,業界幾乎所有大廠都在推出自己的Service Mesh 產品。2018年Service Mesh大事件如下:

  • 2018年7月31日,Istio 1.0版本發佈,標誌着Istio可用於生產環境。
  • 2018年9月19日,Conduit,這個史上唯一一個主打rust語言的Mesh,宣佈合入到Linkerd,後續作爲linkerd2.x版本繼續演進。
  • 2018年11月28日,Istio的官配sidecar,高性能邊緣代理Envoy,成爲了繼k8s以及prometheus之後,第三個從CNCF畢業的項目。
  • 2018年12月5日,AWS推出服務網狀網絡App Mesh公開預覽版,供用戶輕鬆的監視與控制AWS上,構成應用程序微服務之間的通信。

早在2017年騰訊雲中間件團隊就選定Istio爲技術路線,開始Service Mesh的相關研發工作,作爲騰訊雲TSF(微服務平臺)的無侵入式服務框架的核心實現,並在18年初在騰訊廣告平臺投入,打磨穩定後陸續開始對外輸出,目前在銀行、電商、零售、汽車等行業都有落地案例。

落地過程並非一帆風順,本文將對騰訊雲Service Mesh 在生產實踐過程中遇到的典型問題以及解決方案進行總結分享,同時對騰訊雲Service Mesh後續重點探索的技術方案進行簡要闡述。

騰訊雲 Service Mesh 核心技術實現

騰訊雲Service Mesh,遵循Service Mesh的理念進行設計,爲應用提供服務自動註冊發現、灰度路由、限流、熔斷等服務治理能力,且應用無需對源碼進行侵入式變更即可與該服務框架進行集成。

在實現上,基於業界達到商用標準的開源軟件Istio、envoy進行構建。整體架構上,從功能邏輯上分爲數據面和控制面:

控制面主要提供配置及控制指令支撐sidecar的正常運行,以及對服務運行過程中的相關數據進行採集。

數據面主要是提供通信代理(sidecar)來進行透明的服務調用,支撐正常的業務流程。

接下來,讓我們對騰訊雲Service Mesh各關鍵優化點做詳細的描述。

解耦k8s,擁抱其他計算平臺

衆所周知,Istio強依賴於Kubernetes ,大部分功能都依託於Kubernetes 平臺進行構建。下面列舉幾個核心的功能:

  1. 服務配置管理:Istio配置通過Kubernetes Crd (custom resources definition) 以及configmap進行存取。

  1. 服務發現及健康檢查:Istio全功能的服務註冊發現能力是基於Kubernetes 的PodServices能力以及Endpoints機制實現的,節點健康檢查能力基於ReadinessProbe機制實現 (當前社區上面也有基於Consul的服務發現機制實現,但是缺失健康檢查機制)。

但實際落地過程中,TSF的用戶並非全部是Kubernetes用戶,例如公司內部的一個業務因歷史遺留問題,不能完全容器化部署,同時存在VM和容器環境,架構如下:

從業務架構圖可以看出,業務要求TSF可以支持其部署在自研PAAS以及Kubernetes 的容器、虛擬機以及裸金屬的服務都可以通過Service Mesh進行相互訪問。

因此,爲了實現多平臺的部署,必須與Kubernetes 進行解耦。經過分析發現,脫離Kubernetes 後,Istio存在以下三個問題:

  1. Pilot/Mixer的遠程動態配置能力不可用(只能用本地配置);
  2. Pilot無法獲取服務節點健康信息;
  3. 無法通過Istioctl(Istio小工具)進行服務註冊/反註冊以及寫配置能力。

針對這3個問題,TSF團隊對Istio的能力進行了擴展和增強,增強後的架構如下:

下表更詳細的描述了存在的問題、解決方案以及所得到的目的,同時TSF 團隊實現了Istio 對Consul的完整適配。

經過改造後,Service Mesh成功與Kubernetes 平臺解耦,組網變得更加簡潔,通過REST API可以對數據面進行全方位的控制,可從容適配任何的底層部署環境,對於私有云客戶可以提供更好的體驗。

服務尋址模式的演進

解決了跨平臺部署問題後,第二個面臨的問題就是服務的尋址互通問題。

Istio下的應用使用FQDN(fully qualified domain name)進行相互調用,基於FQDN的尋址依賴DNS服務器,Istio官方對DNS服務器的說明如下:

Istio的官方demo中,Reviews與Ratings之間的完整的服務調用會經過以下過程:

從圖上可以看出,Reviews和Ratings的互通,kube-dns主要實現2個功能:

  1. 應用程序的DNS請求被kube-dns接管;
  2. kube-dns可以將服務名解析成可被iptables接管的虛擬IP(clusterIP)。

在私有云的實際交付中,客戶的生產環境不一定包含Kubernetes 或者kube-dns,我們需要另外尋找一種機制來實現上面的兩個功能。

在DNS選型中,有集中式和分佈式兩種方案,分別如下:

  • 集中式DNS:代表有ConsulDNS, CoreDNS等,通過內置機制或者插件的方式,實現與服務註冊中心進行數據同步。其架構組網如下:

kube-dns也屬於集中式DNS的一種,集中式DNS存在以下問題:組網中額外增加一套DNS集羣,並且一旦DNS Server集羣不可服務,所有數據面節點在DNS緩存失效後都無法工作,因此需要爲DNS Server考慮高可用甚至容災等一系列後續需求,會導致後期運維成本增加。

  • 分佈式DNS:就是將服務DNS的能力下沉到數據平面中,其架構組網如下:

分佈式DNS運行在數據面節點上,DNS無單點故障,無需考慮集羣容災等要素,只需要有機制可以在其down掉後重新拉起即可。但是,由於其與業務進程運行在同一節點,因此其資源佔用率必須控制得足夠低,纔不會對業務進程產生影響。

綜合考慮,最終選用了分佈式DNS的方案,最開始團隊採用獨立進程作爲DNS Server的方案,如下圖:


該方案新增監聽在127.0.0.1:53 上的mesh-dns進程,該進程實時從Pilot同步服務列表。Mesh-dns在節點啓動時將127.0.0.1寫入到/etc/resolv.conf首行中,同時接管/etc/resolv.conf的其他nameserver。這樣,當app發起DNS查詢時,DNS請求首先會到達mesh-dns,遇到匹配服務名的查詢則直接返回,而當遇到不是針對服務名的DNS查詢時,就把DNS請求轉發給其他nameserver進行處理。

該方案看起來簡單可行,但是經測試驗證後發現存在以下問題:

  1. resolv.conf修改時間差問題:該方案需要對/etc/resolv.conf進行修改,在linux環境,域名解析機制是通過glibc提供的。而glibc 2.26之前的版本有個BUG,導致假如在進程啓動後,對resolv.conf就行修改,則該修改無法被該進程感知,直到進程重啓。而由於在容器部署的場景中,mesh-dns和應用分別部署在同一個POD的不同容器中,容器的啓動是相互獨立的,所以無法保證對resolv.conf的修改一定在應用啓動前。即使改成通過InitContainer進行修改,當容器異常重啓後,resolv.conf也同樣會被還原導致服務不可用。
  2. 端口監聽衝突問題:由於mesh-dns必須監聽53端口,假如客戶節點環境已經安裝了dnsmasq等同樣需要佔用53的進程,則可能會出現端口衝突導致啓動失敗。
  3. nameserver選擇策略問題:假如存在多個nameserver,部分操作系統,默認會使用rotate(隨機選取一個作爲首選查詢的nameserver)作爲nameserver的選擇策略。此時會出現一定概率下會選不到127.0.0.1的nameserver,從而導致服務域名解釋失敗。

針對上述問題,對方案進行了進一步的優化,優化後的方案如下圖:

mesh-dns不再監聽53端口,而是監聽在5353端口(可配置),啓動時無需修改resolv.conf。通過增加iptables規則,將所有發往nameserver的流量導入到mesh-dns,從而解決了上文中的“端口監聽衝突”以及“nameserver選擇策略”的問題。

mesh-dns通過inotify監聽/etc/resolv.conf,可以隨時獲取環境中dns配置的更改,從而解決了上文中的“resolv.conf修改時間差”的問題。

與非Service Mesh服務的互通

現實總是複雜的,前面解決mesh服務之間相互訪問的問題,如何解決用戶Service Mesh應用和其他非Mesh 應用的相互訪問呢? 用戶內部有不同技術棧,一部分服務基於service mesh進行實現服務,另外一部分服務基於spring cloud框架進行實現。同時,客戶的微服務組網中,存在大量第三方服務如支付網關、分佈式存儲、設備等,微服務需要與這些第三方服務也存在交互。用戶期望支持的架構如下圖所示:

這個架構中,最大的挑戰在於涉及了兩個不同的微服務框架之間的互通。但是,這兩個微服務框架從架構模式、概念模型、功能邏輯上,都存在較大的差異。唯一相通的點,就是他們都是微服務框架,可以將應用的能力通過服務的形式提供出來,給消費者調用,消費者實際上並不感知服務的具體實現。

基於這個共通點,爲了使得不同框架開發的服務能夠正常工作,TSF團隊做了大量的開發工作,將兩個微服務框架,從部署模式、服務及功能模型上進行了拉通,主要包括如下幾點:

  1. 服務模型的互通:基於統一的服務元數據模型,針對pilot registry及spring cloud registry的服務註冊發現機制進行拉通。
  2. 服務API的互通:基於標準API模型(OpenAPI v3),針對兩邊框架的API級別服務治理能力進行拉通。
  3. 服務路由能力互通:基於標準權重算法以及標籤模型,針對pilot virtual-service以及spring cloud ribbon能力進行拉通。
  4. 服務限流能力互通:基於標準令牌桶架構和模型,以及條件匹配規則,對mixer及spring cloud ratelimiter能力進行拉通。

代理單節點多服務

用戶的需求是多種多樣的,在交付過程中存在如下多服務場景:

  1. 客戶機器資源不足,且沒有做容器化,因此需要把多個服務部署到一個節點上。
  2. 客戶的傳統應用使用OSGI(一種Java模塊化技術)實現,一個進程中包含多個服務,監聽在同一個端口。

爲了支持多服務場景,簡化用戶的使用流程,TSF提供了服務描述文件,可支持多服務場景,服務配置文件與Kubernetes 標準格式一致:

pilot-agent會根據服務配置,按照--的格式將配置中services註冊成多個獨立的服務實例。

在OutBound服務路由時,可以通過LDS->RDS->CDS->EDS的方式進行路由,和獨立部署的服務沒有區別:

然而,在InBound服務路由過程中,通過開源Istio生成的listener會遇到一些坑。

對於多服務監聽同一端口的場景,開源Istio在生成inbound的時候,會將同IP+Port的其中一個服務給reject掉。

因此,生成的LDS中,只有其中一個服務的相關路由信息:

這樣一來,普通消息投遞,不會有什麼問題(目標端點信息是一致的),但是假如需要與mixer結合,做api鑑權或者限流等操作,則會出現兩個服務的mixer_attribute互相混淆的情況,導致功能不可用。

爲了解決這個問題,團隊分析了envoy的filter_chain_match能力,對pilot進行改造,擴展了listener能力,通過server_name來分流數據包到不同的filter中。

最終生成的LDS如下:

經過這樣的改造,同一端口上,不同的服務的filter配置不再衝突,兩個服務的mixer_attribute也能相互隔離,順利支持同端口多服務的場景。

二進制協議的支持

在當前業界的開源Service Mesh產品中,主打的協議都是標準協議(HTTP1/2, GRPC),標準協議都有一個特點,那就是協議頭中包含了目的端相關的所有信息,Service Mesh會根據這些信息進行路由。如下表所示:

對於其他二進制協議,則分爲2大類:

  • 第一種是協議中帶有目標端信息的二進制協議,如thrift,dubbo等;

  • 第二種是協議中不帶有目標端信息的二進制協議,這種就比較多了,一般常見於私有云中的各種私有通信協議。

開源Istio中,對於二進制協議的支持,僅僅侷限於四層的端口轉發,一般用於集成外部服務(mysql, mongodb等),典型場景是對不同入口的流量做轉發,如下圖所示:

單純的四層轉發,無法滿足複雜的微服務路由的需求。當前TSF交付的客戶中,多個客戶都提出了需要支持私有協議路由的需求,因此,針對這個問題,TSF團隊提供了兩種解決方案。

  1. 用戶將私有協議轉換成GRPC協議,接入到Service Mesh:

由於GRPC的Data Frame本身傳輸的就可以是TCP協議,因此用戶可以直接把自己的二進制協議通過GRPC的bytes類型編碼,然後通過Data Frame傳輸過來.
該方案適用於本身有一定的技術積累,願意去做GRPC改造的用戶。

  1. 根據用戶定義的協議頭描述文件,進行私有協議七層路由中間件團隊對envoy的filter進行了擴展,用戶提供一個protobuf格式的描述文件,指定協議頭的字段順序,proxy根據描述文件的定義,進行消息頭的接收及解析,然後根據解析後的消息頭內容,進行七層路由和轉發。

該方案適用於自身帶有目標端信息的二進制協議,可以讓私有協議的用戶無需任何的改造,即可接入Service Mesh。

總結

騰訊雲Service Mesh當前通過TSF平臺在持續交付中,上文主要針對落地過程中遇到的典型功能性需求及技術方案演進進行了總結介紹,除此之外,中間件團隊在Service Mesh性能方面也有很多優化和探索,主要包括減少envoy和mixer之間的網絡交互、優化數據包在envoy節點內部從內核態到用戶態的拷貝次數、envoy 到envoy之間數據的轉發性能等,後續將針對性能優化進行專項分享。


作者簡介:

單家駿,來自騰訊公司。騰訊雲高級工程師,負責騰訊雲中間件paas以及servicemesh的研發與架構,關注雲原生與中間件技術。熱愛開源、崇尚技術,希望能夠使用技術使軟件的應用變得簡單、高效和美好。

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