Dubbo 在 K8s 下的思考

大濤學長發表於2019-11-05

序言

Dubbo在2011開源之後,一直是國內最受歡迎的RPC框架,之後spring boot和Spring Cloud的面世,助推了微服務的火熱程度。計算機的世界變化很快,自從容器和K8s登上舞臺之後,給原有的RPC領域帶來了很大的挑戰。這個文章主要講述RPC領域遇到的問題,以及RPC怎麼去擁抱K8s懷抱的一些思考。

K8S介紹

kubernetes是一個開源的,用於管理雲平臺中多個主機上的容器化的應用,Kubernetes的目標是讓部署容器化的應用簡單並且高效,Kubernetes提供了應用部署,規劃,更新,維護的一種機制。kubernetes簡稱K8s。

Dubbo 在 K8s 下的思考

在Kubernetes中,最小的管理元素不是一個個獨立的容器,而是Pod。Pod的生命週期需要注意以下幾點:
  • 容器和應用可能隨時被殺死
  • Pod Ip和主機名可能變化 (除非使用StatefulSet進行定製)
  • 寫到本地的磁碟的檔案可能消失,如果想不失效,需要用儲存卷

應用,容器,Pod的關係

  • 應用部署在容器中,一般情況下一個應用只部署在一個容器中
  • 一個Pod下可以包含一個或多個容器,一般情況下一個Pod只建議部署一個容器。下列場景除外:
  • side car
  • 一個容器的執行以來與本地另外一個容器。如一個容器下應用負責下載資料,另外一個容器下應用向外提供服務

Service

如果一些Pods 提供了一些功能供其它的Pod使用,在kubernete叢集中是如何實現讓這些前臺能夠持續的追蹤到這些後臺的?答案是:Service。
Kubernete Service 是一個定義了一組 Pod的策略的抽象,這些被服務標記的Pod一般都是透過label Selector決定的。Service抽象了對Pod的訪問。
預設的Service,透過一個叢集Ip獲取A Record。但是有時需要返回所有滿足條件的Pod Ip列表,這時候可以直接使用 Headless Services
參考:
推薦書籍:kubernetes in action

RPC介紹和分析

隨著微服務的普及,應用之間的通訊有了足夠多的成熟方案。Dubbo在2011年開源之後,被大量的中小型公司採用;在Spring Boot推出之後,Spring逐漸煥發出第二春,隨即Spring Cloud面世,逐漸佔領市場,在中國市場中,和Dubbo分庭抗爭;gRPC是google推出的基於Http2的端到端的通訊工具,逐漸地在K8s市場上佔據統治地位,如etcd,istio等都採用gRPC作為通訊工具;Service Mesh從開始概念上就火熱,現在逐漸走向成熟,Istio + Envoy(其他sidecar)逐漸開始走上舞臺。

應用開發者視角

從功能層面來說,對開發者有感知的功能有:服務實現,服務暴露(註解或配置),服務呼叫(註解或配置),服務治理等。
從選型角度會關注以下幾點:易用性(開發易用性和開箱即用),效能,功能,擴充套件性等。

框架開發者視角

關鍵流程:服務暴露,服務註冊,服務發現,服務呼叫,服務治理。
關鍵知識點:序列化,網路通訊,服務路由,負載均衡,服務限流,熔斷,降級等服務治理。

主流技術實現

DUBBO/HSF


Dubbo 在 K8s 下的思考

Dubbo提供了 面向介面的遠端方法呼叫。應用開發者定義介面,編寫服務並暴露;Client端透過介面進行呼叫。
Dubbo註冊服務的維度是 介面維度,每個介面會在註冊中心寫入一條資料。
Dubbo支援條件路由,指令碼路由,Tag路由等。這些 路由規則都是強依賴於IP地址
備註:Dubbo和HSF的大部分機制都是相似的,所以下面都以Dubbo作為方案進行討論。

SpringCloud

