kubernetes在騰訊遊戲的應用實踐


內容來源:2017年2月14日,來自騰訊互娛高級工程師黃惠波帶來了精彩的乾貨分享,本文轉自數人云公衆號(dmesos)。


摘要

騰訊遊戲是國內最大的網絡遊戲社區,致力爲遊戲玩家提供完整的快樂解決方案。騰訊互娛高級工程師黃惠波經過一些實際場景爲你們解析kubernetes在騰訊遊戲中的應用實踐。node

大咖乾貨地址:t.cn/RS1nMFynginx

騰訊在線遊戲的容器化應用場景

2014年,咱們開啓了容器化探索之路,先回顧一下以前遇到的一些問題。

在物理機時代,資源的交付時間較長,資源的利用率較低,也不能作到隔離。到了xen\kvm虛擬機時代,問題獲得了初步的解決,但在彈性伸縮方面仍有不足。隨着Docker技術的興起,咱們開始調研Docker在遊戲容器化方面的應用。咱們的目標有兩個,一是提升資源利用率,二是經過Docker鏡像來標準化部署流程。git

選擇Docker技術以後,咱們開始了容器調度平臺的選型。咱們當時也調研了其它的一些組件,好比Shipyard、Fig等,但這些組件沒法支撐海量遊戲容器調度。而自建調度平臺的話,時間成本很是的高。就在那時,Google開源了kubernetes(當時的版本是kubernetes v0.4),咱們基於這個版本進行了定製和開發,使其成爲咱們遊戲容器的調度管理平臺。github


在2015年初的時候,TDocker平臺上線。以後,咱們開始逐步接入業務。一開始的模式很是簡單,就是把Docker當成虛擬機來使用,但這不意味着遊戲全容器化的實現。
docker

你們知道,對於一項新技術來講,你們都很謹慎,會經過不斷的灰度上線,由點到面的策略推進。截至目前,在全國各地以及加拿大等地區,都有咱們的部署點;接入容器數超過兩萬,接入的業務也有兩百多款,包括手遊、端遊、頁遊。在這麼多的業務中,主要分爲兩種場景,第一種場景是輕量級虛擬機模式,這類容器承載多個服務進程,須要一個具體的內網IP,能夠通稿SSH登陸。另外一種是微服務化模式,這種模式會拆分得很是細,每個容器對應一個服務進程,不須要對外可見的內網IP,可使用虛擬IP。
數據庫

接下來會對每個場景作一些分享。首先來看一下傳統遊戲下的架構。這是很是典型的三層服務架構,包括了接入層、邏輯層、數據庫層。同時,遊戲又分爲:全區全服、分區分服兩種類型。對於分區分服類遊戲,滾服對資源的調度很是頻繁,因此咱們須要一個高效的調度平臺。後端


容器資源的調度管理基於kubernetes v0.4版本,上圖是一個簡化後的調度框架。在Master端包括ApiServer和Scheduler,接收Web請求,而後作資源調度。在每一個node節點上,包括agent進程、Docker進程,還有Lxcfs進程。在鏡像存儲方面,當時用的是Registry V1版,後端用的是ceph存儲。如今,咱們本身維護了一個分支,功能上已知足當前的遊戲需求,並保證運行的穩定。因此在虛擬機模式下,咱們不會升級kubernetes,而是把一些好用的功能合併進來。安全

基於Kubernetes的功能定製與優化

首先講調度器,調度器爲數以萬計的容器提供了一個靈活、穩定、可靠的底層資源計算調度引擎。資源的合理分配像是一場博弈,裏面有不少矛盾的地方,須要咱們根據遊戲的特色作取捨。服務器


咱們在原有的調度策略上根據遊戲特色作了一些定製。好比在網絡方面,傳統遊戲的每一個容器都須要一個對外可見的實體IP,用戶能夠經過SSH登陸到容器裏面,所以對網絡資源進行調度。部署容器的時候,會申請network的資源(好比IP)而後進行劃分,綁定到minions對象。這樣調度器調度的時候,就能夠經過這些配置信息給容器分配好網絡資源。
網絡


