多點生活在 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 的內容,裏面有一個 configSources 的一個數組配置(Istio 1.5 之後沒有這個配置,需要自己添加)、存放的是 MCP Server 的地址;
  • 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 資源對照:

以上內容是根據源碼整理:
https://github.com/istio/istio/blob/5b926ddd5f0411aa50fa25c0a6f54178b758cec5/pilot/pkg/proxy/envoy/v2/ads_common.go#L97

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 chainsstream chains, 其中 listener chains 目前只有 original_dst 實現。stream chains 可以對請求中的:

  • StreamSender;
  • StreamReceiver;
  • StreamAccessLog;

進行 BeforeRoute AfterRoute 這些關鍵步驟進行修改請求信息。

所有的 filter 都只有兩種返回結果:

  • Continue:如果後面還有 filter 那就執行後續 filter
  • Stop:執行完當前 filter 就不再繼續執行了;

conv

看圖中的配置信息 config 的內容, downstream_protocolupstream_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 協議裏面可以根據 pathqueryparamheader 等信息進行匹配,具體匹配規則通過 VirtualService 下發,如果是 Dubbo 協議,那麼可以套用 HTTPRoute 規則,然後把 Dubbo 的 attachment 解析出來當作 header去用,目前 MOSN 沒有解析 attachment,我們自己實現了一個。

匹配到了之後會得到一個 route,圖中所示只有一個 cluster_name,如果是有多個 subset(DestinationRule 定義),那麼就會有 weighted_cluster ,裏面會有 cluster_nameweight 構成的對象的數組,例如:

"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 定義的),必須是非負數的整數。

下面有一些 timeoutretry_policy 服務策略。

匹配上了之後會得到一個 cluster_name,然後我們再看 cluster

cluster

routers 裏面匹配出來的 cluster_name 作爲 keycluster 裏面會找到這麼一個對象。

其中 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/istiobestpractice3005.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-go 直接和註冊中心連接;

這種方案的話就不需要 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 收到資源變化後,就可以動態調整路由策略,從而達到服務治理的目的。

最終架構圖如圖所示:

註冊(灰色部分):

  1. provider 發送註冊信息給 MOSN;
  2. MOSN 修改註冊信息(端口號等),然後註冊到真正到註冊中心(ZK / Nacos 等);

配置下發(藍色部分):

  1. Adapter 連接註冊中心和配置中心並感知其變化;
  2. Adapter 感知到變化之後通過 MCP 把變化的信息傳遞給 Pilot(或者創建 CR 讓 Pilot 自己感知);
  3. Pilot 感知到資源變化觸發配置下發流程,根據變化到資源類型下發對應到 xDS 資源到 連接到它的 Sidecar;

服務請求(黃色部分):

  1. consumer 請求本地 127.0.0.1:20881(MOSN 監聽的端口);
  2. MOSN 根據 listener->router->cluster 找到一個 host,然後把請求轉發到這個 host 上;

以上就完成了服務註冊、發現、治理的所有邏輯。

Istio 1.6 之後可以通過 WorkloadEntry + ServiceEntry 這兩種 CRD 資源來描述集羣外的服務,當實例上線或者下線的時候就會直接觸發 EDS 增量下發

Demo 演示

首先要說明一下:

  • 由於沒有真正的註冊,所以使用手動添加 ServiceEntry 的方式代替 Adapter 功能;
  • Listener 和 Routers 配置信息目前是固定的;
  • Provider 只註冊到本地 ZK;
  • Sidecar 注入到方式使用的是多個 Container;

https://mp.weixin.qq.com/mp/readtemplate?t=pages/video_player_tmpl&auto=0&vid=wxv_1365701935072083969

具體操作可以按照 mosn-tutorial,裏面的 istio-mosn-adapt-dubbo。即使你沒有 Kubernetes 環境也可以嘗試的,後期這個會移植到 MOSN 官網,敬請期待。

mosn-tutorial:https://github.com/mosn/mosn-tutorial

以上就是本期分享的全部內容,感謝大家的收看。

回顧資料

PPT 下載:

https://github.com/servicemesher/meetup-slides/tree/master/2020/05/webinar

視頻回顧:

https://www.bilibili.com/video/BV15k4y1r7n8

本文歸檔在 sofastack.tech。

作者介紹

陳鵬,多點生活平臺架構組研發工程師,開源項目與雲原生愛好者。有多年的網上商城、支付系統相關開發經驗,2019年至今從事雲原生和 Service Mesh 相關開發工作。

本文轉載自公衆號金融級分佈式架構(ID:Antfin_SOFA)。

原文鏈接

https://mp.weixin.qq.com/s?__biz=MzUzMzU5Mjc1Nw==&mid=2247486264&idx=1&sn=419addedb88dbc5f3df540097fe210e0&chksm=faa0e4e2cdd76df45abb30d94c4c1e59cf3d0a25de7a20fc5f4072f85f9ee6757e01e3ae3546&scene=27#wechat_redirect

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