Spring Cloud透過Rest形式進行網路呼叫。應用開發者可以自己編寫暴露Rest服務,如springmvc。
Spring Cloud裡的 服務註冊是應用維度(Eureka),Client端和Server端透過約定的方式進行通訊。
Spring Cloud提供了一套標準API,而其中Netflix是其中的佼佼者,對這套API進行了實現,對大部分開發者來說,可以回直接依賴和使用Netflix,所以可以說是Netflix提供成了Spring Cloud的核心。但是作為商業公司對開源投入往往會多變,如Eureka已經體制維護。

gRPC

gRPC 是一個 基於 HTTP/2 協議設計的 RPC 框架,它採用了 Protobuf 作為 IDL。gRPC作為端到端的通訊方案,可以解決現在的多語言問題。
gRPC本身不提供服務註冊,服務治理的功能。但現在可以看到gRPC有趨勢往這方面擴充套件的野心。

K8s

K8s體系裡暫時沒有公允的通訊框架,一般推薦gRPC作為RPC框架。
K8s體系下,預設情況下,Pod的Ip是變化的,所以Pod和Pod之間需要通訊的話,有幾種方式:
  • Service+DNS:新建一個Service,可以透過標籤選擇到一組Pod列表,這個service對應一個不變的叢集Ip;Client端透過DNS方式或者直接訪問叢集Ip。這個叢集Ip,約等於實現了負載均衡 (Iptable方式)。
  • headless service:headless service和上面的service的區別是,它不提供叢集Ip,透過主機名的形式獲取一組Ip列表,Client端自己決定訪問哪個Pod。

Istio + Envoy


Dubbo 在 K8s 下的思考

Istio的控制層會向K8s的api server請求並監聽Pod資訊,service資訊等資訊。這樣Istio中就有了完整的K8s叢集中的Pod,service等的完整資訊。如果K8s叢集中有資訊變更,istio中也可以得到通知並更新對應的資訊。
Envoy作為Proxy一個最常見的實現,以Envoy作為例子簡單介紹。Envoy 透過查詢檔案或管理伺服器來動態發現資源。對應的發現服務及其相應的 API 被稱作 xDS。協議內容包括LDS,RDS,CDS等等。
參考資料:
備註:上述知識是透過查閱資料(Istio官網),以及和集團service mesh同學溝通獲得。如有問題,歡迎指正。

總結


Dubbo 在 K8s 下的思考

遇到的問題和挑戰

Spring Cloud和Dubbo的共生

基礎理論可以 參考.
Dubbo預設是基於TCP通訊,Spring Cloud大部分基於Rest請求。在阿里雲實施商業化過程中,發現大量公司需要Spring Cloud應用和Dubbo進行通訊,社群主要依靠Dubbo上增加一層閘道器來解決。
是否有方案進行統一服務註冊發現,以及服務呼叫呢?

Dubbo在K8s場景下的挑戰

K8s下Pod的IP是變化的 (預設),dubbo的服務治理高度依賴IP。
K8s的服務註冊透過Pod定義完成,服務發現其實是尋找Pod的過程。Pod和應用有一定的對應關係,和dubbo裡的介面維度的服務註冊發現模型不是很匹配。

Dubbo在Service mesh場景下的生存空間

Dubbo需要進行支援裁剪,Dubbo的大部分功能都可以交由sidecar(proxy)來完成。
如果公司已經在部署了RPC框架,這時候如果需要實施Service Mesh,有什麼好的過渡方案嗎?

問題梳理

服務定義

