Service Mesh Webinar 是由 ServiceMesher 社群和 CNCF 聯合發起的線上直播活動,活動将不定期舉行,為大家帶來 Service Mesh 領域的知識和實踐分享。
本文根據5月28日晚 Service Mesh Webinar#1 多點生活平台架構組研發工程師陳鵬,線上主題分享《多點生活在 Service Mesh 上的實踐 -- Istio + Mosn 在 Dubbo 場景下的探索之路》整理,文末包含本次分享的視訊回顧連結以及 PPT 下載下傳位址。
前言
随着多點生活的業務發展,傳統微服務架構的面臨更新困難的問題。在雲原生的環境下,Service Mesh 能給我們帶來什麼好處。如何使用社群解決方案相容現有業務場景,落地成符合自己的 Service Mesh 成為一個難點。
今天主要給大家分享一下 Service Mesh 的一些技術點以及多點生活在 Service Mesh 落地過程中适配 Dubbo 的一些探索。
首先我們從三個方面入手:
- 為什麼需要 Service Mesh 改造;
- 探索 Istio 技術點;
- Dubbo 場景下的改造;
為什麼需要 Service Mesh 改造
說到為什麼需要改造,應該先說一下 Service Mesh 和傳統微服務架構的一些特點。
微服務
微服務一般有這些子產品:
- 安全;
- 配置中心;
- 調用鍊監控;
- 網關;
- 監控告警;
- 注冊和發現;
- 容錯和限流;
這些子產品在傳統的微服務架構中有的是和 SDK 結合在一起,有的是一個獨立的中間件。
特點:
- 獨立部署;
- 子產品的邊界;
- 技術多樣性;
正是由于技術多樣性,我的微服務系統可以使用不同的語言進行開發,比如我一個商城系統,訂單系統使用 Java 開發,庫存系統使用 Go 開發,支付系統使用 Python 開發,微服務之間通過輕量級通信機制協作,比如:HTTP/GRPC 等。比如目前多點使用的 Dubbo(服務治理架構),随着多點生活的業務發展,目前遇到最棘手的問題就是中間件在更新過程中,推進很慢,需要業務方進行配合,接下來我們看看 Service Mesh。
Service Mesh
優點:
- 統一的服務治理;
- 服務治理和業務邏輯解藕;
缺點:
- 增加運維複雜度;
- 引入延時;
- 需要更多技術棧;
看了 Service Mesh 的優缺點,如果我們 Mesh 化了之後就可以解決我們目前的痛點,更新中間件隻需要重新釋出一下 Sidecar 就好了,不同語言開發的微服務系統可以采用同樣的服務治理邏輯,業務方就可以嘗試更多的技術。
探索 Istio 技術點
在談 Dubbo 場景下的改造之前我們先介紹一下 Istio 相關的技術點,然後結合 Dubbo 場景應該如何進行适配
MCP
MCP(Mesh Configuration Protocol)提供了一套用于訂閱(Watch)、推送(Push)的 API,分為 Source 和 Sink 兩個角色。
- Source 是資源提供方(server),資源變化了之後推送給訂閱者(Pilot),Istio 1.5 之前這個角色就是 Galley 或者自定義 MCP Server;
- Sink 是資源的訂閱者(client),在 Istio 1.5 之前這個角色就是 Pilot 和 Mixer,都是訂閱 Galley 或者自定義 MCP Server 的資源
MCP 的訂閱、推送流程圖:
為了和實際情況結合,我們就以 MCPServer 作為 Source,Pilot 作為 Sink 來介紹訂閱、推送流程,其中 MCP 通信過程中所傳輸的「資源」就是 Istio 定義的 CRD 資源,如:VirtualService、DestinationRules 等。
訂閱
- Pilot 啟動後會讀取 Configmap 的内容,裡面有一個
的一個數組配置(Istio 1.5 之後沒有這個配置,需要自己添加)、存放的是 MCP Server 的位址;configSources
- Pilot 連接配接 MCPServer 之後發送所關注的資源請求;
- MCPServer 收到資源請求,檢查請求的版本資訊(可能為空),判斷版本資訊和目前最新維護的版本資訊是否一緻,不一緻則觸發 Push 操作,一緻則不處理;
- Pilot 收到 Push 資料,處理傳回的資料(資料清單可能為空,為空也标示處理成功),根據處理結果傳回 ACK(成功)/ NACK(失敗),傳回的應答中包含傳回資料的版本資訊,如果傳回的是 NACK,Pilot 會繼續請求目前資源;
- MCPServer 收到 ACK(和資源請求一緻)之後對比版本号,如果一緻則不推送,否則繼續推送最新資料;
推送
- MCPServer 自身資料發生變化,主動推送變化的資源給 Pilot;
- Pilot 收到之後處理這些資料,并根據處理結果傳回 ACK / NACK;
- MCPServer 收到 ACK(和資源請求一緻) 之後對比版本号,如果一緻則不推送,否則繼續推送最新資料;
這樣的訂閱、推送流程就保證了 MCPServer 和 Pilot 資源的一緻。MCPServer 隻能通過 MCP 協定告訴 Pilot 資源發生變化了麼?當然不是,MCPServer 可以使用建立 CR 的方式,Pilot 通過 Kubernetes 的 Informer 機制也能感覺到資源發生變化了,隻是通過 MCP 傳輸的資源在 Kubernetes 裡面看不到,隻是存在于 Pilot 的記憶體裡面,當然也可以通過 Pilot 提供的 HTTP debug 接口(istiod_ip:8080/debug/configz)來查。
https://github.com/champly/mcpserver提供了一個 MCPServer 的一個 demo,如果需要更加細緻的了解 MCP 原理可以看一看。
更多 debug 接口可以檢視: https://github.com/istio/istio/blob/5b926ddd5f0411aa50fa25c0a6f54178b758cec5/pilot/pkg/proxy/envoy/v2/debug.go#L103
Pilot
Pilot 負責網格中的流量管理以及控制面和資料面之前的配置下發,在 Istio 1.5 之後合并了 Galley、Citadel、Sidecar-Inject 和 Pilot 成為 Istiod。我們這裡說的是之前 Pilot 的功能,源碼裡面 pilot-discovery 的内容。
功能
- 根據不同平台(Kubernetes、Console)擷取一些資源,Kubernetes 中使用 Informer 機制擷取 Node、Endpoint、Service、Pod 變化;
- 根據使用者的配置(CR、MCP 推送、檔案)觸發推送流程;
- 啟動 gRPC server 用于接受 Sidecar 的連接配接;
推送流程
- 記錄變化的資源類型;
- 根據變化的資源類型(數組)整理本地資料;
- 根據變化的資源類型判斷需要下發的 xDS 資源;
- 建構 xDS 資源,通過 gRPC 下發到連接配接到目前 Pilot 的 Sidecar;
xDS
Sidecar 通過動态擷取服務資訊、對服務的發現 API 被稱為 xDS。
- 協定部分(ADS、控制資源下發的順序及傳回确認的資料);
- 資料部分(CDS、EDS、LDS、RDS、SDS);
Pilot 資源類型發生變化需要下發的 xDS 資源對照:
資源名稱 | CDS | EDS | LDS | RDS |
---|---|---|---|---|
Virtualservices | ✔ | |||
Gateways | ||||
Serviceentries | ||||
Destinationrules | ||||
Envoyfilters | ||||
Sidecars | ||||
ConfigClientQuotaspecs | ||||
ConfigClientQuotaspecbindings | ||||
Authorizationpolicies | ||||
Requestauthentications | ||||
Peerauthentications | ||||
Other |
以上内容是根據 源碼 整理的
MOSN
MOSN 是一款使用 Go 語言開發的網絡代理軟體,作為雲原生的網絡資料平面,旨在為服務提供多協定、子產品化、智能化、安全的代理能力。MOSN 是 Modular Open Smart Network 的簡稱。MOSN 可以與任何支援 xDS API 的 Service Mesh 內建,亦可以作為獨立的四、七層負載均衡,API Gateway,雲原生 Ingress 等使用。
MOSN:
https://github.com/mosn/mosn配置檔案:
- mosn_config:MOSN 的配置資訊;
- listener:LDS;
- routers:RDS;
- cluster:CDS 和 EDS;
listener
其中
address
就是 MOSN 監聽的位址。
filter chains
filter_chains 在 MOSN 裡面的
network chains
,實作的還有:
- fault_inject;
- proxy;
- tcp_proxy;
和
network chains
同級的還有
listener chains
、
stream chains
, 其中
listener chains
目前隻有
original_dst
實作。
stream chains
可以對請求中的
- StreamSender;
- StreamReceiver;
- StreamAccessLog;
進行
BeforeRoute
AfterRoute
這些關鍵步驟進行修改請求資訊。
所有的
filter
都隻有兩種傳回結果:
- Continue:如果後面還有
那就執行後續filter
;filter
- Stop:執行完目前
就不再繼續執行了;filter
conv
看圖中的配置資訊
config
的内容,
downstream_protocol
upstream_protocol
這裡如果配置不一緻,就需要協定轉換。比如
HTTP1
轉換為
HTTP2
,MOSN 就會先把
HTTP1
common
的中間協定,然後再把
common
HTTP2
,這樣就實作了協定之間的轉換。如果需要自己實作其他協定轉換,那麼隻需要編寫轉換
common
的内容和
common
轉換為目前協定的内容即可實作協定之間的互轉。
proxy
我們再來看
filters
裡面的
proxy
,這個就是一個會經過路由的代理,配置資訊裡面配置了
router_config_name
,就是要路由的
router
名字。
routers
根據
listener
proxy
的配置資訊裡面的
router_config_name
會找到一個
router
,如上圖所示。然後就會根據請求裡面的
domains
去比對
virtual_hosts
, 這裡的
domains
裡面在
HTTP
裡面就會是
host
,當在 Dubbo 協定裡面我們可以把
service
(有些地方叫做 interface、target,我們這裡統一叫 service) 放到
x-mosn-host
這個 MOSN 的
Header
裡面,MOSN 就可以根據這個去比對
domains
。
然後比對到一個
virtual_hosts
之後,就會得到對應的
routers
,這裡又會根據
match
裡面的比對規則進行比對,
HTTP
協定裡面可以根據
path
queryparam
header
等資訊進行比對,具體比對規則通過 VirtualService 下發,如果是 Dubbo 協定,那麼可以套用
HTTPRoute
規則,然後把 Dubbo 的
attachment
解析出來當作
header
去用,目前 MOSN 沒有解析
attachment
,我們自己實作了一個。
比對到了之後會得到一個
route
,圖中所示隻有一個
cluster_name
,如果是有多個
subset
(DestinationRule 定義),那麼就會有
weighted_cluster
,裡面會有
cluster_name
weight
構成的對象的數組,例如:
"route":{
"weighted_clusters":[
{
"cluster":{
"name":"outbound|20882|green|mosn.io.dubbo.DemoService.workload",
"weight":20
}
},
{
"cluster":{
"name":"outbound|20882|blue|mosn.io.dubbo.DemoService.workload",
"weight":80
}
}
],
"timeout":"0s",
"retry_policy":{
"retry_on":true,
"retry_timeout":"3s",
"num_retries":2
}
}
weight
之和必須為 100(Istio 定義的),必須是非負數的整數。
下面有一些
timeout
retry_policy
服務政策。
比對上了之後會得到一個
cluster_name
,然後我們再看
cluster
cluster
在
routers
裡面比對出來的
cluster_name
作為
key
cluster
裡面會找到這麼一個對象。
lb_type
就是節點的負載均衡政策,目前 MOSN 支援:
- ROUNDROBIN;
- RANDOM;
- WEIGHTED_ROUNDROBIN;
- EAST_REQUEST;
hosts
address
裡面也可以配置權重,這個權重必須是大于 0 或小于 129 的整數。可以通過 Istio 1.6 裡面的
WorkloadEntry
來配置權重。然後根據負載均衡政策拿到
host
之後直接請求到對應的節點。
這就完成了流量的轉發。接下來我們看看 Dubbo 場景下應該如何改造。
Dubbo 場景下的改造
所有的改造方案裡面都是要把 SDK 輕量化,關于服務治理的邏輯下沉到 Sidecar,我們在探索的過程中有三種方案。
Istio + Envoy
這個方案是 Istio+Envoy 的方案,是參考的華為雲的方案:
https://support.huaweicloud.com/bestpractice-istio/istio_bestpractice_3005.html- 通過建立 EnvoyFilter 資源來給 xDS 資源打 patch;
- Envoy 解析 Dubbo 協定中的 Service 和 Method;
- 根據路由政策配置把流量轉發到對應的 Provider;
這種方案如果需要解析更多的 Dubbo 内容,可以通過 WASM 擴充。
MOSN + Dubbo-go
- MOSN 提供 Subscribe、Unsubscribe、Publish、Unpublish 的 HTTP 服務;
- SDK 發送請求到 MOSN 提供的這些服務,讓 MOSN 代為與真正的注冊中心互動;
- MOSN 通過 Dubbo-狗直接和注冊中心連接配接;
這種方案的話就不需要 Istio。
Istio + MOSN
這種方案就是我們現在采用的方案,包括:
- 資料面改造;
- 控制面适配;
我們有一個理念就是如果能通過标準的 CRD 最好,如果描述不了的話我們就通過 EnvoyFilter 進行修改。這裡特别說一下,我們一開始也有一個誤區就是 EnvoyFilter 是作用于 Envoy,其實不是的,是對生成好的 xDS 資源進行 ADD, MERGE 等操作,目前隻可以修改 LDS、RDS、CDS,這個修改也是有一定局限性的。如果 EnvoyFilter 修改不了某些特定的場景(比如 Istio 1.6 之前的 ServiceEntry 裡面的 Endpoint 不能單獨為每個執行個體指定不同的端口),那麼我們隻能修改 pilot-discovery 的代碼,xDS 是不會作任何修改的。按照這個理念,我們開始探索如何改造。
資料面改造
首先有三個端口需要說明一下:
- 20880 : provider 監聽端口;
- 20881 : consumer 請求 mosn 的這個端口,mosn 做轉發到 provider;
- 20882 : 接受來自下遊(mosn/consumer)的請求,直接轉到 127.0.0.1:20880;
步驟:
- provider 啟動之後請求本地 mosn 的注冊接口,把服務資訊注冊到注冊中心(zk/nacos),注冊請求到達 mosn 之後,mosn 會把注冊端口号改為 20882;
- consumer 啟動之後不需要連接配接注冊中心,直接把請求發送到 127.0.0.1:20881;
- consumer 端的 mosn 收到請求之後,根據配置資訊 listener->routers->cluster->host,找到合适的 host(可以是 provider 的 mosn 或者 直接是 provider) 發送請求,這裡的比對過程可以修改 MOSN 讓 Dubbo 的 service 作為 domains,attachment 作為 header;
- provider 端 mosn 收到請求後(20882),直接轉發請求到本地 127.0.0.1:20880;
這個隻是通過靜态配置實作的,如果 provider 這些資訊如何通過 Pilot 下發呢?
控制面适配
MOSN 本身支援 xDS API,配置資訊可以通過 xDS 下發,而不是靜态配置。我們有一個對接配置中心,注冊中心的程式我們叫 Adapter,這個主要擷取注冊中心的服務資訊,然後根據配置中心的服務治理政策(比如流程比例,還有一些我們内部的一些單元的資訊)建構出 Istio 支援的 CR,然後建立 CR,Pilot 自己感覺 CR 變化 或者 通過 MCP 把這些資訊直接發送給 Pilot,觸發 Pilot 的資源變化,然後 Pilot 根據資源的變化去下發一些 xDS 資源,Sidecar 收到資源變化後,就可以動态調整路由政策,進而達到服務治理的目的。
最終架構圖如圖所示:
注冊(灰色部分):
- provider 發送注冊資訊給 MOSN;
- MOSN 修改注冊資訊(端口号等),然後注冊到真正到注冊中心(ZK / Nacos 等);
配置下發(藍色部分):
- Adapter 連接配接注冊中心和配置中心并感覺其變化;
- Adapter 感覺到變化之後通過 MCP 把變化的資訊傳遞給 Pilot(或者建立 CR 讓 Pilot 自己感覺);
- Pilot 感覺到資源變化觸發配置下發流程,根據變化到資源類型下發對應到 xDS 資源到 連接配接到它的 Sidecar;
服務請求(黃色部分):
- consumer 請求本地 127.0.0.1:20881(MOSN 監聽的端口);
- MOSN 根據 listener->router->cluster 找到一個 host,然後把請求轉發到這個 host 上;
以上就完成了服務注冊、發現、治理的所有邏輯。
Istio 1.6 之後可以通過 WorkloadEntry + ServiceEntry 這兩種 CRD 資源來描述叢集外的服務,當執行個體上線或者下線的時候就會直接觸發 EDS 增量下發。
Demo 示範
首先要說明一下:
- 由于沒有真正的注冊,是以使用手動添加 ServiceEntry 的方式代替 Adapter 功能;
- Listener 和 Routers 配置資訊目前是固定的;
- Provider 隻注冊到本地 ZK;
- Sidecar 注入到方式使用的是多個 Container;
具體操作可以按照
mosn-tutorial,裡面的
istio-mosn-adapt-dubbo
。即使你沒有 Kubernetes 環境也可以嘗試的,後期這個會移植到 MOSN 官網,敬請期待。
mosn-tutorial:
https://github.com/mosn/mosn-tutorial以上就是本期分享的全部内容,感謝大家的收看。
本期嘉賓介紹
陳鵬,多點生活平台架構組研發工程師,開源項目與雲原生愛好者。有多年的網上商城、支付系統相關開發經驗,2019年至今從事雲原生和 Service Mesh 相關開發工作。
回顧資料
PPT 下載下傳:
https://github.com/servicemesher/meetup-slides/tree/master/2020/05/webinar視訊回顧:
https://www.bilibili.com/video/BV15k4y1r7n8