微服務化的不同階段 Kubernetes 的不同玩法

作為容器集群管理技術競爭的大贏家,Kubernetes已經和微服務緊密聯繫,採用Kubernetes的企業往往都開始了微服務架構的探索。然而不同企業不同階段的微服務實踐面臨的問題千差萬別,註定要在技術路線上產生分叉。如何選擇適合自己的技術,是每一個踐行微服務的團隊面臨的第一個問題。 網易雲 是Kubernetes的第一批重度用戶,在不同業務場景下解決了很多挑戰,在本文中,網易雲首席解決方案架構師劉超梳理了基於Kubernetes構建微服務體系的進階之路。

微服務化的必要性

一個產品的發展,通常可分為冷啟動階段、高速增長階段和成熟階段。

產品冷啟動階段,需求是以最簡單的架構驗證業務。以網易考拉海購(以下簡稱“考拉”)為例,最初的架構設計目標就是快速啟動,驗證產品方向,該架構包括在線、緩存、線下和管理服務四個方面,即一般電商平臺加上跨境電商必備的進銷存系統,採用了Oracle數據庫、OpenStack管理的虛擬機(VM),並沒有諸如高併發之類的考慮。

產品高速增長階段,業務規模逐漸擴大,產品複雜度也隨著增加,企業需要解決快速迭代、高可靠和高可用等問題,一個自然的選擇是服務化的拆分,把一個單體架構拆分成一些較小的模塊,並遵循康威定律,用5-9個小團隊來適應架構的變化。仍以考拉為例,考拉在高速增長階段也慢慢演化出各種新的模塊,比如單獨的支付模塊、貨倉模塊、第三方商家模塊、推送模塊等,並基於Dubbo框架打造服務發現功能來支持各模塊之間的相互調用。

服務化主要解決了變更的問題。在整個架構演進的過程中,各個模塊都面臨爆炸性的增長,比如海淘、自營、第三方商家的供應鏈,Web、APP、H5的呈現,限時購、秒殺、預售的活動頁,以及倉庫與物流系統、支付系統的對接等,緊耦合則牽一髮而動全身,工程臃腫,影響迭代速度,分別獨立上線更有利於適應業務發展的需求。考拉在高速增長階段首先按照主頁、活動頁、優惠券、支付等維度縱向拆分,之後又不斷演進成為100多個相互關聯的模塊,變更頻率由每天2次增長到每天1000多次,產品質量提升52%。

容器化的優勢與挑戰

拆分成大量小模塊之後,虛擬機與服務化架構的配合就出現了很多新的挑戰,於是有了容器化的需求。

劉超解釋說,拆分之前首先要解決“合”的問題,即需要保證功能還是原來的功能,代碼質量還是原來的代碼質量,不會引入新的bug。他認為,微服務化需要從一開始就要做好持續集成,而容器是很好的持續集成的工具,完成從代碼提交到自動測試、自動發佈的工作。容器化會帶來開發流程的變化,把環境交付過程從運維人員提前到開發人員手上。

微服務化的不同階段 Kubernetes 的不同玩法

在架構複雜的情況下,比如100多個模塊,再加上各種副本,所有環境都由一個運維團隊來完成,不僅工作量繁重,而且還容易出錯,但這是使用虛擬機的模式。而如果寫一個Dockerflie放到代碼倉庫,由開發人員來考慮開發完成之後應用部署的配置環境、權限等問題,包括測試環境的部署、聯調環境的部署、生產環境的部署,問題就很好解決了。這就是容器化帶來的流程變化。

然而,這種轉變涉及到開發人員是否願意學習容器技術。劉超推薦的解決辦法,是使用鏡像分層的形式,即最內部的環境包括操作系統及系統工具的鏡像由運維人員來做,中間層環境的鏡像由核心開發人員完成,普通開發人員只需把jar或者war扔到相應的路徑下即可,這就極大降低企業組織容器化的障礙。

微服務化的不同階段 Kubernetes 的不同玩法

場景一:Kubernetes + Docker + VM + Host Network

第一種場景,就是用Kubernetes管理虛擬機,容器的網絡、存儲會面臨各種各樣的選型。企業如果對容器的網絡、存儲瞭解不足,可以把容器當成一個持續集成的工具,把一個容器嵌入到一個虛擬機裡面,相當於用容器鏡像代替腳本部署。這種做法需要解決兩個問題:一是IP保持的問題,二是盤保持的問題。因為原先採用虛擬機的時候,是基於有狀態的設計,認為IP、Volume都是保持不變的。當容器僅僅作為持續集成的工具,團隊的這個習慣可能改不了。

