導讀node
隨着業務的增加,一些傳統企業對諸如灰度發佈、服務路由、服務熔斷、服務限流等服務治理的需求愈來愈強烈,但他們又不想對業務代碼作大量的改造,於是 Service Mesh 成了他們比較好的選擇;不幸的是業內比較成熟能落地的 Service Mesh 方案如 Istio 都是基於各類容器平臺構建的,而這些傳統企業不少沒有接入容器平臺也不想作容器化改造,這就致使 Service mesh 很難應用於這些傳統企業或者一些非容器化的場景。git
做者介紹
github
張培培web
騰訊雲微服務團隊高級工程師shell
TSF Mesh 研發及負責人bootstrap
熱衷於雲原生和開源技術,在容器、Service Mesh、消息隊列、區塊鏈等領域擁有豐富經驗,目前致力於Service Mesh 技術的落地和推廣ubuntu
前言api
爲了兼容 Service mesh 的非容器化場景,TSF Mesh 基於 Istio 構建了 Service mesh 微服務平臺,對原生 Istio 實現進行了適當的改造,支持應用同時運行於容器環境和虛擬機環境(同時也打通了 Spring Cloud 框架,實現 Mesh 服務和 Spring Cloud 服務互聯互通,服務統一治理)。微信
TSF Mesh 對容器和虛擬機統一化的改造主要體如今如下幾個方面:網絡
-
應用部署和Sidecar注入 -
流量劫持 -
服務註冊與發現
針對這幾點,下面會先剖析對比 Istio service mesh 基於 K8s 的實現方案,再講述 TSF Mesh 是如何實現的,作了哪些改造。
應用部署和 Sidecar 注入
首先,回顧下 Istio service mesh 的應用部署和 Sidecar 注入方式:
應用部署:Istio service mesh 依賴 K8s 進行應用的生命週期管理,包括應用的部署和管理(擴縮容、自動恢復、發佈)
Sidecar 注入:分爲手動注入和自動注入, 以下圖所示:
手工注入經過手工執行 istioctl kube-inject 來從新構造應用的 CRD yaml
自動注入經過 K8s 的 mutable webhook 回調 istio-sidecar-injector 服務來從新構造應用的 CRD yaml
不管是手工注入仍是自動注入,Sidecar 注入的本質是將運行 Sidecar 所須要的鏡像地址、啓動參數、所鏈接的 Istio 集羣(Pilot、Mixes、Citadel)及配置信息填充到注入模版,並添加到應用的 CRD yaml 中,最終經過 K8s 持久化資源並拉起應用和 Sidecar 的 POD。
那 TSF Mesh 如何作應用部署和 Sidecar 注入的呢?
因爲 TSF Mesh 須要同時支持容器和虛擬機環境,則首先須要解決虛擬機部署的問題,要實現等同 K8s 的部署能力,須要解決如下幾個問題:
-
資源和配置管理,如 Istio 集羣信息、配置信息等 -
對應於容器的鏡像,虛擬機就是程序包,那就涉及到包管理 -
虛擬機應用生命週期的管理 -
虛擬機 Sidecar 注入
爲了解決容器和虛擬機統一部署問題,不能再用 K8s 的存儲方式,而是須要更高層的管理模式,咱們引入了 tsf-resource 資源管控模塊來負責容器和虛擬機相關資源的統一管理,像 Istio 集羣相關的信息在控制平臺部署時會持久化在 TSF 的 DB 中。
對於容器平臺,當用戶從 TSF 控制檯部署一個容器應用時,tsf-resource 從 DB 中獲取像容器的鏡像地址、Istio 集羣信息、配置、啓動參數等,進行 K8s CRD 的組裝,組裝完將 CRD 建立請求發送給容器平臺完成應用 POD 的拉起,其實這裏在組裝 CRD 時已經實現了 Sidecar 的自動注入,注入時的動態參數由控制檯傳遞,靜態參數如 Sidecar 鏡像地址、啓動參數等從 DB 中獲取。
對於虛擬機平臺,TSF 引入瞭如下幾個模塊來解決程序包管理和應用部署的問題:
-
tsf-repo,程序包倉庫管理,存儲應用程序包及相關依賴 -
tsf-master,虛擬機節點管理 master,發送部署/下線/啓動/中止等任務給 tsf-agent -
tsf-agent,虛擬機節點管理 agent,部署在應用機器上,負責初始化機器環境、執行應用部署/下線/啓動/中止等任務
對於虛擬機應用的變動,如例如應用部署、啓動、中止、下線,TSF 經過任務的方式來跟蹤每一個變動,在任務下發的具體流程中,全部任務都是異步執行的,tsf-resource 將任務轉發給 tsf-master 後就返回給 TSF 控制檯,並由 tsf-master 完成任務的下發和狀態跟蹤;用戶在 TSF 控制檯執行操做後,能夠根據返回的任務 ID 查詢執行結果。
流量劫持
Service mesh 須要透明的進行服務治理,也就須要透明的接管服務進出流量,將流量劫持到 Sidecar,由 Sidecar 進行流量管理,傳統的方式是 iptables 流量劫持(也可採用 BPF、IPVS 等方式),一樣下面先回顧下 Istio 的 Service mesh 方案具體是如何劫持流量的,而後再看下 TSF mesh 爲了統一容器和虛擬機作了哪些改造。
查看通過 Sidecar 注入後的應用 YAML 文件,發現 istio-sidecar-injector 服務在注入 Sidecar 容器自己時,還注入了 istio-init 容器,istio-init 容器屬於 init 容器(init 容器在應用程序容器啓動以前運行,用來初始化一些應用鏡像中不存在的實用工具或安裝腳本),下面是官方例子中注入的 init 容器部分:
initContainers: args: -p "15001" -u "1337" -m REDIRECT -i '*' -x "" -b 9080, -d "" image: istio/istio-release-proxy_init:1.0.1 imagePullPolicy: IfNotPresent name: istio-init resources: {} securityContext: capabilities: add: NET_ADMIN privileged: true ...
能夠看出 init 容器 istio-init,被賦予了 NET_ADMIN 的 POD 網絡空間權限,具體執行了哪些初始化還看不出來,那再來看下 istio/istio-release-proxy_init:1.0.1 鏡像的 Dockerfile。
FROM ubuntu:xenialRUN apt-get update && apt-get install -y \ iproute2 \ iptables \ && rm -rf /var/lib/apt/lists/*ADD istio-iptables.sh /usr/local/bin/ENTRYPOINT ["/usr/local/bin/istio-iptables.sh"]
istio-init 容器的 ENTRYPOINT 是 /usr/local/bin/istio-iptables.sh 腳本,顧名思義用於 Istio iptables 流量劫持的腳本,組合上面 istio-init 容器的啓動參數,完整命令爲:
local/bin/istio-iptables.sh -p 15001 -u 1337 -m REDIRECT -i '*' -x "" -b 9080 -d "" /usr/
該命令的主要做用是,將應用容器中訪問9080端口的流量(inbound 流量)和全部出站流量(outbound 流量)重定向到 Sidecar(即 envoy)的15001端口。
總結下來,Istio 是經過 init 容器完成了流量劫持到 Sidecar 的初始化工做。
TSF Mesh 如何實現流量劫持的呢?
TSF Mesh 一樣採用 iptables 方式,不過要兼顧虛擬機平臺,須要解決兩個主要問題:
-
虛擬機下如何執行 iptables 應用劫持策略 -
虛擬機下如何劫持流量,不能劫持虛擬機整個網絡空間的流量
問題1的解決比較簡單,咱們對 pilot-agent 作些一些擴展,在 pilot-agent 中執行 iptables 腳本,pilot-agent 一個主要工做是生成 envoy 的 bootstrap 配置並啓動 envoy、管理 envoy 的生命週期,相似容器環境下作 envoy 啓動前的 init 準備,在啓動 envoy 前執行 iptables 腳本,也比較合理。
問題2的解決就比較麻煩了,但又很是重要,不像 K8s 的 POD,POD 間網路是隔離的,一個 POD 通常只會運行一個應用,劫持整個 POD 網路空間裏的流量徹底沒有問題,而虛擬機中可能還有其它進程的存在,這些進程可能也有 Outbound 的流量,所以咱們不能劫持虛擬機全部的流量,一種比較合理的劫持方案應該是:
-
對於 Inbound 流量,只劫持到部署應用的端口,這個原生 Istio 已經作到,無需改造 -
對於 Outbound 流量,只劫持註冊中心已註冊服務的流量
下面來具體講下 TSF Mesh 如何針對服務來劫持 Outbound 流量的。
其實咱們的方案和 K8s 的 kube-DNS+kube-proxy 的服務發現機制相似,TSF Mesh 在數據平面引入了一個 mesh-dns 模塊,經過鏈接 pilot-discovery 同步獲取註冊中心的服務變動來更新本地的 DNS cache,對於來自注冊中心的服務會被解析到一個特定的 IP,而後在 iptables 策略中把目的地址到這個特定 IP 的流量重定向 envoy,固然,還須要劫持 DNS 53 端口的流量,先把 DNS 請求引到 mesh-dns,能夠看下 iptables nat 表中完整的規則內容:
Inbound 流量劫持跟原生 Istio 實現相似就不贅述了,下圖顯示的是 Outbound 流量 iptables 劫持的詳細過程,其中紅色部分爲新增的 DNS 劫持規則。
註冊服務的域名劫持,除了引入了 mesh-dns 自研模塊,還涉及到 pilot-discovery 和 pilot-agent 的改造:
pilot-discovery 改造點
-
pilot-discovery 擴展一個 ServiceInfos 的 grpc 服務,提供註冊服務變動同步接口 -
pilot-discovery 早期的 consul controller 實現是,定時經過 Consul 的 Rest API 獲取服務數據並和上一次的查詢結果進行對比,若是數據發生了變化則通知 Pilot discovery 進行更新,這裏咱們進行了優化,採用 Consul 的 watch 機制來代替輪詢(下面服務註冊與發現中也有提到),並在 ServiceInfos 服務初始化時向 consul controller 註冊了服務變動的 event 通知 -
ServiceInfos 服務在 mesh-dns 請求第一次到來時同步全量的服務註冊表,以後則根據服務的變動狀況增量同步
mesh-dns實現
-
DNS 服務基於 github.com/miekg/dns 實現(一個很是輕量級的 DNS 庫) -
和 pilot-discovery 保持註冊服務列表的同步,mesh-dns 啓動時進行全量同步,運行時進行增量同步 -
處理 DNS 請求時,先檢查 Domain 是否在註冊服務列表裏,若是在則返回一個特定的 IP(可配置),不然請求本地配置的域名服務進行解析
pilot-agent 改造點
-
相似對 envoy 的管理,pilot-agent 擴展了 mesh-dns 的支持,負責了 mesh-dns 啓動配置組裝、啓動 mesh-dns 及 mesh-dns 生命週期的管理
服務註冊與發現
基於流量比例的路由 Istio 中負責流量管理的組件爲 Pilot,其中服務發現也是經過 Pilot 完成,Pilot 的高層架構圖以下所示:
Istio服務註冊與發現的原理
-
Istio 服務註冊:Istio 架構中自己不提供服務註冊的能力,而是依賴於各類底層平臺,底層平臺是具體服務信息的生產者和維護者,如 Kubernetes 在 POD 部署時會保存 Service 以及對應的 Pod 實例信息。 -
Istio服務發現:Istio 是服務信息的消費者,服務發現是經過 Pilot 組件實現的,Pilot 組件負責維護網格中的標準服務模型,該標準服務模型獨立於各類底層平臺,Pilot 組件經過適配器和各底層平臺對接,以使用底層平臺中的服務數據填充此標準模型,再經過標準 xDS 協議(CDS 集羣信息和 EDS 實例信息)同步給 envoy。
TSF Mesh如何實現服務註冊與發現的呢?
一樣,TSF Mesh 要兼顧虛擬機平臺,須要解決三個主要問題:
-
註冊中心如何選擇 -
服務如何註冊 -
實例健康狀態如何維護
問題1: TSF Mesh 容器和虛擬機統一採用 Consul 做爲註冊中心,由於虛擬機部署不會依賴容器平臺,所以服務註冊發現也不能依賴容器平臺;Pilot 原生實現是支持 Consul 的,但早期的實現比較雞肋(1.4.0之前仍是經過 Rest API 輪詢的方式去獲取 Consul 註冊服務信息的變動),TSF Mesh 針對早期的版本也作了優化,採用 Consul watch 來代替輪詢。
問題2: TSF Mesh 統一了容器和虛擬機的服務註冊方式,服務註冊都在 envoy 中完成 ,
1. 相似經過 K8s 部署服務同樣,TSF Mesh 在部署時須要用戶在應用程序所在目錄中建立一個 spec.yaml 服務描述文件,spec.yaml 格式以下:
apiVersion: v1kind: Applicationspec: services: name: user # 服務名 ports: targetPort: 8091 # 服務監聽端口 protocol: http # 服務協議類型 healthCheck: path: /health # 健康檢查 URL
2. Pilot-agent 將 spec.yaml 文件中內容讀出並填充到 envoy-rev0.yaml(envoy啓動時的靜態配置文件)文件的 node 信息中;
3. Pilot-agent 啓動 envoy,envoy 自己是支持 HDS 的,TSF Mesh 改造了 envoy 代碼默認把本地部署的服務 Endpoint 做爲 HealthCheck 請求的 cluster,在啓動時把請求發送給 Pilot-discovery;
4. Pilot-discovery 原生是不支持 HDS 控制的,TSF Mesh 擴展 Pilot-discovery 以支持 HDS 服務端來接收 envoy 的 HealthCheck 請求,因爲 HDS 定義的請求數據結構裏包含 node 信息,也就包含了上面的服務描述信息,Pilot-discovery 組裝服務描述信息將服務註冊到 consul;
5. Pilot-discovery 是註冊中心服務信息的消費者,所以原生是不支持服務註冊的,TSF Mesh 再次擴展了 Pilot-discovery,在 Consul Apater 中增長了 RegisterService 接口;
問題3:TSF Mesh 中服務的實例健康狀態也是由 envoy 來維護的:
-
envoy 在收到 pilot-discovery 的 HealthCheckSpecifier 迴應後,會根據迴應中的參數如 check 間隔、check 的實例(這裏就是本地服務實例)、check 的 Path(這裏就是 spec.yaml 中的 healthCheck path)等異步執行本地實例的 Health Check,根據 check 的結果更新本地實例的健康狀態; -
HDS 除了支持 HealthCheckRequest 請求,還支持 EndpointHealthResponse 請求,envoy 根據當前實例的健康狀態經過 EndpointHealthResponse 週期性同步給 pilot-discovery; -
TSF Mesh 徹底擴展了 Pilot-discovery 的 HDS 服務,支持對 EndpointHealthResponse 請求的處理,根據請求中實例的健康狀態進行 TTL 上報; -
TSF Mesh 再次擴展了 Pilot-discovery,在 Consul Apater 中增長了 ReportHealthCheckInfo 接口以支持服務治理的 TTL 上報
總結
TSF Mesh 在深刻理解了 Istio service mesh 方案的基礎上對其進行了針對性的改造和優化,使得應用能同時運行於容器環境和非容器環境,經過抽象出更高層的管控平臺,TSF Mesh 能夠不依賴於具體的底層平臺而對應用進行統一管理和控制;而 TSF Mesh 做爲 TSF 的一種微服務框架實現,不只僅解決了平臺統一化問題,還提供了應用全生命週期管理、數據化運營、立體化監控和服務治理的整套解決方案,具體介紹和使用可參考 TSF Mesh 官網。
參考連接:
- ServiceMesher 社區:
https://www.servicemesher.com/
- TSF Mesh 微服務平臺:
https://cloud.tencent.com/product/tsf-mesh
- Istio 服務註冊插件機制代碼解析:
https://www.servicemesher.com/blog/istio-pilot-service-registry-code-analysis/
往期
推薦
《原生應用 「0」 代碼改造,無侵入接入,縱享絲滑般上雲體驗!》
《200 行代碼告訴你 TDMQ 中 Pulsar 廣播如何實現》
掃描下方二維碼關注本公衆號,
瞭解更多微服務、消息隊列的相關信息!
解鎖超多鵝廠周邊!
本文分享自微信公衆號 - 騰訊雲中間件(gh_6ea1bc2dd5fd)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。