開發與維運

Dubbo 3.0 前瞻之對接 Kubernetes 原生服務

Kubernetes 是當前全球最流行的容器服務平臺,在 Kubernetes 集群中,Dubbo 應用的部署方式往往需要藉助第三方註冊中心實現服務發現。Dubbo 與 Kubernetes 的調度體系的結合,可以讓原本需要管理兩套平臺的運維成本大大減低,而且 Dubbo 適配了 Kubernetes 原生服務也可以讓框架本身更加融入雲原生體系。基於 Dubbo 3.0 的全新應用級服務發現模型可以更容易對齊 Kubernetes 的服務模型。

Kubernetes Native Service

1.png

在 Kubernetes 中,Pod 是可以在 Kubernetes 中創建和管理的、最小的可部署的計算單元。通常一個 Pod 由一個或多個容器組成,應用則部署在容器內。

對於一組具有相同功能的 Pod,Kubernetes 通過 Service 的概念定義了這樣一組 Pod 的策略的抽象,也即是 Kubernetes Service。這些被 Kubernetes Service 標記的 Pod 一般都是通過 Label Selector 決定的。

在 Kubernetes Service 內,服務節點被稱為 Endpoint,這些 Endpoint 也就是對應提供服務的應用單元,通常一對一對應了 Pod。

因此,我們可以將微服務中的應用本身使用 Service 來進行調度,而微服務間的調用通過 Service 的一系列機制來實現服務發現,進而將微服務整合進 Kubernetes Service 的體系中。

Dubbo 應用級服務發現

在 Dubbo 體系結構內,為了更好地符合 Java 開發人員的編程習慣,Dubbo 底層以接口粒度作為註冊對象。但是這個模型對現在主流的 Spring Cloud 註冊模型和 Kubernetes Service 註冊模型有很大的區別。

目前在非 Dubbo 體系外的註冊模型主要是以服務粒度作為註冊對象,為了打通 Dubbo 與其他體系之間的註冊發現壁壘,Dubbo 在 2.7.5 版本以後引入了服務自省的架構,主要通過元數據服務實現從服務粒度到接口粒度的過渡。在 2.7.5 版本以後到 3.0 版本,服務自省模型進行了很多方面的優化,並且在生產環境下進行了驗證。

1. 元數據服務

2.png

元數據服務主要是存儲了服務(Instance)與接口(Interface)的映射關係,通過將原本的寫入到註冊中心的接口信息抽象到元數據進行存儲,一方面可以大大減少註冊中心存儲的數據量、降低服務更新時集群的網絡通信壓力,另一方面,實現了註冊中心層面只存儲應用粒度信息的目標,對齊了其他註冊模型。

在服務自省模型中,服務提供者不僅僅往註冊中心寫入當前實例的信息,還需要往(本地或者遠程的)元數據服務寫入暴露的服務 URL 信息等;而對於服務消費者,在從註冊中心獲取實例信息後,還需要(通過 RPC 請求內建或者中心化配置中心獲取)元數據服務獲取服務提供者的服務 URL 信息等來生成接口粒度體系下的接口信息。

2. Revision 信息

3.png

Revision 信息是元數據服務引入的一種數據緩存機制,對於同一組應用很多情況下暴露的接口其實都是一樣的,在進行服務(Instance)與接口(Interface)映射的時候會有許多重複的冗餘數據,因此可以使用類似對元數據信息進行 MD5 計算的方式來對實例本身加上版本號,如果多個實例的版本號一致可以認為它們的元數據信息也一致,那麼只需要隨機選擇一臺來獲取元數據信息即可,可以實現把通行量從一組實例都需要通信到只需要與一個實例通信的壓縮。

如上圖所示,服務消費者註冊中心的工作機制可以總結為:

  • 服務消費者向註冊中心獲取服務實例列表。
  • 註冊中心向服務消費者返回服務實例信息,在實例列表中包括了服務提供者向註冊中心寫入的 Revision 參數。
  • 服務消費者根據獲取到實例信息的 Revision 參數進行分組,分別從每組實例中隨機選擇一臺獲取元數據服務。
  • 服務消費者通過 RPC 發起調用或者通過配置中心獲取得到指定實例的元數據信息。
  • 服務消費者根據獲取到的元數據信息組建接口粒度的服務信息。

關於應用級服務發現更多的信息可以參考:《Dubbo 邁出雲原生重要一步 - 應用級服務發現解析
image.gif

對接實現方式

本次實現的與 Kubernetes 對接的方式有兩種,一種是通過 Kubernetes API Client 的形式獲取信息,另外一種是通過 DNS Client 的形式獲取。

1. Kubernetes API Client

Kubernetes 控制平面的核心是 API Server,API Server 提供了 HTTP API,以供用戶、集群中的不同部分和集群外部組件相互通信。對於 Dubbo 來說,通過使用 Kubernetes API Client 便可以做到與 Kubernetes 控制平面通信。

1)Provider 側細節

4.png

根據前文說到 Dubbo 應用級服務發現模型,對於 Provider 側在應用啟動、接口更新時需要向註冊中心寫入 Revision 信息,因此大致的邏輯如上圖所示。

