微服務與 DevOps實踐:技術架構與組織架構 | IDCF

2021-04-07 09:02:25

一、概述

首先,我們來看看微服務的定義:微服務是一個界限明確、高度封裝、鬆耦合、可以獨立部署和獨立擴充套件的服務應用元件。如圖所示。微服務架構基於 SOA 和領域驅動設計(DDD)構建,其主要目的包含以下三個方面:開發的敏捷性、部署的便利性及明確的可延伸性。

image.png

其次,微服務和傳統的 SOA 有什麼異同:

  • 從設計原則上來講,微服務架構遵循 SOA 的設計原則。
  • 小的、可重用的服務並不一定是微服務,微服務架構強調敏捷、獨立開發、獨立部署、獨立擴充套件,重用在某種程度上會影響敏捷性。

微服務架構為了實現其敏捷特性,在 SOA 架構約束的基礎之上又新增了新的約束,微服務之間不能互相依賴,因此要求微服務能夠獨立部署、獨立擴充套件,微服務之間的依賴越少越好。

微服務中的一個服務只實現一個獨立的特性。

對於微服務而言,儘量不要為外部應用釋出程式碼級 API,可以通過服務呼叫或者事件解決依賴問題。

微服務中的服務之間最好通過非同步事件互動。

微服務中的每個服務都擁有自己獨立的資料。

另外,微服務架構的優點有很多:

  • 敏捷性:微服務不僅可以提高開發的敏捷性,還可以加速持續部署(CD),從而使開發團隊能夠儘快部署新的功能。
  • 減少風險:由於每個微服務都是比較小巧並且獨立部署的,因此可以減少每次部署的風險。
  • 適合分散式開發:微服務之間依賴程度低,因此可以更加靈活地獨立開發。
  • 技術靈活性:微服務之間的耦合程度低,因此技術團隊可以根據不同的特點選擇最合適的技術棧,例如利用不同的程式語言解決不同的領域問題。
  • 可延伸性:一個應用有許多微服務組成,每個微服務可以根據需要獨立擴充套件,而無須對整個應用做整體擴充套件。

鑑於以上優點,我們來看一下微服務的本質。微服務在本質上是一種服務實現模式,微服務可以實現一個應用中獨立的業務功能,而不是整個應用或者模組,因此,微服務其實是將單體應用的複雜性從程式內部轉移到了服務元件之間,這就對微服務抽象的粒度有一個比較高的權衡要求。

對於微服務的粒度,大家討論的最多,這也是實踐過程中經常遇到的令人糾結的問題。實際上,如果抽象粒度太細,就需要大量的服務編排來滿足業務場景,而大量使用服務編排就可能會導致微服務退回到 SOA 模式(服務編排是類似 BPM 或者 ESB 的系統);而如果服務粒度太粗,則不利於開發的敏捷性和部署的便利性。

微服務的主要目的是實現敏捷,微服務架構的主要構建方式是採用領域驅動設計,而領域驅動設計主要包括如下五個概念:

  • Bounded Context
  • Context Map
  • Event Sourcing
  • CQRS
  • BASE

微服務的粒度並沒有一個明確的界限,也就是說「尺寸」是相對的。通常,微服務的粒度和對敏捷性的要求密切相關,往往粒度越細其敏捷度也越高,但是並不是所有的應用對敏捷的要求都那麼高,也就是說在微服務的設計和實現過程中,對於粒度的大小可以做適當的調整。對於想要採用微服務架構的團隊,首先要考慮如下幾個前提條件:

  • 現有軟體架構是否已經服務化或者按照系統功能做了模組化切分。
  • 團隊的敏捷成熟度如何,是否有足夠的 DevOps 經驗。
  • 開發團隊是否有足夠的架構設計能力來適應採用微服務所帶來的設計方法、模式以及技術架構上的巨大差異。
  • DBA 團隊是否有能力和意願制定新的資料管理模式——將資料管理的方式由原來的集中管理轉變為去中心化管理。
  • 運維團隊是否有足夠的能力為微服務提供全新的環境管理工具、部署工具和監控工具。

