做者 | 夏梓耀
杏仁後端工程師,勵志成爲計算機藝術家mysql
首先簡單介紹一下什麼是配置中心,咱們爲何須要它,爲何要花力氣去完善它。git
傳統單體應用( monolithic apps )因種種潛在缺陷,如:隨着規模的擴大,部署效率逐漸下降,團隊協做效率差,系統可靠性變差,維護困難,新功能上線週期長等,迫切須要一種新的架構去解決這些問題,而微服務( microservices )架構正是當下一種正確的解法。spring
不過,解決一個問題的同時,每每會誕生出不少新的問題,故:微服務化的過程當中伴隨着不少的挑戰,其中一個挑戰就是有關服務(應用)配置的。sql
當系統從一個單體應用,被拆分紅分佈式系統上一個個服務節點後,配置文件也必須更着遷移(分割),這樣配置就分散了,不只如此,分散中還包含着冗餘,冗餘分兩方面:服務與服務之間(如:有 A,B,C 三個服務調用 D 服務,那麼 D 服務的地址會被複制三份,由於 A,B,C 三個服務是 share nothing 的),同服務實例之間(如:A服務的全部實例都是同樣的配置,且它們在物理上頗有多是分散的,即:不在一臺機器上)。數據庫
在單體應用時期,咱們管理配置只須要考慮環境(develop,test,staging,producting...)這一個維度,那麼如今就多了服務(應用)這個維度。 再明確一下上面說的問題:配置文件分散且冗餘,映射到配置管理上就是:分散意味着須要一個個處理,冗餘意味着重複操做。後端
爲了解決這個問題,配置必須集中管理(邏輯上分散,物理上集中),而實現這個功能的系統組件(或基礎設置)就是配置中心。安全
既然集中管理,那麼就是要將應用的配置做爲一個單獨的服務抽離出來了(配置再也不和應用一塊兒進代碼倉庫),同理也須要解決新的問題,好比:版本管理(爲了支持回滾),權限管理,灰度發佈等網絡
以上討論的還都停留在靜態配置的層面上,而應用除了靜態的配置(如:數據庫配置,一些雲服務的參數配置,服務啓動後通常不會變更),還會有一些動態的配置(如:灰度開關,一些常量參數:超時時間,限流閾值等),還有理論上:架構
在一個大型的分佈式系統中,你沒有辦法把整個分佈式系統停下來,去作一個軟件的、硬件的或者系統的升級app
業務需求的一些自然動態行爲(如:一些運營活動,會動態調整一些參數),加之理論上必需要支持這個特性,因此,配置中心服務還得支持動態特性,即:配置熱更新。
簡單總結一下,在傳統巨型單體應用紛紛轉向細粒度微服務架構的歷史進程中,服務配置中心是微服務化不可缺乏的一個系統組件,其解決的就是:分佈式系統的動態配置問題。
那麼咱們是怎麼解決的呢?那就是 Matrix 1.0 的故事了。
的配置文件都交給 Matrix 管理,擁有環境隔離,版本控制,權限管理等功能。
配置文件是在 CI 構建階段靜態注入的,不一樣環境注入相應的配置文件,對不一樣的 build 工具(如:maven,sbt)都實現了配置注入的插件,來從 Matrix 上拉取配置文件
那麼爲何還要進行 Matrix 2.0 的工做呢?Matrix1.x 有什麼缺陷嗎?
在服務(應用)數很少的狀況下,Matrix1.x 是徹底夠用的,可是隨着業務規模的發展,問題會漸漸暴露出來。
Matrix 並無解決配置冗餘的問題,咱們須要相似模版的東西,將冗餘部分抽取成一份並共享,下降配置維護成本。
其次,CI 打包與配置注入耦合,意味着打包與環境耦合,一個環境對應一個包(鏡像),這其實違背了容器的"一份鏡像處處運行"的理念,即:鏡像與環境無關,應用須要什麼環境的配置,在啓動階段肯定(注入)就能夠了。
最後是沒有配置動態(熱)更新能力,前面已說過這個也是必需要支持的;只有咱們的基礎設施是完美的,其之上的業務才能是完美的。
全部問題,在服務規模小(管理/運維複雜度低)時,都不是問題。
隨着業務複雜度的不斷提升,和微服務架構的不斷深化,現有的 Matrix 1.x 版本暴露出不少缺失的關鍵能力(好比權限管理,配置模板,熱發佈等),逐漸成爲產品快速迭代的瓶頸之一,咱們亟需對 Matrix 進行升級改造,打造一個更強大、更易用的微服務配置中心。
因此咱們須要 Matrix 2.0。
不過開始作以前,仍是得先靜下心來評估一下,是繼續走自研,仍是選擇別人開源的進行二次開發?我作了一段時間的調研,顯然繼續自研的開發 cost 是巨大的,核心功能如:配置熱更新,灰度發佈,配置模版(去冗餘)都得從零開始開發, 其次還要保證高性能,高可用等非功能性需求。
二次開發的選擇其實也很少(這並非什麼壞事),參考網絡上已有的對比與討論,可得出如下結論:
註冊中心 | 配置存儲 | 時效性 | 數據模型 | 維護性 | 優勢 | 缺點 |
---|---|---|---|---|---|---|
disconf | zookpeer | 實時推送 | 支持傳統的配置文件模式,亦支持 KV 結構數據 | 提供界面操做 | 基於分佈式的 Zookeeper 來實時推送穩定性、實效性、易用性上均優於其餘 | 代碼複雜, 2016 年 12 月以後沒有更新,功能不夠強大,不能徹底知足咱們的需求 |
zookpeer | zookpeer | 實時推送 | 支持傳統的配置文件模式,亦支持 KV 結構數 | 命令操做 | 實時推送穩定性、實效性 | 太底層,開發量大 |
diamond | mysql | 每隔15s拉一次全量數據 | 只支持 KV 結構的數據 | 提供界面操 | 簡單、可靠、易用 | 數據模型不支持文件,使用不方便 |
Spring Cloud Config | git | 人工批量刷新 | 文件模式 | Git 操做 | 簡單、可靠、易用 | 須要依賴 GIT,而且更新 GIT |
Apollo | mysql | 實時推送 + 定時拉取 | 支持傳統的配置文件模式,亦支持 KV 結構數 | 提供界面操 | 架構設計和穩定性考慮比較完善,很多大廠在用,Portal 的體驗不錯, 更新活躍 | 總體架構略顯複雜,和咱們容器環境不太一致 |
就目前看來"真正能打的"就 Apollo 了,因爲自研成本較大,而且 Apollo 的代碼也並不複雜(是標準的 Spring Boot 項目),其功能也基本上能覆蓋咱們的需求,因此咱們最後選擇基於 Apollo 進行二次開發。
對於不瞭解 Apollo 的同窗,下面簡單介紹一下,詳情可參考官方文檔
Apollo(阿波羅)是攜程框架部門研發的開源配置管理中心,可以集中化管理應用不一樣環境、不一樣集羣的配置,配置修改後可以實時推送到應用端,而且具有規範的權限、流程治理等特性。
其 Portal 界面的截圖以下:
目前其最新版本(1.0.0)主要提供的功能有:
能夠看到 Apollo 提供了不少強大的功能,解決了 Matrix 1.0 待解決的問題;錦上添花的是其對 Spring 項目的支持很是好,這又能大大節省開發時間。
二次開發主要是作適應咱們環境的定製改造,而非添加大的新功能(至少這個階段不會),首當其衝的就是部署模式的改變,下圖是 Apollo 的架構圖:
這個圖是從上往下看的,第一層是 client,也就是與應用集成的 SDK,還有 Protal(包括本身的 server 和 db),它們經過一層 SLB 訪問到 Meta Server,而Meta Server 是 Eureka(服務註冊中心)的一層封裝,用於發現 Config Service 和 Admin Service(它們啓動時會向 Eureka 註冊本身),也就是最後一層,這兩個服務共同管理着咱們的配置,配置則存儲在 ConfigDB 中。
外面還有一條 Client 到 Config Service 的箭頭,就是其實時推送機制的實現原理:Client 經過(HTTP)Long Polling的方式,不停的詢問 Config Service,若是配置有更新(發佈)則會當即返回,無更新則返回 HTTP 狀態碼 304。
而咱們的服務都是部署在 Kubernetes 集羣上的,Kubernetes 有本身的服務發現功能,包括 LB 功能,因此與 Eureka 重複了,須要將 Eureka 剝離出來,將 Meta Server 去掉,咱們的部署方式以下圖:
全部非生產環境對應一個 portal,每一個環境獨立部署 config service 和 admin service,其中生產環境在單獨的 k8s 集羣中(單獨對生產環境作了隔離,是由於 Apollo 的權限管理還不夠強大,不支持區分環境的訪問權限)。
這個改造分三部分,第一部分和第二部分是分別去除 Protal 和 Client 對 Meta Server 對依賴,即:去除在 Client 和 Protal 中配置的 Meta server 地址,而是直接使用 Kubernetes 暴露的服務域名(開發和 local 環境由於須要本地訪問,有公網地址)。
第三部分是去除 Config Service 上的 Eureka Server,Admin Service 上的 Eureka Client,由於 Eureka 很是成熟易用,且是個聲明式(基於註解和配置)的框架,因此改動起來並不麻煩,咱們只是將 Eureka 的註解去掉了而已,沒有把依賴去掉(若是要去掉依賴,那麼工做量會很大,由於依賴的地方多,整個 Meta Server 都是對其 Client API 的封裝)。
惟一的坑就是記得在配置文件中把spring.cloud.discovery.enabled設置爲 false,由於 Protal 對 Admin Service 的健康檢查是基於spring boot actuator,而它是自動的,檢測到有 Eureka 的依賴,就會啓動相關健康檢查的 Endpoint。
若是想作的好一點,能夠作個配置開關,啓動時配置是否啓用 Eureka,固然這樣工做量會很大,或許官方會考慮(畢竟容器編排系統是趨勢,大中小廠都會用)。
其次咱們還添加了 LOC 環境(針對本地開發),UT 環境(針對單元測試),這方面 Apollo 有方便自定義環境的方法提供。不過咱們並不滿意官方推薦的本地開發模式,咱們但願配置中心對本地開發是透明的,對於 Spring Boot 項目,原來是基於profile 覆蓋的,那麼如今仍是 profile 覆蓋的方式,這是最好的。然而 SDK 是將 Apollo 上拉取的配置覆蓋在 Spring 的配置上的,即 Spring 的 profile 機制會失效,經討論後決定在 Client 啓動時:若是檢測到當前是 LOC 環境,則將 Apollo 和 Spring 的配置覆蓋順序倒置。
這裏簡單的說一下實現原理,SDK 對 Spring 項目的集成是經過往 Environment 的 propertySourceList 中以 addFirst 的方式添加本身的配置,放在了查找鏈的最前面,故達到了覆蓋一切的目的,而咱們在檢測到是 LOC 環境時以 addLast 的方式添加 Apollo 上的配置,就不會覆蓋任何配置了,原來的 profile 機制依然有效。
還有一個坑,就是 SDK 的初始化時間問題,或者說是 Client 拉取配置的時間點問題;任何應用都有框架級別的配置,而有些配置是在一個很是早的時間點生效的,這是"應用啓動時讀取配置"這種方式必須考慮的問題,Spring Boot 項目的 Logging System 就是初始化早於 Apollo 的配置初始化(覆蓋)的例子,也就是說:關於 log 的配置,不會生效。
咱們解決的方式就是在 Apollo 配置初始化以後,從新初始化這些模塊,使得 Apollo 的配置生效,這種解法的副產物是:咱們使得這些模塊的配置具備了動態性(咱們能夠安全的在運行期去從新初始化這些模塊)
爲此咱們封裝了本身的 SDK:vapollo(依賴於 apollo-client 的 SDK),提供定製功能。
二次開發結束後,接着面對的問題就是配置遷移問題,由於咱們選擇了對 Apollo 進行二次開發,而不是對 Matrix1.x 進行擴展,故:咱們須要將配置遷移過去。
咱們編寫了詳細的配置遷移操做手冊(主要涵蓋了操做流程和配置規範),並設定了遷移計劃,以後項目的配置會慢慢所有遷移過去,爲了更安全的實施這個過程,咱們在實際遷移一個簡單項目後,發現還能夠編寫工具來幫助咱們,好比配置的 diff(檢查是否遺漏項,不一樣項)。
Matrix2.0 的工做暫時告一段落,本文雖然是總結工做內容的,可是最後我但願跳出眼前的工做,看一看將來,從而瞭解咱們如今的不足之處。
主要分兩部分:Matrix2.x 和 Matrix3.0
Matrix2.x 指現階段工做的延續,就目前來講(實際使用一段時間後),還有不少操做上的可優化點(好比:添加更多的默認設置,讓開發人員對 Apollo 更加無感知),其次是關於配置的規範也會不斷的完善(什麼環境必需要有那些配置,namespace 的建立和關聯),甚至會有完善的配置發佈流程,有了規範,就會有 review,config review(就像代碼 review 那樣)
世界是變化的,現階段的工做只是符合了咱們如今的環境,以後會怎樣?咱們的微服務架構還在發展,咱們還沒上 service mesh,現在基礎設置都在不斷的下沉,咱們的配置中心須要怎樣?我想也是同樣的,對開發來講:咱們對配置的存在會愈來愈無感知,我不關心這個配置項是哪來的,不關心當前是何種部署環境,不關心配置項是變化仍是不變的,我只關心用到它的業務是怎樣的;另外一方面對配置的管理和維護也應該愈來愈智能,愈來愈自動。但願 Matrix3.0 能實現這些目標。