分享一個用 go 開發的一款跨雲 vpc 通訊專案

yingjiu.hulu發表於2021-08-08

cframe 介紹

cframe 是我個人利用業務時間開發的跨雲 vpc 通訊專案,如果要用一句話總結,那可以理解為: 使用 cframe 能夠讓任意不衝突的網路進行互聯互通。

下面展示的是使用 cframe 將六個不衝突的網路進行打通,只要我身處任何一個網路當中,都可以通過內網 IP 訪問到其他網路的主機。

➜  ~ cfctl edge list
edge list:
      Name            Listener                  CIDR
-----------------------------------------------------------
1     edge-gcl-tc     x.x.x.x:58423       10.0.8.6
2     edge-sz-branch  x.x.x.x:48423       10.60.6.0/24
3     edge-sz-home    x.x.x.x:58424       192.168.31.0/24
4     edge-us-aws     x.x.x.x:58423       172.31.39.169
5     edge-aliyun-hk  x.x.x.x:58423       172.17.8.10
6     edge-aliyun-sz  x.x.x.x:58423       172.18.0.0/16

關於 cframe 的程式碼可以檢視cframe 的專案主頁。目前整個系統的程式碼也非常少,只有兩千多行程式碼,只需要半天時間即可閱讀完。

本文首先介紹 cframe 以及 cframe 的整體框架,然後針對整體框架當中的每個角色進行逐步細化,並從如何實現的,為什麼這樣實現進行闡述。

整體框架

正如上圖所示,cframe 包含三個角色以及一個開源元件(etcd)

  • cfctl - 一個 cli 工具,給使用者進行系統管理的
  • controller - 全域性控制器
  • edge - 邊緣節點
  • etcd - 儲存系統

首先是 cfctl 工具,用來管理namespaceedge以及路由,在最初的版本當中,並沒有考慮設計這個工具,當時設計的是一個apiserver的服務,通過 http 介面的方式管理叢集,甚至還希望能夠在此之上做一個控制皮膚。但是後面版本當中並沒有繼續採用這種方案,主要原因是: apiserver 的方式固然可以讓使用者更加方便,但是畢竟不是我所擅長的領域,目前階段不願意過多的投入,倒不如集中力量把最核心的功能做好,當然如果有人願意參與進來的話我也是非常歡迎的。

其次是 controller,controller 是為了方便給使用者一個入口的,試想一下,如果沒有 controller,那麼新增 edge 節點時,就需要在每個 edge 節點上加網路配置,一方面容易出錯,另外一方面需要對網路比較熟悉,設計了 controller 之後,controller 就是一個統一的入口,與所有 edge 互動,下發變更,edge 程式自動執行變更操作。

接下來是 edge 節點,edge 節點的功能只有一個路由轉發。每個 edge 節點最初只設計包含一個 VPC 網路,也就是如果有三個 edge 節點,那麼整個系統就只有三個網路,但是後來發現,像部署在公有云上的 k8s 叢集,VPC 有一個網路,service 有一個網路,pod 還有一個網路,那麼也就是說一個 edge 下面掛了三個網路,因此除了 edge 節點自身的網路之外,還設計了全域性路由功能,在 controller 上新增路由之後,這個 edge 的就會新增一個網路,其他 edge 的發往這個網路的資料就會被該 edge 包接收,詳細設計在後續章節會有討論。

最後是 etcd,最初的版本也沒有考慮使用儲存系統,資料都儲存在記憶體當中,但是會存在多節點資料不一致的問題,而且還需要變更配置,為了簡化互動過程,最後索性用了 etcd。

瞭解了整體框架之後,那麼接下來會實現細節進行詳細闡述。

租戶隔離

租戶隔離 cframe 採用的是 namespace 的概念,每個 namespace 下的 edge,路由不會相互影響,從而實現隔離。正是因為設計了 namespace,所以在 cfctl 當中,每個命令基本上都需要帶--ns的引數指定 namespace 名稱。

最初的設計是按使用者進行隔離,但是詳細考慮了下,需要做一個使用者系統,顯然這個不是我目前所希望進行的,這裡的 namespace 應該和程式語言當中的 namespace 的概念和作用類似。

當然現在的設計也有問題,不允許不同的使用者有相同的 namespace,但是針對開源專案而言我覺得這個是可以接受的,如果是打磨成一款產品,這裡可能還需要琢磨下。

etcd 儲存設計

在 etcd 當中主要儲存以下三種資訊:

  • edge 節點的資訊
  • 路由資訊
  • namespace 資訊

其目錄設計如下:

/edges/$namespace/$edgename

/routes/$namespace/$routename

/namespaces/$namespace_name

除了對上述 key 進行增刪查之外,還需要實時 watch /edges/*/routes/*的變化,當發生變更時,及時下發到所有執行中的 edge 節點當中,使配置生效。

controller 和 edge 的設計

controller 和 edge 是最核心的部分,最核心的地方我希望保持最簡單,只做最核心的一件事,controller 和 edge 無疑保持了我這樣的想法。

controller 只做路由下發,edge 只做路由,兩者配合,前者給菜譜,後者做菜。

controller

controller 的工作非常的存粹,負責與客戶端保持長連線,並在 etcd 的/edges/*/routes/*兩個目錄發生變更時,通過長連線通知 edge 節點。

edge

edge 則相對要多一點設計,edge 維護與 controller 的連線,以及三種路由關係,這三種路由分別是:

  • VPC 路由,目的是讓本 VPC 當中其他雲伺服器的流量發往 edge 所在的例項。
  • 系統靜態路由,目的是讓在本機流轉的資料包能夠被 edge 應用程式所劫持
  • 程式內部維護的路由,目的是知道下一跳 edge 的公網 IP 和埠

VPC 路由有過一個坎坷的經歷,最初是設計了 VPC 路由,後面又移除,現在還在考慮是否需要加入。

VPC 路由是可以由運維人員在公有云控制皮膚上配置的,這樣子就不需要 VPC 路由,但是又會稍顯麻煩,加入 VPC 路由的話,則需要新增公有云的 accesstoken 資訊,這又是一個比較危險的操作,最終擱置。

controller 與 edge 互動

controller 與 edge 之間的互動方式是 tcp 長連線,在以下幾個時機會觸發配置更新:

  • 剛建立連線時,controller 會把當前所有 edge 節點以及路由資訊返回給 edge
  • 當 etcd 當中儲存的 edge,路由資訊變更時,會主動推響應的命令以及資料給 edge 節點
  • 當 edge 節點被刪除時,會給刪除的 edge 節點傳送 exit 指令。

除此之外 edge 會定時上報一些資料給 controller,當然這部分資料目前還沒用到,也沒有儲存,但是以後如果需要新增的話彼時 controller 將不會那麼存粹。

路由設計

為了實現一個 vpc 當中存在多個網路的問題,設計了路由模組,允許自定義路由。

在路由模組出現之前,只有 edge 模組,edge 模組關聯一個 cidr,這個通常是 VPC 的地址段,只有一個,但是後面為了應付容器,k8s 元件構建的網路,因此設計了自定義路由模組,允許往該 edge 節點下掛多個子網。

在上圖中,三個 edge 節點下分別有其他子網,這一功能是通過自定義路由模組來實現的。

當前不足之處

整個 cframe 目前也僅僅只是一個可用的版本,在資料安全,鏈路監控,安全性檢查方面都沒有做太多的考慮,當前 cframe 還有很多需要打磨的地方,程式碼也還有很多需要不斷優化的地方,也還有許多場景需要測試驗證。

更多原創文章乾貨分享,請關注公眾號
  • 分享一個用 go 開發的一款跨雲 vpc 通訊專案
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章