在社區中,CPU的分配用的是共享CPU的方式,遊戲採用的是一種混部的模式。也就是說,將不一樣遊戲業務部署到同一臺母機,採用綁定核的方式。這樣作一方面能夠防止不一樣遊戲之間的CPU搶佔,另外一方面對遊戲成本的核算也會更加精細。例如,某個遊戲用了多少CPU這些指標都是能夠量化的。在容器分配CPU時,結全numa技術,對於CPU CORE的分配會盡可能地分配到同一個numa node上,這樣能夠提高性能,由於同個numa node的CPU訪問只需經過自身的local memory,無需經過系統總線。


在磁盤容量分配方面,因爲遊戲業務是有狀態的服務,須要存儲,因此咱們把磁盤也做爲一個可調度的資源分配給容器。還有非親和性的調度。咱們知道,在容器的可靠性與碎片優化之間須要一個權衡,讓用戶根據這些策略去選擇、部署本身的容器。例如在非親和性的策略中,用戶但願把容器是分散到各個母機上的,在母機宕機時,能夠減小對遊戲的影響。


在IDC Module分配方面,遊戲容器的部署會按地區劃分,好比按照上海、深圳或天津地區的IDC來劃分,因此咱們提供了IDC部署策略。因爲遊戲須要考慮IDC的穿越流量問題,還有網絡延時的問題,因此同一個遊戲的的不一樣模塊通常會部署到同一個IDC Module下面。

海量應用過程當中遇到的問題與解決方案


以上是基於遊戲行業特色定製的調度規劃。在資源調度過程當中,也遇到過一些問題,例如容器資源的重複調度。首先在調度過程當中它會跟ScheduledPod(已徹底調度的容器)進行比較,判斷如今是否是有足夠的資源分配給待調度容器,最後經過Bind(異步)把Pod信息寫入到ETCD。這裏就會出現一個問題,那就是異步寫入慢了或者ScheduledPod同步慢了致使ScheduledPods不能及時刷新,Scheduler計算出錯,從而形成資源重複計算。針對這個問題,咱們的解決方案是在資源調度完成後,作一個檢測的邏輯,檢測調度的容器信息是否已在ScheduledPod Cache這裏,而後再進入下一個容器的調度。固然這會帶來必定的性能損耗。

解決了這個問題,又產生了另一些問題,那就是性能的問題。在0.4版本的pod接口是很是低效的,在查每個pod狀態的時候,會經過實時查全部的Host來肯定,設計不太合理。社區也作了一些方案,當時咱們也是參考了社區的一些方案作了一些改造,把pod狀態放在Cache裏面,定時更新,從而提升查詢效率。


還有一點就是RESTClient。在kubernetes中,rest API大部分是異步進行,對於這些異步的接口的請求,並不會馬上返回結果。這裏有一個輪詢檢測狀態的邏輯,在檢測輪詢的時候有幾秒的休眠,而後進再行下一個輪詢。默認的休眠時間是2秒,這個時間對大部分場景來講有點過長,咱們經過一些功能點的調整,從物理機的小時級到虛擬機分鐘級的調度,再到還未調整以前的秒級調度,到如今達到的毫秒級調度,如今的調度能力已能知足遊戲的需求。

講完調度,再看一下網絡方面。網絡是很是關鍵、也是最爲複雜的一環。在虛擬機模式下,結合公司網絡環境爲遊戲提供高性能、穩定的網絡環境,包括Bridge+VLAN\SR-IOV兩種方案。


先來講,Docker的網絡還有kubernetes的網絡。對於Docker的NAT網絡來講,性能是最大的瓶頸,同時它與物理機或虛擬機的通訊路徑不一致也會對業務帶來一些未知的影響。好比,外界不能看到容器真實的IP(必需要使用主機IP+port方式,端口自己就是稀缺資源,而且ip+port的方式,無疑增長了複雜度),TGW仍然能夠爲業務程序服務。Host模式沒有隔離,也不符合需求。在kubernetes中,pod做爲最小調度單元,每一個pod有兩個容器,一個就是網絡容器,接管pod的網絡,提供網絡服務,並與其它容器共享net\IPC。另外一個是App Container,也就是業務容器,使用第一個網絡容器的網絡。在這種模式下,容器之間的通信是很是簡單的。對於pod到pod、pod到物理機、物理機到pod的通信,咱們爲每一個pod分配一個內網IP,對外可見,也能夠互相通信。


接下來,經過兩個方案給你們分析。首先是Bridge+Vlan的方案,母機都是部署在虛擬化區上,經過Vlan作網絡的隔離。在母機上架部署時,建立Bridge設備和VLAN設備並將它們進行關聯。建立容器的時候,使用pipework腳本建立容器所須要的虛擬網卡設備,並把它們綁定到容器和Bridge上,同時會設置容器內的IP、MAC地址以及路由等信息。從而打通容器到外界的網絡通訊。這裏也作了一些優化以提升性能,這裏能夠看到一個性能的對比,其中Bridge相對NAT網絡有至關大的提高。這種方式能夠知足一部分遊戲的需求,而有一些業務,像FPS、Moba遊戲等大流量,對網絡要求很是高的業務,還有相似MySQL-Proxy這種組件,在Bridge場景下是沒法知足需求的。

因此咱們探索了另外一種網絡方式,就是SR-IOV模式,這種模式在zen\kvm虛擬化上用的比較多,遊戲也須要這種網絡方案,所以咱們把這種網絡方案也結合到Docker容器裏面。


這裏須要硬件的支持,結合SR-IOV技術的網卡,在Docker容器內就能夠直接經過驅動來加載虛擬的網卡並使用。使用的方式就如同在一臺物理機上使用一個真實的物理網卡同樣,這個虛擬網卡也擁有驅動程序,也擁有PCI BUSID,所以全部的虛擬機網絡操做都如同操做普通網卡通常,從而在性能上獲得提高。爲了進一步發揮SR-IOV的網絡性能,還須要對容器的相關網絡參數進行配置,主要包括如下幾個方面:VF中斷CPU綁定;關閉物理機的irqbalance;容器內設置RPS(軟中斷均衡)網卡不能中斷均衡,對高性能的網絡造成了阻礙。爲了解決這個問題,須要設置容器內的軟中斷均衡。經過上述的調整,性能獲得了大幅度提高。

這是咱們測試的一個結果,這邊是物理機的,這是Bridge,這是SR-IOV,SR-IOV在網絡性能方面基本上已經接近了物理機,因此這個對於遊戲大包量、大流量的應用是很是適合的,如今咱們把SR-IOV網絡做爲傳統遊戲裏默認的網絡模式。

在遊戲容器化過程當中,咱們但願資源的使用是明確的、合理的、可量化的。因此咱們會爲每一個容器分配固定的資源,好比多少CPU、多少內存,還有須要多大磁盤、IO帶寬。在啓動容器的時候,好比CPU/Memory,經過Cgroup去作一個限制,disk經過xfs quota去作配額的限制。還有Buffered-IO帶寬的限制。


在資源分配方面,咱們開始作的是限定的CPU、內存的分配。在容器的整個生命週期,這個配置並不是一沉不變,好比在業務運行過程當中都會有一些起伏和動態調整,這是遊戲的一張生命週期圖像,生命週期比較短,多是一年半載的時間,並且這裏在線人數起伏也會比較大,須要動態調整。而動態調整就會涉及兩個方面,一是橫向的水平擴展,二是垂直伸縮。

每一個遊戲都會有一個IP,所以橫向拓展比較困難,於是更傾向於穩定的垂直擴縮。在虛擬化時代,擴縮容是有損的,須要重啓機器來實現,而Docker能夠作到無損的擴縮容。咱們對這個需求作了一些定製開發,好比CPU或者內存,經過修改Cgroup的配額去把它提高上去或是削減下來。

當在線人數上來的時候,咱們能夠給業務作到無損擴容,不影響業務服務。過了一段時間,當人數降下來時,資源會閒置,咱們會對空閒的資源作一些重複利用,將其回收。這個時候作一些縮容,針對縮容咱們作一個常態的動做,檢測這些容器的CPU、內存,結合業務的負載、活動、定時驅動。


