Kubernetes在微服務化遊戲中的探索實踐_Kubernetes中文社群
本文來自DockOne社群技術分享。
大家好,我是黃惠波,今天分享的主題是kubernetes在微服務化遊戲中的探索實踐
首先來看下微服務化遊戲容器化探索之路
隨著docker技術在近幾年的快速發展,國內外掀起了一股容器之風。而我們也在這時,開啟了遊戲容器化的探索之路。最開始在docker容器的應用上,還是以VM的模式去部署,畢竟遊戲是非常複雜的應用,沒有統一的模式。除此之外,對於一項全新技術的應用,大家都很謹慎,一步一步地去實踐。
而在近一兩年,部分遊戲的架構也逐漸往微服務化方向轉變,以下是一款遊戲的架構
遊戲的邏輯層按不同的服務劃分為不同的模組,每個模組都是高內聚低耦合,之間的通訊通過訊息佇列(或者API)來實現。模組的版本通過
我們再來看另一款微服務化遊戲的架構
也是按功能模組劃分不同的服務,前端通過HAProxy來代理使用者請求,後端服務可以根據負載來實現擴縮容。在服務發現模組中,通過registrator來監視容器的啟動和停止,根據容器暴露的埠和環境變數自動註冊服務,後端儲存使用了consul,結合consul-template來發現服務的變化時,可以更新業務配置,並重載。
對於這些微服務化的遊戲,服務模組小且多。那麼,怎樣快速部署,怎樣彈性伸縮,怎樣實現服務發現等等,都是我們需要解決的問題。容器化這些服務是一個不錯的方案,接下來就是容器排程、編排平臺的建設了。在當前也有多種方案可選,
以下會從幾個維度來分析kubernetes在微服務化遊戲上的實踐
定製的網路與排程方案:叢集的網路方案,是最為複雜,也是最為基礎的一項。結合業務各模組之間的訪問關係,我們選定的方案如下
叢集內各模組之間的通訊:overlay網路
我們基於flannel來實現overlay網路,每個主機擁有一個完整的子網,在這個扁平化的網路裡面,管理簡單。當我們建立容器的時候,會為容器分配一個唯一的虛擬IP,容器與容器之間可以方便地通訊。當然,在實現中,業務也並非單純的用IP來訪問,而是結合DNS服務,通過域名來訪問,後面會講到
以下是基於flannel實現的overlay網路的通訊案例:
假設當sshd-2訪問nginx-0:當packet{172.16.28.5:port => 172.16.78.9:port} 到達docker0時,根據node1上的路由規則,選對flannel.1作為出口,同時,根據iptables SNAT規則,將packet的源IP地址改為flannel.1的地址(172.16.28.0/12)。flannel.1是一個VXLAN裝置,將packet進行隧道封包,然後發到node2。node2解包,然後根據node2上的路由規則,從介面docker0出發,再轉給nginx-0。最終實現通訊
公司內網到叢集內模組的通訊:sriov-cni
sriov-cni是我們基於CNI定製的一套SRIOV網路方案,而CNI作為kubernetes network plugins,外掛式接入,非常方便,目前已開源,地址:https://github.com/hustcat/sriov-cni
以下是SRIOV網路拓撲圖與實現細節
1、母機上開啟SRIOV功能,同時向公司申請子機IP資源,每個VF對應一個子機IP
2、Kubernetes在排程時,為每個pod分配一個VF與子機IP
3、在pod拿到VF與IP資源,進行繫結設定後,就可以像物理網絡卡一樣使用。
同時我們也做了一些優化:包括VF中斷CPU繫結同時關閉物理機的irqbalance功能,容器內設定RPS,把容器內的中斷分到各個CPU處理,來提升網路效能。
此類容器除了SRIOV網路之外,還有一個overlay網路介面,也即是多重網路,可以把公司內網流量匯入到overlay叢集中,實現叢集內外之間的通訊。在實際應用中,我們會用此類容器來收歸通往叢集內的通訊,例如我們用haproxy LB容器來提供服務。
對接公網:採用公司的TGW方案
TGW接入時,需要提供物理IP,所以對接TGW都會用到SRIOV網路的容器,例如上面提到的haproxy LB容器。這樣公網通過TGW訪問haproxy,再由haproxy轉到叢集內容器,從而打通訪問的整個鏈路。
叢集內模組訪問公司內網通訊:NAT方案
接下來再來看下定製的排程方案
在上述網路方案中,我們講到SRIOV需要繫結物理IP,所以在容器排程中,除了kubernetes原生提供的CPU\Memory\GPU之外,我們還把網路(物理IP)也作為一個計算資源,同時結合kubernetes提供的extender scheduler介面,我們定製了符合我們需求的排程程式(cr-arbitrator)。其結構如下
cr-arbitrator做為extender scheduler,整合到kubernetes中,包括兩部分內容:
- 預選演算法:在完成kuernetes的predicates排程後,會進入到cr-arbitrator的預選排程演算法,我們以網路資源為例,會根據建立的容器是否需要物理IP,從而計算符合條件的node(母機)
- 優選演算法:在整個叢集中,需要物理IP的容器與overlay網路的容器並未嚴格的劃分,而是採用混合部署方式,所以在排程overlay網路的容器時,需要優化分配到沒有開啟sriov的node上,只有在資源緊張的情況下,才會分配到sriov的node上。
除了cr-arbitrator實現的排程策略外,我們還實現了CPU核繫結。可以使容器在其生命週期內使用固定的CPU核,一方面是避免不同遊戲業務CPU搶佔問題;另一方面在穩定性、效能上(結合NUMA)得到保障及提升,同時在遊戲業務資源核算方面會更加的清晰。
2、域名服務與負載均衡
在網路一節,我們講到kubernetes會為每個pod分配一個虛擬的IP,但這個IP是非固定的,例如pod發生故障遷移後,那麼IP就會發生變化。所以在微服務化遊戲架構下,業務模組之間的訪問更多地採用域名方式進行訪問。在kubernetes生態鏈中,提供了skydns作為DNS伺服器,可以很好的解決域名訪問問題。
在kubernetes叢集中,業務使用域名訪問有兩種方式:
- 一是通過建立service來關聯一組pod ,這時service會擁有一個名字(域名),pod可以直接使用此名字進行訪問
- 另一個是通過pod的hostname訪問(例如redis.default.pod…)。原生功能不支援,主要是kube2sky元件在生成域名規則有缺陷。針對這個問題,我們進行了優化,把pod的hostname也記錄到etcd中,實現skydns對pod的hostname進行域名解析
在負載均衡方面:通常情況下,遊戲的一個模組可以通過deployment或者是replication controller來建立多個pod(即多組服務),同時這些容器又需要對外提供服務。如果給每個pod都配置一個公司內網IP,也是可以解決,但帶來的問題就是物理IP資源浪費,同時無法做到負載均衡,以及彈性伸縮。
因此,我們需要一個穩固、高效的Loadbalancer方案來代理這些服務,其中也評估了kubernetes的service方案,不夠成熟,在業務上應用甚少。剛好kubernetes的第三方外掛service-loadbalancer提供了這方面的功能,它主要是通過haproxy來提供代理服務,而且有其它線上遊戲也用了haproxy,所以我們選擇了service-loadbalancer。
service-loadbalancer除了haproxy服務外,還有一個servicelb服務。servicelb通過kubernetes的master api來時時獲取對應pod資訊(IP和port),然後設定haproxy的backends,再reload haproxy程序,從而保證haproxy提供正確的服務。
3、監控與告警
監控、告警是整個遊戲運營過程中最為核心的功能之一,在遊戲執行過程中,對其效能進行收集、統計與分析,來發現遊戲模組是否存在問題,負載是否過高,是否需要擴縮容之類等等。在監控這一塊,我們在cadvisor基礎上進行定製,其結構如下:
每個母機部署cadvisor程式,用於收集母機上容器的效能資料,目前包括CPU使用情況,memory,網路流量,TCP連線數
在儲存方面,目前直接寫入到TenDis中,後續如果壓力太大,還可以考慮在TenDis前加一層訊息佇列,例如kafka叢集
Docker-monitor,是基於cadvisor收集的資料而實現的一套效能統計與告警程式。在效能統計方面,除了對每個容器的效能計算外,還可以對遊戲的每個服務進行綜合統計分析,一方面用於前端使用者展示,另一方面可以以此來對服務進行智慧擴縮容。告警方面,使用者可以按業務需求,配置個性化的告警規則,docker-monitor會針對不同的告警規則進行告警。
4、日誌收集
Docker在容器日誌處理這一塊,目前已很豐富,除了預設的json-file之外,還提供了gcplogs、awslogs、fluentd等log driver。而在我們的日誌系統中,還是簡單的使用json-file,一方面容器日誌並非整個方案中的關鍵節點,不想因為日誌上的問題而影響docker的正常服務;另一方面,把容器日誌落地到母機上,接下來只需要把日誌及時採集走即可,而採集這塊方案可以根據情況靈活選擇,可擴充套件性強。我們當前選擇的方案是filebeat+kafka+logstash+elasticsearch,其結構如下
我們以DaemonSet方式部署filebeat到叢集中,收集容器的日誌,並上報到kafka,最後儲存到Elasticsearch叢集,整個過程還是比較簡單。而這裡有個關鍵點,在業務混合部署的叢集中,通過filebeat收集日誌時怎樣去區分不同的業務?而這恰恰是做日誌許可權管理的前提條件,我們只希望使用者只能檢視自己業務的日誌。
以下是具體的處理方案與流程:
- 首先我們在docker日誌中,除了記錄業務程式的日誌外,還會記錄容器的name與namespace資訊。
- 接著我們在filebeat的kafka輸出配置中,把namespace作為topic進行上報,最終對應到Elasticsearch的index
在我們的平臺中,一個namespace只屬於一個業務,通過namespace,可以快速的搜尋到業務對應的日誌,通過容器的name,可以檢視業務內每個模組的日誌
5、基於image的釋出擴容
在微服務化遊戲中,模組與模組之間是高內聚低偶合,模組的版本內容一般都會通過持續整合來構建成一個個映象(即image),然後以image來交付、部署。同時,遊戲版本釋出都有一個時間窗,整個釋出流程都需要在這個時間窗裡完成,否則就會影響使用者體驗。怎樣做到版本的高效釋出? 這裡有兩個關鍵點:一是基於kubernetes的釋出有效性;一是image下發效率;
Kubernetes在容器image釋出這一塊的支援已比較穩定,對於無狀態的服務,還可以考慮rolling-update方式進行,使遊戲服務近乎無縫地平滑升級,即在不停止對外服務的前提下完成應用的更新。
在提高image下發效率方面,我們基於Distribution打造了一個企業級映象中心,主要涉及到以下幾點:
- ceph叢集提供穩定、強大的後端資料儲存
- 效能優化:mirror方案與P2P方案,實現快速的下載映象。同時對於時效性更高的使用者需求,還可以實現image預部署方案
- 安全方面:不同型別使用者不同的許可權驗證方案。例如公司內部使用者會提供安全認證,其它使用者提供使用者名稱密碼認證
- Notification Server實現pull\push日誌記錄,便於後續分析與審計
以上便是kubernetes在微服務化遊戲中的一個解決方案,定製的網路與排程方案,為遊戲容器的執行提供基礎環境;域名服務與負載均衡,解決遊戲高可用、彈性伸縮問題;通過效能資料、日誌的收集、統計分析,及時發現程式問題與效能瓶頸,保證遊戲容器穩定、可持續性執行;最後,基於image的釋出擴容,使得遊戲部署流程更加標準化以及高效。
以上便是今晚的全部分享,感謝大家