背景介紹
Service Mesh(服務(wù)網(wǎng)格)是一個(gè)基礎(chǔ)設(shè)施層,讓服務(wù)之間的通信更安全、快速和可靠,是云原生技術(shù)棧的關(guān)鍵組建之一。2018年是Service Mesh高歌猛進(jìn)的一年,Service Mesh數(shù)據(jù)面板百花齊放,遍地開(kāi)花,業(yè)界幾乎所有大廠都在推出自己的Service Mesh產(chǎn)品。2018年Service Mesh大事件如下:
a.2018年7月31日,Istio 1.0版本發(fā)布,標(biāo)志著Istio可用于生產(chǎn)環(huán)境
b.2018年9月19日,Conduit,這個(gè)史上唯一一個(gè)主打rust語(yǔ)言的Mesh,宣布合入到Linkerd,后續(xù)作為linkerd2.x版本繼續(xù)演進(jìn)。
c.2018年11月28日,Istio的官配sidecar,高性能邊緣代理Envoy,成為了繼k8s以及prometheus之后,第三個(gè)從CNCF畢業(yè)的項(xiàng)目。
d.2018年12月5日,AWS推出服務(wù)網(wǎng)狀網(wǎng)絡(luò)App Mesh公開(kāi)預(yù)覽版,供用戶(hù)輕松的監(jiān)視與控制AWS上,構(gòu)成應(yīng)用程序微服務(wù)之間的通信。
早在2017年騰訊云中間件團(tuán)隊(duì)就選定Istio為技術(shù)路線,開(kāi)始Service Mesh的相關(guān)研發(fā)工作,作為騰訊云TSF(微服務(wù)平臺(tái))的無(wú)侵入式服務(wù)框架的核心實(shí)現(xiàn),并在18年初在騰訊廣告平臺(tái)投入,打磨穩(wěn)定后陸續(xù)開(kāi)始對(duì)外輸出,目前在銀行、電商、零售、汽車(chē)等行業(yè)都有落地案例。
落地過(guò)程并非一帆風(fēng)順,本文將對(duì)騰訊云Service Mesh在生產(chǎn)實(shí)踐過(guò)程中遇到的典型問(wèn)題以及解決方案進(jìn)行總結(jié)分享,同時(shí)對(duì)騰訊云Service Mesh后續(xù)重點(diǎn)探索的技術(shù)方案進(jìn)行簡(jiǎn)要闡述。
騰訊云Service Mesh核心技術(shù)實(shí)現(xiàn)
騰訊云Service Mesh,遵循Service Mesh的理念進(jìn)行設(shè)計(jì),為應(yīng)用提供服務(wù)自動(dòng)注冊(cè)發(fā)現(xiàn)、灰度路由、限流、熔斷等服務(wù)治理能力,且應(yīng)用無(wú)需對(duì)源碼進(jìn)行侵入式變更即可與該服務(wù)框架進(jìn)行集成。
在實(shí)現(xiàn)上,基于業(yè)界達(dá)到商用標(biāo)準(zhǔn)的開(kāi)源軟件Istio、envoy進(jìn)行構(gòu)建。整體架構(gòu)上,從功能邏輯上分為數(shù)據(jù)面和控制面:
控制面主要提供配置及控制指令支撐sidecar的正常運(yùn)行,以及對(duì)服務(wù)運(yùn)行過(guò)程中的相關(guān)數(shù)據(jù)進(jìn)行采集。數(shù)據(jù)面主要是提供通信代理(sidecar)來(lái)進(jìn)行透明的服務(wù)調(diào)用,支撐正常的業(yè)務(wù)流程。
接下來(lái),讓我們對(duì)騰訊云Service Mesh各關(guān)鍵優(yōu)化點(diǎn)做詳細(xì)的描述。
解耦k8s,擁抱其他計(jì)算平臺(tái)
眾所周知,Istio強(qiáng)依賴(lài)于Kubernetes,大部分功能都依托于Kubernetes平臺(tái)進(jìn)行構(gòu)建。下面列舉幾個(gè)核心的功能:
(1)服務(wù)配置管理:Istio配置通過(guò)Kubernetes Crd(custom resources definition)以及configmap進(jìn)行存取
(2)服務(wù)發(fā)現(xiàn)及健康檢查:
Istio全功能的服務(wù)注冊(cè)發(fā)現(xiàn)能力是基于Kubernetes的PodServices能力以及Endpoints機(jī)制實(shí)現(xiàn)的,節(jié)點(diǎn)健康檢查能力基于ReadinessProbe機(jī)制實(shí)現(xiàn)(當(dāng)前社區(qū)上面也有基于Consul的服務(wù)發(fā)現(xiàn)機(jī)制實(shí)現(xiàn),但是缺失健康檢查機(jī)制)。
但實(shí)際落地過(guò)程中,TSF的用戶(hù)并非全部是Kubernetes用戶(hù),例如公司內(nèi)部的一個(gè)業(yè)務(wù)因歷史遺留問(wèn)題,不能完全容器化部署,同時(shí)存在VM和容器環(huán)境,架構(gòu)如下:
從業(yè)務(wù)架構(gòu)圖可以看出,業(yè)務(wù)要求TSF可以支持其部署在自研PAAS以及Kubernetes的容器、虛擬機(jī)以及裸金屬的服務(wù)都可以通過(guò)Service Mesh進(jìn)行相互訪問(wèn)。
因此,為了實(shí)現(xiàn)多平臺(tái)的部署,必須與Kubernetes進(jìn)行解耦。經(jīng)過(guò)分析發(fā)現(xiàn),脫離Kubernetes后,Istio存在以下三個(gè)問(wèn)題:
(1)Pilot/Mixer的遠(yuǎn)程動(dòng)態(tài)配置能力不可用(只能用本地配置)
(2)Pilot無(wú)法獲取服務(wù)節(jié)點(diǎn)健康信息
(3)無(wú)法通過(guò)Istioctl(Istio小工具)進(jìn)行服務(wù)注冊(cè)/反注冊(cè)以及寫(xiě)配置能力針對(duì)這3個(gè)問(wèn)題,TSF團(tuán)隊(duì)對(duì)Istio的能力進(jìn)行了擴(kuò)展和增強(qiáng),增強(qiáng)后的架構(gòu)如下:
下表更詳細(xì)的描述了存在的問(wèn)題、解決方案以及所得到的目的,同時(shí)TSF團(tuán)隊(duì)實(shí)現(xiàn)了Istio對(duì)Consul的完整適配。
經(jīng)過(guò)改造后,Service Mesh成功與Kubernetes平臺(tái)解耦,組網(wǎng)變得更加簡(jiǎn)潔,通過(guò)REST API可以對(duì)數(shù)據(jù)面進(jìn)行全方位的控制,可從容適配任何的底層部署環(huán)境,對(duì)于私有云客戶(hù)可以提供更好的體驗(yàn)。
服務(wù)尋址模式的演進(jìn)
解決了跨平臺(tái)部署問(wèn)題后,第二個(gè)面臨的問(wèn)題就是服務(wù)的尋址互通問(wèn)題。
Istio下的應(yīng)用使用FQDN(fully qualified domain name)進(jìn)行相互調(diào)用,基于FQDN的尋址依賴(lài)DNS服務(wù)器,Istio官方對(duì)DNS服務(wù)器的說(shuō)明如下:
Istio的官方demo(https://Istio.io/docs/examples/bookinfo/)中,Reviews與Ratings之間的完整的服務(wù)調(diào)用會(huì)經(jīng)過(guò)以下過(guò)程:
從圖上可以看出,Reviews和Ratings的互通,kube-dns主要實(shí)現(xiàn)2個(gè)功能:
(1)應(yīng)用程序的DNS請(qǐng)求被kube-dns接管
(2)kube-dns可以將服務(wù)名解析成可被iptables接管的虛擬IP(clusterIP)
在私有云的實(shí)際交付中,客戶(hù)的生產(chǎn)環(huán)境不一定包含Kubernetes或者kube-dns,我們需要另外尋找一種機(jī)制來(lái)實(shí)現(xiàn)上面的兩個(gè)功能。
在DNS選型中,有集中式和分布式兩種方案,分別如下:
集中式DNS:代表有ConsulDNS,CoreDNS等,通過(guò)內(nèi)置機(jī)制或者插件的方式,實(shí)現(xiàn)與服務(wù)注冊(cè)中心進(jìn)行數(shù)據(jù)同步。其架構(gòu)組網(wǎng)如下,
kube-dns也屬于集中式DNS的一種,集中式DNS存在以下問(wèn)題:組網(wǎng)中額外增加一套DNS集群,并且一旦DNS Server集群不可服務(wù),所有數(shù)據(jù)面節(jié)點(diǎn)在DNS緩存失效后都無(wú)法工作,因此需要為DNS Server考慮高可用甚至容災(zāi)等一系列后續(xù)需求,會(huì)導(dǎo)致后期運(yùn)維成本增加。
分布式DNS:就是將服務(wù)DNS的能力下沉到數(shù)據(jù)平面中,其架構(gòu)組網(wǎng)如下:
分布式DNS運(yùn)行在數(shù)據(jù)面節(jié)點(diǎn)上,DNS無(wú)單點(diǎn)故障,無(wú)需考慮集群容災(zāi)等要素,只需要有機(jī)制可以在其down掉后重新拉起即可。但是,由于其與業(yè)務(wù)進(jìn)程運(yùn)行在同一節(jié)點(diǎn),因此其資源占用率必須控制得足夠低,才不會(huì)對(duì)業(yè)務(wù)進(jìn)程產(chǎn)生影響。
綜合考慮,最終選用了分布式DNS的方案,最開(kāi)始團(tuán)隊(duì)采用獨(dú)立進(jìn)程作為DNS Server的方案,如下圖
該方案新增監(jiān)聽(tīng)在127.0.0.1:53上的mesh-dns進(jìn)程,該進(jìn)程實(shí)時(shí)從Pilot同步服務(wù)列表。Mesh-dns在節(jié)點(diǎn)啟動(dòng)時(shí)將127.0.0.1寫(xiě)入到/etc/resolv.conf首行中,同時(shí)接管/etc/resolv.conf的其他nameserver。這樣,當(dāng)app發(fā)起DNS查詢(xún)時(shí),DNS請(qǐng)求首先會(huì)到達(dá)mesh-dns,遇到匹配服務(wù)名的查詢(xún)則直接返回,而當(dāng)遇到不是針對(duì)服務(wù)名的DNS查詢(xún)時(shí),就把DNS請(qǐng)求轉(zhuǎn)發(fā)給其他nameserver進(jìn)行處理。
該方案看起來(lái)簡(jiǎn)單可行,但是經(jīng)測(cè)試驗(yàn)證后發(fā)現(xiàn)存在以下問(wèn)題:
(1)resolv.conf修改時(shí)間差問(wèn)題:該方案需要對(duì)/etc/resolv.conf進(jìn)行修改,在linux環(huán)境,域名解析機(jī)制是通過(guò)glibc提供的。而glibc 2.26之前的版本有個(gè)BUG,導(dǎo)致假如在進(jìn)程啟動(dòng)后,對(duì)resolv.conf就行修改,則該修改無(wú)法被該進(jìn)程感知,直到進(jìn)程重啟。而由于在容器部署的場(chǎng)景中,mesh-dns和應(yīng)用分別部署在同一個(gè)POD的不同容器中,容器的啟動(dòng)是相互獨(dú)立的,所以無(wú)法保證對(duì)resolv.conf的修改一定在應(yīng)用啟動(dòng)前。即使改成通過(guò)InitContainer進(jìn)行修改,當(dāng)容器異常重啟后,resolv.conf也同樣會(huì)被還原導(dǎo)致服務(wù)不可用。
(2)端口監(jiān)聽(tīng)沖突問(wèn)題:由于mesh-dns必須監(jiān)聽(tīng)53端口,假如客戶(hù)節(jié)點(diǎn)環(huán)境已經(jīng)安裝了dnsmasq等同樣需要占用53的進(jìn)程,則可能會(huì)出現(xiàn)端口沖突導(dǎo)致啟動(dòng)失敗。
(3)nameserver選擇策略問(wèn)題:假如存在多個(gè)nameserver,部分操作系統(tǒng),默認(rèn)會(huì)使用rotate(隨機(jī)選取一個(gè)作為首選查詢(xún)的nameserver)作為nameserver的選擇策略。此時(shí)會(huì)出現(xiàn)一定概率下會(huì)選不到127.0.0.1的nameserver,從而導(dǎo)致服務(wù)域名解釋失敗。
針對(duì)上述問(wèn)題,對(duì)方案進(jìn)行了進(jìn)一步的優(yōu)化,優(yōu)化后的方案如下圖:
mesh-dns不再監(jiān)聽(tīng)53端口,而是監(jiān)聽(tīng)在5353端口(可配置),啟動(dòng)時(shí)無(wú)需修改resolv.conf。通過(guò)增加iptables規(guī)則,將所有發(fā)往nameserver的流量導(dǎo)入到mesh-dns,從而解決了上文中的“端口監(jiān)聽(tīng)沖突”以及“nameserver選擇策略”的問(wèn)題。
mesh-dns通過(guò)inotify監(jiān)聽(tīng)/etc/resolv.conf,可以隨時(shí)獲取環(huán)境中dns配置的更改,從而解決了上文中的“resolv.conf修改時(shí)間差”的問(wèn)題。
與非Service Mesh服務(wù)的互通
現(xiàn)實(shí)總是復(fù)雜的,前面解決mesh服務(wù)之間相互訪問(wèn)的問(wèn)題,如何解決用戶(hù)Service Mesh應(yīng)用和其他非Mesh應(yīng)用的相互訪問(wèn)呢?用戶(hù)內(nèi)部有不同技術(shù)棧,一部分服務(wù)基于service mesh進(jìn)行實(shí)現(xiàn)服務(wù),另外一部分服務(wù)基于spring cloud框架進(jìn)行實(shí)現(xiàn)。同時(shí),客戶(hù)的微服務(wù)組網(wǎng)中,存在大量第三方服務(wù)如支付網(wǎng)關(guān)、分布式存儲(chǔ)、設(shè)備等,微服務(wù)需要與這些第三方服務(wù)也存在交互。用戶(hù)期望支持的架構(gòu)如下圖所示:
這個(gè)架構(gòu)中,最大的挑戰(zhàn)在于涉及了兩個(gè)不同的微服務(wù)框架之間的互通。但是,這兩個(gè)微服務(wù)框架從架構(gòu)模式、概念模型、功能邏輯上,都存在較大的差異。唯一相通的點(diǎn),就是他們都是微服務(wù)框架,可以將應(yīng)用的能力通過(guò)服務(wù)的形式提供出來(lái),給消費(fèi)者調(diào)用,消費(fèi)者實(shí)際上并不感知服務(wù)的具體實(shí)現(xiàn)。
基于這個(gè)共通點(diǎn),為了使得不同框架開(kāi)發(fā)的服務(wù)能夠正常工作,TSF團(tuán)隊(duì)做了大量的開(kāi)發(fā)工作,將兩個(gè)微服務(wù)框架,從部署模式、服務(wù)及功能模型上進(jìn)行了拉通,主要包括如下幾點(diǎn):
(1)服務(wù)模型的互通:基于統(tǒng)一的服務(wù)元數(shù)據(jù)模型,針對(duì)pilot registry及spring cloud registry的服務(wù)注冊(cè)發(fā)現(xiàn)機(jī)制進(jìn)行拉通
(2)服務(wù)API的互通:基于標(biāo)準(zhǔn)API模型(OpenAPI v3),針對(duì)兩邊框架的API級(jí)別服務(wù)治理能力進(jìn)行拉通
(3)服務(wù)路由能力互通:基于標(biāo)準(zhǔn)權(quán)重算法以及標(biāo)簽?zāi)P停槍?duì)pilot virtual-service以及spring cloud ribbon能力進(jìn)行拉通。
(4)服務(wù)限流能力互通:基于標(biāo)準(zhǔn)令牌桶架構(gòu)和模型,以及條件匹配規(guī)則,對(duì)mixer及spring cloud ratelimiter能力進(jìn)行拉通。
代理單節(jié)點(diǎn)多服務(wù)
用戶(hù)的需求是多種多樣的,在交付過(guò)程中存在如下多服務(wù)場(chǎng)景:
(1)客戶(hù)機(jī)器資源不足,且沒(méi)有做容器化,因此需要把多個(gè)服務(wù)部署到一個(gè)節(jié)點(diǎn)上。
(2)客戶(hù)的傳統(tǒng)應(yīng)用使用OSGI(一種Java模塊化技術(shù))實(shí)現(xiàn),一個(gè)進(jìn)程中包含多個(gè)服務(wù),監(jiān)聽(tīng)在同一個(gè)端口。
為了支持多服務(wù)場(chǎng)景,簡(jiǎn)化用戶(hù)的使用流程,TSF提供了服務(wù)描述文件,可支持多服務(wù)場(chǎng)景,服務(wù)配置文件與Kubernetes標(biāo)準(zhǔn)格式一致:
pilot-agent會(huì)根據(jù)服務(wù)配置,按照--的格式將配置中services注冊(cè)成多個(gè)獨(dú)立的服務(wù)實(shí)例。
在OutBound服務(wù)路由時(shí),可以通過(guò)LDS->RDS->CDS->EDS的方式進(jìn)行路由,和獨(dú)立部署的服務(wù)沒(méi)有區(qū)別:
然而,在InBound服務(wù)路由過(guò)程中,通過(guò)開(kāi)源Istio生成的listener會(huì)遇到一些坑。
對(duì)于多服務(wù)監(jiān)聽(tīng)同一端口的場(chǎng)景,開(kāi)源Istio在生成inbound的時(shí)候,會(huì)將同IP+Port的其中一個(gè)服務(wù)給reject掉
因此,生成的LDS中,只有其中一個(gè)服務(wù)的相關(guān)路由信息:
這樣一來(lái),普通消息投遞,不會(huì)有什么問(wèn)題(目標(biāo)端點(diǎn)信息是一致的),但是假如需要與mixer結(jié)合,做api鑒權(quán)或者限流等操作,則會(huì)出現(xiàn)兩個(gè)服務(wù)的mixer_attribute互相混淆的情況,導(dǎo)致功能不可用。
為了解決這個(gè)問(wèn)題,團(tuán)隊(duì)分析了envoy的filter_chain_match能力(https://www.envoyproxy.io/docs/envoy/v1.8.0/api-v2/api/v2/listener/listener.proto.html?highlight=filter_chain_match#envoy-api-msg-listener-filterchainmatch),對(duì)pilot進(jìn)行改造,擴(kuò)展了listener能力,通過(guò)server_name來(lái)分流數(shù)據(jù)包到不同的filter中。
最終生成的LDS如下:
經(jīng)過(guò)這樣的改造,同一端口上,不同的服務(wù)的filter配置不再?zèng)_突,兩個(gè)服務(wù)的mixer_attribute也能相互隔離,順利支持同端口多服務(wù)的場(chǎng)景。
二進(jìn)制協(xié)議的支持
在當(dāng)前業(yè)界的開(kāi)源Service Mesh產(chǎn)品中,主打的協(xié)議都是標(biāo)準(zhǔn)協(xié)議(HTTP1/2,GRPC),標(biāo)準(zhǔn)協(xié)議都有一個(gè)特點(diǎn),那就是協(xié)議頭中包含了目的端相關(guān)的所有信息,Service Mesh會(huì)根據(jù)這些信息進(jìn)行路由。如下表所示:
對(duì)于其他二進(jìn)制協(xié)議,則分為2大類(lèi):
第一種是協(xié)議中帶有目標(biāo)端信息的二進(jìn)制協(xié)議,如thrift,dubbo等;
第二種是協(xié)議中不帶有目標(biāo)端信息的二進(jìn)制協(xié)議,這種就比較多了,一般常見(jiàn)于私有云中的各種私有通信協(xié)議。
開(kāi)源Istio中,對(duì)于二進(jìn)制協(xié)議的支持,僅僅局限于四層的端口轉(zhuǎn)發(fā),一般用于集成外部服務(wù)(mysql,mongodb等),典型場(chǎng)景是對(duì)不同入口的流量做轉(zhuǎn)發(fā),如下圖所示:
單純的四層轉(zhuǎn)發(fā),無(wú)法滿(mǎn)足復(fù)雜的微服務(wù)路由的需求。當(dāng)前TSF交付的客戶(hù)中,多個(gè)客戶(hù)都提出了需要支持私有協(xié)議路由的需求,因此,針對(duì)這個(gè)問(wèn)題,TSF團(tuán)隊(duì)提供了兩種解決方案。
(1)用戶(hù)將私有協(xié)議轉(zhuǎn)換成GRPC協(xié)議,接入到Service Mesh
由于GRPC的Data Frame本身傳輸?shù)木涂梢允荰CP協(xié)議,因此用戶(hù)可以直接把自己的二進(jìn)制協(xié)議通過(guò)GRPC的bytes類(lèi)型編碼,然后通過(guò)Data Frame傳輸過(guò)來(lái).
該方案適用于本身有一定的技術(shù)積累,愿意去做GRPC改造的用戶(hù)
(2)根據(jù)用戶(hù)定義的協(xié)議頭描述文件,進(jìn)行私有協(xié)議七層路由
中間件團(tuán)隊(duì)對(duì)envoy的filter進(jìn)行了擴(kuò)展,用戶(hù)提供一個(gè)protobuf格式的描述文件,指定協(xié)議頭的字段順序,proxy根據(jù)描述文件的定義,進(jìn)行消息頭的接收及解析,然后根據(jù)解析后的消息頭內(nèi)容,進(jìn)行七層路由和轉(zhuǎn)發(fā)。
該方案適用于自身帶有目標(biāo)端信息的二進(jìn)制協(xié)議,可以讓私有協(xié)議的用戶(hù)無(wú)需任何的改造,即可接入Service Mesh。
總結(jié)
騰訊云Service Mesh當(dāng)前通過(guò)TSF平臺(tái)在持續(xù)交付中,上文主要針對(duì)落地過(guò)程中遇到的典型功能性需求及技術(shù)方案演進(jìn)進(jìn)行了總結(jié)介紹,除此之外,中間件團(tuán)隊(duì)在Service Mesh性能方面也有很多優(yōu)化和探索,主要包括減少envoy和mixer之間的網(wǎng)絡(luò)交互、優(yōu)化數(shù)據(jù)包在envoy節(jié)點(diǎn)內(nèi)部從內(nèi)核態(tài)到用戶(hù)態(tài)的拷貝次數(shù)、envoy到envoy之間數(shù)據(jù)的轉(zhuǎn)發(fā)性能等,后續(xù)將針對(duì)性能優(yōu)化進(jìn)行專(zhuān)項(xiàng)分享。