原文:http://www.dockone.io/article/753web
本次分享從四個方面展開:基於Jenkins的CI過程、基於Docker的應用發佈、基於Dubbo的跨主機的容器鏈接、困難與展望。docker
基於Jenkins的CI過程
一切要從2013年4月開始提及,當我4月份從委內瑞拉回來以後當即投身到國內一個運營商的大型後端建設項目的尾聲中(項目歷時3年多,當時已經接近尾聲),這個項目涉及100多臺主機,包含數十個集羣,除了傳統的WEB應用外,還用到了流程引擎、ESB、規則引擎、搜索引擎以及緩存和日誌,是當時比較複雜的體系結構(固然不能跟如今的雲平臺相比,但在項目開始的年代這仍是一個很不錯的架構),整個項目當時一兩百號人佔了局方整整一層樓十幾個辦公室。
我到了項目組以後成爲了一個小組的小頭目,管個四五我的,小組美其名曰「平臺組」,乾的都是打雜的事情,包括編譯、打包、部署,平常監控以及系統優化等工做,提及來簡單,作起來仍是很複雜的,當時全部的工做基本上是靠人工的,可想而知,100來臺機器的環境一臺一臺的部署環境,還得靠人工監控,手工檢查,四五個處處救火忙得不可開交,當時我雖然還不知道CI爲啥物(壓根兒就沒這個概念),但也下定決心要改變忙亂的狀態,累一點沒關係,可是累得跟狗似的還幹很差那就白辛苦了。
在2013年的4~8月份,咱們主要研究的是自動編譯、打包和發佈,採用的基本方式是各類腳本,包括windows下的批處理bat、Linux上的 shell甚至Python,雖基本上完成了自動從SVN取代碼、自動編譯、自動打包以及將應用發佈到WebSphere上的這些工做(以下圖):
但也明顯存在一些問題:shell
- 自動執行靠的是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研究了,不過既然已是一個正式的項目了,那光有幾個愛好者是不夠的,須要有正規軍了,因而請出了公司技術委員會下屬的一個研發團隊,大約有六七人,也就是「雲應用管理平臺」的開發團隊,一塊兒進行相關的研究。給生產環境用的發佈平臺跟咱們前面講的用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,查閱資料總結起來大概有兩種作法:windows
- 設置容器的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自己就有服務路由的功能。