Buffered IO Throttle須要內核支持,咱們與內核團隊進地了緊密的合做,提供了支持 Buffered IO Throttle功能的內核版本。根據容器在母機資源的佔比分配必定比例的IO帶寬。這在某種程序上解決了遊戲之間互相影響的問題。


監控、告警是整個遊戲運營過程當中最爲核心的功能之一。容器上的監控有別於物理機,cAdvisor和kubenetes結合得比較緊密,是個不錯的方案。但它也會帶來問題,那就是須要自建監控平臺,並且它與周邊各系統的兼容性也有待考驗,同時改變運維的使用習慣也須要時間。綜合考慮各類因素後,咱們放棄了cAdvisor,從新調研其它方案,但願能夠沿用公司成熟的監控平臺,並且兼容周邊系統。最終咱們選用的是lxcfs + 公司agent的方案,經過lxcfs去實現Docker容器內的虛擬proc文件系統,加強容器的隔離性。

咱們這裏以meminfo內存統計信息爲例,爲你們講解如何經過lxcfs用戶態文件系統實現Docker容器內的虛擬proc文件系。掛載虛擬proc文件系統到Docker容器,經過Docker的volume功能,將母機上的/var/lib/dockerfs/docker-xxx/proc掛載到Docker容器內部的虛擬proc文件系統目錄下/proc/。此時在容器內部/proc/目錄下能夠看到一些列proc文件,其中包括meminfo。用戶在容器內讀取/proc/meminfo時,其實是讀取宿主機上的/var/lib/dockerfs/docker-xxx/proc/meminfo掛載到容器內部的meminfo文件。內核VFS將用戶請求轉發到具體文件系統——fuse,fuse文件系統封裝VFS請求,將請求轉發給Fuse設備(/dev/fuse)。若是設備上有已經處理完成的請求(例如Cache),文件系統獲取處理結果並返回給VFS,VFS再反饋給用戶。用戶庫(fuse daemon)直接訪問Fuse設備,讀取文件系統轉發到設備上的請求,分析請求類型,調用用戶接口處理請求,處理完成後將處理結果返回給設備,再由設備返回給VFS,VFS再反饋給用戶,從而實現容器內的隔離。公司agent能夠經過讀取memory等信息,上報到監控平臺作分析與報警。同時運維經過SSH登陸到這個容器,經過free、top等命令查看性能,維持了運維原來的使用習慣。


在傳統遊戲裏,更多的是有狀態的服務會涉及到數據的存儲,咱們經過Docker的volume提供持久化存儲。最開始咱們採用HostPath方式,把host上的目錄掛載到容器裏(例如/data)做爲數據存儲。這種作法很是方便、簡單,無需額外的支持,但數據的安全性、可靠性方面比較差。因此咱們採用了另一種方案,即Ceph。改造kubenetes支持ceph,經過volume掛載,提供更安全、更可靠的數據存儲方案。解決在host故障時,數據丟失的問題,應用場景也變得更加普遍,包括數據庫存儲,鏡像存儲,容器遷移等。


今年,咱們開始支撐第一款微服務化遊戲(極品飛車online),源於以前對kubernetes的使用經驗。在微服化容器的調度中咱們沿用了kubernetes,但在版本上從新作了選擇,跟隨着社區的發展,選用了v1.2版。在微服務化模式下,遊戲的架構產生了很大的變化。按功能細分到各個小模塊,經過鏡像交付、分發,最後以容器來部署服務。每一個模塊相對獨立,之間信息流交互經過消息組件(例如RabbitMQ)來實現。同時每一個容器無須配置內網IP,能夠經過域名來訪問。因此在網絡方面也有所調整,咱們也評估了docker overlay、flannel、vxlan、maxvlan、SR-IOV等,結合其中的優缺點,最後咱們選定的方案以下:

一、集羣內pod與pod的之間的通訊,因爲不須要內網IP(能夠用虛擬IP)因此採用overlay網絡,由flannel組件實現。

