2016年伊始,Docker無比興盛,現在Kubernetes萬人矚目。在這個無比須要創新與速度的時代,由容器、微服務、DevOps構成的雲原生席捲整個IT界。在近期舉辦的QCon全球軟件開發大會上,個推應用平臺基礎架構高級研發工程師王志豪,基於他在基礎架構方面多年的經驗,分享了《個推基於Docker和Kubernetes的微服務實踐》。sql
個推應用平臺基礎架構高級研發工程師王志豪數據庫
1、微服務化編程
微服務架構後端
微服務是將單一的應用程序拆分紅多個微小的服務,各個小服務之間鬆耦合,高內聚,每一個小的服務能夠單獨進行開發,不依賴於具體的編程語言,也可使用不一樣的數據存儲技術,各個服務能夠獨立部署,擁有各自的進程,相互之間經過輕量化的機制進行通訊(如基於HTTP的API接口),全部的服務共同實現具體的業務功能。緩存
客戶端與服務端通訊有2種方式,第一種是客戶端直接與各個微服務進行通訊,這樣的架構有4個缺點:網絡
(1)屢次服務請求,效率低;架構
(2)對外暴露服務接口;併發
(3)接口協議沒法統一;框架
(4)客戶端代碼複雜,服務端升級困難。運維
第二種方式是由API網關統一代理各個服務,對外提供統一的接口協議,該架構有3 個優點:
(1)封裝服務接口細節,減小通訊次數;
(2)統一通訊協議,減小客戶端代碼耦合;
(3)統一鑑權,流控,防攻擊;
在該架構下,網關也有可能成爲系統瓶頸。
相應地,這2種架構也帶來了2種服務註冊發現的方式,第一種是客戶端經過向服務的註冊中心查詢微服務的地址與其通訊,第二種是增長統一的API網關來查詢。前者會增長客戶端的複雜度,開發成本高,第二種操做會顯得更加簡潔,所以咱們在實踐的時候選擇了第二種架構方式。
微服務數量增長之後,服務之間的調用關係易產生耦合,甚至出現循環調用的狀況,最好的應對方法是對服務進行分層,即將相互依賴的服務經過消息隊列等技術進行異步解耦,減小服務間的依賴。
服務分層
微服務的具體實踐
1.技術選型
在實踐中,咱們的API Gateway使用的是OpenResty, OpenResty基於Nginx並擴展了對Lua的支持,可構建高併發的Web服務。咱們經過HTTP接口實現客戶端通訊,數據基本封裝成JSON格式,服務間的通訊接口也是基於HTTP,並利用消息隊列進行異步解耦;至於服務註冊發現,咱們使用的是Consul;咱們選擇了Lua(擴展API Gateway的功能),Node.js(用於開發後端服務),Java(用於密集計算和與大數據通訊的場景)做爲主要的開發語言。
2.具體實現過程
在實踐過程當中,咱們使用Lua開發了本身的微服務框架——WebLua,其封裝服務之間的通訊協議和訪問外部資源(如Mysql、Rdis等)的方法和依賴,同時提供了應用插槽。咱們能夠將每個APP當作一個功能模塊,每一個APP都須要插到WebLua中才能運行。WebLua能夠方便地將模塊進行組合,既能夠一個APP運行一個微服務,也能夠多個APP一塊兒對外提供服務。如此,開發者只需關注業務APP開發,很大程度上提升了開發效率。上圖右側是具體的代碼目錄結構,每一個APP可分爲Actions,Page,Data三層,Action層在請求處理先後進行攔截,可作某些特殊處理,如請求前進行權限校驗等;Page層主要對請求的參數進行解析和校驗;Data層負責具體業務處理,同時提供了Shell腳本,可實現APP打包和部署安裝。
2、 API網關
在架構中一個重要角色就是API網關,下面來作一個介紹。
從上面的對比圖中能夠看到,左側是沒有API Gateway的,不少的模塊如Auth,Logging等,這些代碼都須要本身去實現,形成了模塊的重複建設,同時侵入了服務,功能擴展比較困難;右側的圖是使用了API Gateway以後的架構圖,全部通用模塊均在API Gateway實現,維護簡單,一處建設,各處受益。在這種狀況下,對API Gateway也提出了更高要求——其功能必須能夠很方便地擴展。
爲了實現這樣的API網關,咱們基於 OpenResty,借鑑了Kong和Orange的插件機制,經過插件來擴展API網關功能。
從上面的API Gateway架構圖中能夠看到,網關安裝諸多插件,每一個插件會在請求的一個或多個階段發揮做用。插件配置會在Consul上更新,實時生效,插件規則可靈活配置。在操做中,咱們爲插件開發者提供了更多自由選擇,開發者能夠本身定義格式。
3、容器化
在微服務落地實踐時咱們選擇了Docker,下面將詳細介紹個推基於Docker的實踐。
首先網絡組件選擇的是Calico,服務註冊發現和配置管理選擇的是Consul。Consul-Template可實時監測Consul配置和服務的變化。
個推鏡像體系是以Centos爲基礎系統鏡像,安裝OpenResty,Nodejs,Jdk,由此獲得環境鏡像,再在這個基礎上安裝微服務框架,得到Gorp鏡像。再在這個基礎上安裝具體應用服務,獲得應用服務鏡像。
服務註冊發現和配置更新流程
在API網關中,服務註冊經過Consul-Agent來實現,配置更新經過Consul-Template實現。Consul-Template主要更新3類配置,包括:Services:代理的全部微服務的服務地址;Products:簡言之即請求到微服務的映射表,如左上所示,全部請求都有統一個規範,從Host中能夠獲取Prod,從URI中能夠獲取APP,這 2個信息可將請求動態路由到具體服務;Nging-Conf:產品的Nginx配置。
應用服務容器,服務註冊的方式跟API網關一致。首先,服務經過容器內部運行的Consul Agent將服務註冊到Consul上,其次經過Consul-Template來監測觀察 Consul上配置的變化,並更新配置文件。OpenResty或者WebNode配置的更新是直接覆蓋相應的配置文件,而後重啓對應的服務。
上圖是個推基於Docker的集羣架構,從中可看到,Docker集羣包括3個節點,整個微服務分爲3層,最上層是API Gateway,中間是業務層,最下層是一些多產品公用的基礎的微服務。
4、Kubernetes實踐
微服務雖然有不少好處,但也帶來了不少問題,其中一個就是運維複雜。之前運維只須要面對一個單體應用便可,如今可能面臨的是幾十甚至上百的微服務。在這種狀況下,咱們須要藉助Kubernetes來解決問題。Kubernetes是Google開源的一個容器編排工具,可用於協助管理容器。
一開始,咱們將容器向Kubernetes集羣遷移時,沒作任何改變,只是採用Pod將全部的服務體系在Kubernetes集羣運行。但隨着深刻使用Kubernetes,咱們對微服務作了一些改變。
1.首先咱們換成用Deployment的方式來部署服務,Deployment會保證服務時刻有必定的副本存活,提升了服務穩定性。
2.其次,咱們使用了Service,它能夠代理Pod實現負載的均衡。
3. Kube-DNS能夠將Service名解析成具體的ClusterIP,而且當Service沒有刪除重建時,其clusterIP不變,如此DNS解析的緩存就不存在失效問題。基於Kube-DNS和Service的特性,後續咱們改造了服務註冊發現體系。
上圖是咱們當前的服務部署方式,Pod用Deployment的方式建立,用Service來進行代理。
在實踐過程當中,咱們還遇到了另外一個問題,即配置管理問題。
(1)微服務化後配置文件多而分散;
(2)不一樣環境之間有不少沒必要要的差別,如數據庫名;
(3)在不少不一樣環境中,相同的配置項暴露給測試和運維;
(4)沒有版本控制,回滾比較麻煩;
(5)基於Consul的Web UI沒法對非法的輸入進行校驗。
針對這些問題咱們作了如下調整:
(1) 統一不一樣環境間沒必要要的差別;
(2) 對配置文件進行模板化,只暴露差別部分,同時可實現不一樣配置文件集中配置;
(3)基於Consul開發配置中心,對產品配置集中管理;對輸入進行合法性校驗;增長版本控制,方便回滾。
配置中心流程圖
關於日誌服務,咱們在應用容器中集成了Fluent-Bit,配置了2個輸入源,TCP和tail, 輸出也有2個,一個是Elasticsearch,全部的日誌都會上傳到ES經過Kibana展現查詢,另外一個是日誌審計服務,有些須要進行審計的操做日誌會發送到日誌審計服務進行進一步的分析處理。
微服務數量增長之後,請求鏈路可能延長,開發者在追蹤問題和排查性能瓶頸時會很不方便,所以咱們引入了Zipkin,其主要用於分佈式鏈路追蹤,在API Gateway實現了一個插件進行Span收集,後端服務則經過開源的中間件來實現。
上圖是個推目前的總體架構圖,最底層是K8S集羣,上面部署了Kube-DNS,Consul用於服務註冊發現和配置管理,再者是咱們分層的微服務體系,右側是一些輔助的管理系統。
5、總結
上述是個推基於Docker和Kubernetes的整個微服務實踐過程,咱們在實踐微服務過程當中作了9件重要的事情,簡化了操做流程,提升了工做效率。個推設計實現了本身的微服務框架,完成微服務的容器化部署,自研API網關,並基於Consul的服務註冊和配置管理,使用Kubernetes對容器進行編排,基於Service和Kube-DNS對服務註冊和發現體系進行改造,搭建了本身的配置中心,優化了日誌服務,實現了Zipkin鏈路追蹤。