效率提升10倍,網(wǎng)易游戲面向終態(tài)的應(yīng)用交付實踐
講師介紹
林香鑫,網(wǎng)易游戲團隊負責(zé)人。2014年加入網(wǎng)易游戲,目前任網(wǎng)易游戲技術(shù)中心平臺服務(wù)組團隊負責(zé)人、技術(shù)專家,長期負責(zé)運維開發(fā)方向的工作,當(dāng)前主要負責(zé) CMDB 和配置管理、流程引擎、交付平臺、基礎(chǔ)組件和工具等研發(fā)管理工作。
一、應(yīng)用交付形態(tài)
1、應(yīng)用交付

大家對應(yīng)用交付都很熟悉,游戲應(yīng)用交付簡單來說就是將游戲代碼分發(fā)到服務(wù)器上對外提供服務(wù),然后玩家通過客戶端連接,使玩家能夠順利進入體驗游戲。
應(yīng)用交付這一過程說起來簡單,但在準(zhǔn)備過程中,需要運維同學(xué)完成許多工作。例如資源管理、配置管理、多環(huán)境需求、面向游戲的業(yè)務(wù)運維。游戲的業(yè)務(wù)運維與其它類型應(yīng)用的業(yè)務(wù)運維有很大區(qū)別,這也是我們在設(shè)計整套機制時遇到的問題,下文中的許多案例也會圍繞這一點展開。
游戲的發(fā)布和運維具有特殊性,例如游戲會強調(diào)開服、關(guān)服時間,可能會對整個資源的交付時間以及開服時效有較高的要求。除此之外,游戲本身是一個交互式的服務(wù),它可能會影響玩家的體驗,所以對于日常服務(wù)運維過程中的問題排查,也會有較高的時間要求。
2、應(yīng)用交付形態(tài)
在服務(wù)交付的過程中,我們都用什么工具或者手段來解決上文中提到的問題呢?

首先,shell腳本大家都很熟悉,在早期,運維同學(xué)會用這種方式完成比較簡單的部署。之后逐漸用較為規(guī)范化的流程工具將日常的運維流程串聯(lián)起來。在這一過程中,我們內(nèi)部誕生了一個被稱為Aladdin的自動化系統(tǒng),它滿足了現(xiàn)階段大量游戲的自動化運維需要。后來隨著云原生的演進,業(yè)界也逐漸出現(xiàn)了一些資源編排技術(shù),這一階段我們做過一些簡單的嘗試,但并未持續(xù)太久。
最后我們直接使用了以應(yīng)用為中心的編排方式,在多環(huán)境下實現(xiàn)快速應(yīng)用交付。在社區(qū)中,比較有代表性的是叫KubeVela的開源產(chǎn)品,在我們內(nèi)部則是通過一個叫Atlasx的產(chǎn)品實現(xiàn)了一套應(yīng)用編排機制。
在上圖整個過程中,自動化程度是逐步提高的。前兩個階段自動化程度提高體現(xiàn)在工具或技術(shù)上,步入資源編排或應(yīng)用編排之后,自動化體現(xiàn)在了基礎(chǔ)設(shè)施是否可編程,甚至業(yè)務(wù)邏輯是否可編程。與前面兩個階段相比,后面兩個階段更強調(diào)整個應(yīng)用編排的可編程能力,下面的分享我將圍繞這一內(nèi)容展開。
二、問題和解決方案
1、面臨的挑戰(zhàn)
上文中提到我們內(nèi)部已經(jīng)有一個大型的系統(tǒng)去承載標(biāo)準(zhǔn)化的流程運轉(zhuǎn),那么我們?yōu)槭裁催€要去實現(xiàn)可編程能力呢?

如果大家經(jīng)常玩游戲或進入過網(wǎng)易游戲官網(wǎng)的話,可以關(guān)注到僅頁面展示的可能就有近百個游戲,雖然這些游戲看上去大同小異,但其實不然。例如有的游戲可能走國內(nèi)IDC部署,也有可能走海外發(fā)行, 有可能是滾服,也有可能是單一服,甚至還有之前某一階段出現(xiàn)的全球服,這些差別使我們的游戲運維存在很大差別。

