dubbo-go K8s 註冊中心的設計方案與實現

隨着雲原生的推廣,越來越多的公司或組織將服務容器化,並將容器化後的服務部署在 K8s 集羣中。

今天這篇文章將會介紹 dubbo-go 將 K8s 作爲服務註冊中心的方案設計,以及具體實現。到目前爲止該方案的實現已經被合併到 dubbo-go 的 master 分支。具體實現爲關於 Kubernetes 的 PullRequest 

Kubernetes的PullRequest:

https://github.com/apache/dubbo-go/pull/400

K8s管理資源的哲學


K8s作爲容器集羣化管理方案可以將管理資源的維度可主觀的分爲服務實例管理和服務接入管理。

1、服務實例管理,主要體現方式爲 Pod 設計模式加控制器模式。控制器保證具有特定標籤(Label)的 Pod 保持在恆定的數量(多刪,少補)。

2、服務接入管理,主要爲 Service ,該 Service 默認爲具有特定標籤(Label)的一批 Pod 提供一個 VIP(ClusterIP)作爲服務的接入點,默認會按照 round-robin 的負載均衡策略將請求轉發到真正提供服務的 Pod 。並且 CoreDNS 爲該 Service 提供集羣內唯一的域名。

K8s服務發現模型


爲了明確 K8s 在服務接入管理提供的解決方案,我們以 kube-apiserver 提供的 API(HTTPS) 服務爲例。K8s 集羣爲該服務分配了一個集羣內有效的 ClusterIP ,並通過 CoreDNS 爲其分配了唯一的域名 kubernetes 。如果集羣內的 Pod 需要訪問該服務時直接通過 https://kubernetes:443 即可完成。

具體流程如上圖所示(紅色爲客戶端,綠色爲 kube-apiserver ):

1、首先客戶端通過 CoreDNS 解析域名爲 Kubernetes 的服務獲得對應的 ClusterIP 爲 10.96.0.1 。

2、客戶端向 10.96.0.1 發起 HTTPS 請求。

3、HTTPS 之下的 TCP 連接被 kube-proxy 創建的 iptables 的 PREROUTING 鏈攔截並 DNAT 爲 10.0.2.16 或 10.0.2.15 。

4、Client 與最終提供服務的 Pod 建立連接並交互。

由此可見,K8s 提供的服務發現爲域名解析級別。

Dubbo服務發現模型


同樣爲了明確 Dubbo 服務發現的模型,以一個簡單的 Dubbo-Consumer 發現並訪問 Provider 的具體流程爲例。

具體流程如上圖所示:

1、Provider 將本進程的元數據註冊到 Registry 中,包括 IP,Port,以及服務名稱等。

2、Consumer 通過 Registry 獲取 Provider 的接入信息,直接發起請求。

由此可見,Dubbo 當前的服務發現模型是針對 Endpoint 級別的,並且註冊的信息不只 IP 和端口還包括其他的一些元數據。

K8s service vs dubbo-go 服務


過上述兩個小節,答案基本已經比較清晰了。總結一下,無法直接使用 K8s 的服務發現模型的原因主要爲以下幾點:

1、K8s 的 Service 標準的資源對象具有的服務描述字段 中並未提供完整的 Dubbo 進程元數據字段因此,無法直接使用該標準對象進行服務註冊與發現。

2、dubbo-go 的服務註冊是基於每個進程的,每個 Dubbo 進程均需進行獨立的註冊。

3、K8s 的 Service 默認爲服務創建 VIP ,提供 round-robin 的負載策略也與 Dubbo-go 自有的 Cluster 模塊的負載策略形成了衝突。

Dubbo-go 當前的方案


服務註冊

K8s 基於 Service 對象實現服務註冊/發現。可是 dubbo 現有方案爲每個 dubbo-go 進程獨立註冊,因此 dubbo-go 選擇將該進程具有的獨有的元數據寫入運行該 dubbo-go 進程的 Pod 在 K8s 中的 Pod 資源對象的描述信息中。每個運行 dubbo 進程的  Pod 將本進程的元數據寫入 Pod 的 Annotations 字段。爲了避免與其他使用 Annotations 字段的 Operator 或者其他類型的控制器(istio)的字段衝突。 dubbo-go 使用 Key 爲 dubbo.io/annotation  value 爲具體存儲的 K/V 對的數組的 json 編碼後的 base64 編碼。

樣例爲:

