這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
【編者的話】本次分享從四個方面展開:基於Jenkins的CI過程、基於Docker的應用發布、基於Dubbo的跨主機的容器串連、困難與展望。
基於Jenkins的CI過程
一切要從2013年4月開始說起,當我4月份從委內瑞拉回來之後立即投身到國內一個電訊廠商的大型後端建設項目的尾聲中(項目曆時3年多,當時已經接近尾聲),這個項目涉及100多台主機,包含數十個叢集,除了傳統的WEB應用外,還用到了流程引擎、ESB、規則引擎、搜尋引擎以及緩衝和日誌,是當時比較複雜的體繫結構(當然不能跟現在的雲平台相比,但在項目開始的年代這還是一個很不錯的架構),整個項目當時一兩百號人佔了局方整整一層樓十幾個辦公室。
我到了項目組之後成為了一個小組的小頭目,管個四五個人,小組美其名曰“平台組”,乾的都是打雜的事情,包括編譯、打包、部署,日常監控以及系統最佳化等工作,說起來簡單,做起來還是很複雜的,當時所有的工作基本上是靠人工的,可想而知,100來台機器的環境一台一台的部署環境,還得靠人工監控,手工檢查,四五個到處救火忙得不可開交,當時我雖然還不知道CI為啥物(壓根兒就沒這個概念),但也下定決心要改變忙亂的狀態,累一點不要緊,但是累得跟狗似的還幹不好那就白辛苦了。
在2013年的4~8月份,我們主要研究的是自動編譯、打包和發布,採用的基本方式是各種指令碼,包括windows下的批處理bat、Linux上的shell甚至Python,雖基本上完成了自動從SVN取代碼、自動編譯、自動打包以及將應用發布到WebSphere上的這些工作(如):
但也明顯存在一些問題:
- 自動執行靠的是Windows任務計劃,執行過程、執行情況只能通過檢查指令碼執行時寫的記錄檔,不直觀。
- 代碼只作了編譯,沒有做代碼走查,對代碼品質的提升作用不大。
- 發布過程利用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架構的基礎上,我們作了一些二次開發,實現了:
- 根據任務單增量從SVN取代碼(有一些奇葩的項目現場要求挑任務單升級,因此我們修改了jenkins的svn外掛程式以支援這種需求)。
- 支援增量編譯(採用兩個Jenkins的JOB,一個做全量編譯,作為首次編譯併產生一個jar包給增量編譯使用,此全量編譯JOB只使用一次;另一個JOB就引用全量JOB產生的jar包,只對變更(或新增)的代碼編譯產生class等檔案,並將它們按部署目錄放好以便於作增量發布,同時將這些class檔案再打入到全量JOB下的jar包中以備下次增量編譯使用)。
- 支援增量發布,通過調用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%'
這樣的指令碼來進行增量發布,將編譯後的結果與部署環境上的進行自動比對更新)。
- 支援基於Ant和基於Maven的代碼走查,編寫Ant指令碼和Maven指令碼以支援PMD、Checkstyle、findBugs進行代碼走查(由於jenkins中代碼走查外掛程式產生介面時會消耗大量系統資源,對機器效能影響很大,後面我們改成了通過指令碼方式產生並將走查結果打成壓縮包發郵件給相關人員)。
- 支援基於Maven的代碼的單元測試(採用TDD編碼方式)。
- 支援自動化測試(調用ZTP,ZTP是我司自產的一個自動化測試載入器,支援自動化指令碼錄製、回放等工作,其工作原理與robotframework比較類似,ZTP工具支援批處理指令碼調用,故可以整合到jenkins中)。
當時,我們還想在Jenkins上整合更多的功能,包括:
- 改進websphere-deploy外掛程式,支援介面部署WebSphere應用程式套件(這個計劃後面擱淺了,主要是指令碼方式已經能支援絕大部分WAS叢集的部署了)。
- 應用環境遷移,通過將應用環境遷移過程自動化為Jenkins中的任務,實現應用環境遷移過程的自動化和可視化(目的就是想實現研發、測試及生產都是一套環境,測試通過後的環境能隨即轉移到生產上去,當時只是做營運的一種本能的想法,但也由此引發了對Docker的關注)。
- 與業務監控系統相結合,形成流程化的跨多個Jenkins任務的、整體的應用環境部署自動化和可視化,為將來生產環境部署的自動化和可視化作準備(曾經研究過一段時間jenkins的FLOW外掛程式,當時FLOW外掛程式的版本還比較低,功能還很弱,引入第三方的工作流程工作量會比較大,而事實上這種流程化的編譯部署過程實用性也比較低,這事就慢慢擱淺了)。
目前我所在的產品線所有的項目都已經採用Jenkins進行編譯、打包、代碼走查及自動部署到測試環境和准生產環境(電訊廠商項目的生產環境發布後面會逐漸由我司自主開發的另一利器“雲應用管理平台”來支援,後面還會講到)。
基於Docker的應用發布
前面講了,關於應用環境遷移的想法引發了我對Docker的興趣,實際上這時已經是2014年的6月份了,於是就跟一些同事自學鼓搗一下,當時Docker 1.0才剛剛發布,當時也就把官網的例子都做了一遍,參考官網作了Hadoop的鏡像,自己又作了WebSphere的鏡像,搭建了Registry,斷斷續續的作了一些東西,算不上很深入,而Docker本身也在不斷髮展,感覺隔幾天就有一個新版本發布出來。
Docker大潮來勢洶湧,到9月份的時候,我一開始說的那個巨大項目的電訊廠商中有個技術專家提出了要用Docker來作應用發布平台,當時簡直是不謀而合,於是有了一個項目,也就可以明正言順的進行Docker研究了,不過既然已經是一個正式的項目了,那光有幾個愛好者是不夠的,需要有正規軍了,於是請出了公司技術委員會下屬的一個研發團隊,大約有六七人,也就是“雲應用管理平台”的Team Dev,一起進行相關的研究。給生產環境用的發布平台跟我們前面講的用jenkins作的自動部署還是有些不同的,生產環境上版本的發布一般是有嚴格限制的,包括版本要求、時間要求(升級時間、故障率和故障解決時間)等,這一點是與現在的互連網企業升級自家的系統是完全不同的。
“雲應用管理平台”圍繞著Docker進行了大量的開發工作,製作了主機管理、容器管理、叢集管理、版本計劃管理、版本執行管理等等,其架構如:
1)Jenkins打包鏡像
- 自動擷取SVN代碼版本
- 使用Dockerfile打包鏡像,並自動上傳到Docker Registry。
2)叢集配置
- 應用基本資料配置
- 叢集應用綁定
- 環境變數配置
- 固定連接埠號碼配置
3)制定發布計劃
- 選擇鏡像版本(Docker Registry API擷取鏡像列表)
- 選擇主機,並設定啟動的容器執行個體數
4)執行發布計劃
- 使用Docker Java API串連docker daemon啟動容器
- 記錄容器ID
5)容器管理
- 主機檢測(能檢測主機上是否安裝了docker,如果沒有可以自動安裝Docker)
- 容器節點增加、縮減,使用者選擇的應用鏡像版本和實際運行版本一致時執行伸縮
- 容器狀態監測
基於Dubbo的跨主機的容器串連
一開始我們在測試環境上將所有容器都放在一台主機上,測試過程很順利,但在移到准生產環境上時,由於要類比生產環境只能將容器部署到不同的主機上,這時候就發現了一個奇怪的現象,應用之間調不通了,這裡要說一下,我們應用程式是由20多個服務組成的、通過Dubbo【阿里提供的一個服務架構】作為服務匯流排串聯起來的,Dubbo提供了一個方便的服務發現機制,各個服務(稱為服務提供者)只要向Dubbo註冊中心註冊過,註冊中心就會將服務的地址發送給同樣在註冊中心註冊的服務調用方(稱為消費者),之後即使dubbo註冊中心掛了也不影響服務的調用。
當服務提供者部署在容器中時,這時候發現其在Dubbo中心註冊的是容器的IP地址,而對處於另一個主機上的消費者來說這個IP是不可訪問的,我們當時也參考了多種方式,想讓消費者能夠串連上服務提供者的IP,查閱資料總結起來大概有兩種做法:
- 設定容器的IP與主機IP在同一網段內,使容器IP可直接存取【會佔用大量的IP地址,且IP會限制在同一網段,在生產環境中往往不可能】。
- 通過複雜的iptables路由規則,通過多層橋接方式打通網路【此法是可行的,也是我們今後要考慮的,但當時一堆開發人員對網路這塊都不是太熟悉】。
考慮到當時公司技術委員會下屬另一個研發團隊正在做dubbo的研究和改造,於是拉他們進來討論,結果他們說這個很容易解決,由於主機之間是連通的,而容器在建立時也映射了主機和連接埠,只需要在服務註冊時註冊的是映射的主機IP和連接埠就可以連通了,該研發團隊的效率很高,討論的當天就給出了實現,考慮到局方要求嚴格管理容器和主機間的映射,我們將主機IP和連接埠作為環境變數在容器啟動時傳入【擴充了dubbo protocol配置,增加了兩個配置項 publish host、 publishport,對應主機的ip port,並且在註冊服務時將主機的ip port寫到註冊中心】,果然解決了這個問題。
當然這是一種特殊情況下的跨主機容器串連方式,更為普遍的方式目前我們正在討論當中,基於ovs的串連方式是正在考慮的一個方案。
困難與展望
目前我們對Docker的使用還比較初步,雖然基本滿足了項目的要求,但考慮到將來雲平台要求自動擴充、服務發現,這些還有待我們進一步研究。
Q&A
Q:你好,問一個問題,我們前段時間也把Dubbo架構運行在Docker裡面,也是採用你們現在的把宿主機和連接埠作為環境變數傳入的方式實現的,我比較想瞭解的是後繼你們有什麼更好的方式實現,我看你提到了基於OVS的方案?
A:有兩種解決辦法:
- 一種是將顯式傳遞環境變數做成隱式的自動擷取宿主機和連接埠,從而減少配置工作;
- 另一種則是通用的Open vSwitch(OVS)方案,這是與Dubbo無關的。
Q:容器中的Dubbo註冊問題,擴充Dubbo的protocol配置,增加publishhost和publishport解決了註冊問題,能不能說的詳細一點?
A:目前我們寫入程式碼了Dubbo的protocol,在裡面加了兩個欄位,這種擴充方式有點野蠻,但Dubbo本身提供的擴充方式目前很難支援傳遞環境變數方式,我們在考慮將環境變數隱式擷取,這樣就不用寫入程式碼了。
Q:你們用的還是連接埠映射吧,那麼也會存在很多個連接埠的問題吧,像IP可以訪問一樣?
A:在這個項目中作連接埠映射是電訊廠商的要求,他們要求能通過配置來設定每個容器的連接埠映射,這與他們現有的營運方式有關,一開始我們考慮的是docker的自動連接埠映射,當然這種需求將來肯定是趨勢,我們的“雲應用管理平台”中也有考慮。
Q:為何考慮Dubbo而不是etcd做服務發現,Dubbo的優勢是什嗎?
A:選中Dubbo是很偶然的,公司本身有ESB產品,但相對來說比較重,一般用於多個產品間的調用,而Dubbo我們一般用於產品內部多個模組之間的調用,是一種輕量級的服務匯流排,Dubbo這種方式不依賴於硬體和作業系統,etcd並不是所有作業系統都能支援的吧,當然我也沒有對etcd作深入的研究。
Q:Jenkins的slave是選用了虛擬機器還是直接物理機?
A:我們的Jenkins的master和slave都是用的虛擬機器。
Q:代碼提交上去,如果測試有問題,復原是腫麼處理,也是通過Jenkins?
A:這裡要分情況來說,一種是測試發現問題,提單子給開發修改,開發修改完代碼提交到scm,然後觸發Jenkins下一輪的編譯和部署;另一種情況是如果某次部署失敗,則會用部署前的備份直接還原。
Q:請問用的Registry V1還是V2 ,分布式儲存用的什麼,有沒有加Nginx代理?
A:目前我們用的是V1。生產環境多是叢集環境,需要加Nginx作分發。目前應用中分布式儲存用的並不多,一般來說用hdfs來儲存一些日誌便於後面分析,也有用FastDFS和MongoDB的。
Q:底層雲平台用的是私人雲端?
A:底層平台一開始想用私人雲端,但電訊廠商已經有了vCenter的環境,因此後來我們改用Ansible來管理各類物理機和虛機,用Docker API來管理容器。
Q:Dubbo實現的服務發現是否具備failover功能,自動檢測並遷移失敗容器?
A:Dubbo目前不具備遷移容器的功能,其failover是通過負載平衡和心跳串連來控制的,自動檢測和容器遷移我們一般會考慮放在監控系統裡面來做,如果放在Dubbo裡面會加重Dubbo,只所以用Dubbo也是考慮到它的輕便性。
Q:能否談下對Jenkins+Mesos的看法,這個涉及到docker-in-docker的必要性?
A:Mesos我們才剛剛接觸,我瞭解的不太多,至於docker-in-docker我覺得生產上很難用,因為效能方面損失比較嚴重,我們做過效能測試,非
--net=host方式的容器效能損失接近30%。
Q:能具體介紹下利用Dockerfile打包鏡像嗎,jar包也是在這一步編譯出來的嗎,這樣發布出去的鏡像會既包括代碼又包含jar包吧?
A:我們的鏡像中是不包含代碼的,鏡像裡面是產品包,編譯是在打鏡像之前做的。
Q:對不生產環境中不適合以容器啟動並執行組件,Jenkins+Docker是否就沒有優勢了?
A:開發與測試環境還是很有優勢的,當然有些有大量IO操作的服務其實不適合放在容器裡面,這主要是效能方面的考慮。
Q:雲平台是怎麼管理容器的,有沒有使用Docker生態系統相關的組件?
A:目前沒有用到Swarm\Compose之類的組件,將來要看這塊的發展了,也有可能會引入k8s或者Mesos來作管理,這些目前都在考慮當中 。
Q:在怎麼判斷部署Docker服務不可用,不可用後自動遷移還是如何操作?
A:目前雲應用平台只在發布時才對Docker容器進行狀態檢測,如果檢測到失敗,會根據指定的容器數目進行重新建立。後續我們會把對容器狀態的持續檢測統一放到監控系統中。
Q:我是不是可以這麼理解,你們的Jenkins是主要用來CI,而實際叢集管理則是雲應用平台做的?
A:是的,這個是嚴格分工的,當時作雲應用管理平台時,是以測試交付物為起始點的,這裡的測試交付物就是CI的產物,容器方式下就是鏡像了。
Q:我可以理解Docker是部署在實體機,實體機上都有一個agent的東西負責與管理端通訊,主要負責Docker的管理(安裝,部署,監控等)嗎?
A:我們的Docker目前都是部署在虛擬機器上的,作業系統是Redhat 7.1,你所謂的agent其實應該就是Docker daemon吧。
徐新坤:這個我補充一下,作為Jenkins的slave,會向slave裡面啟動一個agent來執行相關指令碼命令的。這個屬於Jenkins的功能,可以去體驗下。
Q:一個應用多個容器你們怎麼負載平衡?
A:前面其實回答過,要加Nginx的。
Q:利用Dockerfile打包鏡像並上傳到Registry更像是CD環節的事情,那在單元測試、整合測試環境是否有利用到Docker呢,是否使用Jenkins中Docker相關的外掛程式了?
A:當前項目的單元測試、整合測試都用到docker容器的。Jenkins中沒有用Docker外掛程式,試過感覺都不太成熟,目前還是Docker命令列最方便。
Q:開始的時候有講如果沒有Docker自動部署會自動部署,這個是如何部署的?
A:這個前面講過,是通過lftp指令碼比對編譯環境與待部署的遠程目錄。
Q:也就是你們在虛擬機器裡面部署的Docker?
A:是的,當前的項目是在虛擬機器裡面部署Docker的,但我個人觀點從長遠看來,其實在物理機上部署Docker會更好,所以現在很多私人雲端比如OpenStack、CloudStack都能支援直接管理容器,不過目前虛擬機器還是不能缺少的,容器的隔離性不如VM。
Q:如果用nat模式 容器如何指定IP啊?
A:不需要指定容器IP,只需要映射連接埠。
Q:有通過Dubbo做服務路由嗎?
A:Dubbo本身就有服務路由的功能。
===========================
以上內容根據2015年10月20日晚群分享內容整理。分享人
方進,ZTEsoft資深工程師,十多年間從事過研發、測試、整合、技術經理、專案經理、架構師和產品經理等各種崗位,不忘初心,始終懷著對技術的渴望和追求,不斷追逐新架構新技術,推行CI改善研發流程,推進雲化應用部署與自動化營運,近期對容器化比較癡迷,如獲至寶,希望能儘早在生產中使用起來,為此借DockOne群與大家分享心得,互相學習。 DockOne每周都會組織定向的技術分享,歡迎感興趣的同學加:
liyingjiesx,進群參與,您有想聽的話題可以給我們留言。