基於Jenkins和docker的CI過程
ZTEsoft-方進
1、基於Jenkins的CI過程
一切要從2013年4月開始說起,當我4月份從委內瑞拉回來之後立即投身到國內一個運營商的大型後端建設專案的尾聲中(專案歷時3年多,當時已經接近尾聲),這個專案涉及100多臺主機,包含數十個叢集,除了傳統的WEB應用外,還用到了流程引擎、ESB、規則引擎、搜尋引擎以及快取和日誌,是當時比較複雜的體系結構(當然不能跟現在的雲平臺相比,但在專案開始的年代這還是一個很不錯的架構),整個專案當時一兩百號人佔了局方整整一層樓十幾個辦公室,我到了專案組之後成為了一個小組的小頭目,管個四五個人,小組美其名曰“平臺組”,乾的都是打雜的事情,包括編譯、打包、部署,日常監控以及系統優化等工作,說起來簡單,做起來還是很複雜的,當時所有的工作基本上是靠人工的,可想而知,100來臺機器的環境一臺一臺的部署環境,還得靠人工監控,手工檢查,四五個到處救火忙得不可開交,當時我雖然還不知道CI為啥物(壓根兒就沒這個概念),但也下定決心要改變忙亂的狀態,累一點不要緊,但是累得跟狗似的還幹不好那就白辛苦了。
在2013年的4~8月份,我們主要研究的是自動編譯、打包和釋出,採用的基本方式是各種指令碼,包括windows下的批處理bat、Linux上的shell甚至Python,雖基本上完成了自動從SVN取程式碼、自動編譯、自動打包以及將應用釋出到WebSphere上的這些工作(如下圖),
但也明視訊記憶體在一些問題:
1. 自動執行靠的是windows任務計劃,執行過程、執行情況只能通過檢查指令碼執行時寫的日誌檔案,不直觀
2. 程式碼只作了編譯,沒有做程式碼走查,對程式碼質量的提升作用不大
3. 釋出過程利用IBM提供的wsadmin指令碼,只能進行全量的釋出,釋出過程較長
9月份之後,專案基本穩定後我也離開了專案現場,但自那之後對這塊工作更加著迷,我從專案現場回來之後也組建了一個科室,還是四五個人,當時我查閱了一些資料,尤其是看到了一本書《持續整合 軟體質量改進和風險降低之道》,從此學到一個名詞:CI(持續整合),一發而不可收拾,逢人就鼓吹CI。我組織科室人員一起研究了CruiseControl、Apache Continuum、QuickBuild、Hudson等業界CI常用工具,最後決定以Hudson為框架來逐步實現CI.。一早採用的是Hudson,後來為便於作二次開發切換到其社群版本Jenkins上。
Jenkins提供了一個管理介面,並且有豐富的第三方外掛,支援定時任務,支援從SVN取程式碼,支援Ant編譯和Maven編譯(我們產品程式設計框架逐漸從ANT轉向maven模式),支援向tomcat、JBoss、Weblogic和WAS釋出應用(jenkins的WAS外掛不支援叢集模式,我們仍然沿用了wsadmin指令碼),支援用PMD、Checkstyle、findBugs進行程式碼走查並以圖形化方式展現走查結果(包括趨勢圖和結果詳情),支援呼叫Windows批處理bat、Linux的Shell等。
在採用Jenkins框架的基礎上,我們作了一些二次開發,實現了:
1. 根據任務單增量從SVN取程式碼【有一些奇葩的專案現場要求挑任務單升級,因此我們修改了jenkins的svn外掛以支援這種需求】
2. 支援增量編譯【採用兩個jenkins的JOB,一個做全量編譯,作為首次編譯併產生一個jar包給增量編譯使用,此全量編譯JOB只使用一次;另一個JOB就引用全量JOB產生的jar包,只對變更(或新增)的程式碼編譯產生class等檔案,並將它們按部署目錄放好以便於作增量釋出,同時將這些class檔案再打入到全量JOB下的jar包中以備下次增量編譯使用】
3. 支援增量釋出,通過呼叫lftp指令碼實現快速的應用部署【在比較了cwRsync、unison、wget、lftp、ftpsync、csync、Syncrify、DeltaCopy、tar、bacula等工具後,最終lftp勝出,我們採用:
lftp -c 'open -e "mirror --allow-chown -x vssver.scc -R --parallel=10 --use-pget-n=10 --log=%LOG_FILE% %LOC_DIR% %REMOTE_DIR%" sftp://%USER%:%PASSWORD%@%IP%'
這樣的指令碼來進行增量釋出,將編譯後的結果與部署環境上的進行自動比對更新】
4. 支援基於Ant和基於Maven的程式碼走查,編寫Ant指令碼和Maven指令碼以支援PMD、Checkstyle、findBugs進行程式碼走查【由於jenkins中程式碼走查外掛生成介面時會消耗大量系統資源,對機器效能影響很大,後面我們改成了通過指令碼方式生成並將走查結果打成壓縮包發郵件給相關人員】
5. 支援基於Maven的程式碼的單元測試【採用TDD編碼方式】
6. 支援自動化測試【呼叫ZTP,ZTP是我司自產的一個自動化測試工具,支援自動化指令碼錄製、回放等工作,其工作原理與robotframework比較類似,ZTP工具支援批處理指令碼呼叫,故可以整合到jenkins中】
當時,我們還想在Jenkins上整合更多的功能,包括:
1. 改進websphere-deploy外掛,支援介面部署WebSphere應用包【這個計劃後面擱淺了,主要是指令碼方式已經能支援絕大部分WAS叢集的部署了】
2. 應用環境遷移,通過將應用環境遷移過程自動化為Jenkins中的任務,實現應用環境遷移過程的自動化和視覺化【目的就是想實現研發、測試及生產都是一套環境,測試通過後的環境能直接遷移到生產上去,當時只是做運維的一種本能的想法,但也由此引發了對Docker的關注】
3. 與業務監控系統相結合,形成流程化的跨多個Jenkins任務的、整體的應用環境部署自動化和視覺化,為將來生產環境部署的自動化和視覺化作準備【曾經研究過一段時間jenkins的FLOW外掛,當時FLOW外掛的版本還比較低,功能還很弱,引入第三方的工作流工作量會比較大,而事實上這種流程化的編譯部署過程實用性也比較低,這事就慢慢擱淺了】
目前我所在的產品線所有的專案都已經採用jenkins進行編譯、打包、程式碼走查及自動部署到測試環境和準生產環境【運營商專案的生產環境釋出後面會逐漸由我司自主開發的另一利器“雲應用管理平臺”來支援,後面還會講到】
2、基於Docker的應用釋出
前面講了,關於應用環境遷移的想法引發了我對Docker的興趣,實際上這時已經是14年的6月份了,於是就跟一些同事自學鼓搗一下,當時Docker1.0才剛剛釋出,當時也就把官網的例子都做了一遍,參考官網作了hadoop的映象,自己又作了websphere的映象,搭建了registry,斷斷續續的作了一些東西,算不上很深入,而Docker本身也在不斷髮展,感覺隔幾天就有一個新版本釋出出來。
Docker大潮來勢洶湧,到9月份的時候,我一開始說的那個巨大專案的運營商中有個技術專家提出了要用docker來作應用釋出平臺,當時簡直是不謀而合,於是有了一個專案,也就可以明正言順的進行docker研究了,不過既然已經是一個正式的專案了,那光有幾個愛好者是不夠的,需要有正規軍了,於是請出了公司技術委員會下屬的一個研發團隊,大約有六七人,也就是“雲應用管理平臺”的開發團隊,一起進行相關的研究。給生產環境用的釋出平臺跟我們前面講的用jenkins作的自動部署還是有些不同的,生產環境上版本的釋出一般是有嚴格限制的,包括版本要求、時間要求【升級時間、故障率和故障解決時間】等,這一點是與現在的網際網路企業升級自家的系統是完全不同的。
“雲應用管理平臺”圍繞著Docker進行了大量的開發工作,製作了主機管理、容器管理、叢集管理、版本計劃管理、版本執行管理等等,其架構如下圖:
1)Jenkins打包映象
自動獲取svn程式碼版本
使用dockerfile打包映象,並自動上傳到docker registry
2)叢集配置
應用基本資訊配置
叢集應用繫結
環境變數配置
固定埠號配置
3)制定釋出計劃
選擇映象版本(Docker registry API獲取映象列表)
選擇主機,並設定啟動的容器例項數
4)執行釋出計劃
使用Docker Java API連線docker daemon啟動容器
記錄容器ID
5)容器管理
主機檢測(能檢測主機上是否安裝了docker,如果沒有可以自動安裝docker)
容器節點增加、縮減,使用者選擇的應用映象版本和實際執行版本一致時執行伸縮
容器狀態監測
3、基於dubbo的跨主機的容器連線
一開始我們在測試環境上將所有容器都放在一臺主機上,測試過程很順利,但在移到準生產環境上時,由於要模擬生產環境只能將容器部署到不同的主機上,這時候就發現了一個奇怪的現象,應用之間調不通了,這裡要說一下,我們應用程式是由20多個服務組成的、通過dubbo【阿里提供的一個服務框架】作為服務匯流排串連起來的,dubbo提供了一個方便的服務發現機制,各個服務(稱為服務提供者)只要向dubbo註冊中心註冊過,註冊中心就會將服務的地址傳送給同樣在註冊中心註冊的服務呼叫方(稱為消費者),之後即使dubbo註冊中心掛了也不影響服務的呼叫
當服務提供者部署在容器中時,這時候發現其在dubbo中心註冊的是容器的IP地址,而對處於另一個主機上的消費者來說這個IP是不可訪問的,我們當時也參考了多種方式,想讓消費者能夠連線上服務提供者的IP,查閱資料總結起來大概有兩種做法:
1)設定容器的IP與主機IP在同一網段內,使容器IP可直接訪問【會佔用大量的IP地址,且IP會限制在同一網段,在生產環境中往往不可能】
2)通過複雜的iptables路由規則,通過多層橋接方式打通網路【此法是可行的,也是我們今後要考慮的,但當時一堆開發人員對網路這塊都不是太熟悉】
考慮到當時公司技術委員會下屬另一個研發團隊正在做dubbo的研究和改造,於是拉他們進來討論,結果他們說這個很容易解決,由於主機之間是連通的,而容器在建立時也映射了主機和埠,只需要在服務註冊時註冊的是對映的主機IP和埠就可以連通了,該研發團隊的效率很高,討論的當天就給出了實現,考慮到局方要求嚴格管理容器和主機間的對映,我們將主機IP和埠作為環境變數在容器啟動時傳入【擴充套件了dubbo protocol配置,增加了兩個配置項 publishhost、 publishport,對應主機的ip port,並且在註冊服務時將主機的ip port寫到註冊中心】,果然解決了這個問題。
當然這是一種特殊情況下的跨主機容器連線方式,更為普遍的方式目前我們正在討論當中,基於ovs的連線方式是正在考慮的一個方案。
4、困難與展望
(1)目前我們對Docker的使用還比較初步,雖然基本滿足了專案的要求,但考慮到將來雲平臺要求自動擴充套件、服務發現,這些還有待我們進一步研究
(2)。。。。
問答階段:
-
你好,問一個問題,我們前段時間也把dubbo框架執行在docker裡面,也是採用你們現在的把宿主機和埠作為環境變數傳入的方式實現的,我比較想了解的是後繼你們有什麼更好的方式實現?我看你提到了基於ovs的方案?
有兩種解決辦法:
一種是將顯式傳遞環境變數做成隱式的自動獲取宿主機和埠,從而減少配置工作;
另一種則是通用的openvswitch(ovs)方案,這是與dubbo無關的。
2.容器中的dubbo註冊問題,擴充套件dubbo的protocol配置,增加publishhost和publishport解決了註冊問題,能不能說的詳細一點,謝謝
目前我們硬編碼了dubbo的protocol,在裡面加了兩個欄位,這種擴充套件方式有點野蠻,但dubbo本身提供的擴充套件方式目前很難支援傳遞環境變數方式,我們在考慮將環境變數隱式獲取,這樣就不用硬編碼了
3.上海Q1:你們用的還是埠對映吧,那麼也會存在很多個埠的問題吧,像ip可以訪問一樣
在這個專案中作埠對映是運營商的要求,他們要求能通過配置來設定每個容器的埠對映,這與他們現有的運維方式有關,一開始我們考慮的是docker的自動埠對映,當然這種需求將來肯定是趨勢,我們的“雲應用管理平臺”中也有考慮
4.深圳Q3:為何考慮dubbo而不是etcd做服務發現?dubbo的優勢是什麼?
選中dubbo是很偶然的,公司本身有ESB產品,但相對來說比較重,一般用於多個產品間的呼叫,而dubbo我們一般用於產品內部多個模組之間的呼叫,是一種輕量級的服務匯流排,dubbo這種方式不依賴於硬體和作業系統,etcd並不是所有作業系統都能支援的吧,當然我也沒有對etcd作深入的研究。
5.jenkins的slave是選用了虛擬機器還是直接物理機?
我們的jenkins的master和slave都是用的虛擬機器。
6.上海Q3:程式碼提交上去,如果測試有問題,回滾是腫麼處理,也是通過jenkins??
這裡要分情況來說,一種是測試發現問題,提單子給開發修改,開發修改完程式碼提交到scm,然後觸發jenkins下一輪的編譯和部署;另一種情況是如果某次部署失敗,則會用部署前的備份直接還原。
7.請問 用的registryv1還是v2 ? 分散式儲存用的什麼? 有沒有加nginx代理
目前我們用的是v1。生產環境多是叢集環境,需要加nginx作分發。目前應用中分散式儲存用的並不多,一般來說用hdfs來儲存一些日誌便於後面分析,也有用fastdfs和mongodb的。
8.深圳Q7:底層雲平臺用的是私有云?
底層平臺一開始想用私有云,但運營商已經有了vCenter的環境,因此後來我們改用ansible來管理各類物理機和虛機,用docker API來管理容器。
9.深圳Q10:dubbo實現的服務發現是否具備failover功能,自動檢測並遷移失敗容器?
dubbo目前不具備遷移容器的功能,其failover是通過負載均衡和心跳連線來控制的,自動檢測和容器遷移我們一般會考慮放在監控系統裡面來做,如果放在dubbo裡面會加重dubbo,只所以用dubbo也是考慮到它的輕便性。
10.能否談下對jenkins+mesos的看法,這個涉及到docker-in-docker的必要性
mesos我們才剛剛接觸,我瞭解的不太多,至於docker-in-docker我覺得生產上很難用,因為效能方面損失比較嚴重,我們做過效能測試,非--net=host方式的容器效能損失接近30%
11.深圳Q11:能具體介紹下利用Dockerfile打包映象嗎?jar包也是在這一步編譯出來的嗎?這樣釋出出去的映象會既包括程式碼又包含jar包吧。
不好意思,回覆的晚了些,我們的映象中是不包含程式碼的,映象裡面是產品包,編譯是在打映象之前做的。
12.西安Q2:對不生產環境中不適合以容器執行的元件,Jenkins+docker是否就沒有優勢了?
開發和測試環境還是很有優勢的,當然有些有大量IO操作的服務其實不適合放在容器裡面,這主要是效能方面的考慮
13.深圳Q8:雲平臺是怎麼管理容器的?有沒有使用docker生態系統相關的元件?
目前沒有用到swarm\compose之類的元件,將來要看這塊的發展了,也有可能會引入k8s或者mesos來作管理,這些目前都在考慮當中
14.深圳Q7:在怎麼判斷部署docker服務不可用,不可用後自動遷移還是如何操作?
目前雲應用平臺只在釋出時才對docker容器進行狀態檢測,如果檢測到失敗,會根據指定的容器數目進行重新建立。後續我們會把對容器狀態的持續檢測統一放到監控系統中。
15.我是不是可以這麼理解。你們的jenkins是主要用來CI。而實際叢集管理則是雲應用平臺做的?
是的,這個是嚴格分工的,當時作雲應用管理平臺時,是以測試交付物為起始點的,這裡的測試交付物就是CI的產物,容器方式下就是映象了。
16.深圳Q1:我可以理解docker是部署在實體機,實體機上都有一個agent的東西負責與管理端通訊,主要負責docker的管理(安裝,部署,監控等)嗎
我們的docker目前都是部署在虛擬機器上的,作業系統是redhat7.1,你所謂的agent其實應該就是docker demeaon吧
17.上海Q2:一個應用多個容器你們怎麼負載均衡?
前面其實回答過,要加nginx的。
18.深圳Q2:利用Dockerfile打包映象並上傳到registry更像是CD環節的事情,那在單元測試、整合測試環境是否有利用到Docker呢?是否使用Jenkins中Docker相關的外掛了?
當前專案的單元測試、整合測試都用到docker容器的。jenkins中沒有用docker外掛,試過感覺都不太成熟,目前還是docker命令列最方便。
19.深圳Q3:開始的時候有講如果沒有docker自動部署會自動部署,這個是如何部署的
這個前面講過,是通過lftp指令碼比對編譯環境與待部署的遠端目錄。
20.上海Q4追問:也就是你們在虛擬機器裡面部署的docker?
是的,當前的專案是在虛擬機器裡面部署docker的,但我個人觀點從長遠看來,其實在物理機上部署docker會更好,所以現在很多私有云比如openstack,cloudstack都能支援直接管理容器,不過目前虛擬機器還是不能缺少的,容器的隔離性不如VM。
websphere叢集部署的時候,會終端現有業務嗎?看官方好像有解決方案,在不影響現有訪問的情況下實現版本的部署和更新工作,是如何實現的能否說一下?
目前是需要中斷業務的,我們也正在研究灰度釋出的方案。
杭州Q2:如果用nat模式 容器如何指定ip啊
不需要指定容器IP,只需要對映埠。
深圳Q5:還想問下:有通過dubbo做服務路由麼?
dubbo本身就有服務路由的功能。