服務怎麼定義呢?需要從應用開發者角度看待怎麼定義服務。
服務在功能維度對應某一功能,如查詢已買訂單詳情。在Dubbo中,對應某個介面下的方法;在Spring Cloud和gRPC對應一個Http請求。如果從面向函式程式設計角度,一個服務就是一個function。在Java語言中,class是一切程式設計的基礎,所以將某些服務按照一定的維度進行聚合,放到某個介面中,就成了一個介面包含了很多的服務。
從Dubbo角度來解釋下:Dubbo是面向介面程式設計的遠端通訊,所以Dubbo是面向服務集的程式設計,你如果想呼叫某個服務,必須透過介面的方式引入,然後呼叫介面中的某個服務。Dubbo Ops中提供的服務查詢功能,其實不是查詢單個服務,而是透過查詢介面(服務集)之後獲得具體的方法(服務)。
而在Spring Cloud的世界裡,服務提供方會將自己的應用資訊(Ip+port)註冊成應用下的一個例項,服務消費方和服務提供方按照約定的形式進行Rest請求。每個請求對應的也是一個服務。

和K8s裡的service的區別

K8s裡的service其實是對應到一組Pod+port列表,和DNS聯絡緊密;用通俗易懂的方式表達:維護了Pod集合的關係對映。和上面講的服務是屬於不同場景下的兩個概念。
按照這個方式定義服務,服務治理的粒度其實也是按照服務粒度,可以針對每個服務設定超時時間,設定路由規則等等。但是服務註冊的粒度和服務有什麼關係呢?

服務註冊粒度

一個應用下包含了很多介面,一個介面下包含了很多服務(Dubbo);或者一個應用包含了很多的服務(Spring Cloud)。分析下應用維度註冊和介面維度註冊的優缺點。 會有一篇獨立的文章來闡述應用維度註冊的方案

介面維度註冊

優點:
  • 服務查詢按照介面維度查詢非常方便,實現難度低
  • 應用拆分或者合併的時候,Client端(消費者)無需關心,做到了讓使用者無感
缺點:
  • 和K8s等主流平臺的模型對應關係不匹配
  • 註冊的資料量非常大,有一定的效能風險

應用維度

優點:
  • 和K8s,Spring Cloud等模型對應關係一致
  • 效能上可以得到很大緩解
缺點:
  • 應用拆分或者合併的時候,Client端需要感知 (如果想做到不感知,需要框架開發者維護一份介面和應用對映關係的儲存)
  • 如果想對使用者保持Dubbo原有的介面維度的查詢,需要較多的工作量來保證。
  • 對使用者透明度有所減少,需要在OPS上提供其他一些工具。如供應用開發者可以檢視具體某個Ip是否提供了某個服務等等。

Dubbo 和 Spring Cloud

目標:Dubbo和Spring Cloud的服務發現進行統一;Dubbo和Spring Cloud可以互相呼叫。

服務發現統一

Dubbo改造成應用維度的服務註冊。(具體不展開,後面文章說明)

打通呼叫

Dubbo實現中,支援將以Rest協議進行暴露,並且讓Spring Cloud識別。@桃谷

Dubbo + K8S

在K8s已經闡述過,下面的內容也是假設一個應用部署在一個容器裡,一個容器部署在一個Pod裡。
接下來方案的討論,互相之間其實是有關聯的,如服務治理可能會影響到服務註冊發現,服務查詢也不能依賴於服務註冊的內容。整個設計的過程是不斷最佳化的過程。下面所說的內容,以Dubbo來舉例說明。

服務治理

Dubbo原有體系裡的服務治理是強依賴於IP,當配置了一套服務治理規則的時候,最後都是基於一個或多個Ip地址。
到K8s體系下之後,要考慮的是Pod的Ip不是固定的。所以當前的路由規則不能滿足條件,而且會產生很多規則垃圾資料。K8s體系下,透過service查詢Pod,是基於label selector; 透過deployment管理Pod,其實也是基於Pod label selector。所以Pod label selector是在K8s習題中比較通用的解決方案。
以路由規則為例,需要支援一種新的路由規則:label路由。透過一定條件匹配之後,將結果定位到以label selector查詢到的Pod列表裡,而非原來的Ip列表。
要支援label路由,Client端需要獲取到Client端自己的Pod label資訊,還需要獲取到server Pod列表中每個Pod的label資訊。