這些差別首先體現(xiàn)在異構(gòu)性上,一個游戲會因為引擎的不同導(dǎo)致其架構(gòu)甚至業(yè)務(wù)類型的不同,例如制作頁游經(jīng)常選擇滾服這一類型,這就意味它的管理模式也會不同。
上文中提到有的游戲走國內(nèi)IDC部署,有的則走海外發(fā)行,我們除了自身私有云的構(gòu)建,海外發(fā)行的游戲還會用上公有云,例如AWS、GCP等,這導(dǎo)致了我們的運維人員在資源準(zhǔn)備或游戲部署的過程中,會面臨基礎(chǔ)設(shè)施的異構(gòu)性問題。
除異構(gòu)性外,管理的多樣性也是一個問題。我們每天面對的資源數(shù)量極其龐大,僅僅一個計算資源、網(wǎng)絡(luò)資源、存儲資源拆分出來,我們就需要面對許多的實體,其中還未包括一些服務(wù)本身系統(tǒng)配置的維護。再回到運維操作本身,游戲運維操作會更復(fù)雜,這也是上文中提到游戲運維可能比傳統(tǒng)業(yè)務(wù)運維更復(fù)雜的原因。根據(jù)我們的不完全統(tǒng)計,每個SRE在完成一些日常工作的過程中可能會涉及到100多個操作,這給運維工作帶來了許多問題。
于是,我們開始思考一個問題,如果圍繞像圖中這樣一個游戲架構(gòu),人工重復(fù)部署100次,能保證這100次部署出的游戲服是等價的嗎?
在過去我們無法回答,因為我們在一個游戲服交付過程中,不可避免會出現(xiàn)問題,那么原因是什么呢?因為這些事情都是人為進行的,人的能力、狀態(tài)、知識背景等都會影響人的行為,導(dǎo)致其最終的交付成果不同。
2、應(yīng)用交付的目標(biāo)

人無法像函數(shù)式編程一樣,在接收同樣的輸入后,執(zhí)行固定的步驟,輸出同樣的結(jié)果。但是業(yè)務(wù)層面又要求不論何人在何時何地部署、部署多少次,都能得到同樣的游戲服。因為對玩家來講,他感受的是游戲服務(wù)的體驗,我們不可能給玩家提供一個差異性非常高的游戲服務(wù),導(dǎo)致可能玩家在體驗時出現(xiàn)非??D這種不好的體驗。
我們的目標(biāo)是消除這種差異性,所以我們最終要通過一些技術(shù)化手段提供標(biāo)準(zhǔn)化、統(tǒng)一化的應(yīng)用,我們將這一過程稱為一致性交付。

為實現(xiàn)這一過程,我們需要解決三方面的問題。
- 不同的環(huán)境能夠一致定義。我們需要解決環(huán)境的差異性問題。
 - 不同的游戲類型能夠一致定義。我們需要有一個模型框架,能夠把我們現(xiàn)在所有的游戲類型以這種模型框架定義出來。
 - 運維能力可描述、可管理、可插拔。上文中提到交付結(jié)果可能與人的能力、狀態(tài)有關(guān),我們應(yīng)該把這種運維經(jīng)驗通過技術(shù)化或者工程化的手段積累、沉淀下來,最終做到知識驅(qū)動代替人工驅(qū)動,這也是我們整個項目的目標(biāo)。
 
為達到上述目標(biāo),目前有兩種方案可供選擇。
3、解決方案
- 基于命令式的交付。上圖左側(cè)是我們內(nèi)部的流程,這些流程都是靠人工驅(qū)動,過往經(jīng)驗表明人為驅(qū)動不可能實現(xiàn)一致性交付。
 - 基于聲明式編排交付。這一方式也更適合如今業(yè)界的發(fā)展。根據(jù)規(guī)范編寫編排文件,通過提交編排啟動服務(wù)。
 
但是聲明式編排的底層邏輯是整個業(yè)務(wù)可編程。那么我們是怎么做的呢?
上文中提到的人為驅(qū)動模式不僅復(fù)雜,而且多種內(nèi)容交叉。我們需要通過分層將各類內(nèi)容進行區(qū)分,為了使用戶得到一致性體驗,我們需要達到每一層的一致性交互。


首先我們從底層的基礎(chǔ)設(shè)施入手,現(xiàn)在不論是國內(nèi)外研發(fā),都有相應(yīng)的基礎(chǔ)設(shè)施,它能夠為我們提供一些等價的環(huán)境能力,但是它暴露出的API服務(wù)對我們來說不一定一致,所以我們需要提供一個統(tǒng)一的資源訪問層,不論是對底層資源還是系統(tǒng)編排進行操作,它都能提供一致的資源訪問行為。
解決了基礎(chǔ)設(shè)施的異構(gòu)性問題,面對業(yè)務(wù)架構(gòu)層面的不一致,我們又是怎么做的呢?
首先我們需要識別區(qū)分現(xiàn)在有多少種類型或多少種架構(gòu)的游戲,通過上文中提到的模型框架將其組織起來,最終體現(xiàn)為一份編排。有了這份編排后,基本上就可以描述定義我們需要一致性定義的內(nèi)容。

而到了業(yè)務(wù)層面,不論是開發(fā)階段、測試階段,還是生產(chǎn)階段的任何一個操作,我們都可以基于這種編排實現(xiàn)它的變更,從而實現(xiàn)面向用戶的一致性交付體驗。
我們通過這一方式進行封裝與交付之后,不論是SRE還是游戲研發(fā)的同學(xué),都能夠在最上層得到比較一致的體驗。
有了上述模式后,我們重塑了整個游戲服的交付過程(如下圖所示)。

首先我們要先完成游戲架構(gòu)的定義,其次基于這樣的定義實現(xiàn)整個游戲集群的編排,然后通過上文中提到的集群創(chuàng)建與交付模式,完成整個游戲集群的一致性交付,最終通過拓撲圖的方式呈現(xiàn)整個集群的狀態(tài)信息。
4、具體實踐
接下來介紹這個過程中我們進行了哪些實踐。
1)應(yīng)用可定義
首先我們對架構(gòu)進行分析,到底要有哪些組成部分才能夠完成一個游戲集群的組裝。
如上圖所示,我們一個游戲集群運轉(zhuǎn)起來可能需要mongo服務(wù),可能需要etcd服務(wù)。游戲服務(wù)本身可能有這樣一些進程,例如commander、game、world、gate。對于一個游戲集群,我們能夠把它拆解成一個個比較獨立的個體,在這一階段我們要完成對這些單一個體的定義。
2)資源可定義

以MongoDB為例,我們聲明它需要使用多少CPU,需要什么類型的、多大的磁盤,也包括它要部署在哪里,可能的shard數(shù)量、版本,通過這種方式,我們就把一個資源的定義固化。今后所有人對資源的需求都以這樣的方式去聲明,其他內(nèi)容也是一樣。
大家接觸過k8s、Docker等聲明式的語言,制定一個聲明式的邏輯并不難,但其背后邏輯還是整個基礎(chǔ)設(shè)施的可編程能力。
那么我們整個基礎(chǔ)設(shè)施的可編程能力處于什么樣的階段呢?得益于網(wǎng)易游戲過去幾年云化與SaaS化的發(fā)展,我們內(nèi)部有了一整套比較成熟的基礎(chǔ)設(shè)施體系,同時它又能以API的方式去提供服務(wù),這為我們提供了很大便利。不論是云網(wǎng)絡(luò)、容器,還是MongoDB、MySQL、Etcd等服務(wù),我們都實現(xiàn)了云化或SaaS化,所以能夠較好地對接這部分內(nèi)容。
看起來所有的定義都不難,但是背后整個可編程的能力才是重點。
3)架構(gòu)可定義
有了這些個體,隨意將它們組裝到一起并不能形成一個架構(gòu)。從上文中的架構(gòu)中我們可以看出,服務(wù)間會互相調(diào)用,底層的資源間可能也有一些固化的調(diào)用關(guān)系。

上圖中是我們實現(xiàn)的一個簡單的游戲架構(gòu),從中可以發(fā)現(xiàn),整個游戲架構(gòu)驅(qū)動起來包括許多內(nèi)容,例如基礎(chǔ)環(huán)境、項目配置、可對外開放的端口范圍,以及網(wǎng)絡(luò)資源的使用與容量,這些東西也是我們在架構(gòu)定義中需要去思考補齊的一部分。這背后體現(xiàn)出的是我們對一個應(yīng)用架構(gòu)模型的定義。
大家使用 K8S、Docker、Compose的定義時會發(fā)現(xiàn)不同,造成這種不同的原因是它們的模型定義不同,即它們的模型框架不同。所以我們?yōu)榱诉m應(yīng)自己內(nèi)部的體系,我們也自己定義了一套應(yīng)用聲明的模型,下圖中是架構(gòu)部分。

4)運維可定義
對游戲來說,在運維工作上會遇到一些簡單的Web類服務(wù)不會遇到的問題。

例如有些游戲會開關(guān)服,這里涉及到開關(guān)網(wǎng)絡(luò)的動作,我們內(nèi)部其實能夠把它收斂下來。通常來說,開關(guān)網(wǎng)絡(luò)包括幾種動作,首先是公網(wǎng)全部開放訪問,然后面向玩家開放連接,其次僅開放辦公網(wǎng),QA可以連接游戲服做當(dāng)次版本的驗證。而在close狀態(tài)下,QA和玩家都無法訪問。
但歷史經(jīng)驗告訴我們,這看起來是簡單的幾個狀態(tài)之間的轉(zhuǎn)換,但要實現(xiàn)這些狀態(tài)背后可能會有一些關(guān)注不到的點。例如我們要求在limit狀態(tài)下禁止玩家連接,但可能因為是從open的狀態(tài)切換到limit狀態(tài),這時外部玩家的連接狀態(tài)是沒有斷掉的。我們在這方面是踩過坑的,即我們以為進入了limit狀態(tài),但其實這個時候玩家還在玩,對一些沒有注意到這一情況的人員,如果他是用另外一種方式,例如上文提到的命令式的方式,他可能調(diào)用API實現(xiàn)了狀態(tài)的切換,保證新的玩家進不來,但是原來處于連接狀態(tài)的外部玩家還可以繼續(xù)玩,這可能就導(dǎo)致了一個故障。
但如果我們通過聲明式的方式把這些東西收斂,將上文中提到的運維策略定義出來,例如進去limit狀態(tài)就一定會清除剩余的連接流量,這就實現(xiàn)了我們在運維操作上一致性。我們也已經(jīng)實現(xiàn)了這一點。

游戲還有另一個特殊點,它是有狀態(tài)的,這導(dǎo)致一個進程會啟動多個端口,除了本身要去對玩家提供服務(wù)的端口外,調(diào)試代碼、進行運營操作還需要額外提供端口。如果我們像之前一樣通過人工規(guī)劃端口,其實很難保證一致性。但如果我們把它面向端口的分配定義成一個可聲明的運維操作,其實很簡單,即聲明周圍有哪些端口,可能要開多少個,通過系統(tǒng)本身的實現(xiàn),就可以成功分配好這些端口,包括端口是否連續(xù)等都可以在這樣的運維策略中實現(xiàn)。
以上就是前文中提到的應(yīng)用可定義、架構(gòu)可定義以及運維可定義,我們是通過上述方式逐步實現(xiàn)的,但是我們真的把一個應(yīng)用定義出來就夠了嗎?

大概在一年半前我們實現(xiàn)過第一版,定義出來的編排文件大概有1000多行。大家都知道一個代碼文件如果行數(shù)過多是很難維護的,改動它也容易出問題。當(dāng)你面對一個1000多行編排的時候,你可能是崩潰的,所以這一版本就無法繼續(xù)了。
接下來我們進行了一些參考和調(diào)研,例如AWS針對用戶使用方面有一些原則,這里我們重點考慮一點,用戶應(yīng)該考慮的是架構(gòu),而不是基礎(chǔ)設(shè)施。我們原來那份1000多行的版本中,包括了許多內(nèi)容,這是比較繁雜的。

我們進一步了解到阿里和微軟也在推進OAM即應(yīng)用開發(fā)模型,其中一個關(guān)鍵點就是區(qū)分使用者,關(guān)注點分離,另外是其運維能力能夠模塊化封裝、可管理,這與我們的目標(biāo)適配,這個東西我們到底如何去實現(xiàn)呢?

上圖模型我們可以借鑒,但是無法套用,這與我們內(nèi)部一些基礎(chǔ)設(shè)施的狀態(tài)有關(guān)。在這基礎(chǔ)上我們對該模型進行了擴展。首先是環(huán)境,還有Stack以及全局trait。擴展后,我們的游戲基本架構(gòu)大約如上圖右側(cè)所示。


我們的應(yīng)用實現(xiàn)流程如上圖,其中最復(fù)雜是OAM解釋器邏輯以及整個provider的執(zhí)行邏輯,這里我們會根據(jù)前文中的架構(gòu)定義,對整個編排模型做解析,然后形成后續(xù)要去執(zhí)行的工作計劃。之前定義的所有組件的執(zhí)行流程都會體現(xiàn)在里面,最終通過這種有向無環(huán)圖的方式實現(xiàn)整個業(yè)務(wù)流程的交付。

