基於容器的微服務架構技術選型與設計

背景介紹

作爲金融企業,國投瑞銀基金多年以來IT工作主要還是以運維爲主,主要業務系統基本採用外購模式,但隨着業務的不斷髮展,業務部門個性化需求越積越多,外購與外包已經不能很好滿足業務員部門的需要了。2016年底公司着手開發團隊的組建工作,同時對公司的業務開發平臺進行架構選型與設計,以求統一開發平臺,提升研發效率,從而加快業務部門的業務需求處理效率。

下面我們將就這兩年在平臺架構選型、平臺架構設計、平臺及相關子系統的逐步完善背後的一些經驗進行分享。

適用對象

該架構全部基於開源平臺,經過三年多的生產上線實踐,平臺運行平穩,可擴展性強,可用性高,可以很好滿足公司對於金融業務不斷髮展的需要,這對類似的中小型企業的業務架構選型也具有一定的參考意義。

注:UFOS:國投瑞銀基金運營系統

架構設計與選型

架構設計考量因素

在初期平臺架構設計與選型時,我們根據現有業務系統的需求,梳理出了技術架構選型需要考量關鍵因素:

  1. 架構平臺的前瞻性或先進性,符合當前潮流與未來發展趨勢,有較好的生態鏈和較強的生命力,不能因爲平臺架構選型不當,導致未來平臺重新架構,造成大量的遷移和重構工作,需保障平臺架構能在一段時間內保持技術領先。

    這裏有兩點注意,一是對技術成熟度的考量,是否採用有風險的前沿技術以及擁抱這種技術帶來的風險,這一點類似於金融投資中收益與風險的關係,我們需要在系統的先進性與系統的穩定性之間進行平衡;二是採用前沿技術可能會面對更多的困難,如國內可能缺乏相關資料,或難以找到可以參考的成功案例,很多時候只能通過官方和論壇獲取相關技術信息,會對架構按時交付帶來風險。

  2. 平臺的可擴展性

    能滿足基金行業業務不斷的發展與創新的需求,儘量能做到平臺的橫向平滑擴展,滿足以上特性其實就決定了架構的分佈式特性,當然我們更希望它是雲原生的架構

  3. 系統的可靠性可用性

    作爲金融業務系統平臺,需保證業務系統連續不間斷運行,保證平臺的高可用。採用集羣或平臺自動恢復功能,確保平臺局部出錯也不影響系統整體的運行;這裏有兩個層面,一是業務系統中的功能組件能相互隔離,其中一個組件的不可用不能影響到系統的其他部分;二是平臺基礎系統採用集羣架構,有自動恢復功能,確保即使系統中有節點出錯,也可在很短時間內完成出錯節點中服務的切換與恢復

  4. 開銷

    不同的架構/技術選擇有着不同的開發成本,包括技術框架,平臺的學習成本,我們期望平臺能支持異構的技術,使得開發人員可以採用比較適合的技術棧來快速實現業務功能的開發

  5. 開發運維一體化思想(DevOps),在設計時考慮運維,儘量減少後期運維操作的複雜度,減輕後期業務系統運維的負擔。

  6. 讓開發人員更多專注在業務功能需求開發,其它非功能需求如負載均衡、高可用等儘量由平臺提供,做到對開發人員透明,以提升開發效率。

當公司規模不大,實力不足以自己實現部分或全部架構,選擇現成的“輪子”來組裝自己的架構就成了一種自然的選擇。在選擇上可能會更多考慮如何使用更“標準”的“輪子”來滿足自己業務的需求,以便於今後業務的升級和擴展。

要實現上述平臺的擴展性和高可用性,一般都離不開分佈式架構,而分佈式架構一般離不開服務來承載 。

基於服務架構演進

基於服務的架構設計早已有之,比如基於RPC的服務調用,最早可追溯到CORBA,以及現在還有很多金融公司在交易系統中使用的BEA早期的框架Tuxedo(主要編程語言爲C/C++)。後來者有Facebook的Thrift,Google Protocolbuf框架/grpc,阿里的Dubbo框架等等。這些框架支持消息的二進制編碼(序列化與反序列化),效率高,因此成了對網絡傳輸,併發處理要求高的應用如App應用,遊戲,交易軟件等的首選。

後來隨着HTTP協議的廣泛應用,發展衍生出面向服務的架構(SOA)的架構設計,該架構一般都應用在比較複雜,大型項目中,爲了異構系統中的功能複用,或系統性能的考量將功能模塊獨立出來成爲服務,服務可以分佈式部署,服務之間通過標準的軟件接口方式在網絡中相互調用;爲了統一服務調用標準,SOA往往還引入了數據總線概念,服務可以通過數據總線進行服務註冊,服務的查找與調度。

SOA架構中的服務之間是鬆耦合的,服務的顆粒度相對比較粗,而近些年出現的微服務,則可以看作是對SOA服務的一種精簡,細化,或者說是SOA服務的輕量版。

微服務

在談及微服務的時候,大都會對應到單體應用,以示鮮明對比;單體應用其實就是一個服務中包含了太多種功能的應用,它跟面向對象的設計裏的單體類(包含太多功能實現的類)的提法頗有些類似,英文單詞中有一個專有名詞monolithic來描述二者:

如果你再仔細對照微服務和類,你會發現兩者有諸多相似之處,比如微服務和類在設計原則上也是一致的,也就是高內聚/封裝與鬆耦合,高內聚也就是隻負責一項任務,也就是單一職責原則,而鬆耦合則是指模塊之間的接口儘量簡單,減少耦合度,這樣也使得開發,獨立部署和升級微服務更加容易。

微服務同時也很好地匹配了敏捷開發團隊,減少了開發團隊的溝通成本,更小的代碼庫同時有效降低了開發團隊之間的衝突,使得小團隊開發更加有效。

微服務框架選型

2016年,當時的微服務還不像在現在的市場上一樣炙手可熱,當時微服務興起不算太久,在互聯網企業有早行者,但市場整體上參考資料與可參考的案例相對並不多,市場上對選型微服務架構也不是太明朗。但對照前面架構選型的各種考量因素,微服務還是非常匹配我們的選型標準的,而且能與我們小團隊敏捷開發的組織架構相匹配,因而微服務架構基本成爲了我們架構中的首選。

既然選擇了微服務,接下來的工作就是微服務框架的選型,選型中我們主要考量的因素是:

  1. 編程語言,編程語言需要考慮現有開發人員的技術棧和語言的流行度,以及其生態

  2. 框架的生態鏈,比如安全,服務註冊,服務發現與監控等等

我們列出了當時市面上比較流行的微服務框架候選者:

  • 基於RPC協議的框架,如阿里的Dubbo框架,在國內電商,物流,金融軟件等行業都有較多的應用場景,還有Java RMI,Apache Thrift和Google grpc

  • 基於Spring Cloud Java微服務框架

  • 基於容器的微服務框架

當時微服務興起不久,市場框架選擇並不是太明朗,Spring Cloud當時也在不斷地演進與完善中,Dubbo已經停止更新,但相對來說,國內市面上這兩者的選擇比較多,微服務開發的主流語言也是Java。

通過對比和調研我們最終選擇了基於容器的微服務方案,這主要是基於:

  • 微服務設計中有一個比較的重要原則是,技術異構性,可以使用與需求比較匹配的語言和相關的數據存儲方案,而不限於哪種編程語言和哪一種統一的數據庫來實現微服務的開發。比如對於數據分析型微服務,你可以採用Python;對於性能有要求的微服務,你可以採用Go或者C++進行開發,底層數據存儲也不限於RDBMS,可以使用NoSQL,嵌入式數據庫,這使得我們可以更快地使用新技術。基於容器的方案可以很好的滿足以上微服務設計的需求,每個容器運行一個微服務,微服務之間通過協議實現互聯,衆多微服務組合起來組成一個大的完整的應用系統;而Dubbo和Spring Cloud基本就是捆綁在了Java語言平臺。

  • 基於容器雲原生方案,微服務的平臺更寬泛,可以選擇基於雲的部署方案。

  • 微服務容器基於標準的容器基礎鏡像構建,這樣容器的運行環境統一,便於統一管理,也便於排查問題。

  • 由於容器的標準化,容器平臺可以通過容器提供標準的服務註冊,服務負載均衡,服務監控,服務的failover機制等,而這些都不需要植入源碼,可以最大限度讓開發人員專注於業務的開發。