應用獲取當前Pod的資訊方式

  1. Pod定義環境變數,應用獲取
Dubbo提供對環境變數讀取的支援,Pod中需要按照Dubbo定義的環境變數設定具體的Pod資訊。
  1. 透過Downward Api傳遞Pod資訊
Dubbo需要提供對Downward的目錄讀取,Pod中需要定製downward的對應配置。
  1. 透過Api server獲取資料
最強大的方式,但是應用需要強依賴於Api server。

應用獲取其他Pod的資訊方式

  1. 透過呼叫其他Pod的服務獲取
依賴於應用能獲取自身的Pod資訊,同時將自身的Pod資訊暴露成服務(rest或dubbo協議)
Client端透過呼叫對用的Pod獲取到對應Pod的完整資訊。
  1. 透過api server獲取資料
很強大,但增加了對api server的依賴。

服務註冊和發現

K8s體系下,RPC服務發現有幾種方式:
  • 序號產生器制:將Ip寫入註冊中心,用心跳保持連線;當心跳停止,從註冊中心刪除。
  • 利用Service+DNS:新建一個Service,可以透過標籤選擇到一組Pod列表,這個service對應一個不變的叢集Ip;Client端透過DNS方式或者直接訪問叢集Ip。這個叢集Ip,約等於實現了負載均衡 (Iptable方式)。
  • 利用headless service(DNS):headless service和上面的service的區別是,它不提供叢集Ip,透過主機名的形式獲取一組Ip列表,Client端自己決定訪問哪個Pod。
  • api server: Client端直接請求Api server,獲取到Pod的列表,Client自己決定訪問Pod的邏輯。同時獲取的時候增加watch,api server會將Pod的變化資訊同步Client。
透過拿到Server端的Ip或者host,Client端就可以發起Http或者其他協議的請求。
下面介紹符合Dubbo的可行方案:

1. Dubbo + Zookeeper Pod cluster (HSF+CS cluster)

這是最簡單的方式,Dubbo本身不需要做任何改造。
帶來的問題是增加了ZooKeeper的維護,同時這個方案很不雲原生,和K8s的體系沒有任何關係。

腦暴

上面方案是將ZooKeeper作為註冊中心,那麼是否可以將K8s裡service作為註冊中心呢?dubbo裡每個介面去建立一個service,每個應用例項啟動過程中去更新Endpoint資訊,建立Service-> Endpoint-> Ip列表的關係。
這種方案中K8s service的定義被改造了,而且定義了過多的service,service的維護管理是個難題。

基於K8s的場景

在傳統的RPC領域,服務分成服務註冊和服務發現。在K8s領域Pod和應用是一對一的關係,K8s本身就提供了Pod查詢的能力,所以一定程度上服務註冊其實可以不存在,而只需要服務發現。但是這個其實需要一個前提:
dubbo需要將服務註冊發現的粒度改造成應用維度。 在運維層面,將app=xxx (應用名)寫入到Pod的label中。

2. Dubbo + K8s DNS

如果K8s service提供了cluster Ip,那麼Dubbo只負責呼叫該叢集Ip,路由和負載均衡的邏輯則交給了K8s的proxy來完成。此方案削減了Dubbo的核心能力。
接下來討論headless service提供的能力。
透過請求 <service>.<ns>.svc.<zone>. IN A 的方式發起請求獲取Ip列表,但是需要輪詢方式不斷獲取更新的Ip列表。 參考
服務治理相關的功能,需要在上述服務治理部分中獨立支援。

3. Dubbo + Api Server


Dubbo 在 K8s 下的思考

Pod的容器中部署的dubbo應用, 服務註冊流程可以直接刪除,服務發現功能透過和Api Server進行互動,獲取Pod和service資訊,同時watch Pod和service的變更。透過這種方式之後,服務治理相關的資訊也可以透過Api Server直接獲取。