二、公司內網到集羣內pod通訊,例如HAProxy,遊戲某些模塊,採用SR-IOV網絡,由本身定製的sriov-cni組件實現。這類pod具有雙重網絡,eth0對應overlay網絡,eth1對應SR-IOV網絡。

三、pod到公司內網之間的通訊。在微服務場景下,遊戲的數據存儲,周邊系統等,部署在物理機或者虛擬機上,所以pod到這些模塊、系統的訪問,走的是NAT網絡。

四、公網(Internet)接入,採用公司的TGW方案。


在整個微服化平臺上,涉及到的關健技術點會更多:

一、網絡方案:即上述講到了overlay + SR-IOV + TGW + NAT方案

二、日誌,監控:對於微服務化架構的遊戲,版本的交付都是經過鏡像,不會把公司的agent打到鏡像,因此原來的lxcfs + agent監控就不適應了,因此這裏咱們從新打造了一個新的日誌、監控平臺,與藍鯨團隊合做,實現了遊戲業務日誌採集;容器健康狀態、性能的監控

三、高可用方案:在資源的部署方面,咱們採用了replication controller方式,經過kubernetes的controller manager模塊來監測pod的狀態,在發生故障的時候,實現快速的遷移、恢復服務。另外一方面,在load balance場景下,咱們採用了HAProxy來實現

四、安全方面:kubernetes集羣承載着整個遊戲容器資源的調度、管理。無論是人爲誤操做,仍是黑客入侵,形成的影響將是很是之大。因此安全是咱們須要考慮的重點,結合kubernetes,咱們目前作了以幾方面,後面會有更加的安全策略提供

4.1 Authentication 和 Authorization。使用https來加密流量,同時在用戶權限驗證上,提供了token驗證方式、ABAC權限認證方式

4.2 Admission Controllers:配置具體的准入規則

4.3 ServiceAccount:主要解決運行在pod裏的進程須要調用kubernetes API以及非kubernetes API的其它服務問題

五、配置管理:經過configmap\secret爲遊戲提供簡易的配置管理

六、服務發現:kubernetes會爲每一個pod分配一個虛擬的IP,但這個IP是非固定的,例如pod發生故障遷移後,那麼IP就會發生變化。因此在微服務化遊戲架構下,業務pod之間的訪問更多地採用域名方式進行訪問。在kubernetes生態鏈中,提供了skydns做爲DNS服務器,結合kubernetes的server能夠很好的解決域名訪問問題


開始講遊戲容器化的時候談到用鏡像來標準化部署,因此咱們花了不少時間打造企業級的鏡像倉庫。目前支持registry v1\v2兩個版本,如右圖所示,在client端(docker)與registry之間採用nginx做爲代理,實現v1\v2不一樣請求的轉發,這樣一來,用戶無需關心到底請求的是v1仍是v2。在安全方面,不一樣類型用戶不一樣的權限驗證方案。公司內部用戶接入OA認證,與公司平臺打通。外部用戶須要申請訪問權限,由管理員分配賬號,而後經過分配的賬號來請求。在大批量拉取鏡像的時候,鏡像中心的性能、效率是咱們須要考慮的問題。前期咱們經過mirror方案來實現,在主要城市部署mirror registry,經過就近原則來拉取鏡像,解決性能瓶頸。後續咱們還會採用P2P方案來提高鏡像拉取性能。同時咱們定製了Notification Server,用於鏡像pull\push日誌記錄,便於後續分析與審計。在鏡像後端存儲方面,採用ceph集羣方案,從而提供穩定、強大的數據存儲。


微服務化之路咱們剛剛起航,在面臨挑戰的同時也帶來了機遇。不只僅是在線業務的探索,咱們也會探索離線計算、深度學習等系統的支持。


來源於社區,回饋於社區。後續咱們還會更多地參與社區互動,爲社區作貢獻。這也是咱們想去作的一點。目前有個開源的項目,sriov kubernetes的網絡插件(github.com/hustcat/sri…),集中了騰訊遊戲兩種模式下容器的高性能網絡經驗,你們感興趣的能夠關注下。

相關文章
相關標籤/搜索