開發語言我們還是選定了主流的Java,即使我們有一些歷史遺留的C#項目,Java畢竟是編程語言的排頭兵,也是開源的主力軍,有很多的開源“輪子”使用,可以大大加快開發進程,即使其編譯後的執行包偏大(幾十到近百兆,雖然自Java 9的模塊化編程有所改善),但對我們的業務平臺相對來說並沒有多大影響。

Java框架我們選擇了Spring Boot,它在Spring MVC基礎上簡化了配置管理,也有多種starter,簡化了編程,可以快速搭建微服務應用。

微服務間前端框架選型

架構前期,由於人力有限,我們並未對微服務前端框架進行選型,更多是依賴外包開發商的現成框架,比如第一個項目的幾個微服務就是採用的是基於jQuery的框架;第二個項目的開發商採用的是vue.js,並基於vue.js進行了組件包裝(尚未完全完工),不過該組件包裝需要額外付費。由於我們對外包方包裝的組件信心不是很足,因爲包裝的組件基本沒有測試用例,這迫使我們下定決心進行前端選型,這也有利於後續業務開發的一致性與可維護性,保證應用研發的質量。

隨後我們花了兩個多星期對市面上的前端主流框架進行了初略快速的選型,並做了Demo,最終Google公司當年的前端新品Angular 2框架被採納。一是AngularJS受衆大,這次Google團隊不兼容重新設計也從AngularJS 1.x吸取了大量的經驗,並結合近年來新的Web的進化和前端開發尤其是移動方面的變革,運用全新的思路進行重新架構,精簡了1.x的概念與指令,並利用單向數據流,服務端渲染機制等大大提升框架性能;二是Angular支持組件式開發,並支持TypeScript,TypeScript吸收了許多面向對象的編程語言優勢,跟後端語言更接近,使得後端人員上手前端開發比較容易。我們的開發人員通過Demo的學習與服務的前後端一體化指導開發,很快就成爲前後端一體開發的全棧開發工程師。

後續Angular的高速發展印證我們選擇還是正確的,對於自身沒有很強研發實力的,選擇正確的框架還是非常重要的,尤其是大廠商的產品,因爲其周邊的生態也會日趨完善,產品生命週期更長久(慎用小開發商的產品,對開發中組件的引入最好也要做好審批)。

在Angular之上我們選擇了開源的PrimyNG組件套件以簡化前端開發,PrimeNG 是一個極爲完善的開源 Angular UI 組件庫,現在已經發展到 80 多個組件,基本可滿足我們業務開發中的所有 UI 需求,雖然當時的版本還偶有Bug或功能未完善的組件。現在,我們的前端已經形成了一套比較成熟的開發模板,可以快速完成微服務前端模塊的搭建。

當然,在Angular上還有其他一些成熟的開源組件平臺可以選擇,比如官方的Material和阿里的NG-ZERO組件等。

微服務通信

運行中的微服務實例其實就是一個個的進程,它們可以在網絡中分佈式部署,微服務調用涉及到網絡之間數據的交換,其實也就是數據對象的序列化與反序列化。

RPC框架通常有自己的接口描述語言(Interface Definition Language,IDL),框架提供工具可以將IDL生成服務端和客戶端的stub,stub可以實現消息序列化與反序列化,以便於數據消息對象通過網絡在客戶端與微服務之間傳輸。由於RPC框架支持二進制編碼方式進行序列化,因此傳輸效率更高,可以獲得更高的性能和更低的延遲;而且相對純文本方式的數據傳輸,數據安全性更高。爲增加安全性,一般這種編碼還會設計相應的數據頭,以便於對主體數據進行加密與簽名。

RPC通信由於雙邊需要stub,更多用在有獨立客戶端的情況如Client/Server模式下,很多還受限於具體框架支持的語言,也與平臺相關(比如很多本質上是基於二進制,有的對大端字節順序的平臺可能就不支持),因此選用和語言和平臺無關的通信協議是一種更好的方式。

