網易容器雲平臺的微服務化實踐(一)

此文已由做者馮常健受權網易雲社區發佈。數據庫

歡迎訪問網易雲社區,瞭解更多網易技術產品運營經驗。編程

摘要:網易雲容器平臺指望能給實施了微服務架構的團隊提供完整的解決方案和閉環的用戶體驗,爲此從 2016 年開始,咱們容器服務團隊內部率先開始進行 dogfooding 實踐,看看容器雲平臺能不能支撐得起容器服務自己的微服務架構,這是一次頗有趣的嘗試。安全

一旦決定作微服務架構,有不少現實問題擺在面前,好比技術選型、業務拆分問題、高可用、服務通訊、服務發現和治理、集羣容錯、配置管理、數據一致性問題、康威定律、分佈式調用跟蹤、CI/CD、微服務測試,以及調度和部署等等,這並不是一些簡單招數可以化解。實踐微服務架構的方式有千萬種,咱們探索並實踐了其中的一種可能性,但願能夠給你們一個參考。本文是《網易容器雲平臺的微服務化實踐》系列文章的第一篇。服務器

Docker 容器技術已通過了最先的喧囂期,逐漸在各大公司和技術團隊中應用。儘管以今天來看,你們從觀念上已經逐漸承認 「將鏡像定義爲應用交付標準,將容器做爲應用運行的標準環境」 的觀點,但仍是有至關一部分人在迷惑容器技術做爲一個標準,應該怎麼落地,怎樣才能大規模線上應用,怎麼玩才能真正解放生產力,促進軟件交付效率和質量?答案其實在應用的架構當中。網絡

微服務架構不是因 Docker 容器技術而生,但確實是因容器技術而火。容器技術提供了一致性的分發手段和運行環境,使得只有微服務化後的應用架構,才能配合容器發揮其最大價值。而微服務化架構引入了很大的複雜性,只有應用容器化以及規模化的容器編排與調度才能避免運維效率降低。容器技術和微服務化架構之間本是一種相輔相成的互補關係。架構

網易容器雲平臺的前身是網易應用自動部署平臺 (OMAD),它可以利用 IaaS 雲提供的基礎設施,實現包括構建和部署一體化在內的整個應用生命週期管理。2014 年,以 Docker 爲表明的容器技術進入大衆視野,咱們驚喜地發現,容器技術是自動部署平臺從工具型應用進化爲平臺型應用過程當中最重要的一塊拼圖。本來用戶須要初始化主機,而後藉助自動部署平臺完成應用的構建和部署。引入容器技術以後,用戶從功能開發到測試到一鍵部署上線,整個應用交付過程當中不用關心主機初始化、主機間通訊、實例調度等一系列應用以外的問題。這簡直是信仰 DevOps 的人的福音。負載均衡

咱們從 2015 年開始探索容器技術的最佳實踐方式,從當初 「胖容器」 與容器集羣的產品形態,到後來關於有狀態和無狀態服務的定義,以及現在的新計算與高性能計算,咱們一直在思考並豐富着容器技術的應用場景。不管產品形態如何調整,容器雲平臺的核心概念一直是 「微服務」,經過微服務這一抽象提供高性能的容器集羣管理方案,支持彈性伸縮、垂直擴容、灰度升級、服務發現、服務編排、錯誤恢復、性能監測等功能,知足用戶提高應用交付效率和快速響應業務變化的需求。網易雲容器平臺指望能給實施了微服務架構的團隊提供完整的解決方案和閉環的用戶體驗,爲此從 2016 年開始,咱們容器服務團隊內部率先開始進行 dogfooding 實踐,一方面檢驗容器雲平臺能不能支撐得起容器服務自己的微服務架構,另外一方面經過微服務化實踐經驗反哺容器雲平臺產品設計,這是一次頗有趣的嘗試,也是咱們分享容器雲平臺微服務化架構實踐的初衷。框架

在談容器服務的微服務架構實踐以前,有必要先把網易雲容器服務大體作個介紹。目前網易雲容器服務團隊以 DevOps 的方式管理着30+微服務,每週構建部署次數 400+。網易雲容器服務架構從邏輯上看由 4 個層次組成,從下到上分別是基礎設施層、Docker 容器引擎層、Kubernetes (如下簡稱 K8S)容器編排層、DevOps 和自動化工具層:運維

容器雲平臺總體業務架構以下:異步

拋開容器服務具體業務不談,僅從業務特徵來講,能夠分紅如下多種類型(括號內爲舉例的微服務):

○ 面向終端用戶 (OpenAPI 服務網關)、面向服務(裸機服務)
○ 同步通訊(用戶中心)、異步通訊(構建服務)
○ 數據強一致需求(etcd 同步服務)、最終一致需求(資源回收服務)
○ 吞吐量敏感型(日誌服務)、延時敏感型(實時服務)
○ CPU 計算密集型(簽名認證中心)、網絡 IO 密集型(鏡像倉庫)
○ 在線業務(Web 服務)、離線業務(鏡像檢查)
○ 批處理任務(計費日誌推送)、定時任務(分佈式定時任務)
○ 長鏈接(WebSocket 服務網關)、短鏈接(Hook 服務)
○ ……

一旦決定作微服務架構,有不少現實問題擺在面前,好比技術選型、業務拆分問題、高可用、服務通訊、服務發現和治理、集羣容錯、配置管理、數據一致性問題、康威定律、分佈式調用跟蹤、CI/CD、微服務測試,以及調度和部署等等……這並不是一些簡單招數可以化解。

做爲主要編程語言是 Java 的容器服務來講,選擇 Spring Cloud 去搭配 K8S 是一個很天然的事情。Spring Cloud 和 K8S 都是很好的微服務開發和運行框架。從應用的生命週期角度來看,K8S 覆蓋了更廣的範圍,特別像資源管理,應用編排、部署與調度等,Spring Cloud 則對此無能爲力。從功能上看,二者存在必定程度的重疊,好比服務發現、負載均衡、配置管理、集羣容錯等方面,但二者解決問題的思路徹底不一樣,Spring Cloud 面向的純粹是開發者,開發者須要從代碼級別考慮微服務架構的方方面面,而 K8S 面向的是 DevOps 人員,提供的是通用解決方案,它試圖將微服務相關的問題都在平臺層解決,對開發者屏蔽複雜性。舉個簡單的例子,關於服務發現,Spring Cloud 給出的是傳統的帶註冊中心 Eureka 的解決方案,須要開發者維護 Eureka 服務器的同時,改造服務調用方與服務提供方代碼以接入服務註冊中心,開發者需關心基於 Eureka 實現服務發現的全部細節。而 K8S 提供的是一種去中心化方案,抽象了服務 (Service),經過 DNS+ClusterIP+iptables 解決服務暴露和發現問題,對服務提供方和服務調用方而言徹底沒有侵入。

對於技術選型,咱們有本身的考量,優先選擇更穩定的方案,畢竟穩定性是雲計算的生命線。咱們並非 「K8S 原教旨主義者」,對於前面提到的微服務架構的各要點,咱們有選擇基於 K8S 實現,好比服務發現、負載均衡、高可用、集羣容錯、調度與部署等。有選擇使用 Spring Cloud 提供的方案,好比同步的服務間通訊;也有結合二者的優點共同實現,好比服務的故障隔離和熔斷;固然,也有基於一些成熟的第三方方案和自研系統實現,好比配置管理、日誌採集、分佈式調用跟蹤、流控系統等。

咱們利用 K8S 管理微服務帶來的最大改善體如今調度和部署效率上。以咱們當前的狀況來看,不一樣的服務要求部署在不一樣的機房和集羣(聯調環境、測試環境、預發佈環境、生產環境等),有着不一樣需求的軟硬件配置(內存、SSD、安全、海外訪問加速等),這些需求已經較難經過傳統的自動化工具實現。K8S 經過對 Node 主機進行 Label 化管理,咱們只要指定服務屬性 (Pod label),K8S 調度器根據 Pod 和 Node Label 的匹配關係,自動將服務部署到知足需求的 Node 主機上,簡單而高效。內置滾動升級策略,配合健康檢查 (liveness 和 readiness 探針)和 lifecycle hook 能夠完成服務的不停服更新和回滾。此外,經過配置相關參數還能夠實現服務的藍綠部署和金絲雀部署。集羣容錯方面,K8S 經過副本控制器維持服務副本數 (replica),不管是服務實例故障(進程異常退出、oom-killed 等)仍是 Node 主機故障(系統故障、硬件故障、網絡故障等),服務副本數可以始終保持在固定數量。

Docker 經過分層鏡像創造性地解決了應用和運行環境的一致性問題,可是一般來說,不一樣環境下的服務的配置是不同的。配置的不一樣使得開發環境構建的鏡像沒法直接在測試環境使用,QA 在測試環境驗證過的鏡像沒法直接部署到線上……致使每一個環境的 Docker 鏡像都要從新構建。解決這個問題的思路無非是將配置信息提取出來,以環境變量的方式在 Docker 容器啓動時注入,K8S 也給出了 ConfigMap 這樣的解決方案,但這種方式有一個問題,配置信息變動後沒法實時生效。咱們採用的是使用 Disconf 統一配置中心解決。配置統一託管後,從開發環境構建的容器鏡像,能夠直接提交到測試環境測試,QA 驗證經過後,上到演練環境、預發佈環境和生產環境。一方面避免了重複的應用打包和 Docker 鏡像構建,另外一方面真正實現了線上線下應用的一致性。

