效率提升10倍,網易游戲面向終態(tài)的應用交付實踐
講師介紹
林香鑫,網易游戲團隊負責人。2014年加入網易游戲,目前任網易游戲技術中心平臺服務組團隊負責人、技術專家,長期負責運維開發(fā)方向的工作,當前主要負責 CMDB 和配置管理、流程引擎、交付平臺、基礎組件和工具等研發(fā)管理工作。
一、應用交付形態(tài)
1、應用交付
大家對應用交付都很熟悉,游戲應用交付簡單來說就是將游戲代碼分發(fā)到服務器上對外提供服務,然后玩家通過客戶端連接,使玩家能夠順利進入體驗游戲。
應用交付這一過程說起來簡單,但在準備過程中,需要運維同學完成許多工作。例如資源管理、配置管理、多環(huán)境需求、面向游戲的業(yè)務運維。游戲的業(yè)務運維與其它類型應用的業(yè)務運維有很大區(qū)別,這也是我們在設計整套機制時遇到的問題,下文中的許多案例也會圍繞這一點展開。
游戲的發(fā)布和運維具有特殊性,例如游戲會強調開服、關服時間,可能會對整個資源的交付時間以及開服時效有較高的要求。除此之外,游戲本身是一個交互式的服務,它可能會影響玩家的體驗,所以對于日常服務運維過程中的問題排查,也會有較高的時間要求。
2、應用交付形態(tài)
在服務交付的過程中,我們都用什么工具或者手段來解決上文中提到的問題呢?
首先,shell腳本大家都很熟悉,在早期,運維同學會用這種方式完成比較簡單的部署。之后逐漸用較為規(guī)范化的流程工具將日常的運維流程串聯(lián)起來。在這一過程中,我們內部誕生了一個被稱為Aladdin的自動化系統(tǒng),它滿足了現階段大量游戲的自動化運維需要。后來隨著云原生的演進,業(yè)界也逐漸出現了一些資源編排技術,這一階段我們做過一些簡單的嘗試,但并未持續(xù)太久。
最后我們直接使用了以應用為中心的編排方式,在多環(huán)境下實現快速應用交付。在社區(qū)中,比較有代表性的是叫KubeVela的開源產品,在我們內部則是通過一個叫Atlasx的產品實現了一套應用編排機制。
在上圖整個過程中,自動化程度是逐步提高的。前兩個階段自動化程度提高體現在工具或技術上,步入資源編排或應用編排之后,自動化體現在了基礎設施是否可編程,甚至業(yè)務邏輯是否可編程。與前面兩個階段相比,后面兩個階段更強調整個應用編排的可編程能力,下面的分享我將圍繞這一內容展開。
二、問題和解決方案
1、面臨的挑戰(zhàn)
上文中提到我們內部已經有一個大型的系統(tǒng)去承載標準化的流程運轉,那么我們?yōu)槭裁催€要去實現可編程能力呢?
如果大家經常玩游戲或進入過網易游戲官網的話,可以關注到僅頁面展示的可能就有近百個游戲,雖然這些游戲看上去大同小異,但其實不然。例如有的游戲可能走國內IDC部署,也有可能走海外發(fā)行, 有可能是滾服,也有可能是單一服,甚至還有之前某一階段出現的全球服,這些差別使我們的游戲運維存在很大差別。
這些差別首先體現在異構性上,一個游戲會因為引擎的不同導致其架構甚至業(yè)務類型的不同,例如制作頁游經常選擇滾服這一類型,這就意味它的管理模式也會不同。
上文中提到有的游戲走國內IDC部署,有的則走海外發(fā)行,我們除了自身私有云的構建,海外發(fā)行的游戲還會用上公有云,例如AWS、GCP等,這導致了我們的運維人員在資源準備或游戲部署的過程中,會面臨基礎設施的異構性問題。
除異構性外,管理的多樣性也是一個問題。我們每天面對的資源數量極其龐大,僅僅一個計算資源、網絡資源、存儲資源拆分出來,我們就需要面對許多的實體,其中還未包括一些服務本身系統(tǒng)配置的維護。再回到運維操作本身,游戲運維操作會更復雜,這也是上文中提到游戲運維可能比傳統(tǒng)業(yè)務運維更復雜的原因。根據我們的不完全統(tǒng)計,每個SRE在完成一些日常工作的過程中可能會涉及到100多個操作,這給運維工作帶來了許多問題。
于是,我們開始思考一個問題,如果圍繞像圖中這樣一個游戲架構,人工重復部署100次,能保證這100次部署出的游戲服是等價的嗎?
在過去我們無法回答,因為我們在一個游戲服交付過程中,不可避免會出現問題,那么原因是什么呢?因為這些事情都是人為進行的,人的能力、狀態(tài)、知識背景等都會影響人的行為,導致其最終的交付成果不同。
2、應用交付的目標
人無法像函數式編程一樣,在接收同樣的輸入后,執(zhí)行固定的步驟,輸出同樣的結果。但是業(yè)務層面又要求不論何人在何時何地部署、部署多少次,都能得到同樣的游戲服。因為對玩家來講,他感受的是游戲服務的體驗,我們不可能給玩家提供一個差異性非常高的游戲服務,導致可能玩家在體驗時出現非??D這種不好的體驗。
我們的目標是消除這種差異性,所以我們最終要通過一些技術化手段提供標準化、統(tǒng)一化的應用,我們將這一過程稱為一致性交付。
為實現這一過程,我們需要解決三方面的問題。
- 不同的環(huán)境能夠一致定義。我們需要解決環(huán)境的差異性問題。
- 不同的游戲類型能夠一致定義。我們需要有一個模型框架,能夠把我們現在所有的游戲類型以這種模型框架定義出來。
- 運維能力可描述、可管理、可插拔。上文中提到交付結果可能與人的能力、狀態(tài)有關,我們應該把這種運維經驗通過技術化或者工程化的手段積累、沉淀下來,最終做到知識驅動代替人工驅動,這也是我們整個項目的目標。
為達到上述目標,目前有兩種方案可供選擇。
3、解決方案
- 基于命令式的交付。上圖左側是我們內部的流程,這些流程都是靠人工驅動,過往經驗表明人為驅動不可能實現一致性交付。
- 基于聲明式編排交付。這一方式也更適合如今業(yè)界的發(fā)展。根據規(guī)范編寫編排文件,通過提交編排啟動服務。
但是聲明式編排的底層邏輯是整個業(yè)務可編程。那么我們是怎么做的呢?
上文中提到的人為驅動模式不僅復雜,而且多種內容交叉。我們需要通過分層將各類內容進行區(qū)分,為了使用戶得到一致性體驗,我們需要達到每一層的一致性交互。
首先我們從底層的基礎設施入手,現在不論是國內外研發(fā),都有相應的基礎設施,它能夠為我們提供一些等價的環(huán)境能力,但是它暴露出的API服務對我們來說不一定一致,所以我們需要提供一個統(tǒng)一的資源訪問層,不論是對底層資源還是系統(tǒng)編排進行操作,它都能提供一致的資源訪問行為。
解決了基礎設施的異構性問題,面對業(yè)務架構層面的不一致,我們又是怎么做的呢?
首先我們需要識別區(qū)分現在有多少種類型或多少種架構的游戲,通過上文中提到的模型框架將其組織起來,最終體現為一份編排。有了這份編排后,基本上就可以描述定義我們需要一致性定義的內容。
而到了業(yè)務層面,不論是開發(fā)階段、測試階段,還是生產階段的任何一個操作,我們都可以基于這種編排實現它的變更,從而實現面向用戶的一致性交付體驗。
我們通過這一方式進行封裝與交付之后,不論是SRE還是游戲研發(fā)的同學,都能夠在最上層得到比較一致的體驗。
有了上述模式后,我們重塑了整個游戲服的交付過程(如下圖所示)。
首先我們要先完成游戲架構的定義,其次基于這樣的定義實現整個游戲集群的編排,然后通過上文中提到的集群創(chuàng)建與交付模式,完成整個游戲集群的一致性交付,最終通過拓撲圖的方式呈現整個集群的狀態(tài)信息。
4、具體實踐
接下來介紹這個過程中我們進行了哪些實踐。
1)應用可定義
首先我們對架構進行分析,到底要有哪些組成部分才能夠完成一個游戲集群的組裝。
如上圖所示,我們一個游戲集群運轉起來可能需要mongo服務,可能需要etcd服務。游戲服務本身可能有這樣一些進程,例如commander、game、world、gate。對于一個游戲集群,我們能夠把它拆解成一個個比較獨立的個體,在這一階段我們要完成對這些單一個體的定義。
2)資源可定義
以MongoDB為例,我們聲明它需要使用多少CPU,需要什么類型的、多大的磁盤,也包括它要部署在哪里,可能的shard數量、版本,通過這種方式,我們就把一個資源的定義固化。今后所有人對資源的需求都以這樣的方式去聲明,其他內容也是一樣。
大家接觸過k8s、Docker等聲明式的語言,制定一個聲明式的邏輯并不難,但其背后邏輯還是整個基礎設施的可編程能力。
那么我們整個基礎設施的可編程能力處于什么樣的階段呢?得益于網易游戲過去幾年云化與SaaS化的發(fā)展,我們內部有了一整套比較成熟的基礎設施體系,同時它又能以API的方式去提供服務,這為我們提供了很大便利。不論是云網絡、容器,還是MongoDB、MySQL、Etcd等服務,我們都實現了云化或SaaS化,所以能夠較好地對接這部分內容。
看起來所有的定義都不難,但是背后整個可編程的能力才是重點。
3)架構可定義
有了這些個體,隨意將它們組裝到一起并不能形成一個架構。從上文中的架構中我們可以看出,服務間會互相調用,底層的資源間可能也有一些固化的調用關系。
上圖中是我們實現的一個簡單的游戲架構,從中可以發(fā)現,整個游戲架構驅動起來包括許多內容,例如基礎環(huán)境、項目配置、可對外開放的端口范圍,以及網絡資源的使用與容量,這些東西也是我們在架構定義中需要去思考補齊的一部分。這背后體現出的是我們對一個應用架構模型的定義。
大家使用 K8S、Docker、Compose的定義時會發(fā)現不同,造成這種不同的原因是它們的模型定義不同,即它們的模型框架不同。所以我們?yōu)榱诉m應自己內部的體系,我們也自己定義了一套應用聲明的模型,下圖中是架構部分。
4)運維可定義
對游戲來說,在運維工作上會遇到一些簡單的Web類服務不會遇到的問題。
例如有些游戲會開關服,這里涉及到開關網絡的動作,我們內部其實能夠把它收斂下來。通常來說,開關網絡包括幾種動作,首先是公網全部開放訪問,然后面向玩家開放連接,其次僅開放辦公網,QA可以連接游戲服做當次版本的驗證。而在close狀態(tài)下,QA和玩家都無法訪問。
但歷史經驗告訴我們,這看起來是簡單的幾個狀態(tài)之間的轉換,但要實現這些狀態(tài)背后可能會有一些關注不到的點。例如我們要求在limit狀態(tài)下禁止玩家連接,但可能因為是從open的狀態(tài)切換到limit狀態(tài),這時外部玩家的連接狀態(tài)是沒有斷掉的。我們在這方面是踩過坑的,即我們以為進入了limit狀態(tài),但其實這個時候玩家還在玩,對一些沒有注意到這一情況的人員,如果他是用另外一種方式,例如上文提到的命令式的方式,他可能調用API實現了狀態(tài)的切換,保證新的玩家進不來,但是原來處于連接狀態(tài)的外部玩家還可以繼續(xù)玩,這可能就導致了一個故障。
但如果我們通過聲明式的方式把這些東西收斂,將上文中提到的運維策略定義出來,例如進去limit狀態(tài)就一定會清除剩余的連接流量,這就實現了我們在運維操作上一致性。我們也已經實現了這一點。
游戲還有另一個特殊點,它是有狀態(tài)的,這導致一個進程會啟動多個端口,除了本身要去對玩家提供服務的端口外,調試代碼、進行運營操作還需要額外提供端口。如果我們像之前一樣通過人工規(guī)劃端口,其實很難保證一致性。但如果我們把它面向端口的分配定義成一個可聲明的運維操作,其實很簡單,即聲明周圍有哪些端口,可能要開多少個,通過系統(tǒng)本身的實現,就可以成功分配好這些端口,包括端口是否連續(xù)等都可以在這樣的運維策略中實現。
以上就是前文中提到的應用可定義、架構可定義以及運維可定義,我們是通過上述方式逐步實現的,但是我們真的把一個應用定義出來就夠了嗎?
大概在一年半前我們實現過第一版,定義出來的編排文件大概有1000多行。大家都知道一個代碼文件如果行數過多是很難維護的,改動它也容易出問題。當你面對一個1000多行編排的時候,你可能是崩潰的,所以這一版本就無法繼續(xù)了。
接下來我們進行了一些參考和調研,例如AWS針對用戶使用方面有一些原則,這里我們重點考慮一點,用戶應該考慮的是架構,而不是基礎設施。我們原來那份1000多行的版本中,包括了許多內容,這是比較繁雜的。
我們進一步了解到阿里和微軟也在推進OAM即應用開發(fā)模型,其中一個關鍵點就是區(qū)分使用者,關注點分離,另外是其運維能力能夠模塊化封裝、可管理,這與我們的目標適配,這個東西我們到底如何去實現呢?
上圖模型我們可以借鑒,但是無法套用,這與我們內部一些基礎設施的狀態(tài)有關。在這基礎上我們對該模型進行了擴展。首先是環(huán)境,還有Stack以及全局trait。擴展后,我們的游戲基本架構大約如上圖右側所示。
我們的應用實現流程如上圖,其中最復雜是OAM解釋器邏輯以及整個provider的執(zhí)行邏輯,這里我們會根據前文中的架構定義,對整個編排模型做解析,然后形成后續(xù)要去執(zhí)行的工作計劃。之前定義的所有組件的執(zhí)行流程都會體現在里面,最終通過這種有向無環(huán)圖的方式實現整個業(yè)務流程的交付。
這種關注點分離可以給我們帶來什么呢?如果我們在Istio微服務這樣的路由轉發(fā)需求下,如果按以前我們要去理解那些純概念的話,我們需要知道gateway、VS、destinationrule怎么配置,這部分內容對于大多同學來說較難上手,但如果我們把這些內容封裝抽象形成一個基本的路由定義, 則容易很多。
還有一個更復雜的問題就是我們怎樣實現運維能力的可管理,不同環(huán)境下要求不同。
研測環(huán)境下,我們是可以直連的。我們開放了 Gate端口讓客戶端可以直連,這時候不需要配任何的端口轉發(fā)策略。但是對于某種游戲的線上環(huán)境,它可能要求集群獨享一個EIP,并通過DNAT 的方式實現訪問。有的模式可能更加特殊,例如在滾服機制下,假設我們這個量級是1萬個服務的話,IP資源肯定是不夠的。所以在這個情況下,我們要實現多集群共享EIP。
上述僅僅是運維能力上的不同,但事實上對Gate的管理是比較一致的。通過這樣一種能力的管控,我們可以明確是否需要EIP,EIP的管理模式、分配模式是什么樣子的,通過這種方式把運維能力定義出來,并且讓它能夠在這樣的配置中去管理。
最后我們將這些內容匯總,以前信息是圍繞人的,現在我們通過一份編排把這些內容都驅動了。因為所有信息都是自包含的,這就很好地跟我們整個內部的運維配置數據體系 (CMDB)實現了聯(lián)動。
其中最有價值的是關系數據。游戲群組交付之后,形成了這樣的一個業(yè)務拓撲,但是把它放到整個架構中,它僅僅是其中的一小部分。這時我們可以做許多事,例如我們通過這種方式明確我與外部依賴的MongoDB是否連通的、一個宿主如果出現數組故障是否會影響到我,還有一些報警收斂,我們都可以基于這樣的整套機制實現,目前我們在十幾二十個場景進行了落地。
三、當前成果
接下來分享一下當前的成果。
我們這套機制開始使用了近一年,當前重點項目也在逐步交接,我們能夠適應多種游戲的架構與微服務。按最保守的評估方式,我們也能夠將交付效率提高到10倍。例如從之前一個多小時,現在我們控制在5分鐘或3分鐘之內。整體的使用規(guī)模也在不斷擴張,現在達到了千級別。上圖提到的每天的發(fā)布操作頻率也證明這套機制一直在使用狀態(tài)。
四、未來展望
上文中提到的許多內容都離不開業(yè)務架構、應用模型、基礎設施及代碼這三個核心的因素。對我們來說,游戲引擎決定了業(yè)務架構。整個游戲行業(yè)在發(fā)展,引擎本身的能力也在發(fā)展,引擎發(fā)展之后,就會影響業(yè)務架構。
例如我們在做一些游戲微服務化的工作,微服務化的結果就是架構的調整或改變,架構的改變需要在應用模型上進一步拓展,但是整個應用模型離不開底層的基礎設施,依賴于整個基礎設施的可編程能力,所以這三個部分是不可分割的。這決定了我們在不同層的工作人員要聯(lián)動才能解決問題,未來我們也都會圍繞這一方面不斷去發(fā)展,從而促進我們整個游戲的發(fā)展。
這套平臺的發(fā)布,是我們針對基于OAM以及游戲上云應用管理交付使用場景的實踐。在這個過程中,我們也迭代出了一套怎么面向游戲定義模型架構的方式,目前也在持續(xù)使用中。
整個游戲運維有兩大難點,一個是狀態(tài),一個是復雜的運維邏輯,借助整個云化與基礎設施能力的結合,我相信我們未來可以做得更好,能夠讓整個應用的開發(fā)與發(fā)布更加令人享受。?