RESTful API是基於超文本傳輸協議HTTP之上一種架構設計風格,當時已經在Web應用開發中比較流行了,它通過URI來唯一定位一個需要操作的資源,而使用標準的HTTP方法來完成對資源的CURD操作,這種設計簡潔,輕量,易用。URI中的數據傳輸簡單的可以採用Key/Value的形式,複雜的可以採用JSON數據格式,後者在諸多編程語言中可以方便實現數據的序列化和反序列化。

選擇開放標準的傳輸協議,其上的生態鏈更豐富。我們微服務平臺架構選擇了當時流行的B/S架構模式,RESTful API就成爲我們一種自然的選擇,結合SpringBoot框架,它給我們帶來以下好處:

  1. 編程簡單,SpringBoot的Controller可以方便直接獲取HTTP中的參數對象,前端的TypeScript也可以直接映射到對象。

  2. 調試和測試比較輕鬆,畢竟URI中的參數都是文本編碼可見,可以方便結合測試工具進行接口測試,比如Postman;我們的平臺也集成了Swagger API,可以直接在Swagger界面上直接進行測試。

除了基於REST API方式的數據交換以外,我們在設計規範中也規定了其他的RPC通信模式,比如Thrift和Protocolbuf,當遇到以下場景可以考慮使用:

  • 對性能有要求的微服務。

  • 有異種語言之間通信,而另外一種語言標準中對基於HTTP通信協議支持不是很好,例如在我們的傳真微服務開發中,我們就使用了Protocolbuf協議,傳真服務採用C++開發,Java客戶端通過消息隊列異步與後端的傳真服務通信。

微服務間的交互模式

RESTful API微服務調用一般都採用請求/響應的同步模式(單向通知除外),如果需要進行異步的API調用,比如有些耗時的請求,客戶端一直阻塞等待響應可能不是一種好的處理方式,採用異步處理方式有:

  • 大多數RPC框架都提供異步調用機制,如Thrift與grpc,通常它與同步調用區別在於調用返回的不是結果,而是一個句柄,該句柄可以用於隨後的結果獲取。

  • 使用一些高級語言的同步控制機制,比如使用 future與相關的Provider,處理機制與第一種方式比較類似。

  • 採用異步的消息通信機制,客戶端通過向服務端發送消息提交請求。如果服務端需要回復,則會發送另外一個獨立的消息給客戶端,消息通信一般通過獨立的消息隊列實現,經常使用的消息隊列包括RabbitMQ,ActiveMQ。

消息系統

消息系統使用方式通常有以下幾種方式:

  1. 消息隊列模式。

  2. 發佈、訂閱模式。

  3. 消息路由模式。

  4. 主題(Topic)消息訂閱。

  5. RPC模式,可以理解爲異步遠程過程。

採用消息機制的優點:

  • 解耦客戶端和服務端:客戶端只需要將消息發送到正確的channel,客戶端完全不需要了解後端具體的服務實例,更不需要一個發現機制來確定服務實例的位置。

  • 消息緩存,不需要像同步調用,必須保證所有的客戶端和服務端在交互期間保持可用,即使是服務端處理不過來,客戶端依然可以發送請求。

消息機制的缺點:

  • 增加了操作系統的複雜性:需要安裝、配置和部署消息系統,消息broker(代理)必須保證高可用,否則系統可靠性將會受到影響。

  • 增加了編程的複雜性,消息驅動開發的代碼比較難以理解和維護。

消息系統選型

根據業務需求,我們對消息系統的選型更側重在可靠性,其他方面如吞吐量等並無更多的特別要求,市面上有較多成熟的消息系統可以選擇,在金融系統中,RabbitMQ就因其較強的擴展性,較高的可靠性和可用性被廣泛使用,ActiveMQ也有不少使用案例。

ActiveMQ 是基於 JMS實現的消息系統,它主要提供了兩種類型的消息:點對點以及發佈/訂閱。它是一個高可靠性、高性能和可擴展的消息系統,支持消息標準協議,例如AMQP(1.0標準)和STOMP,官方支持多種編程語言,包括經常使用的Java, C++(RabbitMQ官方並不提供C++編程語言包,而我們在平臺中規劃了C++應用比如傳真微服務)和Python, ActiveMQ可以很好滿足現有業務的需求,最終我們選擇了ActiveMQ作爲我們消息系統。

