天天看點

多點生活在 Service Mesh 上的實踐 | 線上直播整理

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 的訂閱、推送流程圖:

多點生活在 Service Mesh 上的實踐 | 線上直播整理

為了和實際情況結合,我們就以 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 資源對照:

資源名稱 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

多點生活在 Service Mesh 上的實踐 | 線上直播整理

其中

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

多點生活在 Service Mesh 上的實踐 | 線上直播整理

根據

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

裡面會找到這麼一個對象。

多點生活在 Service Mesh 上的實踐 | 線上直播整理

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 是不會作任何修改的。按照這個理念,我們開始探索如何改造。

資料面改造

多點生活在 Service Mesh 上的實踐 | 線上直播整理

首先有三個端口需要說明一下:

  • 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 收到資源變化後,就可以動态調整路由政策,進而達到服務治理的目的。

最終架構圖如圖所示:

多點生活在 Service Mesh 上的實踐 | 線上直播整理

注冊(灰色部分):

  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;

具體操作可以按照

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

繼續閱讀