Dubbo 在 K8s 下的思考

做者 | 曹勝利  Apache Dubbo PMCnode

導讀:Dubbo 做爲高性能 Java RPC 框架的刻板印象早已深刻人心,在 Cloud Native 的架構選型上,Spring Cloud 或許纔是業界的優先選擇。實際上,Dubbo 已經悄然地衍進爲 Cloud Native 基礎設施,不只承襲過去 RPC 時代的榮耀,並且也完善了現有基礎設施的缺失。自從容器和 K8s 登上舞臺以後,給原有的 RPC 領域帶來了很大的挑戰,本文主要講述 RPC 領域遇到的問題,以及 RPC 怎麼去擁抱 K8s 的一些思考。git

K8s 介紹

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

file

在 Kubernetes 中,最小的管理元素不是一個個獨立的容器,而是 Pod 。Pod 的生命週期須要注意如下幾點:spring

  • 容器和應用可能隨時被殺死;
  • Pod Ip 和主機名可能變化 (除非使用 StatefulSet 進行定製);
  • 寫到本地的磁盤的文件可能消失,若是想不失效,須要用存儲卷。

應用 & 容器 & Pod 的關係

  • 應用部署在容器中,通常狀況下一個應用只部署在一個容器中;
  • 一個 Pod 下能夠包含一個或多個容器,通常狀況下一個 Pod 只建議部署一個容器。下列場景除外:
    • side car;
    • 一個容器的運行以來與本地另一個容器。如一個容器下應用負責下載數據,另一個容器下應用向外提供服務。

Service

若是一些 Pods 提供了一些功能供其它的 Pod 使用,在 Kubernete 集羣中是如何實現讓這些前臺可以持續的追蹤到這些後臺的?答案是:Service 。編程

Kubernete Service 是一個定義了一組 Pod 的策略抽象,這些被服務標記的 Pod 通常都是經過 label Selector 決定的。Service 抽象了對 Pod 的訪問。api

默認的 Service ,經過一個集羣 IP 獲取 A Record 。可是有時須要返回全部知足條件的 Pod IP 列表,這時候能夠直接使用 Headless Services 。安全

參考: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

file

  • 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

file

Istio 的控制層會向 K8s 的 Api server 請求並監聽 pod 信息,service 信息等信息。這樣 Istio 中就有了完整的 K8s 集羣中的 pod,service 等的完整信息。若是 K8s 集羣中有信息變動,Istio 中也能夠獲得通知並更新對應的信息。

Envoy 做爲 Proxy 一個最多見的實現,以 Envoy 做爲例子簡單介紹。Envoy 經過查詢文件或管理服務器來動態發現資源。對應的發現服務及其相應的 Api 被稱做 xDS 。協議內容包括 LDS、RDS、CDS 等等。

參考資料

Service Mesh 介紹:www.infoq.cn/article/pat…Istio
路由規則:istio.io/docs/tasks/…

備註:上述知識是經過查閱資料( Istio 官網),以及和集團 Service Mesh 同窗溝通得到。若有問題,歡迎指正。

小結

file

遇到的問題和挑戰

Spring Cloud 和 Dubbo 的共生

Dubbo 默認是基於 TCP 通訊,Spring Cloud 大部分基於 Rest 請求。在阿里雲實施商業化過程當中,發現大量公司須要 Spring Cloud 應用和 Dubbo 進行通訊,社區主要依靠 Dubbo 上增長一層網關來解決。

是否有方案進行統一服務註冊發現,以及服務調用呢?

基礎理論能夠參考:

yq.aliyun.com/articles/58…

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 的信息方式

  • Pod 定義環境變量,應用獲取;Dubbo 提供對環境變量讀取的支持,Pod 中須要按照 Dubbo 定義的環境變量設置具體的 pod 信息。

  • 經過 Downward API 傳遞 Pod 信息;Dubbo 須要提供對 Downward 的目錄讀取,Pod 中須要定製 downward 的對應配置。

  • 經過 API Server 獲取數據;最強大的方式,可是應用須要強依賴於 API Server 。

應用獲取其餘 Pod 的信息方式

  • 經過調用其餘 Pod 的服務獲取;依賴於應用能獲取自身的 Pod 信息,同時將自身的 Pod 信息暴露成服務( rest 或 dubbo 協議)。client 端經過調用對用的 Pod 獲取到對應 Pod 的完整信息。

  • 經過 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 提供的能力。

經過請求 ..svc..  IN A  的方式發起請求獲取 IP 列表,可是須要輪詢方式不斷獲取更新的 IP 列表。

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

參考:github.com/kubernetes/…

3. Dubbo + Api Server

file

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

file

  • 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 列表。

file

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

只支持應用查詢

和 Spring Cloud 相似,支持應用維度的查詢。查詢到具體應用以後,應用詳情下包含了 ip+port 列表,每一個 ip+port 其實就是一個應用的實例。點擊開每一個應用實例,能夠查看每一個應用的詳細信息,詳細信息包含了該實例提供了哪些服務。

接口+應用查詢均衡

在原來只支持應用查詢的基礎上,增長一步:支持查詢某個接口對應的應用列表,而大部分接口只屬於一個應用。

再點擊應用列表下具體的應用以後,會跳到應用詳情。

總結

上述討論的是開源的方案,因此相對歷史包袱比較少。對一些大公司想從原有的 RPC 方案切換到雲原生的支持,須要考慮更多兼容性和性能,須要付出更大的代價。

雲原生的趨勢已經勢不可擋,在 RPC 領域究竟哪一種方案最終可以勝出,如今還言之過早。我相信 Service Mesh 和傳統的 RPC  (Dubbo/ gRPC)  都會有本身的一席之地,一切讓時間給咱們答案吧。

「 阿里巴巴雲原生微信公衆號(ID:Alicloudnative)關注微服務、Serverless、容器、Service Mesh等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,作最懂雲原生開發者的技術公衆號。」

相關文章
相關標籤/搜索