服務發現與部署

設想一下,微服務按照我們上面所述通信方式提供了REST API或者RPC接口調用,爲了完成一次服務請求,調用方需要知道服務實例的網絡位置(IP地址和端口)。傳統應用都運行在物理硬件上,服務實例的網絡位置都是相對固定的(DNS或者靜態IP地址)。而對於一個現代的,基於雲部署的微服務應用來說,這卻是一個很麻煩的問題,服務實例的網絡位置都是動態分配的,而且因爲擴展、失效和升級等需求,服務實例會經常動態改變,因此,客戶端代碼需要使用一種更加複雜的服務發現機制。

在我們的架構設計中,我們選擇了容器作爲微服務的載體。其設計思路就是把一個微服務裝入一個容器中,也就是一個容器中運行一個微服務,微服務通過容器對外提供服務接口調用;而容器作爲一種標準構件,非常容易在網絡中實現管理和監控;服務的發現和註冊可以通過容器相關技術來實現,這會用到容器的編排與管理技術。

當時在容器市場上,Docker可謂一枝獨秀,但容器編排還處在一片混戰中,局勢並不是太明朗,市面上流行的編排方案有Kubernetes,Mesos和Docker Swarm等,Docker Swarm是由Docker容器廠商創建的集羣工具,它對外提供的是完全標準的 Docker API,可以與Docker引擎無縫集成,任何使用 Docker API 與 Docker 進行通訊的工具(Docker CLI, Docker Compose)都可以完全無縫地和 Docker Swarm協同工作,因此Docker的經驗也可以繼承過來,非常容易上手,學習曲線和二次開發成本相對Kubernetes都比較低。同時Docker Swarm本身專注於Docker集羣管理(Kubernetes對容器管理進行了抽象,支持Docker,rtk等),非常輕量,佔用資源也非常少,運行效率也高,也有支持幾千個容器的集羣案例。

雖然Kubernetes發展勢頭更猛,根據項目需求和公司研發實力,我們最終還是選擇了能更快實施與部署的Docker Swarm方案(當然到了2017年中,容器編排之爭基本落下帷幕,最新統計Kubernetes佔據了75%以上市場,Docker Swarm從曾經的三成已經減少到了個位數)。

Docker Swarm在版本1.12之前是一個獨立的項目叫Swarm Standalone,這也是我們使用的第一個版本,它的服務發現依賴於外部的 k/v存儲,我們按官方指引選用了Consul集羣;Swarm也是獨立的進程,需要獨立安裝,我們採用了容器模式運行Swarm進程。

版本1.12後Docker Swarm升級爲Swarm Mode,Swarm也合併到了Docker引擎之中,也就是安裝了Docker引擎Swarm服務(內置安全認證,服務發現,負載均衡,集羣放置在內置的datastore的meta data,調度,容器網絡)就已經在那裏了,不需要額外安裝,你所需要的只是創建集羣。

Swarm Mode內置的服務發現是通過每個節點Docker引擎內置的DNS Server來實現,在集羣中創建的服務都會在相應的節點的DNS Server進行登記,各節點的DNS Server之間通過gossip協議進行信息交換;每個服務都有一個DNS解析器,它將DNS查詢轉發到節點Docker引擎,由DNS Server來進行解析, 如果不能解析則轉發到配置的外部DNS Server,這樣服務在集羣中任意節點就可以相互訪問了。

服務的負載均衡(LB)缺省是通過虛IP(VIP,使用微服務的服務名)或一組服務的IP(tasks.服務名)根據內置的IPVS來實現,也可以在創建服務的時候指定dnsrr模式通過round-robin輪詢來實現。

就Swarm Mode集羣本身來說,安裝比Swarm Standalone簡單了不少,升級和移植也比較簡單。通過近3年多我們業務系統生產的營運實踐表明,Docker Swarm可以說完美滿足我們的現有業務平臺的需要,包括其穩定性,集羣的高可用性(包括容器自我檢測與自動重啓,錯誤節點中的容器自動轉移)和可擴展性(包括集羣節點的動態擴容,容器服務的實例動態擴充),也可以滿足業務未來多年發展的需要。

Figure 1: Docker Swarm服務發現與負載均衡

HTTP反向代理/服務網關

微服務除了內部相互之間調用和通信之外,最終要以某種方式暴露出去,才能讓外界系統(例如Web應用、移動應用等)訪問到,這就涉及服務的前端路由,它是連接內部微服務和外部應用系統的一個通道。

HaProxy與Ngix等工具也可以實現HTTP反向代理,但基於以下特性,開源的HTTP反向代理與負載均衡工具Traefik成爲我們的最終選擇:

  1. Traefik更適合需要服務發現和服務註冊的應用場景,它 支持多種後臺應用自動發現,如Docker,Swarm,Kubernetes,Consul等,它還可以動態監測後臺服務的變動以自動實時更新自己的配置。

  2. 支持限流與自動熔斷功能。

  3. 支持配置熱更新。

可以說Traefik非常適合容器化的微服務,採用Traefik可以帶來以下好處:

  1. 服務反向路由,Traefik將外部請求反向路由到內部具體的微服務,這樣雖然系統平臺內部是複雜的分佈式微服務架構,但是外部系統從代理上看到的就像是一個統一的完整服務,代理屏蔽了後臺服務的複雜性(類似Facade模式),同時也可以屏蔽後臺服務的升級和變化。

  2. 便於安全控制,服務通過代理統一訪問後端的微服務,而代理訪問微服務是通過容器內部網絡進行,也就是微服務都可以不用暴露端口到容器外端,外部應用也就不能直接訪問容器裏邊的微服務了,而必須通過 Traefik代理。代理有微服務的註冊信息,它可以根據微服務名正確路由到相應的IP/端口的微服務容器。這樣我們的安全策略就只需要集中在Traefik代理端控制即可。

  3. 提供多種格式度量數據,比如可以提供我們採用的Prometheus監控數據格式,提供訪問量,調用延遲,錯誤計數等數據,爲後端的性能優化或者擴容提供數據支持。

Figure 2: HTTP 反向代理

日誌子系統

由於採用分佈式架構,並且使用容器來承載微服務,如果使用本地日誌文件模式,日誌就散落在各個容器內部或各個宿主機上了,這樣不利於日誌的統一管理和使用,因此,採用一個集中的日誌系統中心,也就成了一個必然的選擇。

Figure 3: 日誌子系統

在我們的架構選型中,我們選擇了流行的開源框架ELK棧;日誌寫入遠端的Elasticsearch,通常可以採用兩種方式,一種方式是通過日誌代理,如Elasticsearch提供的高效的Beats工具,可以將Beats與業務服務部署在一起,這適合第三方服務(沒有源碼)或開發語言無標準日誌組件的服務。而另外一種方式則是通過日誌的SocketAppender,直接將日誌通過網絡寫入遠程的日誌服務,如LogStash,很多標準的日誌組件都支持這種方式,如Java標準日誌輸出如Log4j,Logback等。這種方式也比較適合在容器中部署的微服務,不需要額外再部署另外的日誌工具。在我們微服務平臺中,日誌輸出我們選用了性能較高的Logback,並選用了與之配套的LogStash輸出插件,通過該插件(代理)Logback可以將日誌通過Socket直接輸出到Logstash服務,而這毋須對代碼做任何改動,僅需要通過簡單的配置文件配置即可方便實現,對調用日誌的應用微服務完全透明。

爲便於後續的日誌查找和Kibana中的日誌數據展示 我們需要對日誌的格式進行規範化,以便將日誌中的關鍵信息以鍵值對的方式存入ElasticSearch,規範化涉及到日誌文本的編碼與解碼,分別在應用端和LogStash端,LogStash服務可以通過配置來對消息進行Mapping和過濾。

如果日誌量比較大,則需要在日誌輸出與LogStash中間增加消息緩衝,Kafka是一個高吞吐量的消息系統,Log4j2有直接輸出到Kafka的Appender。

監控子系統

監控系統是平臺服務治理中的一個重要組成部分,沒有監控的應用系統可以稱作一個裸奔的系統;我們原有的業務平臺已經有了一套傳統的監控系統Netgain,但更多是對基礎設施的監控,缺乏對應用系統內部狀態的真正監控,比如對微服務和容器的支持,不能很好滿足UFOS微服務平臺的需求。

Prometheus作爲從CNCF畢業的第二個開源項目(第一個是容器編排項目Kurbernetes,Prometheus本來也是源自Google對Kurbernetes的監控),它能很好地監控服務以及容器,除了能與Kurbernetes無縫集成以外,也可以與Swarm很好地集成,尤其是配合Docker Swarm中的label與global配置選項使用,可以非常方便實施遠程應用監控代理(exporter)的部署。

由於Prometheus是一個開放的監控平臺,因此有大量的官方及第三方的監控代理Exporter(監控代理可以協助不支持Prometheus數據採集接口的第三方服務公開自身的監控數據),在UFOS中主要使用了以下監控/代理:

其中BlackBox採用的是非代理模式,由於已有netgain做基礎設施監控,所以並未使用SNMP Exporter。

Prometheus本身也提供了告警服務模塊AlertManager,你可以通過基於Prometheus內置的強大的查詢語言PromQL來設置告警規則,Prometheus會根據設定的時間間隔從配置好的服務收集度量指標,如果某個指標與定義好的規則匹配,則觸發告警。AlertManager支持告警分組,告警抑制和靜默,告警撤銷,告警規則正則表達式匹配,告警模板等功能特點。AlertManager支持多種告警通知,如郵件,Scribe,Hipchat,Wechat(官方原生支持)等,還支持web接口調用,可以通過webhook與微服務集成,比如阿里釘釘就可以通過該種方式接入。

Figure 4: 監控子系統架構圖

Prometheus提供多種客戶端API接口調用庫,如官方提供Java,Python,Go,第三方提供C++,PHP等庫,通過這些庫你可以很方便在你的微服務中植入監控的度量數據(通過微服務Web接口,如果是批處理任務,則可以將生成的監控度量數據發送到PushGateway服務進行託管),爲Prometheus服務進程拉取到,這樣我們可以方便實現對業務數據的監控。

監控界面展示使用Grafana,Grafana 是一個開源的圖表可視化系統,支持多種時序數據庫如InfluxDB,當然也支持Prometheus,Grafana有豐富的圖形展示組件,官方網站也提供大量現成的模板,UFOS中對Swarm節點,微服務,數據庫,告警等資源進行了監控展示。

高可用設計

爲保障業務的穩定可用,平臺應保證持續可用,不會無故宕機,即使出現故障也可以快速發現和定位,通過監控機制,能在系統用戶發現之前儘快解決問題,抑或系統能通過設計自動發現故障並進行自動故障轉移,比如通過主備或集羣的冗餘方式來避免單點的問題,這裏我們將針對後者,從系統設計來提升系統高可用性進行簡要介紹。

接入層

UFOS運行平臺基於Linux系統,平臺入口是HTTP反向代理Traefik,爲實現入口的高可用,我們必須保證Traefik的冗餘備份。

Traefik本身支持集羣方式的HA方式,基於配置的K/V存儲,官方推薦的是Consul。但是由於我們服務平臺是基於Swarm集羣,Traefik是以Swarm服務方式運行(限制在Swarm Manager節點),它可以通過Swarm Manager節點讀取到足夠的Swarm中運行的服務實例的相關信息。而Swarm Manger之間通過Raft算法實時交換信息,因此運行多個獨立的Traefik實例它們獲的服務實例信息是最新也是對等的,所以我們並不需要按官方指引的使用K/V存儲來實現Traefik的高可用。

爲實現Traefik的故障自動轉移,我們對運行Traefik Replica實例的Swarm Manager節點設計了基於VIP的Linux集羣方案,使用Pacemaker+Corosync,其中Corosync用於檢測節點間通訊是否正常,而pacemaker則用於管理集羣資源。當檢測到Linux集羣中的任何一臺節點故障時VIP會自動切換到其他的正常節點,入口也自動切換到該節點上運行的Traefik上來,保證HTTP訪問代理的可用。

應用服務層

所有的微服務都是以Swarm服務的方式運行在Swarm容器平臺上,微服務的高可用性由Swarm提供。Swarm容器編排系統本身支持高可用,在UFOS Swarm集羣中配置了三臺Manager節點(最多可以承受一臺Manager故障),Manager之間通過Raft進行Leader的選舉,這種選舉保證了單個節點的異常不影響整個Swarm集羣的運行。

Swarm中運行的微服務容器也是高可用的,一是可以通過啓動多個相同微服務實例來實現微服務的高可用,Swarm內部可以通過VIP的方式來實現微服務容器之間的負載均衡與故障的無縫切換(VIP只會轉發到健康的服務)。即使是單個微服務容器實例,Swarm仍能保證微服務的高可用性,如因節點故障,導致節點中運行的微服務容器異常,Swarm Manager可以自動檢測到節點異常,然後把異常節點中的微服務容器,轉移到集羣中其他健康的節點上去,並在其他節點重啓微服務應用,這樣仍然可以保證容器中運行的微服務可以被訪問,從而實現微服務的高可用性(容器編排技術可以保證容器的動態發現,即使容器被轉移到其他節點上重啓,從而實現微服務的動態訪問,當然這裏可能有個延遲,要實現這點還有一個就是需要保證微服務被設計爲無狀態的)。

數據層

Oracle Database採用典範的RAC集羣,MongoDB則是基於官方提供的容器鏡像,以容器方式實現了三臺MongoDB的Replica配置。

Redis採用主從複製模式,配置了一主二從三個節點,同時配置了相等數目的Redis Sentinel,這些Sentinel能共同合作完成故障發現與判斷,以及故障轉移,並通知應用方,從而實現真正的高可用。

ActiveMQ採用官方推薦方式,實現了基於RDBMS的主從模式,從消息隊列定時從RDBMS共享表中檢測主消息隊列的刷新情況,如主消息隊列異常,未能在指定時間內更新,從消息隊列提升自己爲主消息隊列,從而實現主從的切換。這裏需要注意的是必須保證主從服務節點的系統時間的同步。

文件系統的高可用是通過NFS文件系統與底層的存儲來實現。

經過生產環境的實踐,隨着平臺的不斷完善和運維經驗的不斷積累,UFOS平臺的可用性已從99.95%逐步提升到了99.99%。

微服務整體技術架構

以上各小節對微服務平臺的各個子系統依次進行了描述,下圖是各子系統集成到一起組成的一個完整的微服務平臺整體架構圖:

Figure 5: 微服務平臺整體技術架構圖

總結

該基於容器的微服務架構平臺給我們的研發帶來了以下益處:

經過三年多微服務平臺運營實踐,總結起來該基於容器的微服務架構平臺給我們的研發帶來以下益處:

  1. 由於完全基於開源系統,可以實現自主可控

  2. 平臺基本對於開發人員來說透明,同時DevOps使得運維簡單,有效提升了研發效率,節省了人力資源方面的投入

  3. 平臺微服務開發語言選擇更具彈性,現在平臺中已有三種語言開發的微服務,而且平臺還可以根據開發語言的發展進行語言的更迭,也可以根據市場的變化對開發語言進行調整,最大限度保護現有投資,以及最佳化未來投入

  4. 實現公司的統一開發服務雲平臺,可以無縫整合現存的第三方服務商提供的服務,有效利用平臺在服務治理方面的資源

  5. 可以方便整合第三方開源軟件系統爲平臺直接使用,爲平臺提供服務,有效節省開發人力投入

  6. 容器運行環境高度統一,微服務問題可以排除干擾,便於問題分析與排查

  7. 該架構平臺運行非常穩定,可用性高,可擴展性強,可以根據業務需要進行動態擴展,可以滿足公司業務的未來長期發展的需要,並且技術架構有一定的前瞻性,有效避免了因平臺架構選型不當導致後續的平臺改造移植造成大量的遷移和重構工作,保護了投資(資源投入,包括人力)。

該平臺架構可以作爲中小企業對微服務平臺架構選型的一種參考,當然你可以使用Kubernetes替換Docker Swarm, 畢竟後者成爲了小衆產品(如果從入手的簡潔性,Swarm依然還是具有吸引力的,幾天之類上手),其他子系統的選型也可以作爲參考。

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