基于 Docker、Kubernetes 實現(xiàn)高效可靠的規(guī)?;?CI/CD 流水線的搭建

大數(shù)據(jù)

作者:?jihong10102006

高效可靠的CI/CD流水線是一個IT組織實現(xiàn)軟件服務快速交付的基礎(chǔ),現(xiàn)如今大量企業(yè)采用jenkins集群來搭建其交付流水線。然而,如何管理大量Jenkins Slave的差異化?如何簡單快速實現(xiàn)Jenkins能力的橫向擴展?如何實現(xiàn)流水線的高可用?如何有效利用閑置的Jenkins Slave資源?上述這些問題一直困攏著集群管理員,近兩年隨著虛擬化技術(shù)突飛猛進的發(fā)展,Docker, Kubernetes 等現(xiàn)代化工具徹底顛覆了交付團隊的交付流程,同時也為CI/CD流程水線的搭建與管理提供了全新思路。

Setup

目前流行的CI工具很多,鑒于本文討論CI/CD流水線在企業(yè)中的應用,考慮到企業(yè)不會有意愿將源代碼的訪問權(quán)開放給第三方,像Travis CI等這些基于SaaS的CI工具自然被pass,由于Jenkins在CI領(lǐng)域的主導性地位,所以本文的CI工具只涉及Jenkins。另外,本文默認您對Jenkins, Docker, Kubernetes 等工具有一些基礎(chǔ)的了解。

持續(xù)交付流水線簡介

簡單介紹一下持續(xù)交付流水線,如下圖所示,簡單來說流水線工作流程是這樣的,當代碼庫有代碼變更的時候持續(xù)集成服務器(Jenkins)會監(jiān)聽到代碼變更并自動觸發(fā)第一個階段 — 持續(xù)集成階段,這個階段做的事情是自動構(gòu)建,單元測試,靜態(tài)代碼分析以及生成報告。如果所有步驟都如預期成功通過的話會自動進入下一個階段 — 自動化測試階段,在跑自動化測試套件之前,首先要把成功通過第一階段的產(chǎn)出物(artifact), 如果選擇java做為開發(fā)語言的話就是生成的war包,ruby的話就是gem文件,部署到測試服務器上,部署完成之后觸發(fā)自動化測試套件,包括驗收測試,容量測試,性能測試等會自動執(zhí)行,如果所有測試用例都順利通過(有些公司還需要做一些手工測試如探索性測試等)的話,那么這個版本就會被標記成一個可發(fā)布的侯選版本。一旦業(yè)務需要,就會通過一鍵部署的方式將相應的侯選版本發(fā)布到生產(chǎn)環(huán)境上去。如果你想更多的了解持續(xù)交付相關(guān)的知識請閱讀David Farley and Jez Humble的名著《Continues Delivery》。

大數(shù)據(jù)

踐與痛點

上述這個過程實現(xiàn)起來需要這樣做,首先需要把上圖中各個階段的工作腳本化,說具體一點就是需要寫一個構(gòu)建腳本來完成編譯,單元測試,靜態(tài)代碼分析,生成報告等步驟,從而完成持續(xù)集成階段的工作,然后是自動化測試腳本,這個腳本可以觸發(fā)自動化測試套件并生成相關(guān)報告,最后需要寫一個部署腳本,用于將持續(xù)集成階段的產(chǎn)出物部署到測試環(huán)境上(當然最后的發(fā)布階段也會重用這個腳本),這里需要注意的是要確保部署都是可重復的,重復部署同一個產(chǎn)出物N次的效果與只部署一次的效果相同,也就是大家常說的冪等。接下來就輪到Jenkins出場了,首先我們需要配置一下Jenkins來監(jiān)聽代碼庫的變更,這就意味著只要有代碼遷入就會觸發(fā)相對應的流水線,然后我們用Jenkins job或pipeline將這幾個階段的腳本串聯(lián)起來(下圖是以job為例),這樣一個簡單的CI/CD流水線就搭建完成了。

大數(shù)據(jù)

如上圖所示,每一個Job負責運行某一階段的腳本,可以簡單類比為Job1運行持續(xù)集成階段的腳本將源代碼從代碼庫中遷出,編譯,單元測試,靜態(tài)代碼分析以及生成報告,Job2首先將持續(xù)集成階段的產(chǎn)出物部署到測試環(huán)境并運行自動化測試套件,生成報告并標記產(chǎn)出物,Job3用于按需發(fā)布。每個Job所運行的腳本依賴的語言或運行環(huán)境會有所不同,可以通過label方式選擇到相應的Slave上運行。

