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。这些项目确定是比本身从头开始造一个轮子更加有效率并且使用风险低,毕竟全部人都想完成工做上的任务早点下班。可是从我的发展来讲,有些轮子仍是值得本身去制造一次的,这样子你才会了解到这个组件的工做原理,底层的东西。因此我我的的推崇的是,假如你找到了合适接近完美的轮子那就直接用,若是找到了一个可用可是总以为用起来不太爽的组件,那么你就把轮子造起来吧。

 

总结

 

持续发布不少团队想有这样的工具达到这个效果,有些团队以为不须要。任何工具、流程都须要符合自身团队的实际。从我开始参与团队内的这个和持续发布有关的项目,查看了许多资料,结合团队项目内的实践。给出的一些经验的和看法和你们一块儿分享,若有错误或者建议欢迎你们及时沟通。谢谢你们的参与。

相关文章
相关标签/搜索