目前不少企業仍是採用基於 SDK 的傳統微服務框架進行服務治理,而隨着 Service Mesh 的普及,愈來愈多的企業開始佈局本身的 Service Mesh 框架體系,但多數企業剛開始不會激進地將全部業務遷移至 Serivice Mesh,像 Java 系應用依然保留原框架,而非 Java 系應用採用 Mesh 框架,不一樣開發語言能夠用不一樣的技術框架,但業務不能被框架割裂,那在兩種架構體系下應用服務如何互聯互通?微服務如何統一治理?傳統微服務又如何平滑遷移至 Service Mesh 呢?前端
騰訊爲了解決以上的技術問題,自研了 TSF Mesh 微服務框架。也許有人會問,開源 Istio 已是比較完善的 Service Mesh 方案了,爲何要再造一個 Mesh 微服務框架?和原生 Istio 什麼區別呢?web
QCon 大會講師張培培來自騰訊雲微服務產品團隊,在這裏分享 TSF Mesh 有哪些不一樣點?解決了什麼樣的問題?算法
雲原生的概念在 2013 年被首次說起,在 2015 年又被 CNCF 從新定義,這兩年之因此這麼火,主要仍是雲原生的技術可以幫助技術團隊構建一個容錯性強、易於管理和便於觀察的鬆耦合系統。Service Mesh 做爲雲原生 SaaS 層的技術表明,與傳統基於 SDK 的微服務框架相比,在提供更加完善的服務治理能力的同時,其無侵入鬆耦合的 Sidecar 接入方式是不少企業開始將微服務架構遷移到 Service Mesh 的一個重要緣由。後端
那如何將傳統微服務框架遷移到 Service Mesh 呢?先來看下這樣一個遷移場景,客戶的業務架構是這樣的,就是左圖這個架構:api
應用是部署在虛擬機或物理機上的安全
業務是基於 Spring Cloud 框架開發的網絡
註冊中心採用的是 Consul 或 Eureka架構
目前開源 Istio 已經成爲 Service Mesh 事實上的標準,所以客戶但願嘗試遷移到 Istio 上,原生 Istio 是構建在 K8s 上的,所以客戶要遷移到 Istio,首先須要容器化改造,再將框架遷移至 Service Mesh;而在 Mesh 化過程當中,因爲 Spring Cloud 的服務註冊體系和 Istio 的服務註冊體系不同,也就是若是應用不全量遷移的話,新老業務就無法互相服務發現,業務間通訊就被割裂了。可是對於體量稍大的業務,這幾乎是不可能的事情;並且不少客戶只想單純的作框架遷移,並不想容器化改造;因此實際的遷移場景多是這樣的。併發
咱們平常在跟一些客戶交流時,他們在 Mesh 化改造中也確實有這樣的訴求:框架
一些存量老業務運行在虛擬機或者物理機上,暫時沒有容器化改造計劃,但但願經過 Service Mesh 來作服務治理
新上的業務或者存量的非關鍵業務能夠作爲試點,先容器化、Mesh 化,其它業務依然採用原有的運行方式和微服務框架
對於未遷移的存量應用和遷移完成的 Mesh 應用依然能保持業務上的互通
所以,最終遷移的架構圖可能就是這樣的,左邊兩個綠色框是未遷移的存量應用,右上藍色框是容器化 Mesh 化的試點應用,右下紅色框依然運行在 CVM 上 Mesh 化的應用, 但不管如何改造,應用間依然是保持互通的;
而這些訴求倒是原生 Istio 沒有辦法知足的,雖然社區一直在努力,Istio 2020 的目標也是爲了商用,但願應用可以更方便、更簡單的接入進來,特別是對虛擬機的支持,不過依然沒有一個完整的方案,不少特性仍是圍繞 K8s 來實現的。
因此這正是咱們構建 TSF Mesh 微服務框架的初衷:從業務的實際場景出發,幫助傳統微服務框架如 Spring Cloud 平穩過渡至 Service Mesh 框架,並最終造成基於 Service Mesh 的全方位服務治理體系;
咱們在 2018 年末左右就發佈了 TSF Mesh 的第一個版本,並以 TSF 騰訊微服務開發平臺的產品形態投入商用,併兼容 Spring Cloud 的開發框架,目前有很多行業客戶已經接入商用,特別是一些有歷史包袱、不太方便作大規模改造的傳統客戶。
當時考慮到服務治理能力的完善度、技術棧以及開源社區影響力等因素,咱們選擇 Istio + Envoy 做爲 Service Mesh 底座進行構建,而在實際實現中,咱們一樣面臨三大挑戰:
第一,運行平臺的挑戰,咱們知道 Istio 是強依賴於 K8s 平臺的,如應用生命週期管理、服務註冊、健康檢查、服務尋址、策略配置管理等等,那對於物理機或虛機平臺這些能力如何支撐呢?
第二,微服務框架的挑戰,Spring Cloud 和 Service Mesh 都各自完善的服務治理體系和實現機制,如何讓二者的服務互相通訊?服務註冊發現、路由、限流、熔斷等治理能力如何拉通而且統一治理呢?
第三,可觀測性的挑戰,Spring Cloud 和 Service Mesh 服務治理打通後,如何統一觀測兩個體系的服務?日誌、監控、調用鏈如何打通?
下面將從 PaaS 平臺解耦、註冊中心、服務治理、數據運營 4 個切面出發,介紹咱們是如何一步步實現 TSF Mesh 微服務框架的;最後,當完成了這樣一個框架的構建,針對前面的實際遷移場景,咱們又是如何作應用架構的遷移的。
首先,咱們來看下如何將框架與運行平臺解藕,讓微服務應用可同時運行在虛擬機和 K8s 上。爲何要將框架與運行平臺解藕呢?由於咱們是基於原生 Istio 構建 Service Mesh 框架,而原生 Istio 又是和 K8s 強耦合的,首先就是部署和應用生命週期管理,若是不解藕,很難知足像虛擬機、物理機運行的應用場景。
在業內流行這樣一句話:計算機科學領域的任何問題均可以經過增長一個間接的中間層來解決。
一樣,咱們引入 Resource Controller 資源管控模塊這樣一箇中間層來解決容器和虛擬機統一管理的問題;對於應用容器部署,Resource Controller 適配標準的 K8s 的 API Server 接口,如圖中左邊部分,能夠對接任何支持標準 K8s 接口的容器平臺,如 TKE、靈雀雲等,這裏就不過多介紹了。
對於應用虛擬機部署,咱們自研了一個 aPaaS 虛擬機應用管理平臺,對比左邊 K8s 架構,aPaaS 在實現上有不少相似的地方,這裏主要引入瞭如下四個模塊來解決程序包管理、應用部署以及彈性伸縮的問題:
Repo Manager 模塊,相似容器的鏡像管理系統,負責應用程序包基於版本進行管理(如程序包上傳、下載),並支持對接 Jenkins 等 CD 系統;
TSF-master-api / TSF-master,相似 K8s 的 API Server 和 Kubernetes Master,TSF-master-api 是 aPaas task 任務接入層,並將應用部署、下線、啓停等任務轉發給 TSF Master,TSF Master 再將任務異步調度到具體的虛擬機上,並實時同步任務執行狀態;
TSF Controller,相似 K8s 的 HPA/ReplicationControlle 水平擴縮容控制器,TSF Controller 則負責虛擬機應用的水平擴縮容,根據虛機部署組中設置的 CPU 利用率、內存利用率等指標週期性探測部署資源的使用狀況,若是匹配到設置的目標值,就經過調用 TSF Master 對虛擬機應用進行擴容或者縮容;
tsf-agent,相似 K8s Node 節點上的 Kubelete 組件,負責初始化機器環境以及執行具體的任務,如在執行部署任務時,tsf-agent 根據 task 中的信息先從 Repo Manager 下載相應版本的程序,異步執行並負責應用進程的生命週期管理,經過相似 K8s liveness 的健康檢查機制,探測應用的執行狀態,若是運行異常則從新拉起應用進程,同時也會同步執行狀態給 TSF Master;
這幾個模塊基本承載了 aPaaS 的核心功能,到這裏咱們搭建好了一個統一的部署和應用生命週期管理平臺,那 Mesh 應用的部署有什麼不一樣呢?
咱們知道,要實現透明的 Mesh 服務治理,首先須要爲應用額外部署一個 Sidecar(也就是 Sidecar 的注入),經過 Sidecar 來代理應用的進出流量、實現流量管理;
首先,咱們回顧下在原生 Istio 下部署應用時,如何將 Sidecar 容器注入到應用 POD 中的,看下上面這個圖,Istio 一般採用的是手工注入或自動注入的方式:
手工注入:就是用戶手工執行 istioctl kube-inject 來從新構造應用部署;
自動注入:經過 K8s 的 mutable webhook 回調 istio-sidecar-injector 服務來從新構造應用部署;
其實說白了,注入的本質就是是將運行 Sidecar 所須要的鏡像地址、啓動參數、所鏈接的 Istio 控制面信息(如 Pilot、Mixes、Citadel 地址)填充到注入模版,並添加到應用部署中,再經過 K8s 建立應用和 Sidecar 的 POD。
那流量劫持是原生 Istio 是怎麼作的呢?這裏,Istio 用到了 initContainers 初始化容器,在注入 Sidecar 的同時先注入 istio-init 容器,該容器被賦予了 NET_ADMIN 的 POD 網絡空間權限,執行一個 iptables 腳本,該腳本用來劫持 inbound 和 outbound 的流量到 Envoy 的 15001 端口 ;
理解了原生 Istio 的實現原理後,咱們的實現也就比較容易了,就是下面這個圖畫的:
TSF Mesh 在和 K8s 解藕後,Sidecar 統一都在 Resource Controller 這個模塊中注入。
對於容器,仍是注入到 deployment Crd 中再下發給 kube-apiserver,對於虛擬機,是封裝到 task 任務並下發給 TSF master-api 組件,再分發給 VM 上 tsf-agent,tsf-agent 在啓動應用時拉起 Sidecar。
對於流量劫持,咱們也作了統一,無論是容器仍是虛擬機,統一都在 Sidecar 的 pilot-agent 這個組件中執行 iptables 腳本,這裏有個優化點哈,就是只劫持服務網格中註冊過的服務流量,後面在講註冊中心那章的時候會詳細介紹下。
那到這裏,其實還有兩個部署的邊界問題,一個無損發佈,另外一個就是優雅下線。
那什麼是無損發佈呢?簡單說,應用發佈成功的標誌是應用可以真正提供服務(也就是註冊到註冊中心可被其它服務調用),或者說,有時候應用進程雖然啓動成功了,但因爲初始化過程比較慢好比 Java 應用,這時候還不能真正提供服務,若是簡單以進程啓動成功爲發佈成功的標誌將服務實例註冊到註冊中心,其它服務在同步到該實例並進行調用時就會失敗,這種狀況就不是無損發佈;K8s 中是如何實現無損發佈的呢,咱們知道在 K8s 中,是經過配置一個 readiness 探針的 webhook 來實現的,只有探測成功後纔會更新 POD 的 endpoint 信息。
那 TSF 中既然不能依賴平臺來實現相似 readiness 的功能,咱們把這個能力統一放到框架中來實現,對於 Spring Cloud,咱們對 SDK 進行了擴展,在應用啓動期間會主動探測應用的服務狀態,探測成功後纔會把這個實例註冊到註冊中心。
對於 Service Mesh,Sidecar 會主動探測本地應用的服務狀態(固然這個須要用戶提供一個健康檢查的接口),控制面在同步探測成功的狀態後纔會註冊到註冊中心,這裏 mesh 的服務註冊機制會在下一章會詳細介紹。
那什麼是優雅下線呢?這個應該比較好理解,就是在應用進程處理完當前全部請求後再中止,同時先要把當前服務實例從註冊中心摘除,不然還有源源不斷的請求過來。
K8s 中是如何實現優雅下線的呢?咱們知道在 K8s 中,是經過配置一個 preStop 的 webhook 來實現的,就是在中止應用 POD 前能夠執行用戶配置的命令或腳本後再中止 POD,好比優雅下線一個 Nginx 服務能夠在 preStop 中執行一個 Nginx–squit 命令,固然這裏也是依賴 K8s 要先摘除 Pod 的 Endpoint 信息;
在 TSF 中,優雅下線的實如今容器和虛擬機上有所不一樣,對於容器,咱們依然是經過 preStop 配置,只是會默認添加一個反註冊到 Consul 的命令而且先執行,再執行用戶配置的 preStop 內容;對於虛擬機,一樣支持 preStop 的方式,只不過因爲應用的生命週期是上面說的 tsf-agent 組件來管理,所以這裏的 preStop 也是由 tsf-agent 來執行。
其實,解決了這兩個部署邊界問題後,咱們執行無損的滾動發佈也就不難了。
應用多平臺部署的問題解決之後,下面咱們來聊下注冊中心,就是運行時服務如何互相發現。
原生 Istio 默認採用 K8s 做爲註冊中心,服務發現依賴控制面組件 pilot-discovery 完成,pilot-discovery 經過適配不一樣註冊中心同步服務註冊信息,但 Istio 自己不提供服務註冊和健康檢查的能力,而是依賴 K8s 部署時寫入的 Service 和 Endpiont 信息,並在運行時探測應用 POD 的健康情況,總之,也是依賴部署平臺。
既然 K8s 已被解耦,那註冊中心也無法依賴他了,咱們須要從新挑選一個,然而選擇並很少;
首先,Spring Cloud 支持最好的註冊中心是 Consul 和 Eureka,Eureka 社區中止維護後,就剩下 Consul 了;其次,Istio 當時已經初步適配了 Consul,雖然不太完美,不過咱們能夠作些優化。因此,綜合考慮,咱們選擇 Consul 了。
接下來就是對 Consul 的適配,咱們對 Istio 作了些改造:
剛纔說到 Istio 對 Consul 的支持不夠完美,主要是當時用的 Istio 1.0 的版本仍是經過 Rest API 輪詢的方式去獲取 Consul 註冊服務信息的變動,所以咱們作的第一個改造是把輪訓改成 Consul Watch 的方式;
第二個改造就是:支持服務註冊和健康檢查,咱們結合右邊的流程圖 來具體看下:
在 Istio 中,控制面組件 pilot-discovery 直接對接 Consul,定義了一套本身的 Service Discovery Interface 模型,好比拉取某個 NameSpace 下服務列表、拉取服務的實例列表,這裏咱們對 pilot-discovery 的 Service Discovery Interface 模型進行了一些擴展,增長了服務註冊和心跳上報兩個接口,並在適配 Consul 的 constroller 上實現了這兩個接口;
同時,咱們在 pilot-discover 中擴展了一個 HDS(Health Discovery Service)的 gRPC 服務,用來接收數據面服務註冊和心跳上報的請求;
在數據面側,用戶提供一個 spec.yaml 服務描述文件(裏面主要包含:服務名、服務監聽端口、健康檢查 URL 等服務註冊信息),格式和 K8s 的 Serivice 配置是兼容的:
d. pilot-agent 根據 spec.yaml 文件構造 envoy-rev0.yaml,這裏的 envoy-rev0.yaml 是 Envoy 的啓動引導配置文件;
e. Envoy 啓動時,與 Pilot-discovery 創建 gRPC 長鏈接,提取 envoy-rev0.yaml 服務註冊信息,並經過 HDS 發送服務註冊請求,這裏咱們對 Envoy 也作了擴展,上面pilot-discover 實現的是 HDS 服務端,那在 Envoy 中實現的就是 HDS 的客戶;
f. 對於運行時,Envoy 根據 spec.yaml 文件中配置的健康檢查接口週期探測本地應用的健康狀態,再經過 HDS 上報給 pilot-discovery,這裏在實現中複用了 Envoy 的 Health Check 功能,只是原生實現須要經過主動配置 Envoy 規則來探測外部 upstream 的健康狀態,咱們這裏作了定製,能夠在運行時主動探測本地服務的健康狀態。
這大概就是咱們改造後的服務註冊和健康檢查機制,不管是容器應用仍是虛擬機應用,都採用統一的方式。
服務註冊解決後,下面來看下服務間如何通訊,首先要解決服務間如何尋址;
咱們知道,在微服務框架下一般是經過服務名進行服務調用:
對於 Spring Cloud 應用,依然採用如 RestTemplate/Feign 的調用方式,SDK 自動從註冊中心同步服務列表,實現服務名和 IP 地址的轉換;
對於 Service Mesh 應用,因爲沒有 SDK 的依賴,一般採用 DNS 的方式進行尋址;這裏咱們也作了些架構上的改造。
因爲原生 Istio 是依賴 K8s 的 Kube-DNS 或 Core-DNS 這樣的集中式 DNS 服務進行服務名的尋址的,而與 K8s 解耦後咱們須要從新選擇一個 DNS 方案,這裏咱們採用了一個分佈式 DNS 方案,將 DNS 能力下沉到數據面,而採用這種 DNS 架構方式,主要考慮到如下 4 個緣由:
首先,組網無需部署一套 DNS 集羣
其次,因爲下沉到了每一個數據面,所以也不存在單點故障的問題
因此,也無需考慮集羣容災等高可用性問題
並且,由於僅處理本地 DNS 請求,壓力小解析快
右圖是服務尋址的一個詳細流程圖,服務名的解析是經過咱們自研組件 Mesh-DNS 來實現的,這裏 Mesh-DNS 會對接控制面的 pilot 組件,實時同步服務網絡中註冊的服務列表,若是 DNS 請求中的服務名在服務列表中,就返回一個後續能夠被 iptable 重定向到 Envoy 的特定 IP;
若是服務名不在服務列表中,則把 DNS 請求轉發給本地 resolve.conf 配置的 NameSever;
這裏咱們沒有經過配置 resolve.conf 文件的方式提供 DNS 服務,而是間接地經過將 DNS 請求劫持到 Mesh-DNS 上,爲何要這麼設計呢?
只要有三個緣由:
resolve.conf 的生效時間,在 Linux 中 DNS 解析是經過 glibc 實現,而 glibc 在 2.26 版本以前有個 Bug,若是進程不重啓,新配置的 NameSever 是不會生效的,在容器部署下,Mesh-DNS 和應用分別部署在同一個 Pod 的不一樣容器中,所以容器的啓動是相互獨立的,也就是頗有可能應用容器啓動了,但 Mesh-DNS 所在容器尚未啓動好,這樣 Mesh-DNS 後面配置的 DNS NameSever 就不會生效,有人說能夠在 InitContainer 容器中修改,其實也是有問題,若是容器異常重啓後,resolve 配置文件也一樣會被還原致使服務不可用。
第二個緣由,有些用戶可能會安裝本身的 DNS 服務如 DNSmasq,已經佔用了 53 端口,這樣就會引發端口衝突。
第三個緣由,若是要配置,咱們不會覆蓋 resolve 文件 中原有的 NameServer 配置,只是追加進去,那 glibc 在 DNS 解析時就會進行 NameSever 的輪訓,若是輪訓到其它 NameSever 進行服務名的解析請求,那確定會報錯。
出於這三個方面的考慮,【採用 DNS 劫持】是一個比較合理的方案,具體能夠看下,右圖中 DNS 劫持的 iptables 規則,看下第一個紅框,全部到 53 端口(也就是 DNS 請求)的流量被重定向到本地的 55354 端口,55354 端口運行就是 mesh-dns 服務;
再看下第二個紅框,全部到 168.254.48.46 這個目的 IP 的流量被重定向到本地的 15001 端口,15001 端口運行的就是 Envoy 服務,這裏就是應用出流量的劫持;
這裏 168.254.48.46 這個 IP 就是爲了只劫持服務網格內的服務,Mesh-DNS 解析出的特定 IP。
到這裏,咱們已經解決了註冊中心統一和 Mesh 服務註冊發現的問題,那運行時兩個框架的服務治理能力如何互通呢?
那什麼是服務治理能力的互通呢?
舉個例子,好比一個 Spring Cloud 應用要調用 Mesh 應用,如何發現 Mesh 應用的服務實例列表,如何根據路由規則路由到 Mesh 應用的具體實例;
一樣,對於 Mesh 應用在訪問 Spring Cloud 應用時,若是 Spring Cloud 應用不能正常提供服務,如何實現服務熔斷?
這就須要在服務治理能力上進行拉通;而咱們面臨的挑戰是:兩種微服務架構都有各自完善的服務治理體系,不管從架構模式,仍是數據模型及實現邏輯,都存在較大差別。那咱們是如何實現的呢?
不過咱們發現:不管哪一種微服務框架,治理能力都是經過服務的形式提供出來,實際用戶並不須要感知服務的具體實現;
好比,對於 Spring Cloud 應用經過引入一個 Hystrix 註解就能夠實現服務熔斷,而對於 Service Mesh 應用,經過控制面配置一個 Destination Rule 規則便可;
所以,基於這個共同點, 咱們從部署模式、服務及功能模型上進行了拉通;
首先,也是最重要的,雖然 Spring Cloud 應用和 Mesh 應用都註冊到同一個註冊中心,但若是註冊信息不一致,依然無法實現服務互通,因此這裏咱們先實現了服務註冊元數據模型的統一;
再好比,服務 API 的治理,Spring Cloud 應用採用 Swagger 匯聚 API 列表,目前 Swagger 以標準 OpenAPI v3 版本的格式輸出,而對於 Mesh 應用,由於沒有 SDK 的依賴,咱們對 Mesh 進行了一些擴展,可以支持 用戶根據 OpenAPI 標準 自行定義的 API 列表;
後續即可基於服務 API 進行統一治理,好比基於 API 的路由、熔斷、限流等等,固然也能夠對具體的 API 進行在線調試;
而對於服務路由、熔斷、限流、鑑權幾個治理能力的拉通咱們的思路是相似的。
首先,是統一策略配置的元數據模型,那什麼叫統一策略配置呢?就是不管是 Spring Cloud 仍是 Service Mesh 在控制檯入口都採用一致的配置項、配置視圖;其次,是對齊服務治理的實現算法,好比路由都基於標準權重算法來實現、熔斷都基於標準熔斷器來實現、限流都基於標準令牌桶來實現。
固然,咱們也對一些治理能力作了加強和擴展,好比熔斷,對於 Spring Cloud,開源是 Hystrix 實現,(不過 2018 年 11 月已經中止維護),因此咱們採用了官方推薦的 Resilience4J 做爲底層實現,同時擴展支持實例、API 和服務三個級別的熔斷隔離,以便適應更多的熔斷場景;
那對於 Service Mesh 呢,用過 Envoy 的同窗可能知道,其實原生 Envoy 的 Upstream 裏並無實現業界標準的熔斷器,並且只是實例級別的熔斷,是簡單經過 Upstream 鏈接池和 Outlier Detection 異常檢測的方式來實現的;
所以這裏咱們對 Envoy 也進行了加強,首先是實現了業界標準的熔斷器,其次是也支持實例、API 和服務三個級別的熔斷隔離。
下面,咱們以服務路由爲例,具體介紹下如何拉通兩個框架的路由能力。所謂拉通服務路由,咱們的理解是:在配置相同路由規則的狀況下,輸入相同的請求流量,不管是 Spring Cloud 實例仍是 Service Mesh 實例都能達到一致的路由效果,也就是上面抽象出的公式,
在相同的 Inbound 入流量請求下:
首先,對於路由規則,Spring Cloud 服務和 Service Mesh 服務都採用了一致的配置視圖,如上圖這個配置,保證了公式中 c 參數的一致;
其次,統一的註冊中心,以及一致的服務註冊元數據,保證了兩個框架的 consumer 都能拉取到相同的 provider 實例列表,保證了公式中 p 參數的一致;
最後是算法上的對齊,都基於標準權重算法來實現路由,保證了 Route1 和 Route2 實現上的一致。
所以,輸出結果 out1 和 out2 必然是一致的;雖然中間的實現邏輯不同,如圖中 Mesh 的路由規則下發,流經三個組件最終在 Envoy 中生效,但最終的路由效果和 Spring Cloud 是一致的。這裏簡單介紹下 Mesh 的策略下發過程,因爲原生 Istio 依賴 K8s 的 ConfigMap 進行配置的存取,配置的入口就是 kube-apiserver,那與 K8s 解藕後,咱們統一採用了自研組件 mesh-apiserver 做爲容器和虛擬機 Mesh 的配置入口,控制檯經過 mesh-apiserver 下發 Mesh 各類服務治理配置,mesh-apiserver 再以 pilot 的 Crd 配置模型寫入 Consul,而後 pilot-discovery 再從 Consul 同步配置更新,最終構形成 Envoy xDS 協議標準的數據包下發給 Envoy;後面的流程跟開源 Istio 是同樣的;
對於其它服務治理能力的拉通原理跟上面的路由是相似的,這裏就 不過多介紹了!
到這裏,咱們解決了不一樣平臺統一部署的問題,解決了不一樣框架服務通訊的問題,以及不一樣框架服務治理能力互通的問題。
最後一步,也是很是重要的一步,就是如
何來運營咱們的業務?如何統一觀測服務的運行狀態?
這裏咱們經過自研的 APM 平臺,統一整合了日誌、監控、調用鏈的採集、解析、存儲和查詢方案。
首先,咱們來看下右邊這個架構圖,整個 APM 方案,分爲控制流和數據流;先來看下控制流,圖上的實線:
首先是控制檯向後端 APM 發送日誌配置(如日誌建立、變動)、日誌檢索、調用鏈檢索、服務依賴拓撲等請求;
若是是日誌配置,對於虛機平臺,APM 向虛機管控中心服務(圖中的 TSF Master 模塊)下發日誌配置信息,TSF Master 再下發配置至具體實例的 Agent 上,Agent 構造 Filebeat 配置並啓動 Filebeat 進程;
對於容器平臺,APM 向資源管理模塊(圖中的 TSF Resource 模塊)下發日誌配置信息;
TSF Resource 再根據日誌配置構造 Filebeat 配置並以環境變量方式注入 Filebeat 容器,並拉起 Filebeat 容器;
同時,APM 會根據日誌配置,請求 ES,構建對應的預處理器和權限;若是是日誌檢索、調用鏈檢索的請求,APM 構造一個查詢請求併發送到 ES 集羣;
而對於日誌監控告警指標的配置請求,是經過圖中的 TSF Monitor 模塊來處理,構造一個告警指標的查詢請求併發送給 ES 集羣。
再來看下數據流,圖上的虛線:
首先是虛擬機或者容器中,應用進程產生業務和調用鏈日誌,並寫入到本地日誌文件;
而後,Filebeat 進程採集指定路徑下的日誌文件數據,並進行多行合預處理;再將日誌數據以批量方式發送至 ES 集羣;
這裏,Pipeline 先作數據預處理,解析日誌數據、處理時間戳和索引名再進行存儲;
對於控制檯的查詢請求如日誌檢索,ES 處理查詢請求並返回匹配的原始文檔數據;APM 收到原始文檔數據後,進行建模、合併等處理後再返回給前端展現;
而對於日誌監控告警,TSF Monitor 將請求返回的數據推送至 Barad 事件中心和告警平臺;
Barad 平臺和告警平臺再向【前端頁面】提供監控圖表數據;
上面就是咱們自研 APM 的大致方案,Spring Cloud 應用和 Service Mesh 應用都統一採用了這樣的監控方案。
到這裏,咱們只是講了 APM 平臺的統一。若是要作到指標數據的打通,好比圖中 Service Mesh 應用調用 Spring Cloud 應用的場景,如何在調用鏈層打通呢?
這裏咱們也作了些技術選型和改造:
首先,咱們須要一個統一 Tracing 數據模型,這裏選擇了 OpenTracing 的開源標準模型;
對於 Spring Cloud,咱們的採用的是 Sleuth 實現,由於 Sleuth 自己能比較完美的支持 OpenTracing,不過咱們也作了些簡單擴展,好比支持 Trace 日誌本地落盤、以及調用鏈和日誌的聯動;
對於 Service Mesh,Trace 由 Envoy 輸出,Envoy 原生是支持 Open Tracing 的,但後端須要接 Zipin 或者 Jeager 這樣的 Trace 服務,咱們這裏也作了些擴展,在原生 envoy.zikin 實現的基礎了擴展了 envoy.local,使其支持 Trace 日誌本地落盤。
這裏 Trace 日誌輸出都採用的是本地落盤的方式,主要考慮到幾個緣由:首先,能夠和後端的 Trace 服務解藕,不必定須要對接像 Zipin 或者 Jeager 後端服務其次,就是能夠異步採集,先落盤再採集,同時保證了 Trace 日誌的不丟失還有就是能夠方便對接其它 Trace 服務,好比客戶想對接本身已有的調用鏈平臺。
先看一個的漫畫,上面勾畫了通往雲原生的三條路:
最上面那條路,Go Native 遷移,平臺架構、微服務架構直接重構只雲原生形態,遷移思路很是直接,但道路曲折,改造量大,且風險極高,很容易掉溝裏;
中間那條路,Evolve 遷移,先平臺遷移容器化改造,再作微服務架構遷移,方案相對比較穩妥,遷移道路較平坦,但遷移週期可能會比較長;
最下面那條路,Lift & Shift 遷移,考慮到物理機的運行場景,先物理機上雲,再作容器化改造,最後 Mesh 化改造,方案很是穩妥,但遷移週期可能很是長。
這三條路咱們應該如何選擇呢?
而在咱們搭建好上面的 Mesh 微服務平臺後,其實咱們能夠更平穩、更快速的通達 Service Mesh 雲原生:
咱們能夠按照傳統較穩妥的 Evolve 或者 Lift & Shift 方式遷移,圖中最上面的遷移路線,先容器化再 Mesh 化,這裏就很少說了;
也能夠採用激進點的 Go Native 方式,圖中最下面的遷移路線,直接 Mesh 化,由於咱們的微服務框架已經徹底和 K8s 平臺解藕,能夠完美運行在虛擬機或物理機上;
在 Go Native 的遷移方案上咱們還能夠再激進點,就是圖中中間的遷移路線,一部分業務先作全形態遷移,一部分業務只作 Mesh 遷移,但新老應用要保持業務上的互通,正如開篇介紹的,其實這種更符合實際業務遷移場景;
之因此可以實現這樣的遷移,是由於咱們前面構建的微服務平臺是支持多框架的,框架間的治理能力、監控運營也都一 一打通了。
聊到這兒,是否是像 Go Native 這種比較激進的遷移方式也能夠如此簡單安全呢!
固然,除了知足客戶架構遷移的場景,對於很多公司目前多語言多技術棧的業務場景也是能夠知足的,好比:
Java 應用依然跑在 Spring Cloud 上,其它語言因爲沒有太多統一的服務治理框架,其服務治理 徹底能夠經過 Service Mesh 來接管,
這樣,Java 應用和非 Java 應用不只能夠統一進行服務治理,業務上的互通也徹底不受影響。
固然,除了上面介紹的幾個遷移場景,咱們還能夠應對更多的遷移場景,好比單體架構的遷移、傳統 SOA 架構的遷移等等。
總結
此次分享主要以騰訊雲 TSF Mesh 爲例,介紹瞭如何一步步構建一個跨平臺多框架的基於 Service Mesh 微服務平臺,
來幫客戶解決實際遷移過程的痛點問題,但願能幫助你們在作架構演進或遷移時帶來一些思考和啓發。
往期
推薦
掃描下方二維碼關注本公衆號,
瞭解更多微服務、消息隊列的相關信息!
解鎖超多鵝廠周邊!