但在企業(yè)級大規(guī)模的應用CI/CD流水線時,由于企業(yè)中多團隊多產(chǎn)品的存在,不同的團隊會根據(jù)產(chǎn)品自身的特點來選擇不同的技術(shù)實現(xiàn)方式,這就意味著產(chǎn)品實現(xiàn)語言會有很多種,比如java, C#, ruby, python, nodejs …那么相對應的CI/CD流水線就需要提供所有語言的編譯環(huán)境并安裝相關(guān)的依賴包,當然為了減少依賴,避免沖突以及更好的管理這些編譯環(huán)境聰明的管理員會選擇讓每一個slave只能運行特定編程語言的Job, 如下圖所示:

大數(shù)據(jù)

上圖這個Jenkins集群Maser只用來調(diào)度和收集log,所有的Job都會由Master根據(jù)不同的label導流到對應的Slave上運行。這種集群的實現(xiàn)方式是我們在VM時代不得已的選擇,雖然能解決大部分問題,但也帶來了很多困擾。

單點依賴 Jenkins Master成為單點,一旦Jenkins Master down機,那將是災難性的,整個CI/CD流水線都將處于不可用的狀態(tài)。不易維護 大量差異化的Jenkins Slave管理起來難度很大,由于差異化的存在,維護升級幾乎都需要手動完成,人力成本投入很高。不易擴展 舉個例子,我們發(fā)現(xiàn)流水線對Java7這個Slave發(fā)出的請求量比較大,經(jīng)常出現(xiàn)排隊現(xiàn)像。為了緩解這種情況,管理員需要增加一個可以編譯Java7應用的Slave,那么管理員需要怎么做呢?首先需要準備一臺VM,然后安裝java7以及所有依賴的軟件包,最后配置Slave相關(guān)信息label等并將新安裝好的Slave注冊到Master,基本上都需要人為干預,擴展起來非常不方便。資源浪費 每一臺Jenkins Slave Server都是一臺實實在在運行的VM, 當Slave Sever空閑時也不能將它所占用的資源釋放,因為隨時可能需要這個Slave完成相關(guān)的Job。

解決痛點

下面我們來看一下虛擬化技術(shù)帶來了的福音,下圖是一個基于Kubernetes, Docker搭建起來的Jenkins集群,為了避免混淆我略去了Kubernetes集群中的Master node。我們看到Jenkins Mater以Docker container的形式運行在Kubernetes一個Node上并將所有Jenkins相關(guān)數(shù)據(jù)存儲到一個volume中,Jenkins Slave也以Docker container的形式運行在各個Node中,之所以用虛線來表現(xiàn)Slave是因為Slave不是一直存在的,它會被動態(tài)的按需創(chuàng)建并自動刪除。簡單介紹一下這種動態(tài)創(chuàng)建注冊Slave的方式,它的工作流程是,當Jenkins Master收到一個build的請求時,會用按照label的要求動態(tài)的創(chuàng)建一個運行在Docker container中的Jenkins Slave并注冊到Master上, 然后運行相應的Job,當Job運行完成后這個Slave會被注銷,所在的Docker container也會被自動刪除。

大數(shù)據(jù)

這種基于Docker, Kubernetes搭建的CI/CD流水線給Jenkins集群帶來了諸多益處:

高可用 Jenkins Master被部署在Kubernetes集群上,一旦container運行異常意外退出,那么Kubernetes會自動用相同的Docker image幫我們從新起動一個新的Jenkins,并將volume attach給新創(chuàng)建的Docker container,從而保證不會丟失任何數(shù)據(jù),實現(xiàn)了Jenkins集群的高可用性。自動伸縮 由于每一次運行Job時,Jenkins Master都會動態(tài)創(chuàng)建一個Jenkins slave,Job完成之后Slave會被注銷所在的Docker container也會被自動刪除,所占用的資源就會被自動釋放。也就是說當同時請求的Job數(shù)量越多,生成的Slave container就會越多,占用的資源也就越多,反之亦然,而且這種動態(tài)伸縮是完全不需要人為干預的。完全隔離 由于每一次運行Job都是在一個全新的Jenkins slave中運行,避免了同時運行的Job與Job之間發(fā)生沖突的可能性。容易維護 對比之前每一個Jenkins Slave是一臺固定VM的做法,以這種方式搭建的集群維護的不再是固定的VM而是創(chuàng)建動態(tài)Slave所需要的Docker image,我們可以很容易通過Docker File來build適用于我們自已的Docker image并將它們存儲在私有的Docker registry中,非常易于維護。容易擴展 當我們發(fā)現(xiàn)Jenkins的Queue中存在大量等待執(zhí)行的Job是因為kubernetes集群的資源不足時,能夠很容易的初始化一個kubernetes node并將它添加到集群中來,實現(xiàn)橫向擴展非常的方便。