這種關(guān)注點分離可以給我們帶來什么呢?如果我們在Istio微服務(wù)這樣的路由轉(zhuǎn)發(fā)需求下,如果按以前我們要去理解那些純概念的話,我們需要知道gateway、VS、destinationrule怎么配置,這部分內(nèi)容對于大多同學(xué)來說較難上手,但如果我們把這些內(nèi)容封裝抽象形成一個基本的路由定義, 則容易很多。
還有一個更復(fù)雜的問題就是我們怎樣實現(xiàn)運維能力的可管理,不同環(huán)境下要求不同。

研測環(huán)境下,我們是可以直連的。我們開放了 Gate端口讓客戶端可以直連,這時候不需要配任何的端口轉(zhuǎn)發(fā)策略。但是對于某種游戲的線上環(huán)境,它可能要求集群獨享一個EIP,并通過DNAT 的方式實現(xiàn)訪問。有的模式可能更加特殊,例如在滾服機制下,假設(shè)我們這個量級是1萬個服務(wù)的話,IP資源肯定是不夠的。所以在這個情況下,我們要實現(xiàn)多集群共享EIP。
上述僅僅是運維能力上的不同,但事實上對Gate的管理是比較一致的。通過這樣一種能力的管控,我們可以明確是否需要EIP,EIP的管理模式、分配模式是什么樣子的,通過這種方式把運維能力定義出來,并且讓它能夠在這樣的配置中去管理。

最后我們將這些內(nèi)容匯總,以前信息是圍繞人的,現(xiàn)在我們通過一份編排把這些內(nèi)容都驅(qū)動了。因為所有信息都是自包含的,這就很好地跟我們整個內(nèi)部的運維配置數(shù)據(jù)體系 (CMDB)實現(xiàn)了聯(lián)動。

其中最有價值的是關(guān)系數(shù)據(jù)。游戲群組交付之后,形成了這樣的一個業(yè)務(wù)拓撲,但是把它放到整個架構(gòu)中,它僅僅是其中的一小部分。這時我們可以做許多事,例如我們通過這種方式明確我與外部依賴的MongoDB是否連通的、一個宿主如果出現(xiàn)數(shù)組故障是否會影響到我,還有一些報警收斂,我們都可以基于這樣的整套機制實現(xiàn),目前我們在十幾二十個場景進行了落地。
三、當(dāng)前成果
接下來分享一下當(dāng)前的成果。


我們這套機制開始使用了近一年,當(dāng)前重點項目也在逐步交接,我們能夠適應(yīng)多種游戲的架構(gòu)與微服務(wù)。按最保守的評估方式,我們也能夠?qū)⒔桓缎侍岣叩?0倍。例如從之前一個多小時,現(xiàn)在我們控制在5分鐘或3分鐘之內(nèi)。整體的使用規(guī)模也在不斷擴張,現(xiàn)在達到了千級別。上圖提到的每天的發(fā)布操作頻率也證明這套機制一直在使用狀態(tài)。
四、未來展望

上文中提到的許多內(nèi)容都離不開業(yè)務(wù)架構(gòu)、應(yīng)用模型、基礎(chǔ)設(shè)施及代碼這三個核心的因素。對我們來說,游戲引擎決定了業(yè)務(wù)架構(gòu)。整個游戲行業(yè)在發(fā)展,引擎本身的能力也在發(fā)展,引擎發(fā)展之后,就會影響業(yè)務(wù)架構(gòu)。
例如我們在做一些游戲微服務(wù)化的工作,微服務(wù)化的結(jié)果就是架構(gòu)的調(diào)整或改變,架構(gòu)的改變需要在應(yīng)用模型上進一步拓展,但是整個應(yīng)用模型離不開底層的基礎(chǔ)設(shè)施,依賴于整個基礎(chǔ)設(shè)施的可編程能力,所以這三個部分是不可分割的。這決定了我們在不同層的工作人員要聯(lián)動才能解決問題,未來我們也都會圍繞這一方面不斷去發(fā)展,從而促進我們整個游戲的發(fā)展。
這套平臺的發(fā)布,是我們針對基于OAM以及游戲上云應(yīng)用管理交付使用場景的實踐。在這個過程中,我們也迭代出了一套怎么面向游戲定義模型架構(gòu)的方式,目前也在持續(xù)使用中。
整個游戲運維有兩大難點,一個是狀態(tài),一個是復(fù)雜的運維邏輯,借助整個云化與基礎(chǔ)設(shè)施能力的結(jié)合,我相信我們未來可以做得更好,能夠讓整個應(yīng)用的開發(fā)與發(fā)布更加令人享受。?















 
 
 











 
 
 
 