一個方案是自己實現一個有狀態容器的方式,實現IP的保持,當一個節點掛了,重新啟動的虛擬機和容器仍然可以使用原先分配的IP,二是把Docker容器的鏡像一層層地Mount到外面的Volume裡面,當一個節點掛了,Docker所有的鏡像和Volume其實還掛載在外面的Ceph上,數據並未丟失。這和使用VM很相似,既可以Docker化支持微服務化,也不需要改變用戶習慣。使用Kubernetes壓力相對比較大的團隊,可以通過這種方式切入。

場景二:Kubernetes + Docker + PM + Bridge Network

第二種場景,企業沒有使用虛擬機,有一部分應用部署在物理機(PM)上,同時想把一部分應用遷移到容器裡。此時,不管物理機是否嵌套虛擬機,直接創建一個Bridge Network,把物理網卡也打進去,當Docker的網卡和Bridge連起來的時候,整個網絡就是平的,容器和容器旁邊的物理機都使用同一個指定的網段。網絡打平之後,使用Dubbo的團隊也可以比較順暢地把一部分物理機上部署的應用逐漸遷移到容器裡,而如果沒有Bridge Network,中間過負載均衡(LB)或者NAT時會很彆扭,因為Kubernetes層的維護人員通常很難勸說Dubbo層開發人員改變應用開發的方式。

使用Bridge Network,Kubernetes網絡配置很簡單,使用CNI的方式即可。如果有定製化以適應應用層的需求,可以參考Docker run的手動配置方式,開發自己的CNI插件。大致流程是先創建網橋(如果不存在),獲取Namespace,配置veth pair並放到Namespace裡,然後獲取IP地址,獲取網絡和路由。

場景三:Kubernetes + Docker + PM + SR-IOV Network

Bridge的方式,能夠滿足一般的Java應用部署的需求,但一些需要更高性能的應用,需要高吞吐量、高併發、高PPS,比如電商大促情況下的緩存,這時候可以採用SR-IOV代替Bridge來解決問題,帶寬比較大但PPS上不去(大包或大量小包)的情況,SR-IOV都可以解決,但是需要購買SR-IOV網卡,成本比較高。

高可用設計要點

無狀態。做好持續集成之後,第一件事情應該是把應用分為有狀態(Stateful)和無狀態(Stateless)兩個部分,並且使大部分應用是無狀態的,這樣可以更好地適應彈性伸縮。即便Kubernetes已經可以支持有狀態應用的部署,劉超還是建議在應用層儘量實現無狀態,使得有狀態應用聚集在少數的集群裡面。有狀態最重要的是數據庫和緩存,通常內存數據放在緩存,需要持久化的數據放在數據庫裡。

微服務化的不同階段 Kubernetes 的不同玩法

分佈式數據庫。數據庫的高可用, 網易雲 採用的是DDB(分佈式數據庫)方案,基於MySQL的多臺主備及負載均衡做分庫分表, 網易雲RDS 基於自己的MySQL內核優化,能夠實現主備切換不丟數據,能夠很好地支持容器化,有狀態容器掛掉之後,重新啟動一個容器,只要做好前序重置和冪等,就不會有業務問題。所以 網易雲RDS 的主備切換也從虛擬機向容器過渡。

緩存。高併發應用需要每一層都有緩存,把客戶需求儘可能地攔在前面,吞吐量就大很多。但緩存不像數據庫一樣有持久化機制,其高可用、跨機房就需要做雙寫,因為緩存保持在內存中,掛了就沒有了,修復難度很大。其他的組件,比如ZooKeeper、Kafka、消息隊列、HBase,都有各自的高可用機制。所以,一個發展中的應用應當被分成很顯著的兩個部分,一部分是無狀態的,另一部分有狀態的就放到本身具有高可用機制的組件裡面。

成熟階段架構

產品成熟階段要解決的問題,主要是如何通過服務治理、系統運維自動化提升可靠性和可用性,如何高效完成大項目的複雜協作,如何梳理功能、深化用戶體驗。以正在進行全面服務化的考拉為例,2017年雙11期間其工程數量相對平時增加了20多倍,應用、存儲集群規模膨脹了5倍,挑戰之大不必多說。劉超對成熟階段架構設計強調了兩點:

