原文:BUILDING WITH JENKINS INSIDE AN EPHEMERAL DOCKER CONTAINER
作者:Maxfield Stewart ? ? 譯者:杰微刊兼職翻譯劉曉鵬?
《容器的深入思考》一文告訴大家如何在容器中構(gòu)建一個(gè)項(xiàng)目。今天,我將向大家展示我的團(tuán)隊(duì)當(dāng)前是如何綜合運(yùn)用Jenkins和Docker來(lái)為Riot引擎團(tuán)隊(duì)提供服務(wù)的。在最近的博文中,我承諾不就將會(huì)直接討論實(shí)際的salve構(gòu)建和Jenkins配置。但是,從某種意義上講,這是一件主要的事件:如果你沒(méi)有一臺(tái)接收salve的Jenkins服務(wù)器,最好的辦法是回過(guò)去看看我以前的博客。
不過(guò),在正確的開(kāi)始本教程之前,我們先討論一下方式和方法。通過(guò)Docker,有很多種方式來(lái)創(chuàng)建 slaves。即使縮小場(chǎng)景,只使用 Jenkins,還是有多種選擇。通過(guò)研究與發(fā)現(xiàn),我覺(jué)得有兩種主要的方式你可以采用。從概念上講,我將這兩種模式稱之為“Docker execution”和“Docker ephemeral slave”。
這兩種方式的本質(zhì)區(qū)別是Jenkins如何與構(gòu)建的slave進(jìn)行連接和通信。在執(zhí)行模式下,Jenkins使用一種傳統(tǒng)的流行方式連接:在虛擬機(jī)或者物理設(shè)備上運(yùn)行一個(gè)客戶端,這里指的就是一臺(tái)運(yùn)行的Docker主機(jī)。在臨時(shí)模式中,Jenkins直接連接Docker容器,將其視為一個(gè)構(gòu)建的slave。這兩者之間的區(qū)別很重要,所以我們花一點(diǎn)時(shí)間來(lái)分解這兩種模式。
DOCKER 執(zhí)行模式
在執(zhí)行模式下,我們?cè)诮Y(jié)構(gòu)上假定slave是一臺(tái)Docker主機(jī),但是把它當(dāng)做一臺(tái)物理機(jī)來(lái)處理。當(dāng)一個(gè)Jenkins任務(wù)啟動(dòng)時(shí),它直接在slave上同步/創(chuàng)建一個(gè)工作目錄,并通過(guò)“docker run”和“docker exec”命令來(lái)轉(zhuǎn)換容器,然后將其掛載到本地的工作空間中。容器是一個(gè)虛擬暫存空間,是一個(gè)隔離的環(huán)境。它可以包含所有的自定義版本的工具和二進(jìn)制文件(我們需要對(duì)裝載到容器的源代碼進(jìn)行編譯)。
當(dāng)一個(gè)構(gòu)建任務(wù)完成時(shí),所有二進(jìn)制文件和生成的構(gòu)建歸檔文件將會(huì)發(fā)送到傳統(tǒng)Jenkins工作空間中的slave上。然后,Jenkins可以安全的關(guān)閉容器,正常的執(zhí)行后續(xù)的清理工作。
該模式的最佳代表是Cloudbees定制構(gòu)建環(huán)境插件(Cloudbees Custom Build Environment Plugin),該插件是一個(gè)開(kāi)源插件,由 Jenkins 源碼庫(kù)的主人維護(hù)。
DOCKER 臨時(shí) Slave 模式
臨時(shí)模式的目的在于利用Docker容器的自主性和隔離性來(lái)擴(kuò)展 Jenkins 的構(gòu)建范圍,以滿足在Jenkins上的任何需求。相對(duì)于傳統(tǒng)的預(yù)先分配slave數(shù)組或現(xiàn)成的虛擬機(jī),這種模式將容器自身看做是一個(gè)slave。
當(dāng)Jenkins的執(zhí)行器上有一個(gè)需求時(shí),我們需要轉(zhuǎn)換容器,自動(dòng)地配置Jenkins以便將該容器視為一個(gè)新的slave來(lái)接收,然后執(zhí)行任務(wù),最后關(guān)閉容器,回收slave。當(dāng)然這種模式比執(zhí)行模式更復(fù)雜。
這種方式有幾個(gè)相互競(jìng)爭(zhēng)的插件,這些插件通常都是以如何維護(hù)和構(gòu)建Docker云為中心。迄今為止,這種插件有Kubernetes,Mesos以及“純”Docker方式。
如何選擇模式?
兩種模式都能有效的解決問(wèn)題。在Riot,我們感興趣的是為特定的目標(biāo)獲取分配盒子和“執(zhí)行器”的方式,所以臨時(shí)模型對(duì)我們有很強(qiáng)的吸引力。我們喜歡用一個(gè)容器來(lái)代表整個(gè)slave的想法。因此,我們使用Docker Plugin 來(lái)完成我們的目標(biāo)。
隨著時(shí)間的推移,我們很高興當(dāng)初的選擇。Docker Plugin的主開(kāi)發(fā)者(KostyaSha)對(duì)該插件的維護(hù)非?;钴S,經(jīng)常更新,并且有很強(qiáng)大的社區(qū)。他負(fù)責(zé)響應(yīng)問(wèn)題,并在GitHub repo 提供了清晰的路線圖。沒(méi)有他的工作,我不可能完成這篇文章中提供的教程,該教程是基于 0.16 版本插件的。
此外請(qǐng)注意,KostyaSha最近將“Docker Plugin”分出來(lái)一個(gè)新的倉(cāng)庫(kù)“Yet Another Docker Plugin”進(jìn)行繼續(xù)開(kāi)發(fā)。由于我們現(xiàn)在的生產(chǎn)環(huán)境正在使用的插件是“Docker Plugin”,所以在本文中選擇該插件進(jìn)行分析。
在以后,我們可能會(huì)改變我們的想法。我們盡量保證靈活性,以便在有更好的解決方案時(shí)能夠方便的進(jìn)行轉(zhuǎn)換。我們采取的方式也受到過(guò)教訓(xùn)-在本教程最后我會(huì)對(duì)它們進(jìn)行描述。
教程
這是我寫的最長(zhǎng)的教程。所以我決定在這只保存鏈接以節(jié)省空間(將實(shí)現(xiàn)與方案進(jìn)行分離)。如果你已經(jīng)清楚了之前的文章,該教材只需要花費(fèi)30-45分鐘左右的時(shí)間就可完成。你可以從這獲取到完整的教程:
另外,如果你想跳過(guò)結(jié)構(gòu)部分,盡快開(kāi)始并運(yùn)行起來(lái),你可以從這獲取到完整的教程,然后按照 README 中的說(shuō)明進(jìn)行操作:
https://github.com/maxfields2000/dockerjenkins_tutorial/tree/master/tutorial_07
經(jīng)驗(yàn)教訓(xùn)
運(yùn)營(yíng)這個(gè)平臺(tái)有著諸多其自身獨(dú)特的挑戰(zhàn)。請(qǐng)記住這個(gè)簡(jiǎn)短的列表,希望能幫助你節(jié)省時(shí)間和精力:
運(yùn)營(yíng)一個(gè)大規(guī)模的 DOCKERHOSTS 并不是一個(gè)簡(jiǎn)單的任務(wù)。
磁盤空間是一個(gè)大問(wèn)題。Docker鏡像很消耗空間。每個(gè)運(yùn)行的容器也要耗費(fèi)空間。有時(shí)候,容器掛了也還會(huì)占用空間。有的團(tuán)隊(duì)使用卷來(lái)創(chuàng)建slave,這需要耗費(fèi)更多的空間。
在Docker主機(jī)上監(jiān)控磁盤空間是必不可少的,不要等到磁盤消耗完的時(shí)候再去處理,要及時(shí)的監(jiān)控并處理。
清理鏡像和容器,就像垃圾回收一樣。
每次一個(gè)新的鏡像推送到Docker主機(jī),都會(huì)留下“懸浮”的未使用層,清理這些空間應(yīng)該是日常的操作,否則磁盤空間就會(huì)成為問(wèn)題。
容器的slave有時(shí)無(wú)法停止或無(wú)法清理,關(guān)注“退出”和“死亡”的容器是保證Docker主機(jī)清潔必不可少的一部分。
增加新的鏡像/SLAVE 到 DOCKERHOSTS 隊(duì)列是一個(gè)很耗時(shí)的操作。
最初,要求工程師必須將他們的鏡像在 Jenkins 中進(jìn)行配置。很快我們發(fā)現(xiàn)這個(gè)過(guò)程是一個(gè)不必要的障礙。根據(jù)我們的經(jīng)驗(yàn),在早期的開(kāi)發(fā)中,工程團(tuán)隊(duì)平均每周需要對(duì)他們的Dockerfile做多次修改。
我們創(chuàng)建一個(gè)稱之為“Harbormaster”的工具,該工具通過(guò) Groovy 的API,能夠自動(dòng)的驗(yàn)證工程師提供的鏡像,并自動(dòng)的配置 Jenkins。Harbormaster 通過(guò)一組核心的標(biāo)準(zhǔn)來(lái)測(cè)試每個(gè)鏡像,并驗(yàn)證salve是否正常工作。然后生成一份測(cè)試報(bào)告并自動(dòng)配置Jenkins。
關(guān)于 Harbormaster 如何工作的討論可能會(huì)導(dǎo)致本文過(guò)長(zhǎng)。我將在以后的博客對(duì)其進(jìn)行闡述。
風(fēng)險(xiǎn)監(jiān)控室是必不可少的
關(guān)于如何監(jiān)控 Jenkins 和Docker Swarm,我們也還在不斷的成長(zhǎng)中。Docker Cloud 的監(jiān)控藝術(shù)是發(fā)展過(guò)程中的永恒主題,并且當(dāng)這個(gè)云是一個(gè)構(gòu)建場(chǎng)景而不是應(yīng)用場(chǎng)景時(shí),還會(huì)呈現(xiàn)出一種獨(dú)特的扭曲狀態(tài)。
正如Harbormaster,在這討論監(jiān)控也會(huì)使得本文太長(zhǎng)。同樣,我會(huì)在以后的博客中更多的討論生產(chǎn)環(huán)境下的監(jiān)控問(wèn)題。
JENKINS的審計(jì)可能會(huì)咬到你
曾經(jīng),由于 Jenkins 的崩潰導(dǎo)致磁盤消耗完的問(wèn)題,導(dǎo)致我們工作到半夜。結(jié)果是因?yàn)椤皠?chuàng)建/銷毀”構(gòu)建的slave時(shí),產(chǎn)生了 100,000 個(gè)微小的日志文件。Jenkins保存了所有的創(chuàng)建和銷毀slave時(shí)的審計(jì)文件;一定要注意,否則你會(huì)死的很難看。
當(dāng)然還有更多的經(jīng)驗(yàn)教訓(xùn)。在以后的博客中我還繼續(xù)會(huì)討論我們是如何處理某些問(wèn)題的,所以有問(wèn)題就趕緊問(wèn)吧,不要猶豫!
你的生產(chǎn)環(huán)境系統(tǒng)和我們的研究結(jié)果
隨著本教程的結(jié)束,你應(yīng)該已經(jīng)有了一個(gè)完整功能Docker Jenkins 沙盒。在創(chuàng)建過(guò)程中你會(huì)遇到一些困難,在生產(chǎn)環(huán)境中你也需要做幾件事情。
1.使用Docker Plugin 配置你生產(chǎn)環(huán)境的 Jenkins 主服務(wù)器,正如你在上述教程中做的一樣(安裝插件)。
2.啟動(dòng)一個(gè)“生產(chǎn)環(huán)境”的Docker主機(jī)。這可能有點(diǎn)超出了本教程的范圍。我們的流水線團(tuán)隊(duì)使用 Centos 虛擬機(jī)來(lái)運(yùn)行 VSphere,不過(guò)你也可以使用 AWS 的實(shí)例或物理機(jī),任何你想要的有效的Docker主機(jī)都行。這就是Docker的強(qiáng)大之處。
3.如果在構(gòu)建環(huán)境的Docker主機(jī)中,沒(méi)有使用 TLS 安全機(jī)制(在安全環(huán)境中并不少見(jiàn)),確保在設(shè)置中移除“https”和“Docker cert path”。
4.如果你正在使用 TLS,確保在 Jenkins 中配置生產(chǎn)環(huán)境的證書(shū)。
5.確保你構(gòu)建的slave鏡像是生產(chǎn)環(huán)境下的Dockerhost所能到達(dá)的。在 Riot,我們使用一個(gè)中央鏡像倉(cāng)庫(kù)。
很多東西都涉及到安裝,所以,請(qǐng)不猶豫,有什么問(wèn)題就問(wèn),并提供必要的點(diǎn)。
對(duì)于我們來(lái)說(shuō),這不是一個(gè)“沙盒”或“玩?!钡脑O(shè)置。這個(gè)教程中,列出的只是我們?cè)诰€環(huán)境中改變的一個(gè)組件。我們生產(chǎn)環(huán)境的Dockerhost實(shí)際上是一個(gè)Docker Swarm 應(yīng)用,由 10 臺(tái)Dockerhost機(jī)器支撐。下面這些圖標(biāo)展示我們現(xiàn)在(發(fā)布本文的時(shí)候)是如何設(shè)置這些東西的
抽象結(jié)構(gòu)
物理結(jié)構(gòu)
這是來(lái)自我們生產(chǎn)系統(tǒng)中的某些統(tǒng)計(jì)數(shù)據(jù)
Jenkins 統(tǒng)計(jì)數(shù)據(jù):
Jenkins 統(tǒng)計(jì)數(shù)據(jù)細(xì)節(jié)
平均隊(duì)列大?。?0-30
預(yù)備執(zhí)行器:~650
平均使用的執(zhí)行器:30-40
構(gòu)建節(jié)點(diǎn)的總數(shù)量:~80
每小時(shí)的平均任務(wù)數(shù):~600
DockerJenkins 統(tǒng)計(jì)數(shù)據(jù)
DockerJenkins 統(tǒng)計(jì)數(shù)據(jù)細(xì)節(jié)
平均隊(duì)列大?。?
預(yù)備執(zhí)行器:20(為構(gòu)建流程控制)
平均使用的執(zhí)行器:3
任意時(shí)間的平均節(jié)點(diǎn)數(shù):5
每小時(shí)的平均任務(wù)數(shù):50
去年初年初,我們將系統(tǒng)部署在Docker很早的版本上(Docker 1.2),Docker Plugin 也是的版本(.8),穩(wěn)定性是當(dāng)初關(guān)注一個(gè)核心問(wèn)題。不過(guò)現(xiàn)在的版本(Docker 1.10 + Docker Plugin 0.16),我非常相信,就我們的需求而言,它是足夠穩(wěn)定的。在這一年中,我們一直在使用它們,我們從少量的構(gòu)建任務(wù)和幾個(gè)早期的使用團(tuán)隊(duì)發(fā)展到現(xiàn)在,無(wú)論是任務(wù)數(shù)還是團(tuán)隊(duì)數(shù)都有了巨大的增加。很明顯,一旦我們有機(jī)會(huì)進(jìn)行完整的測(cè)試,我們就會(huì)遷移到新的插件“Yet Another Docker Plugin”上。
事實(shí)上,我們的Dockerized Jenkins平臺(tái)現(xiàn)在構(gòu)建的任務(wù),大概只占我們整個(gè) Jenkins 平臺(tái)所支撐的全部任務(wù)(超過(guò)4000)的25%。網(wǎng)絡(luò)新構(gòu)建任務(wù)創(chuàng)建在傳統(tǒng)Jenkins環(huán)境中的幾乎為0,大部分新創(chuàng)建的任務(wù)都在我們的Docker平臺(tái)上。原因如下:
1、引擎團(tuán)隊(duì)通過(guò)自定義Dockerfiles可以完全控制他們的構(gòu)建環(huán)境。
2、通過(guò)Docker,可以將他們部署的構(gòu)建環(huán)境復(fù)制到本地,這樣那些“構(gòu)建環(huán)境”就可以很容易在本地進(jìn)行測(cè)試。
3、團(tuán)隊(duì)不在需要“系統(tǒng)管理員”來(lái)構(gòu)建虛擬機(jī)或其他環(huán)境,這些權(quán)限“放手”到了每個(gè)人手里。
結(jié)論
2015年8月,當(dāng)我開(kāi)始這一系列博客的寫作之旅時(shí),我們已經(jīng)有了一個(gè)功能性的原型。八個(gè)月以后,博客幾乎趕上了我們當(dāng)前的進(jìn)度,缺乏監(jiān)控和擴(kuò)展。
在這個(gè)點(diǎn)上,你應(yīng)該對(duì)Docker基礎(chǔ)有一個(gè)很堅(jiān)實(shí)的入門,并且知道一些關(guān)于Docker的擴(kuò)展和安全問(wèn)題,這些都通過(guò)Jekins的真實(shí)應(yīng)用場(chǎng)景演示過(guò)。另一方面,你應(yīng)該有一個(gè)功能性配置文件,用于配置一個(gè)完整的Jekins測(cè)試環(huán)境,并通過(guò)它來(lái)運(yùn)行你本地的Docker Toolbox Dockerhost,還包括建立自己的構(gòu)建slave的能力。這幾乎就是“盒子容器中的Jenkins”,在以后的博客中,我將會(huì)更深入的挖掘這個(gè)生態(tài)系統(tǒng),分析我們創(chuàng)建的工具、API以及監(jiān)控。
我真心希望你能覺(jué)得這些東西是有用的。我們收到的反饋也非常棒,我很喜歡社區(qū)的這種熱情。如果你能在下面發(fā)表評(píng)論,我將會(huì)非常感激!
所有有效的文件都在我公開(kāi)的Github上;這里所有的一切僅僅是開(kāi)源的魔法!在這里,可以毫不猶豫的發(fā)起問(wèn)題,提出需求等。