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

原文地址:https://mp.weixin.qq.com/s/sFsdK9uAud_3ELuie6KrvQ

作為容器集群管理技術競爭的大贏家,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 層深度融合

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

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


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


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

小結

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


分享到:


相關文章: