Docker的Jenkins Pipeline工做流

原文地址:http://www.youruncloud.com/blog/127.htmlphp

分享主題html

一個軟件產品的開發週期中,尤爲是敏捷開發,持續集成和持續部署是必不可少的環節,而隨着產品的豐富,模塊的增多。隨即帶來了更加多的問題,各模塊間編譯環境的準備,編譯複雜,耗時增長,還須要專人去負責這個流程。而Jenkins則能夠很好的解決這個單一而容易出錯的CI(持續集成)工做。node

 

Jenkins也存在着編譯環境不隔離的問題,雖然能夠經過集羣的方式解決,但是須要爲每種環境甚至是一種語言的不一樣版本準備多臺機器,這個利用率很低很低。git

 

本次線上活動主要分享在基於Docker的Jenkins pipeline工做流的一些經驗和看法:golang

一、什麼是jenkins?jenkins pipeline如何爲咱們工做帶來便利;web

二、Jenkins pipeline 的基礎概念;docker

三、基於容器的方式Jenkins CI流程;shell

四、Jenkins和Docker、Kubernetes整合、完成集成部署。數據庫

 

傳統交付方案編程

 

傳統咱們的項目開發模式是產品調研提出需求,開發團隊研究決定開發方案選型。而後開始一個週期的開發,模塊開發完成以後開始模塊間的聯調。聯調結束以後打包交付給測試團隊。測試團隊,系統測試或自動化測試,而後提交bug,開發團隊修復bug,周而復始。

 

傳統的模式中,存在着較多的不肯定因素。例如,開發環境、編譯環境、測試環境、生產環境,等不肯定因素。人爲介入打包中的不肯定因素,缺少單元測試和自動化測試的整合。從而致使的結果是,開發-測試-修復的週期較長,並且不少小的問題徹底能夠由單元測試進行覆蓋。

 

持續交付

 

持續交付並非某個特定的軟件,而是一個結果。這個結果要求團隊能夠隨時的發佈一個新的準確版本,並且要求在編譯發佈的過程當中進行自動化測試,經過自動化測試能夠及時的發現並定位存在的bug,修復bug以後再進行快速的發佈到測試環境,測試團隊直接進行測試。

 

與傳統模式的區別在於持續交付能夠提早發現bug的存在和快速修復而沒必要等到測試人員的介入以後才發現。持續交付分解出來就是「持續」和「交付」。

 

  • 持續:持續要求任什麼時候,候任何狀況都能進行準確的發佈,作到準確的發佈須要注意如下幾個關鍵點。

 

一、持續應該是一個週期性的,能夠是天天的某個時間點,也能夠是某次代碼的提交,或者某次人爲觸發。因此人工進行構建是不可能的,須要自動化的構建,自動化要求構建的任何一個流程都必須以腳本的形式運行,代碼檢出、代碼構建、各模塊代碼單元測試、集成測試、UI自動化測試等。

 

二、發佈的程序版本不容許是各個模塊在開發環境編譯出一個版本做爲交付,而要求在一個純淨的編譯環境中進行構建。

 

三、構建的過程應該要求最大可能的固化,例如操做系統的版本,構建環境的版本,相關的依賴等。

 

四、避免從網絡獲取相關的文件,這點以nodejs爲開發或編譯的項目尤爲重要,安裝node的依賴包老是一個漫長的過程,就算有國內的源,通常的項目也須要一兩分鐘的node依賴包,這不符合快速構建。

 

  • 交付:在持續編譯的過程,使用自動化已經能夠避免大多數的錯誤了。可是仍是須要人爲介入的系統測試,畢竟自動化的測試通常只能覆蓋到70%左右。

 

根據咱們團隊內部推廣這種工做方式的效果來看,持續集成確實讓咱們工做便利了許多, 每次代碼的構建和自動化測試讓咱們及時發現存在的bug。好的工做模式也須要團隊成員的遵照,團隊成員應該積極的擁抱這種工做方式,團隊成員須要作好如下幾點。

 

一、使用版本工具例如git。git有強大的版本回溯,成員每次完成一個小的功能點進行代碼提交。合併到master分支,持續交付工具應該配置爲代碼更新觸發。團隊內部應該等到持續交付流程結束以後,確認編譯、自動化測試經過以後方可進行下一個版本的提交,這樣容易定位bug。而不會致使此次bug影響團隊內其餘成員的工做。

 

二、主分支的代碼bug不該該存留時間過長,避免團隊內其餘成員合併代碼的時候引入其餘問題。

 

三、測試驅動開發,任何一個新的功能開發都應該先寫好單元測試腳本,並積極更新自動化測試腳本。而且積極地擁抱測試,雖然你明白這個測試不經過的問題並不會引發很大的系統性問題 ,可是仍是應該進行修復而不是千方百計的跳過這個自動化測試。

 

四、臨近下班的時候不要提交代碼,這主要是由於遵照第2點。

 

一個解決方案

 

  • 使用Docker

 

Docker已經愈來愈火,CICD和Devops也是Docker一個重要的場景。在持續交付中使用Docker有一下優勢。

 

一、Docker強大的環境隔離性能夠將環境和程序打包在一塊兒,測試、運維,人員無需知道咱們的程序是如何配置的,只須要一條Docker 的命令就能夠將咱們的程序運行起來,這也更加容易實現持續部署。

 

二、減小編譯環境的污染,由於Docker自然的隔離性,也避免了傳統編譯環境難以配置多套編譯環境的問題。在基於Docker的持續發佈中,咱們能夠在同一臺宿主機上同時編譯不一樣版本的Java項目,不一樣版本的Python項目,而無需任何配置,鏡像也只是從docker hub中獲取。

 

  • 持續集成

 

在持續集成方面,咱們選擇Jenkins。Jenkins是一款開源軟件,擁有衆多優秀的插件,依靠這些插件,咱們能夠完成一些週期、繁瑣、複雜的任務。例如咱們今天分享的持續發佈,雖然Jenkins解決了咱們繁瑣複雜週期性的操做,可是沒有解決咱們在多種環境下編譯構建的需求。而這個場景正是Docker的強項。

 

經過Jenkins的pipeline咱們能夠實現代碼檢出、單元測試、編譯、構建、發佈、測試等流程的自動化,而最終經過Jenkins的Docker插件將產出物構建成鏡像,方便部署到Docker環境。

 

  • 持續部署

 

持續集成讓咱們新的代碼源源不斷的構建成了鏡像,這些鏡像經歷了單元測試,自動化測試,但尚未接受過測試團隊的嚴格測試。Jenkins是一個強大的持續集成工具,然而持續部署並非Jenkins的強項,可是Jenkins擁有不少強大的插件。2並且咱們持續集成產出的是鏡像,因此持續的部署,咱們只須要將鏡像運行起來,或者利用第三方的容器管理平臺提供的API進行部署。

 

一、本地部署應用到Docker

本地部署到Docker容器可使用Jenkins的docker插件,下面會介紹。

 

二、部署到遠程主機的Docker、Appsoar。

Docker和Appsoar都支持開啓API調用。經過現有的API咱們能夠運行咱們生成鏡像版本。從而達到持續的部署最新版本。

 

三、部署到kubernetes

kubernetes除了能夠經過API調用還能夠在jenkins中配置kubectl的方式建立或更新deployments。

 

Docker中運行Jenkins

 

Docker部署Jenkins的方式簡單方便,下面咱們介紹用Docker的方式運行Jenkins。

 

docker run -d -u root \

-p 8080:8080 \
-v/var/run/docker.sock:/var/run/docker.sock \
-v $(which docker):/bin/docker \
-v /var/jenkins_home:/var/jenkins_home \
jenkins

 

一、這裏將docker.sock和docker的可執行文件掛載到jenkins容器中,這樣咱們就能夠在容器中使用docker了。

 

二、 jenkins容器,默認的用戶是jenkins由於咱們須要使用docker因此咱們須要使用root用戶。

 

三、/var/jenkins_home的掛在卷是可選的,jenkins_home存放了全部任務、日誌、認證、插件等jenkins運行後的文件。可作數據恢復使用。

 

  • 配置Jenkins

 

一、解鎖jenkins

 

 解鎖的密碼在容器的log中能夠查看,或者直接查看jenkins_home指定文件。

 

 

二、選擇插件

 

 

  • 建立Pipeline

 

下面咱們建立一個的Jenkins的Pipeline完成簡單的cicd流程。

 

一、新建pipeline,在左側新建選擇pipeline。

二、在左側的Credentials中新建git和鏡像倉庫的credentials

三、配置pipeline,例如定時觸發,代碼更新觸發,webhook觸發等。

四、在pipeline script中填入下面的demo.

 

如下是僞代碼,僅提供思路

 

node{  
  // 代碼檢出
  stage('get Code') {
    git credentialsId: 'git-credentials-id', url: 'http://192.168.19.250/ufleet/uflow.git'
  }
  
    // 鏡像中進行單元測試
  stage('unit testing'){ 
    // 啓動golnag:1.7並在golang內編譯代碼
    docker.image('golang:1.7').inside {
      sh './script/unittest.sh'
    }
  }
  
  // 鏡像中代碼構建
  stage('Build'){    

    def confFilePath = 'conf/app.conf'
    def config = readFile confFilePath

    writeFile file: confFilePath, text: config
    
    // 啓動golnag:1.7並在golang內編譯代碼
    docker.image('golang:1.7').inside {
      sh './script/build.sh'
    }
  }
  
  // 編譯鏡像並push到倉庫
  def imagesName = '192.168.18.250:5002/ufleet/uflow:v0.9.1.${BUILD_NUMBER}'  
  stage('Image Build And Push'){
    docker.withRegistry('http://192.168.18.250:5002', 'registry-credentials-id') {
      docker.build(imagesName).push()
    }
  }
  
  // 啓動剛運行的容器
  stage('deploy iamegs'){    
    // 須要刪除舊版本的容器,不然會致使端口占用而沒法啓動。
    try{
      sh 'docker rm -f cicdDemo'
    }catch(e){
        // err message
    }
    docker.image(imagesName).run('-p 9091:80 --name cicdDemo') 
  }
}

 

Jenkins pipeline的腳本語法是groovy的語法,其中docker、Git是插件提供的能力。代碼的執行流程以下:

 

 

一、經過Git插件獲取最新代碼到jenkins的工做區,例如/var/jenkins_home/workspace/pipelineDemo。

 

二、docker.image().inside是如何編譯咱們的代碼呢,經過查看Jenkins的console能夠看到以下log.

 

 

[Pipeline] withDockerContainer
$ docker run -t -d -u 0:0 -w /var/jenkins_home/workspace/pipelineDemo --volumes-from d732ae2a92c48248a078bb082e85616abff5c80891710026ef3419b6d1bd782e -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** --entrypoint cat 192.168.18.250:5002/ufleet-build/golang:1.7
[Pipeline] {
[Pipeline] sh
[ufleet-uflow] Running shell script
+ ./script/build.sh

 

 

熟悉Docker命令的朋友應該很容易理解了,原來是docker.image().inside啓動的時候會將當前的目錄掛在到容器中,而後在容器中執行./script/build.sh,這樣咱們就完成了利用容器中存在的環境作單元測試或構建編譯了。

 

三、經過docker插件提供的能力構建鏡像,Dockerfile存放在代碼目錄中。構建鏡像後push到鏡像倉庫,私有倉庫須要自行配置鏡像倉庫。

 

四、鏡像構建完成以後就能夠刪掉舊版本,並從新運行一個新的版本。

經過簡單的例子,可見Jenkins和Docker的結合給CICD帶來了足夠的便利和強大。咱們須要準備的只是一個編譯的腳本,在編譯腳本中可使用任何的環境和任何的版本。

 

  • Pipeline 介紹

 

Jenkins的任務兩個主要版本。

 

free style只是一個自動化的腳本,腳本類型爲shell。全部的腳本在一臺機器上運行,須要的環境須要提早準備。配置不集中,混亂。可是通常狀況下仍是夠用的。

 

pipeline是jenkins2的版本使用了一個基於groovy腳本的任務類型,經過一系列的stage將構建的不一樣部分組合成一個pipline。並且配合step能夠完成異步操做。由於基於groovy可編程性更增強大,並且腳本能夠存放在源碼中,腳本的更改不須要直接到jenkins中修改。

 

  • pipeline的一些使用經驗和技巧

 

一、jenkins的資料較少,官網能夠查看的內容也很少,通常的需求Jenkins內置的pipeline-syntax裏面就有經常使用的命令生成器。能夠知足大多數需求。

 

二、在pipeline腳本調試完成以後應該將腳本以文件的形式放在源碼目錄中,這樣子方便修改。和多分支須要編譯的狀況下進行互相隔離。

 

三、應該多查找下相應的插件,而不是使用sh用執行腳本的方式來解決問題。

 

四、應該將jenkins_home目錄掛在出來,若是趕上了Jenkins崩潰了能夠及時的恢復數據。

 

五、應該新建一個定時的pipeline用來清理生成的鏡像,減小硬盤資源的佔用。

 

六、頁面新建的pipeline,在頁面刪除以後,jenkins_home/workspace中對應的項目文件並不會被刪除。

 

Q&A

 

Q1:請問kubernetes怎麼結合jenkins作持續集成呢?

A:部署到kubernetes。kubernetes除了能夠經過API調用還能夠在jenkins中配置kubectl的方式建立或更新deployments。

 

Q2:必須經過pipeline才能實現jenkins把代碼構建成Docker鏡像麼?

A:不必定,使用Docker主要是方便進行編譯環境的隔離,也能夠配置好NFS,構建完成以後複製到固定的服務器上,這個咱們通常叫製品庫。

 

Q3:Docker目前官方的私有倉庫registry並無提供鏡像刪除功能,請問大家的鏡像是如何進行版本管理的呢?

A:AppHouse是咱們公司的一個鏡像倉庫產品基於Docker的registry,咱們擴展了刪除、複製,等功能。若是有興趣的話能夠到咱們公司官網獲取咱們的AppHouse。

 

Q4:Pipeline如何經過Docker容器部署應用到不一樣的節點上去?發佈遇到問題如何回滾版本的?

A:就如我前面的稿件中提到的,jenkins的能力更多的是作持續集成(CI)的功能,部署和回滾都須要容器管理平臺並非Jenkins的強項,特別是回滾單依靠jenkins很難作到完美的方案。可是部署到不一樣的Docker的節點上,可使用第三方的管理平臺,例如AppSoar和k8s提供的API能力,能夠進行部署。jenkins直接調用curl命令執行容器管理平臺提供的API。

 

Q5:pipeline的每一個環節的報告如何快速獲取?好比代碼靜態檢查,工程構建,測試報告等等

A: http://jenkins:8080/job/clearImages/86/wfapi/ 經過jenkins這個API,能夠獲取一些狀態和時間信息,至於詳細的代碼靜態檢查,每種語言都有不一樣的語法檢查。須要自行配置。固然詳細的須要查看輸出日誌。

 

Q6:關於測試驅動開發,在開發以前寫好的用例必定要是自動化的嗎?爲何?

A:一個系統由若干的方法組成,單元測試就是測試你寫的方法是否符合你的業務要求。因此先寫合理的單元測試,只要你的方法經過了這個單元測試就表示你寫的這個方法是正確的,單元測試代碼是須要開發人員編寫,每種語言有不一樣的單元測試框架例如nodejs的mocha,golang 的go test 。自動化測試由測試人員編寫,單元測試應該須要脫離外部因素,不依賴數據庫,不依賴外部API。

 

Q7:怎麼觸發工做流的?

A: jenkins pipeline 提供了三種方式(若是安裝了SCM的插件可能有其餘的方式觸發),進入到pipeline的設置頁面中的分別有。wbhook(觸發遠程構建 (例如,使用腳本))、定時觸發(Build periodically)、代碼更新觸發(Poll SCM)。

 

Q8:jenkins的編譯環境是怎麼處理的?實際用戶的編譯需求和環境都不同。

A:用戶須要清楚你使用的編譯環境的基本狀況,例如golang的編譯環境,容器中的GOPATH是在什麼位置。你須要將你的代碼ln到什麼目錄才能進行編譯,等這些細節都是須要用戶提早知曉。

 

Q9:jenkins裏的有用戶權限管理嗎?貴公司的CI CD是怎麼實現用戶隔離的,每一個用戶只能看到本身的項目。

A:jenkins當中並無用戶權限。公司在研發的產品中,有一個虛擬的概念叫用戶組,對應的是k8s中的一個或多個namespaces。管理員將成員用戶添加到這個用戶組中,組內成員建立的資源(pipeline、集羣、服務,等)在組內是可見,用戶組來進行邏輯概念上的隔離。

 

Q10:貴公司jenkins和kubernetes是怎麼結合使用的?是什麼的部署形式?如何回滾?

A:我看到不少朋友都提問了,jenkins如何跨主機部署或者如何部署到kubernetes集羣,如何回滾。jenkins對這方面的能力比較弱,僅僅可以支持kube-api-server的調用而已,若是徹底依靠jenkins是很難完成需求,因此咱們的產品當中有一個專門對接kubernetes的deploy的模塊,一個應用商店的模塊,一個封裝了jenkins的uflow模塊,uflow模塊嚮應用商店獲取模板並根據當前編譯構建出來的鏡像tag號替換模板,並交付給deploy模塊建立。回滾和升級都由deploy模塊負責。這樣各自分開,各司其職。

 

Q11:多個php 項目,在Docker 應用中,須要逐個拆分嗎?一個項目對應一個鏡像管理?仍是使用文件夾映射的方式構建鏡像?

A:多個項目服務是放在一個容器中仍是分開容器中,這個並無強制的限定。可是建議仍是分爲多個容器進行部署。Docker的理念就是一個容器完成一個單獨的事情。

 

Q12:Jenkins PIpeline input指令能夠複雜的參數化麼?

A:input是一個比較強大的指令,能夠在pipeline的運行過程當中確認操做,字符輸入,文件上傳等功能。詳細的能夠看下jenkins的pipeline-syntax有使用說明和腳本的生成。

 

Q13:jenkins自動觸發job到build docker image,自動觸發是怎麼實現的,wedhook 定時觸發有沒遇到過問題?不能正常觸發的。

A:自動觸發的原理的原理是,咱們在pipeline中配置一個定時器,這個定時器是用cron表達式表示。例如你設置了 「* * * * * 」就表示每分鐘檢查一次,那麼檢查什麼呢,檢查每次提交的ID,例如git的commit ID 。只要檢測到了這個ID和上一次的不一致就會觸發pipeline的構建。從目前使用並無出現過不能觸發的狀況。若是出現了請檢查是不是配置的錯誤。

 

Q14:CD過程當中,重造的輪子和開源組件是一個什麼樣的比例?我的推崇哪一個?

A:本身重複造輪子和開源組件,應該如何選擇。這個是頗有意思的一個問題。由於開發者都說不要重複造輪子,這是由於不少輪子通過了不少項目考驗和衆多開發者提交代碼和fix的bug。這些項目確定是比本身從頭開始造一個輪子更加有效率並且使用風險低,畢竟全部人都想完成工做上的任務早點下班。可是從我的發展來講,有些輪子仍是值得本身去製造一次的,這樣子你纔會瞭解到這個組件的工做原理,底層的東西。因此我我的的推崇的是,假如你找到了合適接近完美的輪子那就直接用,若是找到了一個可用可是總以爲用起來不太爽的組件,那麼你就把輪子造起來吧。

 

總結

 

持續發佈不少團隊想有這樣的工具達到這個效果,有些團隊以爲不須要。任何工具、流程都須要符合自身團隊的實際。從我開始參與團隊內的這個和持續發佈有關的項目,查看了許多資料,結合團隊項目內的實踐。給出的一些經驗的和看法和你們一塊兒分享,若有錯誤或者建議歡迎你們及時溝通。謝謝你們的參與。

相關文章
相關標籤/搜索