apiVersion: v1
kind: Pod
metadata:
  annotations:
    dubbo.io/annotation: W3siayI6Ii9kdWJibyIsInYiOiIifSx7ImsiOiIvZHViYm8vY29tLmlrdXJlbnRvLnVzZXIuVXNlclByb3ZpZGVyIiwidiI6IiJ9LHsiayI6Ii9kdWJiby9jb20uaWt1cmVudG8udXNlci5Vc2VyUHJvdmlkZXIvY29uc3VtZXJzIiwidiI6IiJ9LHsiayI6Ii9kdWJibyIsInYiOiIifSx7ImsiOiIvZHViYm8vY29tLmlrdXJlbnRvLnVzZXIuVXNlclByb3ZpZGVyIiwidiI6IiJ9LHsiayI6Ii9kdWJiby9jb20uaWt1cmVudG8udXNlci5Vc2VyUHJvdmlkZXIvcHJvdmlkZXJzIiwidiI6IiJ9LHsiayI6Ii9kdWJiby9jb20uaWt1cmVudG8udXNlci5Vc2VyUHJvdmlkZXIvY29uc3VtZXJzL2NvbnN1bWVyJTNBJTJGJTJGMTcyLjE3LjAuOCUyRlVzZXJQcm92aWRlciUzRmNhdGVnb3J5JTNEY29uc3VtZXJzJTI2ZHViYm8lM0RkdWJib2dvLWNvbnN1bWVyLTIuNi4wJTI2cHJvdG9jb2wlM0RkdWJibyIsInYiOiIifV0=

服務發現

依賴 kube-apiserver 提供了 WATCH 的功能。可以觀察特定 namespace 內各 Pod 對象的變化。dubbo-go 爲了避免 dubbo-go 進程 WATCH 到與 dubbo-go 進程無關的 Pod 的變化, dubbo-go 將 WATCH 的條件限制在當前 Pod 所在的 namespace ,以及僅 WATCH 具有 Key 爲 dubbo.io/label Value 爲 dubbo.io-value 的 Pod 。在 WATCH 到對應 Pod 的變化後實時更新本地 Cache ,並通過 Registry 提供的Subscribe接口通知建立在註冊中心之上的服務集羣管理其他模塊。

總體設計圖

具體流程如上圖所示:

1、啓動 dubbo-go 的 Deployment 或其他類型控制器使用 K8s Downward-Api 將本 Pod 所在 namespace 通過環境變量的形式注入 dubbo-go 進程。

2、Consumer/Provider 進程所在的Pod啓動後通過環境變量獲得當前的 namespace 以及該 Pod 名稱, 調用 kube-apiserver PATCH 功能爲本Pod添加 Key 爲dubbo.io/label Value爲 dubbo.io-value 的 label。

3、Consumer/Provider 進程所在的 Pod 啓動後調用 kube-apiserver 將本進程的元數據通過 PATCH 接口寫入當前 Pod 的 Annotations 字段。

4、Consumer 進程通過 kube-apiserver LIST 當前 namespace 下其他具有同樣標籤的 Pod ,並解碼對應的 Annotations 字段獲取 Provider 的信息。

5、Consumer 進程通過 kube-apiserver WATCH 當前 namespace 下其他具有同樣 label 的 Pod 的 Annotations 的字段變化,動態更新本地 Cache 。

總結


K8s 已經爲其承載的服務提供了一套服務發現,服務註冊,以及服務集羣管理機制。而  dubbo-go 的同時也擁有自成體系的服務集羣管理。這兩個功能點形成了衝突,在無法調諧兩者的情況, dubbo-go 團隊決定保持 dubbo 自有的服務集羣管理系,而選擇性的放棄了 Service 功能,將元數據直接寫入到 Pod 對象的 Annotations 中。

當然這只是 dubbo-go 在將 K8s 作爲服務註冊中心的方案之一,後續社區會以更加“雲原生”的形式對接 K8s ,讓我們拭目以待吧。

dubbo-go 社區釘釘羣 :23331795 ,歡迎你的加入。

作者信息:

王翔,GithubID: sxllwx,就職於成都達闥科技有限公司,golang開發工程師。

本文縮略圖:icon by 一隻徐小花

Tips:

# 點下“看”❤️

# 然後,公衆號對話框內發送“消息隊列”,試試手氣?????

# 本期獎品是阿里雲定製版檯曆 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章