簡單實現(xiàn)

下面我們來完成一個簡單的實現(xiàn):

1、首先你需要安裝一個Kubernetes cluster,請參考https://kubernetes.io/docs/setup/ Kubernetes安裝完成之后,首先需要用下面兩條命令和文件部署一個Jenkins到Kubernetes集群:
代碼?

kubectl?create?-f?jenkins-deployment.yamlkybectl?create?-f?jenkins-service.yaml

大數(shù)據(jù)

tip: 標紅的部分很重要,開放8080端口是用來訪問Jenkins web portal用的;而動態(tài)創(chuàng)建的Jenkins slave會默認通過50000(可修改)端口與master建立連接。
檢查jenkins安裝運行情況:

大數(shù)據(jù)

2、查看Jenkins log,用管理員密碼登錄Jenkins并安裝Kubernetes plugin。

3、 配置Kubernetes cloud

Manage Jenkins/Configure System/ Add a new cloud/ Kubernetes:

大數(shù)據(jù)

Add pod template/Kubernetes Pod Template:

大數(shù)據(jù)

點擊Save之后大功告成。

牛刀小試

下面我們來測試一下這個動態(tài)注冊Slave的Jenkins集群是否工作正常,首先登錄Jenkins創(chuàng)建一個簡單的free style job,指定這個Job只能在Label為“jnlp”的agent上運行。點擊build now,你會發(fā)現(xiàn)奇跡發(fā)生了,原來沒有注冊任何Slave的Jenkins動態(tài)的創(chuàng)建一個Slave并注冊到Master上,然后運行相應的Job,當Job運行結(jié)束后這個Slave被自動清除了。

大數(shù)據(jù)

PS:這只是一個簡單的實現(xiàn),在企業(yè)的實踐中,我們需要不同的build環(huán)境,需要我們基于jenkinsci/jnlp-slave這個Image構(gòu)建我們自己的Jenkins slave Image并保存到私有的Registry中,相對應的Kubernetes需要從私有Registry拉取Image。

極客網(wǎng)企業(yè)會員

免責聲明:本網(wǎng)站內(nèi)容主要來自原創(chuàng)、合作伙伴供稿和第三方自媒體作者投稿,凡在本網(wǎng)站出現(xiàn)的信息,均僅供參考。本網(wǎng)站將盡力確保所提供信息的準確性及可靠性,但不保證有關(guān)資料的準確性及可靠性,讀者在使用前請進一步核實,并對任何自主決定的行為負責。本網(wǎng)站對有關(guān)資料所引致的錯誤、不確或遺漏,概不負任何法律責任。任何單位或個人認為本網(wǎng)站中的網(wǎng)頁或鏈接內(nèi)容可能涉嫌侵犯其知識產(chǎn)權(quán)或存在不實內(nèi)容時,應及時向本網(wǎng)站提出書面權(quán)利通知或不實情況說明,并提供身份證明、權(quán)屬證明及詳細侵權(quán)或不實情況證明。本網(wǎng)站在收到上述法律文件后,將會依法盡快聯(lián)系相關(guān)文章源頭核實,溝通刪除相關(guān)內(nèi)容或斷開相關(guān)鏈接。

2017-10-26
基于 Docker、Kubernetes 實現(xiàn)高效可靠的規(guī)?;?CI/CD 流水線的搭建
作者:?jihong10102006 高效可靠的CI CD流水線是一個IT組織實現(xiàn)軟件服務快速交付的基礎(chǔ),現(xiàn)如今大量企業(yè)采用jenkins集群來搭建其交付流水

長按掃碼 閱讀全文