Docker 在英雄聯(lián)盟游戲中的實(shí)踐探索
【原文編者的話(huà)】Riot將Docker和Jenkins相結(jié)合,以此來(lái)構(gòu)建流水線(xiàn)(Pipeline)。這篇博客是Riot實(shí)踐Docker的系列博客的***篇,主要介紹了他們的目標(biāo)和理念;后續(xù)博客則以教程的形式一步一步地記錄Riot的Docker實(shí)踐。
容器技術(shù)已經(jīng)風(fēng)靡全球,我們歡迎容器化領(lǐng)域的新霸主們。
然而,他們也給Riot的流水線(xiàn)工程團(tuán)隊(duì)(Pipeline Engineering team)的同事們帶來(lái)了新挑戰(zhàn)。我叫Maxfield Stewart,是Riot的工程師,我們組主要負(fù)責(zé)構(gòu)建流水線(xiàn)(Pipeline)——從代碼簽入(check in)到部署的一切工作,甚至更多。如果說(shuō)持續(xù)交付是一首主題曲的話(huà),那么我們就是用清唱的方式演唱它。我們運(yùn)轉(zhuǎn)的是一個(gè)類(lèi)似云的環(huán)境,管理著Riot***的一個(gè)服務(wù)器和虛擬機(jī)集群。其中的一個(gè)龐然大物是構(gòu)建集群(Build Farm),由大量的物理機(jī)和虛擬機(jī)組成。它是從數(shù)年前的一個(gè)小集群發(fā)展而來(lái)的,當(dāng)時(shí)只負(fù)責(zé)構(gòu)建英雄聯(lián)盟的游戲客戶(hù)端。
最近,我們已經(jīng)融入了Docker容器技術(shù)。我們是如何將容器與傳統(tǒng)的構(gòu)建集群集成,并使其越來(lái)越像一個(gè)自服務(wù)的基于云的工作引擎呢?我們能否使用Dockerfiles定義構(gòu)建環(huán)境,并與我們常用的開(kāi)源架構(gòu)相結(jié)合呢?我們又能不能拋棄傳統(tǒng)的基于虛擬機(jī)的云,轉(zhuǎn)身?yè)肀萜髟颇?
上述問(wèn)題已經(jīng)持續(xù)了相當(dāng)長(zhǎng)的一段時(shí)間,就像寒冰射手(Ashe)的箭一樣。接下來(lái),我將通過(guò)一個(gè)系列博客介紹我們的團(tuán)隊(duì)是如何嘗試回答上述問(wèn)題的。本文是其中***篇博客,主要是介紹我們的團(tuán)隊(duì)背景,以及我們?yōu)槭裁匆先萜骷夹g(shù)。在后續(xù)博客中,我將具體地分享如何整合Jenkins和Docker。***篇教程是一個(gè)基礎(chǔ)介紹(http://engineering.riotgames.com/news/putting-jenkins-docker-container)。如果你對(duì)于使用容器創(chuàng)建構(gòu)建集群、持續(xù)交付、幫助工程師快速交付,那么這個(gè)系列就是你要的。請(qǐng)準(zhǔn)備好:我將從基本介紹,逐步深入,最終介紹如何使用Docker承載真正的業(yè)務(wù)。
一年之前,我們將持續(xù)集成引入到了英雄聯(lián)盟。在那之前,我們拼命地嘗試以一個(gè)常規(guī)節(jié)奏來(lái)發(fā)布英雄聯(lián)盟,但是我們步履維艱。因此我們打算盡可能自動(dòng)化這一切,從構(gòu)建流水線(xiàn)到創(chuàng)建測(cè)試環(huán)境,獲得了大量的成果,包括提高交付一致性、減少構(gòu)建時(shí)間、改善總體完成度。英雄聯(lián)盟從一天幾次的構(gòu)建,增長(zhǎng)到了每天30次構(gòu)建。
構(gòu)建英雄聯(lián)盟可不是開(kāi)玩笑的,其中包括了超過(guò)150個(gè)任務(wù),我們構(gòu)建每個(gè)重要的版本。每次構(gòu)建有各種形式,從傳統(tǒng)的debug構(gòu)建到新版本,以及專(zhuān)門(mén)為了包括騰訊和Garena在內(nèi)的全球合作伙伴準(zhǔn)備的變種版本。我們可以追蹤到每次構(gòu)建、什么測(cè)試環(huán)境、什么測(cè)試內(nèi)容、PBE以及快速部署成產(chǎn)品。我們可以一鍵創(chuàng)建測(cè)試環(huán)境,并且可以在幾個(gè)星期內(nèi)從20個(gè)測(cè)試環(huán)境增加到70多個(gè),包括450多個(gè)虛擬機(jī)。構(gòu)建英雄聯(lián)盟只是構(gòu)建集群的一部分工作,構(gòu)建集群本身支持了Riot各個(gè)工程團(tuán)隊(duì)的3300多個(gè)構(gòu)建任務(wù)。然而,這一構(gòu)建流程并不是***的,這些陳舊的工具有時(shí)需要連接起來(lái)才能工作。在持續(xù)集成中,我們秉持4項(xiàng)原則:
- 我們認(rèn)為工程師團(tuán)隊(duì)必須能完全掌控他們的技術(shù)棧,包括對(duì)于構(gòu)建環(huán)境的管理員權(quán)限。
- 我們認(rèn)為配置即代碼。團(tuán)隊(duì)?wèi)?yīng)當(dāng)盡可能使用源碼控制來(lái)維護(hù)他們自己的構(gòu)建流水線(xiàn)和環(huán)境。
- 我們認(rèn)為每當(dāng)工程師執(zhí)行一次構(gòu)建,都需要針對(duì)所有可部署的配置構(gòu)建一個(gè)可交付的版本。一個(gè)“構(gòu)建”并不只是編譯代碼而已,而是所有可部署的組件的集合。
- 我們認(rèn)為一次交付就是一個(gè)產(chǎn)品決策(shipping is a product decision)。只需按一下按鈕,產(chǎn)品團(tuán)隊(duì)就能夠部署并查看***版本。
我們需要世界***的技術(shù)棧才能達(dá)到這些目標(biāo)。通常有三種選擇:完全重頭編寫(xiě)、購(gòu)買(mǎi)別人的工具或者定制化開(kāi)源項(xiàng)目。
我們選擇了第三種。在這篇博客中,我不想比較各種CI工具。不過(guò),通過(guò)修改開(kāi)源工具來(lái)符合我們的需求是一個(gè)***的折中方案:不需要重頭編寫(xiě);可以與開(kāi)源世界合作;如果有必要的話(huà),可以輕易脫離它。
#p#
因此,我們的技術(shù)棧非常簡(jiǎn)單:
- 開(kāi)源版本的Jenkins
- Jenkins的任務(wù)DSL插件(Job DSL Plugin)
- Jenkins的構(gòu)建流插件(Build Flow plugin)
- 將各個(gè)組件連接在一起的工程***性
我們選擇和繼續(xù)使用Jenkins,是因?yàn)樗庆`活的、開(kāi)源的、易于處理我們的基本構(gòu)建操作。總體來(lái)說(shuō),Jenkins是易于創(chuàng)建一個(gè)構(gòu)建流水線(xiàn)的,符合我們持續(xù)交付的核心需求(如上所述)。作為一款廣泛應(yīng)用的開(kāi)源工具,我們有一個(gè)***活力的社區(qū)在與我們合作。與重頭編寫(xiě)自定義工具相比,工程師團(tuán)隊(duì)可以利用開(kāi)源標(biāo)準(zhǔn)的實(shí)現(xiàn),這是很有幫助的,也是具有風(fēng)險(xiǎn)的。開(kāi)源標(biāo)準(zhǔn)經(jīng)常變化,昨天的一個(gè)好主意明天就可能變成一個(gè)壞主意。然而,利用合適的插件和技術(shù)訣竅,我們只用了少量的代碼、配置和開(kāi)銷(xiāo),就完成了一個(gè)全自動(dòng)的持續(xù)集成鏈。
那么,Docker發(fā)揮了什么作用呢?讓我們回想一下我提到的持續(xù)交付的核心原則。最近,我們團(tuán)隊(duì)遇到的一個(gè)挑戰(zhàn)是構(gòu)建環(huán)境的所有權(quán)。之前,工程師們通過(guò)Packer.io定義自己的虛擬機(jī)鏡像,然后給產(chǎn)品團(tuán)隊(duì)集群的root權(quán)限。本質(zhì)上,我們需要通過(guò)Jenkins這一個(gè)工作流引擎定義一個(gè)內(nèi)部的云環(huán)境。我們探索了幾個(gè)通用的配置管理工具,如Puppet和Chef,來(lái)實(shí)現(xiàn)虛擬云環(huán)境,并使工程師們能控制這些機(jī)器。
然后,Docker出現(xiàn)了。
這件事情就變得簡(jiǎn)單了:Dockerfile比其他工具更易于維護(hù)。在Docker的幫助下,我們意識(shí)到容器更容易管理了。如果我們把Docker中Dockerfile的概念和構(gòu)建環(huán)境的所有權(quán)結(jié)合起來(lái),我們就進(jìn)入了工程天堂。
Docker很善于解決部署中的挑戰(zhàn)。我主要關(guān)注Docker是如何幫助工作流引擎、構(gòu)建系統(tǒng)和流水線(xiàn),同時(shí)也熟悉了如何將其作為一個(gè)部署工具和方法論。Riot管理著大量的微服務(wù),而容器和微服務(wù)的組合就像花生醬和巧克力的組合。因?yàn)镈ocker成為了一個(gè)“Thing(tm)”,我們也會(huì)使用它來(lái)解決其他的一些問(wèn)題。
流水線(xiàn)工程團(tuán)隊(duì)的夢(mèng)想變得更真實(shí)了:我們想要一個(gè)流水線(xiàn)構(gòu)建工具,它能動(dòng)態(tài)地加速持續(xù)交付流水線(xiàn),使用框架代碼來(lái)按需地一鍵構(gòu)建環(huán)境。為了創(chuàng)建一個(gè)完整的構(gòu)建流水線(xiàn),我們之前是通過(guò)自動(dòng)化配置虛擬機(jī)來(lái)實(shí)現(xiàn)的,現(xiàn)在我們認(rèn)為使用Docker容器來(lái)完成。
需要說(shuō)明的是,Docker并不是一個(gè)***的整體解決方案。它不能解決Windows和OSX的構(gòu)建環(huán)境中的問(wèn)題,也不能和我們使用的每個(gè)工具結(jié)合。但是,Docker確實(shí)解決了Linux平臺(tái)中我們遇到的很多困難。在Riot,我們?cè)谄脚_(tái)和后端上進(jìn)行了大量的工程工作。包括核心的后臺(tái)服務(wù)在內(nèi),幾乎所有的特性都是通過(guò)跑在Linux上的微服務(wù)來(lái)提供的。因此,如何優(yōu)化解決方案空間是值得我們投入時(shí)間和精力的。
我們已經(jīng)開(kāi)始將Docker與現(xiàn)有的構(gòu)建棧結(jié)合,并獲得了一些早期的成就。我們創(chuàng)建了Jenkins的一鍵部署環(huán)境,在容器中部署,加速了測(cè)試和調(diào)試過(guò)程。我們從一個(gè)小型集群(大概500個(gè)任務(wù))開(kāi)始,使用容器作為構(gòu)建環(huán)境,在所有權(quán)和迭代速度上團(tuán)隊(duì)也提供了積極的反饋,包括:
- 基于Linux構(gòu)建微服務(wù)和網(wǎng)站的工程師們能夠以編程的方式定義他們的構(gòu)建環(huán)境了
- 本地的構(gòu)建環(huán)境和構(gòu)建集群中的構(gòu)建環(huán)境是完全一致的(在后續(xù)博客中,我將介紹如何做到這一點(diǎn))
- 動(dòng)態(tài)資源分配意味著降低整體計(jì)算成本
- 一臺(tái)虛擬機(jī)可以處理4個(gè)不同的組合300多個(gè)構(gòu)建任務(wù),這原本是通過(guò)8臺(tái)虛擬機(jī)完成的
這篇博客僅僅是一個(gè)系列話(huà)題的介紹,這個(gè)系列將覆蓋多個(gè)領(lǐng)域,以教程的形式發(fā)布,提供實(shí)例和源碼。首先,系列博客將介紹如何使用Docker來(lái)部署Jenkins,包括各種***實(shí)踐,并通過(guò)一個(gè)真實(shí)應(yīng)用引入Docker的基礎(chǔ)知識(shí);然后,系列博客將探索容器化構(gòu)建環(huán)境的各種方案,并介紹Riot是如何將Docker融入Jenkins的生態(tài)環(huán)境;***,將介紹流水線(xiàn)工程團(tuán)隊(duì)是如何完成最終目標(biāo)的。
我們希望通過(guò)系列博客來(lái)能分享我們的發(fā)現(xiàn)以及遭遇的挫折。這些或許不是什么大秘密,但可能是不容易發(fā)現(xiàn)的。我希望我們的系列博客能回報(bào)社區(qū),并通過(guò)交流和對(duì)話(huà)學(xué)習(xí)到更多。