4. Dubbo + Istio + Envoy

Dubbo可以直接使用指定Ip+埠的方式呼叫同一個Pod下Envoy (也可能是同一個node的Envoy)。Dubbo將路由規則,負載均衡,熔斷等功能交給Istio和Envoy。Envoy需要支援Dubbo協議的轉發。
所以Dubbo需要完成兩個事情:本地IP直連(現有功能), 多餘功能裁剪(暫未實現)。

5. Dubbo + Istio


Dubbo 在 K8s 下的思考

Dubbo 應用不再依賴 Envoy 作為 sidecar ,而是直接和 Istio 進行互動,把 Istio 作為註冊中心,作為服務治理的配置中心。
Dubbo 需要提供類似的 xDS 協議,在pilot將service的instance轉換成dubbo的協議格式。
Dubbo 還需要去適配 istio 的一些功能,如健康檢查,安全相關的邏輯。具體實現可以參考 Envoy 的實現。

6. Dubbo和 Istio 在 K8s 體系下共存

這個可選擇的方案較多,我提供兩種思路,供大家思考:
所有的服務註冊透過K8s的機制完成,所有的服務發現透過 Headless service 完成。sidecar 在建立過程中,需要對原有的 K8s service 進行 update 。
Nacos 作為 Dubbo 的註冊中心,並且需要將 K8s 中的資料進行部分中轉。Dubbo 應用,將服務註冊以應用維度註冊到 Nacos ,Istio Pilot 需要識別 Nacos 資料;Istio 的執行機制基本不變,需要將 K8s service instance 的資料寫入到 nacos ,供 Dubbo 呼叫。

7. 雲上和雲下環境共存 & 雲上多叢集環境

Istio 提供了跨叢集和雲上雲下的解決方案, kubeFed 作為 K8s 的跨叢集解決方案也能起到一定作用。
這個課題的複雜度更加高,心中有了一些答案,期望大家透過上文也有一定的思考。

服務查詢

丟擲三種方式,供大家思考。

Dubbo原有方式

Dubbo原有的服務查詢是針對介面的查詢,每個介面會有版本號和組別。介面名+版本號+組別確定唯一的服務集合,這個服務集下有對應的服務提供者和服務消費者(介面級依賴),服務提供者是一組Ip+port列表,服務消費者也是一組Ip+port列表。

Dubbo 在 K8s 下的思考

當做了改造成應用級別的服務註冊或者直接使用K8s自帶的Pod發現機制的話,需要做一些改造,這部分改造,和前面提到的一樣,放到其他文章裡單獨說明。

只支援應用查詢

和Spring Cloud類似,支援應用維度的查詢。查詢到具體應用之後,應用詳情下包含了Ip+port列表,每個Ip+port其實就是一個應用的例項。點選開每個應用例項,可以檢視每個應用的詳細資訊,詳細資訊包含了該例項提供了哪些服務。

介面+應用查詢均衡

在原來只支援應用查詢的基礎上,增加一步:支援查詢某個介面對應的應用列表,而大部分介面只屬於一個應用。
再點選應用列表下具體的應用之後,會跳到應用詳情。

總結

上述討論的是開源的方案,所以相對歷史包袱比較少。對一些大公司想從原有的RPC方案切換到雲原生的支援,需要考慮更多相容性和效能,需要付出更大的代價。
雲原生的趨勢已經勢不可擋,在RPC領域究竟哪種方案最終能夠勝出,現在還言之過早。我相信Service Mesh 和傳統的RPC (Dubbo/ gRPC) 都會有自己的一席之地,一切讓時間給我們答案吧。
作者簡介:曹勝利,Apache Dubbo PMC,關注RPC領域。在阿里內部負責Dubbo開源和ClassLoader隔離器Pandora Boot。


本文為雲棲社群原創內容,未經允許不得轉載。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69947441/viewspace-2662728/,如需轉載,請註明出處,否則將追究法律責任。

相關文章