Label 標籤作為 Selector 與 Service 進行匹配,Annotation 中則主要存儲了 Revision 等信息,其中 Revision 信息需要由 Dubbo 應用主動向 Kubernetes API Server 發起更新請求寫入,這也對應了服務註冊的流程。

在目前版本的實現中,Kubernetes Service 的創建工作是交由運維側實現的,也即是 Label Selector 是由運維側去管理的,在 Dubbo 應用啟動前就已經配置完畢了,Service 的名字也即是對應接口註解中的 Services 字段(對於不依賴任何第三方配置中心的需要在接口級別手動配置此字段)。

2)Consumer 側細節

5.png

對於 Consumer 側的邏輯大致上與應用級服務發現的模型設計的一樣,在通過 API 獲取到服務信息後通過獲取對應 Pod 的 Annotation 信息補齊 ServiceInstance  信息,後續邏輯與服務自省一致。

3)優缺點

  • 需要指出的是,讓應用本身直接與 Kubernetes 管理平臺的 API 交互本身就存在一定安全隱患,如果配置不當有一定可能性導致拖垮整個 Kubernetes 集群。
  • 當應用大量更新時會給 Kubernetes API Server 帶來一定壓力。
  • 本方案直接將 Dubbo 的服務發現過程對接到 Kubernetes 集群的管理上,可以在 Kubernetes 環境下進一步簡化管理的複雜度。

2. DNS Client

Kubernetes DNS 是 Kubernetes 提供的一種通過 DNS 查詢的方式獲取 Kubernetes Service 信息的機制,通過普通的 DNS 請求就可以獲取到服務的節點信息。

1)全去中心化的元數據服務

由於 DNS 協議本身限制,目前並沒有一個統一的較為簡單的方式向 DNS 附加更多的信息用於寫入 Revision 信息。對於 DNS 的實現方案我們將元數據服務進行了改造,使其不再強依賴往註冊中心寫入 Revision 信息,實現只需要知道 Dubbo 應用的 IP 即可以實現正常的服務發現功能。

6.png

改造後的元數據服務可以分為兩大功能,一個是基於 revision 的獲取 URL 信息等的能力,另外一個是獲取對應 revision 的能力。Dubbo 服務消費者在通過註冊中心獲取到實例 IP 後會主動去與每一個實例的元數據服務進行連接,獲取 revision 信息後,對於 revision 信息一致的實例隨機選擇一個去獲取完整的元數據信息。
由於 Revision 信息採用了點對點的方式獲取,當這個信息更新時要及時通知給消費者端進行更新。在當前版本的實現中,我們依賴了 參數回調 機制實現服務者主動推送給消費者。未來會基於 3.0 的全新 Triple 協議,實現流式推送。

2)Provider 側細節

與 Kubernetes API Client 實現方式類似的,組建服務的過程均需要由運維側進行,在服務提供者啟動的時候會進行元數據信息本地緩存、對外暴露元數據服務接口的機制。

這裡需要特別注意的是,為了使服務發現過程與業務服務本身解耦,元數據服務接口與業務接口對應的端口可以不一致,在使用 DNS 實現方案的時候全集群所有應用的元數據服務端口都需要統一, 通過 metadataServicePort 參數進行配置。這樣亦可以適配那些不支持通過 SRV 獲取端口的 DNS。

3)Consumer 側細節

7.png

對於 DNS 註冊中心來說,獲取實例的流程主要通過向 DNS 發起 A (或 AAAA)查詢請求來獲取,而 Kubernetes DNS 也提供了 SRV 記錄請求來獲取服務的信息。

結合前文說到的全去中心化的元數據服務機制,Consumer 會去主動連接獲取到的每一個實例的元數據服務,獲取對應的 Revision 信息,同時以參數回調的形式向提供者提交回調函數用於更新本地信息。

在獲取到 Revision 信息之後,對於具有相同 Revision 信息的實例,Dubbo 會隨機選擇其中一個獲取完整元數據信息,至此完成服務發現的全過程。

4)優缺點

  • 本方案與前一種方案對比起來避免了 Kubernetes API 的直接交互,避免了交互的安全問題。
  • 由於 DNS 沒有像 API Watch 的通知機制,只能採用輪詢的方式判斷服務的變更,在沒有應用變更時集群內仍有一定量的 DNS 網絡查詢壓力。
  • 本方案設計的時候目標是對任何只要能夠通過 A (或 AAAA)查詢獲取到 Dubbo 應用本身的 IP 的 DNS 都可以適配,對 Kubernetes DNS 並不是強依賴關係。

總結

本次 Dubbo 對接 Kubernetes 原生服務是 Dubbo 往雲原生化發展的一次嘗試,未來我們將基於 xDS 協議實現與 Service Mesh 控制平面的交互,相關功能正在緊鑼密鼓的籌劃中。同時,在未來 Dubbo 3.0 的發版上,Java 社區和 Dubbo-go 社區將同步發版,本次 Kubernetes 的功能也將對齊上線。

系列文章推薦:

作者簡介

江河清,Github 賬號 AlbumenJ,Apache Dubbo Committer。在讀本科生,目前主要參與 Dubbo 社區雲原生 Kubernetes 和 Service Mesh 模塊對接。

Leave a Reply

Your email address will not be published. Required fields are marked *