Spring Cloud Hystrix 在咱們的微服務治理中扮演了重要角色,咱們對它作了二次開發,提供更靈活的故障隔離、降級和熔斷策略,知足 API 網關等服務的特殊業務需求。進程內的故障隔離僅是服務治理的一方面,另外一方面,在一個應用混部的主機上,應用間應該互相隔離,避免進程間互搶資源,影響業務 SLA。好比絕對要避免一個離線應用失控佔用了大量 CPU,使得同主機的在線應用受影響。咱們經過 K8S 限制了容器運行時的資源配額(以 CPU 和內存限制爲主),實現了進程間的故障和異常隔離。K8S 提供的集羣容錯、高可用、進程隔離,配合 Spring Cloud Hystrix 提供的故障隔離和熔斷,可以很好地實踐 「Design for Failure」 設計哲學。

服務拆分的好壞直接影響了實施微服務架構的收益大小。服務拆分的難點每每在於業務邊界不清晰、歷史遺留系統改造難、數據一致性問題、康威定律等。從咱們經驗來看,對於前兩個問題解決思路是同樣的:1)只拆有肯定邊界能獨立的業務。2)服務拆分本質上是數據模型的拆分,上層應用經得起倒騰,底層數據模型經不起倒騰。對於邊界模糊的業務,即便要拆,只拆應用不拆數據庫。

如下是咱們從主工程裏平滑拆出用戶服務的示例步驟:

1.將用戶相關的 UserService、UserDAO 分離出主工程,加上 UserController、UserDTO 等,造成用戶服務,對外暴露 HTTP RESTful API。
2.將主工程用戶相關的 UserService 類替換成 UserFaçade 類,採用 Spring Cloud Feign 的註解,調用用戶服務 API。
3.主工程全部依賴 UserServce 接口的地方,改成依賴 UserFaçade 接口,平滑過渡。

通過以上三個步驟, 用戶服務獨立成一個微服務,而整個系統代碼的複雜性幾乎沒有增長。

數據一致性問題在分佈式系統中廣泛存在,微服務架構下會將問題放大,這也從另外一個角度說明合理拆分業務的重要性。咱們碰到的大部分數據一致性場景都是能夠接受最終一致的。「定時任務重試+冪等」 是解決這類問題的一把瑞士軍刀,爲此咱們開發了一套獨立於具體業務的 「分佈式定時任務+可靠事件」 處理框架,將任何需保證數據最終一致的操做定義爲一種事件,好比用戶初始化、實例重建、資源回收、日誌索引等業務場景。以用戶初始化爲例,註冊一個用戶後,必須對其進行初始化,初始化過程是一個耗時的異步操做,包含租戶初始化、網絡初始化、配額初始化等等,這須要協調不一樣的系統來完成。咱們將初始化定義爲一種 initTenant 事件,將 initTenant 事件及上下文存入可靠事件表,由分佈式定時任務觸發事件執行,執行成功後,清除該事件記錄;若是執行失敗,則定時任務系統會再次觸發執行。對於某些實時性要求較高的場景,則能夠先觸發一次事件處理,再將事件存入可靠事件表。對於每一個事件處理器來講,要在實現上確保支持冪等執行,實現冪等執行有多種方式,咱們有用到布爾型狀態位,有用到 UUID 作去重處理,也有用到基於版本號作 CAS。這裏不展開說了。

當業務邊界與組織架構衝突時,從咱們的實踐經驗來看,寧願選擇更加符合組織架構的服務拆分邊界。這也是一種符合康威定律的作法。康威定律說,系統架構等同於組織的溝通結構。組織架構會在潛移默化中約束軟件系統架構的形態。違背康威定律,很是容易出現系統設計盲區,出現 「兩無論」 互相推脫的局面,咱們在團隊間、團隊內都碰到過這種狀況。
nbsp;
本文是《網易容器雲平臺的微服務化實踐》系列文章的第一篇,介紹了容器技術和微服務架構的關係,咱們作容器雲平臺的目的,以及簡單介紹了網易雲容器服務基於 Kubernetes 和 Spring Cloud 的微服務化實踐經驗。限於篇幅,有些微服務架構要點並未展開,好比服務通訊、服務發現和治理、配置管理等話題;有些未說起,好比分佈式調用跟蹤、CI/CD、微服務測試等話題,這些方面的實踐經驗會在後續的系列文章中再作分享。實踐微服務架構的方式有千萬種,咱們探索並實踐了其中的一種可能性,但願能夠給你們一個參考。

網易雲計算基礎服務深度整合了 IaaS、PaaS 及容器技術,提供彈性計算、DevOps 工具鏈及微服務基礎設施等服務,幫助企業解決 IT、架構及運維等問題,使企業更聚焦於業務,是新一代的雲計算平臺,點擊可免費試用。

文章來源: 網易雲社區

相關文章
相關標籤/搜索