不可變基礎設施:使用Kubernetes容器技術不能沿襲虛擬機時代的操作方式,而是應當採用不可變基礎設施,即所有的改變,都應該在Git的改變裡面有所體現,修改環境就是修改Dockerfile,修改配置文件也是代碼層次的改變,整個環境的部署,當代碼merge的時候,會觸發通過容器自動部署的腳本,這能很好地保持環境的一致性。大規模節點下,如果是手動部署,出錯很容易,排查卻很難。所以,不可變基礎設施非常重要。

IaC(基礎設施即代碼)部署與擴容: 網易雲 在Kubernetes的編排之外封裝了另一個編排,也是在倉庫裡面維護的,任何的修改,比如要升級5個應用,這5個應用的版本號都在這裡面都配置好,代碼commit之後就觸發自動部署,如果發現問題,很容易回滾,只需把代碼revert回來,後續流程會自動觸發。如果依賴於寫Yaml文件來做,頻繁升級且版本號控制不好時,就很容易回滾失誤。

場景四:Kubernetes+Docker+PM+Overlay Network

成熟階段通常使用Kubernetes+Docker+PM+Overlay Network的模式,企業一旦開始用Overlay Network,基本上都會使用物理機,否則使用虛擬機會出現兩層Overlay。這時候Flannel、Calico、Romana或者Weave等很多的選型都可以,Flannel的性能已經越來越好。

場景五:Kubernetes和IaaS層深度融合

[網易雲]( https://www.163yun.com/?tag=M... )的方式,是Kubernetes與IaaS深度融合實現動態擴展資源,目前集群調度規模支持30000+節點。這個規模下,首先要解決的是動態資源創建優化,這樣才符合資源精細利用、成本最優化的設計。同時,不論虛擬機的創建還是容器的創建,對應用都是透明的,也就是說,應用只需要明確一個模塊要變成3個節點還是5個節點,不需要管Docker是不是要變成多少個節點、這些節點要放在哪裡、虛擬機和物理機是否有資源之類的問題,後續的動作都是聯動的。

動態資源創建的實現, 網易雲 改造了Kubernetes創建流程,主要是監聽Pod創建的事件,由Resource Controller判斷有沒有足夠的Volume資源、Network資源,Schedule判斷有沒有足夠的Node資源,有則直接綁定,無則動態申請之後再綁定,然後由Kubernetes下發。添加資源的時候,只有應用層和機房兩層,機房只需要把物理機添加到IaaS層,不需要管上面是否有Kubernetes,虛擬機的創建全部是動態的,應用層只管應用層的事情,中間都是透明的。

微服務化的不同階段 Kubernetes 的不同玩法

其次是 網絡優化。 [網易雲]( https://www.163yun.com/?tag=M... )大部分容器是運行在虛擬機上的,同時也提供採用SR-IOV網卡的裸機容器,用於需要更高性能的緩存、分佈式數據庫等。大部分的應用可以橫向擴展,還是在IaaS裡面。但是[網易雲]( https://www.163yun.com/?tag=M... )希望容器裡面的網卡,讓最外層虛擬機上的OVS也可以看到,即只有一層Overlay,虛擬機裡面有一個Bridge,但如果不需要,也可以直接打到外面的OVS上,另外還有一個管理網絡,跨租戶也是同一個Kubernetes來管理。只有一層Overlay意味著沒有二次的虛擬化,同時原來部署在虛擬機裡面的應用遷移到容器中,虛擬機和容器的網絡都是通過OVS來管理,採用Dubbo做服務發現會非常平滑,這是針對業務層壓力的解決方案。其實OpenStack有一個CNI插件,也採用了類似的做法,和Neutron聯動,把VIF打在外面的OVS上。

如果想學習Java性能優化,工程化、高性能及分佈式、深入淺出。微服務、Spring,MyBatis,Netty源碼分析的朋友可以加下454377428群裡有阿里大牛直播講解技術,以及Java大型互聯網技術的視頻免費分享給大家。

小結

本文結合 網易雲服務 內外部客戶的Kubernetes實踐經驗,總結了產品高速增長期和成熟期使用Kubernetes容器技術實現微服務架構的五種應用場景,針對不同的挑戰提出了易於執行的解決方案,並介紹了 網易雲 的獨門優化方法,希望對讀者有所啟發。


分享到:


相關文章: