本文由 網易雲 釋出。
作為容器叢集管理技術競争的大赢家,Kubernetes 已經和微服務緊密聯系,采用 Kubernetes 的企業往往都開始了微服務架構的探索。然而不同企業不同階段的微服務實踐面臨的問題千差萬别,注定要在技術路線上産生分叉。如何選擇适合自己的技術,是每一個踐行微服務團隊面臨的第一個問題。
網易雲是 Kubernetes 的第一批重度使用者,在不同業務場景下解決了很多挑戰,在本文中,網易雲首席解決方案架構師劉超梳理了基于 Kubernetes 建構微服務體系的進階之路。
微服務化的必要性
一個産品的發展,通常可分為冷啟動階段、高速增長階段和成熟階段。
産品冷啟動階段,需求是以最簡單的架構驗證業務。以網易考拉海購(以下簡稱 “考拉”)為例,最初的架構設計目标就是快速啟動,驗證産品方向,該架構包括線上、緩存、線下和管理服務四個方面,即一般電商平台加上跨境電商必備的進銷存系統,采用了 Oracle 資料庫、OpenStack 管理的虛拟機(VM),并沒有諸如高并發之類的考慮。
産品高速增長階段,業務規模逐漸擴大,産品複雜度也随着增加,企業需要解決快速疊代、高可靠和高可用等問題,一個自然的選擇是服務化的拆分,把一個單體架構拆分成一些較小的子產品,并遵循康威定律,用 5-9 個小團隊來适應架構的變化。仍以考拉為例,考拉在高速增長階段也慢慢演化出各種新的子產品,比如單獨的支付子產品、貨倉子產品、第三方商家子產品、推送子產品等,并基于 Dubbo 架構打造服務發現功能來支援各子產品之間的互相調用。
服務化主要解決了變更的問題。在整個架構演進的過程中,各個子產品都面臨爆炸性的增長,比如海淘、自營、第三方商家的供應鍊,Web、APP、H5 的呈現,限時購、秒殺、預售的活動頁,以及倉庫與物流系統、支付系統的對接等,緊耦合則牽一發而動全身,工程臃腫,影響疊代速度,分别獨立上線更有利于适應業務發展的需求。考拉在高速增長階段首先按照首頁、活動頁、優惠券、支付等次元縱向拆分,之後又不斷演進成為 100 多個互相關聯的子產品,變更頻率由每天 2 次增長到每天 1000 多次,産品品質提升 52%。
容器化的優勢與挑戰
拆分成大量小子產品之後,虛拟機與服務化架構的配合就出現了很多新的挑戰,于是有了容器化的需求。
劉超解釋說,拆分之前首先要解決“合”的問題,即需要保證功能還是原來的功能,代碼品質還是原來的代碼品質,不會引入新的 bug。他認為,微服務化需要從一開始就要做好持續內建,而容器是很好的持續內建工具,完成從代碼送出到自動測試、自動釋出的工作。容器化會帶來開發流程的變化,把環境傳遞過程從運維人員提前到開發人員手上。

在架構複雜的情況下,比如 100 多個子產品,再加上各種副本,所有環境都由一個運維團隊來完成,不僅工作量繁重,而且還容易出錯,但這是使用虛拟機的模式。而如果寫一個 Dockerflie 放到代碼倉庫,由開發人員來考慮開發完成之後應用部署的配置環境、權限等問題,包括測試環境的部署、聯調環境的部署、生産環境的部署,問題就很好解決了。這就是容器化帶來的流程變化。
然而,這種轉變涉及到開發人員是否願意學習容器技術。劉超推薦的解決辦法,是使用鏡像分層的形式,即最内部的環境包括作業系統及系統工具的鏡像由運維人員來做,中間層環境的鏡像由核心開發人員完成,普通開發人員隻需把 jar 或者 war 扔到相應的路徑下即可,這就極大降低企業組織容器化的障礙。
場景一: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 已經可以支援有狀态應用的部署,劉超還是建議在應用層盡量實作無狀态,使得有狀态應用聚集在少數的叢集裡面。有狀态最重要的是資料庫和緩存,通常記憶體資料放在緩存,需要持久化的資料放在資料庫裡。
分布式資料庫:資料庫的高可用,網易雲采用的是 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,虛拟機的建立全部是動态的,應用層隻管應用層的事情,中間都是透明的。
其次是網絡優化。網易雲大部分容器是運作在虛拟機上的,同時也提供采用 SR-IOV 網卡的裸機容器,用于需要更高性能的緩存、分布式資料庫等。大部分的應用可以橫向擴充,還是在 IaaS 裡面。但是網易雲希望容器裡面的網卡,讓最外層虛拟機上的 OVS 也可以看到,即隻有一層 Overlay,虛拟機裡面有一個 Bridge,但如果不需要,也可以直接打到外面的 OVS 上,另外還有一個管理網絡,跨租戶也是同一個 Kubernetes 來管理。隻有一層 Overlay 意味着沒有二次的虛拟化,同時原來部署在虛拟機裡面的應用遷移到容器中,虛拟機和容器的網絡都是通過 OVS 來管理,采用 Dubbo 做服務發現會非常平滑,這是針對業務層壓力的解決方案。其實 OpenStack 有一個 CNI 插件,也采用了類似的做法,和 Neutron 關聯,把 VIF 打在外面的 OVS 上。
小結
本文結合網易雲服務内外部客戶的 Kubernetes 實踐經驗,總結了産品高速增長期和成熟期使用 Kubernetes 容器技術實作微服務架構的五種應用場景,針對不同的挑戰提出了易于執行的解決方案,并介紹了網易雲的獨門優化方法,希望對讀者有所啟發。
網易雲采用 Kubernetes + Docker 建構,設計思路是讓加速應用上雲。
了解 網易雲 :
網易雲官網:https://www.163yun.com/
新使用者大禮包:https://www.163yun.com/gift
網易雲社群:https://sq.163yun.com/