綜上所述,我們建議微服務的演進線路如圖所示。

image.png

  • 從單體應用或者傳統分層架構的應用向服務化過渡,通過封裝和組合等方式提供對外發布介面的能力,從而提升應用的可存取性。
  • 通過重構將 Domain-Level(領域層面)的功能模組轉變為可以獨立部署的服務,從而提升整個應用的敏捷性。我們將這種可獨立部署的服務稱為 MiniService,其粒度比微服務粗,因而抽象難度比較低,但是也能在一定程度上獲得微服務所帶來的敏捷性提升的好處,但是對 DevOps 的基礎設施等要求沒有微服務高,因此建議沒有微服務經驗的團隊可以從 MiniService 開始嘗試。
  • 通過 Feature-Level(特性層面)的抽象,根據單一職責原則將 MiniService 拆分成微服務,從而獲得更高的擴充套件性和敏捷性。

二、融數資料微服務的架構選型

我們在構建微服務體系的過程中,經歷了技術選型、技術驗證、引入開源實現及完全自研等一系列的過程,其中也走過不少彎路,現在回想起來,感觸良多,在這裡跟大家分享一下,希望能夠有所幫助。對於技術選型,我們不希望重複發明輪子,也不希望完全受制於開源的實現,所以在技術選型時遵循如下原則:

image.png

考察社群熱度、架構成熟度、學習曲線、可維護性,如圖所示。

儘量照顧現有服務的開發方式和框架的使用習慣,不對目前的研發團隊造成太大的衝擊。能夠給程式設計師提供何種能力?我們的期望是:

  • 方便開發
  • 方便遷移
  • 多協定支援
  • 多語言支援
  • 方便監控
  • 方便運維

我們比較了可能用到的一些開源服務架構,並對它們的功能點進行了評估,如圖所示。

image.png

由於之前團隊採用 RESTEasy + Spring Boot 的方式實現服務,不希望對現有的系統和產品產生太大的影響,因此決定服務架構首先需要支援 REST 服務,又由於 Netflix 提供了比較全面的解決方案,並且 Spring Cloud 在遵循 cloud-native 原則的基礎上對 Netflix 進行了比較友好的封裝,因此初步的結論是可以基於 Spring Cloud 進行二次開發,封裝我們自己的微服務架構。

經過半年的實踐,我們發現 Netflix 技術棧的思想不錯,但是很多實現並不是特別完美,例如:

  • Zuul 提供的 Edge Service 及 API 閘道器是完全基於 HTTP 協定的過濾器實現的,基於 HTTP 協定的同步呼叫方式會帶來效能的損失,並且缺乏長連線的推播及多路複用的能力。
  • Zuul 不能滿足 RPC 呼叫的需求,而在大規模團隊共同作業的過程中,契約優先的開發方式優於程式碼優先的開發方式,因此對於應用內部互動來講,RPC 框架從管理和共同作業的角度要優於 REST 服務。

基於 Eureka 的服務註冊依然是中心化治理的方式,與傳統基於 ZooKeeper 或者 etctd/consul 的治理方式一樣,中心化的治理會給服務的部署帶來非常大的阻礙,需要對不同的環境設定不同的中心註冊伺服器,服務的版本管理及服務註冊資訊的同步也會帶來問題,更加糟糕的是,在某些服務不正常的情況下,使用者端需要進行大量的判斷,防止出現治理風暴等問題。

因此,我們後來果斷轉換思路,對之前的微服務架構進行了徹底的改造:

  • 基於對 gRPC 的封裝,構建 Service Provider 的全新實現,替代 REST 服務。
  • 通過 Proxy 方式,實現 gRPC 與 REST 服務的相互轉換,保持系統相容性。
  • 進行去中心化治理,通過 Proxy 來實現端點治理,將使用者端治理變為伺服器端治理。
  • 通過擴充套件並整合 Zipkin 實現服務鏈路監控和執行時拓撲收集。
  • 構建 Endpoint inventory 服務,以 semantic versioning 的方式管理服務版本。
  • 將 Proxy 作為服務部署的執行端點,通過 VIP 繫結,透明化使用者端定址工作。利用 Proxy 提供輕量級的負載均衡、流量控制及灰度釋出的功能。

三、設計思想

融數資料微服務架構的整體設計思想如圖所示。

image.png

該設計思想的具體說明如下。

  • 面向運維的設計,配合 DevOps 平臺,提供服務生命週期管理及易於被監控的能力。
  • 面向開發者,合理封裝。開發者無須瞭解 gRPC 的具體實現。
  • 基於 IDL 的契約優先的開發方式。
  • 提供完善的測試框架和 Mock 工具。
  • 提供完善的易於監控的能力。

四、總體架構

融數資料微服務總體架構(Graeae)如圖所示。

image.png

4.1 總體架構的特性

融數資料微服務總體架構有如下特性。

  • Graeae 架構與協定無關。該架構可以基於 Netty4、執行緒模型及 buffer pool 進行調整,以減少 GC 壓力並通過執行緒切換提升效能協定。
  • 遵循 protocol buffer 協定,可以做到通用性強、序列化效能好、壓縮效率高。
  • 語言中立,目前整個架構支援 Java、Python 和 Go 三種語言的開發。
  • 引入了熔斷器機制、流量控制、服務治理。
  • 基於 Proxy 和 PaaS 平臺進行分散式治理監控。
  • 使用整合 Zipkin 的呼叫鏈監控,以及基於 Pinpoint 的 APM 監控。
  • 對於該架構而言,直接呼叫的效能好於反射呼叫,且使用 Netty4 執行緒模型優化。

4.2 具體實現

服務提供者 Endpoint 基於責任鏈模式(如圖所示)對 gRPC 進行封裝,對遮蔽了 gRPC 框架的事件驅動採用同步呼叫方式,方便業務遷移。

image.png

Endpoint 封裝了腳手架工具,提供基於 ProtoBuf 的 IDL 介面定義語言,使用契約優先的方式定義服務,並可以自動生產伺服器端和使用者端的程式碼框架。

程式碼優先意味著實現簡單,能夠快速執行。問題也很明顯,可能和某個具體語言繫結,面對多語言環境,其打通成本較高。

契約優先的中立性提供了一箇中間橋樑,讓面向多語言成為了可能,基於契約的元資訊為後續治理和演進提供了入口點。缺點是需要引入契約語言的學習,並與多語言進行適配。

Endpoint 採用生命週期自管理的方式,提供容器化的生命週期管理 API 和相應的 SPI,方便擴充套件及與 DevOps 工具結合。

外部管理(如 Tomcat)讓使用者不用關注自身的起始、消亡,但帶來的不足是對生命週期的管理相對減弱、部署的依賴管理擴散。

程序自治可以加強其對自身生命週期的管理,高內聚,不將依賴擴散。在一定程度上能夠帶來部署的便利及不同部署環境的適應性(如雲環境)。為優雅關閉提供切入點,進一步增強對系統的可控性。

從對環境適應性和對生命週期的管理能力考慮,程序自治有著不可忽略的優勢。

Endpoint 將設定與程式碼分離,提供多種方式的設定覆蓋能力,使得改變設定無須重新部署。

設定和程式碼一起進行的優點是使開發變得簡單,但不足也很明顯,即面對不同的環境需要部署多套程式碼,複雜度增加。

設定和程式碼分離後的優點是真正做到了只部署一套程式碼。設定資訊按環境獨立設定,不受環境制約,可隨時調整。

整合 Spring Boot,提供自定義註解方式,能夠快速啟動服務,方便開發。

//服務提供方
public class SmsTemplateApplication {
public static void main(String[] args) {
new SpringApplicationBuilder().sources(SmsTemplateApplication.class)
.web(false).showBanner(false).run(args);
}
}
//服務實現
public class SmsTempletServiceImpl implements SmsTemplateService { @Autowired
private SmsTempletDao smsTempletDao;
@Transactional
public DeleteReply deleteById(IdRequest req) { return SmsTemplateReply.newBuilder().build(); }
}

利用 Proxy 部署和設定服務治理端點,進行分層治理,如圖所示。

image.png

image.png

用 Proxy 配合部署方式來相容 semantic versioning 的版本管理,版本格式有主版本號、次版本號、修訂號、版本號遞增同時需要備註,規則如下:

  • 主版本號:當做了不相容的 API 修改時,需要遞增主版本號。
  • 次版本號:當做了向下相容的功能性新增時,需要遞增次版本號。修訂號:當做了向下相容的問題修正時,需要遞增修訂號。

先行版本號及版本編譯資訊可以加到「主版本號.次版本號.修訂號」的後面,作為延伸。

五、對微服務的支撐

融數資料 DevOps 體系主要解決的問題是有效地識別和管理後設資料,以便高效、自動化、高品質地將系統動態組裝並執行起來,該體系架構如圖所示。

下面結合圖對 DevOps 體系做幾點說明。

  • 抽象了部署的最小單元——包。
  • 每個微服務都是由包及其設定組成的,前面提到,服務架構做了程式碼和設定分離,因此這裡可以將包和包的設定分離,提供運維便利性。
  • 邏輯環境包括了微服務、微服務設定及執行微服務所依賴的 Host 或者 Host Group。
  • 版本化邏輯環境,方便回滾。

該體系中的服務元件=(可執行程式碼 + 設定)&(依賴 + 設定)&(基礎設施 + 設定),如圖所示。

image.png

image.png

從部署的角度講,無論包還是包的集合(往往是組成一個獨立業務域的獨立功能)都需要依靠版本才能進行整體部署,可以部署在不同的邏輯環境上,也可以部署在同一個邏輯環境上,如圖所示。

image.png

六、DevOps 平臺總體架構

DevOps 平臺通過構建平臺將程式碼編譯成物理二進位制包,再使用後設資料對這個二進位制物理包進行描述,形成邏輯包,且將部署、依賴和二進位制包本身的後設資料統一儲存到後設資料服務中,最終通過統一環境管理平臺讀取後設資料,按需拉取相應的邏輯包對應的物理包,放置到目標環境的相應目錄下。之後通過程序管理呼叫相應的服務啟動相關微服務,在部署的過程中,由邏輯環境管理系結 VIP 到微服務的 Proxy 上,將資訊註冊到 service inventory 服務上,這樣 Proxy 和服務 Endpoint 等的執行時資訊(例如 IP、埠、版本等)就可以被收集到 service inventory 服務上。

在真正呼叫服務時通過內建的 Zipkin 可以收集服務呼叫鏈的情況,並同 inventory 服務的後設資料資訊進行匹配,便可以準確地知道服務呼叫的關係,從而達到真正的分散式治理;而使用者端呼叫只需要知道服務所在的邏輯環境資訊就可以自動完成服務定址,這個服務地址就是 Proxy 繫結的 VIP 地址,從而簡化使用者端呼叫。DevOps平臺要做的就是保證 Proxy 的健壯性,由於 Proxy 只是簡單的反向代理,不儲存服務狀態,因此只需要做故障漂移就可以了,如圖所示。

image.png

七、融數資料面向微服務的研發團隊介紹

要想成功地實施微服務架構,僅僅有服務架構、開發平臺及 DevOps 平臺是不夠的,組織和文化也需要適應微服務的需求。根據康威定律,架構由組織決定,因此需要對團隊的文化及組織劃分結構進行調整。

融數根據 two-pizza team 的原則,按照業務來劃分研發團隊,建立全棧小團隊,從而提高溝通效率、降低溝通成本,具體的團隊策略如圖所示。

image.png

將團隊切分後,我們按照業務線對組織進行架構規劃,以便技術團隊能夠專注於解決對應業務問題(業務驅動,或者說業務優先)。這時,團隊內部的設計決策將在團隊內部消化,因為團隊的規模已經是 7+/-2 人的量級,因此一般情況下不會對於團隊內成員來說過於複雜的工作。但團隊增多後,團隊的協調將是一個問題,因此微服務從技術上幫助團隊將負責的系統解耦,而計劃流程會幫助團隊在工作安排上找到合理的步驟。

那麼,雖然當一個大的業務被分解到各個小團隊時,還是會有跨團隊的設計工作,但是以上兩點是要嚴格執行的。技術團隊和業務團隊的合作並非經由上層協調,雙方的主要溝通都是團隊之間直接進行水平溝通,也就是說,在底層的團隊之間,需求、問題和日常交流都直接由業務團隊反饋給技術團隊的經理,而解決問題時容許業務團隊直接接觸開發人員,如圖所示。

我們在團隊中強調以結果為導向的工作方式,如圖所示。

image.png

團隊成員要有主人翁意識。團隊的利益就是個人的利益,團隊的成功才是個人的成功,團隊成員之間要相互幫助和補位,不允許存在「事不關己,高高掛起」的情況出現。

image.png

在團隊中,成員可以發表意見,也可以抱怨,但是不能只停留在語言層面,大家需要行動起來,找到解決問題的方法;我們希望每個開發人員都參與架構設計,而不是僅由架構師決定;我們還強調不要過度設計,鼓勵通過抽象和簡化解決問題,當然也鼓勵引入新技術,但前提是能夠有足夠的掌控力而且不影響系統穩定。

軟體工程師負責需求調研、設計、開發、測試、部署、維護、監控、功能升級等一系列的工作,也就是說軟體工程師負責應用或者服務的全生命週期的所有工作。運維是團隊成員的第一要務。在強大的自動化運維工具的支撐下,軟體工程師必須負責服務或者應用的 SLA。

在團隊中引入 oncall 機制,強調保障處理及時性,並引入卓越運維的思想,用資料統計每個團隊的線上故障、解決及時性,並逐步要求故障數量遞減,通過強大的資料支援保證團隊解決問題的 SLA,並逐步改善軟體品質。

八、總結

DevOps 的推行是按業務來組織團隊,團隊包含設計、開發、測試、運維等人員,這樣一方面可以有效減少服務內部修改所產生的內耗;另一方面,團隊邊界可以變得更為清晰。DevOps 實際是一種文化上的變遷,打破了傳統開發與運維之間的壁壘,幫助組織形成從開發、測試到部署、運維這樣一個全功能化的高效能團隊。

image.png

來源:技術瑣話,內容選自《架構寶典》

作者:王東

王東,曾任融數資料北京研發中心 CTO,負責微服務、DevOps 以及巨量資料平臺的研發和管理工作。曾供職於 IBM、普元、Amazon、OneAPM 等國內外知名公司。擁有 15 年以上的 JavaEE 程式設計和架構設計經驗,精通 DevOps 和微服務,曾領導設計和開發普元 ESB 產品。熟悉支付相關的業務流程以及各個銀行和支付機構的業務處理模式,熟悉應用與支付領域的大規模分散式系統設計和開發方法,熟悉電子商務行業的業務模型。專注於客戶行為分析、行銷、產品和服務、客戶關係、線上銷售、物流配送、渠道整合、供應鏈整合、售後服務、預測和推薦等各個電子商務主要環節的分析和設計,以及 IT 系統的規劃和實施。精通 DDD、Scrum 等軟方開發和設計方法論。

4月,【冬哥有話說】DevOps之庖丁解牛,拆解DevOps的工具及具體實戰。公眾號留言「解牛」可獲取地址

  • 《資料庫持續交付流水線分享與演示(Azure DevOps+Flyway)》
  • 《持續交付中的版本管理與基於Azure DevOps擴充套件框架的外掛開發》
  • 《微服務,多團隊共同作業中的API測試怎麼做 - Pact契約測試》
  • 《BoatHouse端到端流水線展示》