Dubbo 在 K8s 下的思考

序言

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。

參考:Https://kubernetes.io/

推薦書籍: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等等。

servicemesh介紹

Istio路由規則

備註:上述知識是通過查閱資料(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>..svc.<zone>. IN A 的方式發起請求獲取Ip列表,但是需要輪詢方式不斷獲取更新的Ip列表。參考/<zone>/<service>

服務治理相關的功能,需要在上述服務治理部分中獨立支持。

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) 都會有自己的一席之地,一切讓時間給我們答案吧。

阿里雲雙11億元補貼提前領,進入抽取iPhone 11 Pro:https://www.aliyun.com/1111/2019/home?utm_content=g_1000083110


分享到:


相關文章: