背景
在邊緣集羣的場景下邊緣節點分佈在不一樣的區域,且邊緣節點和雲端之間是單向網絡,邊緣節點能夠訪問雲端節點,雲端節點沒法直接訪問邊緣節點,給邊緣節點的運維帶來很大不便,若是能夠從雲端SSH登陸到邊緣節點能夠簡化節點的運維工做。針對這一需求,SuperEdge 項目擴展了 tunnel 組件的能力,新增了 SSH 模塊,讓用戶能夠從雲端 SSH 登陸到邊緣節點。node
需求分析
邊緣集羣的節點分佈在不一樣的區域,邊緣節點可以訪問雲端 master 節點,但 master 節點沒法訪問邊端區域內的節點,用戶但願經過訪問 master 節點的端口 SSH 登陸到節點實施運維工做。git
常規方案
使用 SSH 端口轉發技術能夠實現 SSH 運維邊緣節點功能,具體內容以下圖所示:github
- 邊緣節點 node-A 和 node-B 經過 SSH 的遠程轉發(ssh -R)將雲端 master-A 節點的 port-A 和 port-B 端口與本地22端口(SSH Server 的端口)綁定
- user 經過SSH的動態轉發(ssh -D)與 master-A 創建 SSH 隧道同時在本地監聽 local-port 端口
- local-port 的請求都會經過 SSH 鏈接傳到 master-A,由master-A 轉發,例如 SSH 登陸 node-A:ssh -o ProxyCommand=」nc -X 5 -x 127.0.0.1:local-port 127.0.0.1 port-A」 root@127.0.0.1 ,127.0.0.1 port-A 就是 master-A 轉發時請求的目標地址。
常規方案的缺點:
- 邊緣節點映射端口管理複雜 如圖2所示,node-A 和node-B 將本地的22端口在 master-A 上映射爲不一樣的端口,SSH 登陸目標節點須要指定其在master-A映射的端口,當邊緣節點數量不少時端口映射管理很是複雜,直接致使方案不可行。
- 請求涉及的多個鏈接,增長了出錯的機率 以 SSH 登陸 node-A爲例,如圖1所示,sshClient->local-port,user->master-A,master-A->port-A,master-A->node-A,node-A->sshServer,共計須要創建5條鏈接。
- 雲端和邊端的 SSH 維護麻煩 邊端節點和雲端節點的 SSH 鏈接,須要在邊端節點上執行創建,且鏈接不具有斷開重連的能力,維護起來比較麻煩。
tunnel 方案
架構設計
- SSH Client 請求 tunnel-cloud service 暴漏到外網的 NodePort-1 端口,service 根據負載均衡策略將請求轉發 tunnel-cloud pod-A 或 tunnel-cloud pod-B。
- 若是請求轉發到 pod-A(R.1.1),因爲 node-A 沒有和 pod-A 創建隧道,pod-A 會查詢 coredns 獲取與 node-A 創建隧道的 pod-B 的 podIp,而後將請求轉發到 pod-B(R.1.2)
- pod-B 收到請求後將請求經過隧道轉發到 node-A 的 tunnel-edge,tunnel-edge 將請求轉發到 SSH Server。
本方案的優點
- 在雲端和邊緣節點間的隧道不會映射端口,避開端口管理。
- 減小了請求過程當中的創建鏈接數,減小了出錯的機率
- 雲端和邊端的隧道具有斷開重連機制,下降維護成本
雲邊隧道的創建
使用 gRPC 開源項目搭建長鏈接隧道,gRPC 實現斷開重連機制。web
- tunnel-edge 向 tunnel-cloud 發送創建 gRPC 鏈接的請求 tunnel-edge 在向 tunnel-cloud 的 NodePort-2 發送創建鏈接的請求,請求中攜帶了所在節點的節點名和 token 信息,tunnel-cloud service 根據負載均衡策略將請求轉發到 tunnel-cloud pod,如圖3所示,將請求轉發到 tunnel-cloud pod-B
- tunnel-cloud 向 coredns 註冊本 pod 的 podIp 和 tunnel-edge 所在的節點的節點名信息 tunnel-cloud 驗證 tunnel-edge 請求信息中的 token 信息,驗證經過後,節點請求信息中的節點名和本 pod的 podIp 寫入到 coredns
- tunnel-cloud 返回 gRPC 鏈接創建成功的消息
- tunnel-edge 和 tunnel-cloud 之間經過 gRPC 長鏈接發送自定義協議消息 StreamMsg 關於自定義協議消息的字段定義請參考 一文讀懂SuperEdge雲邊隧道
tunnel-cloud 轉發的實現
-
SSH Client 請求 tunnel-cloud service 的 NodePort-1端口,發送 method 爲 Connect的http 請求 SSH Client 經過工具 netcat 或 crokscrew 發送的 method 爲 CONNECT HTTP 請求,即 ProxyCommand 的內容(ProxyCommand= "nc -X connect -x tunnel-cloudIp:NodePort-1 node-A 22" ),參數的定義以下:json
- -X: 參數爲協議的類型,這裏指定的 connect 是 HTTP CONNECT
- -x: HTTP Server 的ip地址和端口,這裏指定的 tunnel-cloudIp:NodePort-1 是 tunnel-cloud service 暴漏到外網的ip和端口
- node-A 爲邊端節點的節點名
- 22 爲邊端節點 SSH Server 的監聽的端口
tunnel-cloud service 根據負載均衡策略將 SSH Client 的請求轉發到 tunnel-cloud pod,如架構設計圖3所示若是轉發到 tunnel-cloud pod-A,tunnel-cloud 的 HTTP Server 收到的消息爲 CONNECT node-A:22 HTTP/1.0 ,其中 node-A 爲邊端節點的節點名,22爲邊端節點 SSH Server 監聽的端口,因爲node-A沒有與tunnel-cloud pod-A 創建雲邊隧道,所以 HTTP Server 會請求 coredns 獲取 node-A 節點名對應的 tunnel-cloud 的 podIp,即爲 tunnel-cloud pod-B , pod-A 會把請求轉發給 pod-Bcentos
-
tunnel-cloud 向 tunnel-edge 發送自定義協議消息(StreamMsg.Type 爲 connecting) tunnel-cloud 會根據 HTTP CONNECT 的請求信息中獲取雲端和邊端節點的隧道,並經過雲邊隧道向 tunnel-edge 發送自定義協議消息用於與 SSH Server 創建 TCP 鏈接,類型爲 connectingapi
-
tunnel-edge 向 SSH Server 發送創建 TCP 鏈接的消息 tunnel-edge 在接收到 connecting 類型的自定義協議消息以後根據消息中 SSH Server 的ip和 port 發送創建TCP鏈接的請求網絡
-
SSH Server 返回 TCP 鏈接創建成功的消息給 tunnel-edge架構
-
Tunnel-edge 返回自定義協議消息(StreamMsg.Type 爲 conneted)給 tunnel-cloud tunnel-edge 在收到鏈接創建成功的消息後向 tunnel-cloud 發送一個 TCP 鏈接已創建的類型 connected 的自定義協議的消息負載均衡
-
tunnel-cloud 返回 SSH Client 狀態爲 200 的 reponse 消息 tunnel-cloud 在接收到 connected 的自定協議消息後會 SSH Client 返回一個狀態碼爲 200的消息: HTTP/1.1 200 Connection established
-
SSH Client 向 tunnel-cloud 發送 SSH 協議的消息 SSH Client在接收到狀態碼爲200的響應消息後,使用和 tunnel-cloud 已經創建的隧道發送 SSH 協議數據
-
tunnel-cloud 將 SSH 協議的消息封裝爲自定義協議消息(StreamMsg.Type 爲 content)發送給 tunel-edge
-
tunnel-edge 將 SSH 協議消息經過TCP鏈接發送給 SSH Server
-
SSH Server 將 SSH 協議消息發送給 tunnel-edge
-
tunnel-edge 將 SSH 協議消息封裝爲 content 類型的自定義消息發送給 tunnel-cloud
-
tunnel-cloud 將 SSH 協議消息返回給 SSH Client
SSH登陸節點
前置條件
安裝 corkscrew,Mac 直接使用 brew 安裝
brew install corkscrew
或者,安裝netcat(centos)
yum install -y netcat
SSH 登陸節點
SSH 登陸邊緣節點 node-A-1,可使用下面的命令:
ssh -o ProxyCommand= "corkscrew masterIP cloud-port node-A-1 22" root@127.0.0.1
或者
ssh -o ProxyCommand= "nc -X connect -x masterIP:cloud-port node-A-1 22" root@127.0.0.1
- materIP: master 節點所在節點的外網ip
- cloud-port: NodePort 端口,對應的 SSH 模塊的 Server 的端口 獲取 cloud-port
kubectl -n edge-system get svc tunnel-cloud -o=jsonpath='{range .spec.ports[*]}{.name}{"\t"}{.nodePort}{"\n"}{end}' | grep ssh | awk '{print $2}'
總結
使用該方案,用戶無需手動搭建,能夠快速SSH登陸邊端節點,實施運維工做。同時本方案提供的隧道相對傳統方案更便於維護且提升了穩定性,用戶體驗獲得提高。
將來展望
支持從雲端統一SSH 運維邊緣節點是tunnel組件的加強功能,也是對 一文讀懂SuperEdge雲邊隧道 的展望的實現,SuperEdge 開源以後社區小夥伴也對 tunnel 組件提了新的需求,大體以下:
- 支持雲端 apiserver 訪問邊緣端的webhook server
- 支持服務之間的跨區域互訪
合做和開源
雲邊隧道的SSH運維邊緣節點的新特性已經在 SuperEdge release 0.4.0 開源,歡迎你們體驗。咱們也會持續提高 Tunnel 的能力,適用更加複雜的邊緣網絡場景,也歡迎對邊緣計算感興趣的公司、組織及我的一塊兒共建 SuperEdge 邊緣容器項目。
<img src="https://main.qcloudimg.com/raw/ded3f46433da0f1727caf3630effadf7.png" style="zoom:50%;" /> >【騰訊雲原生】雲說新品、雲研新術、雲遊新活、雲賞資訊,掃碼關注同名公衆號,及時獲取更多幹貨!! ![](/img/bVbLtIB)