使用阿里云容器服务Jenkins 2.0实现持续集成之Pipeline篇(updated on 2016.12.23)

Jenkins 2.0

Jenkins 2.0新特性:Pipeline as code,全新的开箱体验和UI可用性提升以及完全向后兼容。

  • Pipeline as Code
    通过使用Groovy DSL来描述一套运行于Jenkins上的工作流程,将原本独立运行于单个或者多个节点的任务连接起来,实现单个任务难以完成的复杂发布流程。并且,Jenkins支持从代码库直接读取脚本。
  • 全新的开箱体验和UI可用性提升
    Jenkins 1.XXX 安装默认是开放所有权限,因为安全性存在隐患。而Jenkins 2.0则加强了安全管理。
  • 完全向后兼容
    Jenkins官方公布是完全向后兼容的,所以在Jenkins 1.XXX版本的功能都可以使用,但插件还是得自己验证。

场景描述

我们在定义一个Jenkins项目的时候,希望它是一个清晰的范围。也就是说,如果这个项目叫mvn test and build,我们就应该在脚本里实现测试和jar/war包打包。而事实上是,接下来的需求很多时候是把测试结果展示出来,还有上传jar/war包。这个时候,Devops工程师会陷入一种纠结,就是值不值得为了这个需求再重新创建一个freeStyle的项目。明明可能只有两个语句,却需要整个重新配置一个项目。新建意味着付出更多的精力并且后期维护成本增加;在原有的项目里实现,意味着项目臃肿,定义模糊。Jenkins 2.0 提供的Pipeline项目类型给出了很好的解决办法。

下面我们将用java web源代码来演示一个Pipeline项目如何优雅地实现代码提交触发测试,war包打包,测试结果展示,war包本地存储以及上传OSS,构建镜像,部署应用,邮件推送结果的持续交付方案。 在最后一部分,我们将介绍动态生成slave,执行job,销毁slave。

先给张图来个Pipeline项目的视觉感受:
使用阿里云容器服务Jenkins 2.0实现持续集成之Pipeline篇(updated on 2016.12.23)

使用容器服务部署Jenkins应用和Slave

阿里云容器服务的官方示例模板已经新增了Jenkins 2.0镜像版本,只需要将镜像版本号1.651.3改成2.19.2。编排模板如下:

jenkins:
    image: 'registry.aliyuncs.com/acs-sample/jenkins:2.19.2'
    ports:
        - '8080:8080'
        - '50000:50000'
    volumes:
        - /var/lib/docker/jenkins:/var/jenkins_home
    privileged: true
    restart: always 
    labels:
        aliyun.scale: '1'
        aliyun.probe.url: 'tcp://container:8080'
        aliyun.probe.initial_delay_seconds: '10'
        aliyun.routing.port_8080: jenkins
    links:
        - slave-java
slave-java:
    image: 'registry.aliyuncs.com/acs-sample/jenkins-slave-dind-java'
    volumes:
        - /var/run/docker.sock:/var/run/docker.sock
    restart: always 
    labels:
        aliyun.scale: '1'

(updated)有的情况下用户需要使用root用户来启动slave容器,下面更新的这个模版里的slave镜像registry.cn-beijing.aliyuncs.com/qinyujia-test/jenkins-slave-dind-java-maven是以root用户(密码是passwd)启动的,docker client version为1.12.5,使用容器服务部署的时候请先升级Docker版本到最新版。

jenkins:
    image: 'registry.cn-hangzhou.aliyuncs.com/qinyujia-test/jenkins:2.19.2'
    ports:
        - '8080:8080'
        - '50000:50000'
    volumes:
        - /var/lib/docker/jenkins:/var/jenkins_home
    privileged: true
    restart: always 
    labels:
        aliyun.scale: '1'
        aliyun.probe.url: 'tcp://container:8080'
        aliyun.probe.initial_delay_seconds: '10'
        aliyun.routing.port_8080: jenkins
    links:
        - slave-java
slave-java:
    image: 'registry.cn-beijing.aliyuncs.com/qinyujia-test/jenkins-slave-dind-java-maven'
    volumes:
        - /var/run/docker.sock:/var/run/docker.sock
    restart: always 
    labels:
        aliyun.scale: '1'

在Jenkins master中配置slave。
使用阿里云容器服务Jenkins 2.0实现持续集成之Pipeline篇(updated on 2016.12.23)

创建Pipeline项目实现持续交付流程

选择Jenkins,添加类型为User with Password的Credentials,存储GitLab账号密码。同样添加存储OSS以及阿里云容器服务Registry账号密码的Credentials。了解OSS请参考OSS快速入门
使用阿里云容器服务Jenkins 2.0实现持续集成之Pipeline篇(updated on 2016.12.23)
新建一个Pipeline项目。
使用阿里云容器服务Jenkins 2.0实现持续集成之Pipeline篇(updated on 2016.12.23)
配置General,选择Discard old builds来减少存储空间占用。
使用阿里云容器服务Jenkins 2.0实现持续集成之Pipeline篇(updated on 2016.12.23)
配置Build Trigger,选择Build when a change is pushed to GitLab类型,根据需要来选择触发Event类型,branch,使用Secret token等。
使用阿里云容器服务Jenkins 2.0实现持续集成之Pipeline篇(updated on 2016.12.23)
最后最关键的也就是配置Pipeline,本示例采用的是Pipeline Script,直接在项目中填写脚本,就像前面介绍的一样,用户也可以使用Pipeline script from SCM方式,直接从代码读取Jenkinsfile。
使用阿里云容器服务Jenkins 2.0实现持续集成之Pipeline篇(updated on 2016.12.23)

Note: 请打开上图中的Pipeline Syntax链接,Jenkins提供了很大程度上帮用户生成脚本的功能。

完整的脚本如下:
node指定的是slave节点,必须使用Jenkins已经管理的节点名称。
stage指的是一个view,名称由用户自己指定。本示例一共创建了6个stage来实现持续交付。其中deploy是连接了一个freestyle的应用部署项目。

  • Prepare:拉取代码。
  • mvn test and build:测试和打包war包。
  • publish and archive:测试结果展示,war包本地存储。
  • Upload to oss:上传war包到共享存储OSS。
  • image build:镜像构建,推送。
  • deploy:部署应用。
node('slave-java') {
  stage('Prepare') {
    withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'gitlab',
      usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
      sh 'rm -rf java-demo'
      sh "git clone http://'${USERNAME}':'${PASSWORD}'@139.129.99.183:10080/root/java-demo.git"     
    }
  }
  stage('mvn test and build') {
    sh 'ls'
    dir('./java-demo') {
      sh 'mvn -version'
      sh 'mvn  -Dmaven.test.failure.ignore clean package' 
    }
   }
  stage('publish and archive') {
    dir('./java-demo') {      
      junit '**/target/surefire-reports/TEST-*.xml'
      archive 'target/*.war'
    }
   }
  stage('Upload to oss') {
    withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'gitlab',
      usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
      sh 'rm -rf oss'
      sh "git clone http://'${USERNAME}':'${PASSWORD}'@139.129.99.183:10080/root/oss.git" 
    }  
    dir('./oss') {
      sh 'unzip OSS_Python_API_20160419.zip'  
      withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'oss',
        usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
        sh 'python osscmd put ../java-demo/target/boot-api.war oss://cs-jenkins/boot-api.war --host=oss-cn-qingdao.aliyuncs.com --id=${USERNAME} --key=${PASSWORD}'    
      }     
    }
  }   
  stage('image build') {
    dir('./java-demo') {
      sh 'cp target/boot-api.war .' 
      withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'registry',
        usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
          sh 'sudo docker login -u ${USERNAME} -p ${PASSWORD} -e yujia.qyj@alibaba-inc.com registry.cn-beijing.aliyuncs.com'    
          sh 'sudo docker build -t registry.cn-beijing.aliyuncs.com/qinyujia/boot-api .'
          sh 'sudo docker push registry.cn-beijing.aliyuncs.com/qinyujia/boot-api'
      }       
    }
  }
  stage('deploy') {
    build 'deploy'
  }
  stage('email') {
    emailext body: '''Project: $PROJECT_NAME 
    Build Number: # $BUILD_NUMBER
    Build Status: $BUILD_STATUS
    Check console output at $BUILD_URL to view the results.''', recipientProviders: [[$class: 'DevelopersRecipientProvider']], subject: '$BUILD_STATUS - $PROJECT_NAME - Build # $BUILD_NUMBER', to: 'yujia.qyj@alibaba-inc.com'
  }
}

Dynamic slave

下面我们来介绍一下如何配置动态的生成slave,执行Jenkins job,销毁slave。
首先安装yet another docker插件。
然后选择Manage Jenkins,Configure System。
创建一个阿里云服务的集群,作为slave容器的承载。
Docker URL中填写集群接入点地址。
Host Credentials中添加 Docker Host Certificate Authentication类型的证书。按照图中提示把集群相应的证书输入。这时候可以使用Test Connection来测试集群的连通性。
使用阿里云容器服务Jenkins 2.0实现持续集成之Pipeline篇(updated on 2016.12.23)
使用阿里云容器服务Jenkins 2.0实现持续集成之Pipeline篇(updated on 2016.12.23)

Docker Image Name中填写slave镜像。本示例中使用的是阿里云容器服务提供的示例镜像registry.aliyuncs.com/acs-sample/jenkins-slave-dind-java。
Registry Credentials中添加类型为User with Password的Credentials,输入阿里云容器服务镜像仓库的账号和密码。
Create Container Settings中在Volumes对话框中挂载两个数据卷:
/var/run/docker.sock:/var/run/docker.sock #用来让slave可以使用宿主机的docker deamon。
/home/jenkins/.m2:/root/.m2 #将maven仓库存储在ECS的数据卷中,这样即使容器删除,maven repository还能保留,接下来新生成的slave可以不用重新下载依赖包。
使用阿里云容器服务Jenkins 2.0实现持续集成之Pipeline篇(updated on 2016.12.23)
Jenkins Slave Config部分,Labels是slave节点在Jenkins项目的标识符。Launch method是master连接slave的方式,本示例选用的是Docker SSH computer launcher的方式,Credentials中添加类型为User with Password的Credentials,填写registry.aliyuncs.com/acs-sample/jenkins-slave-dind-java镜像的账号密码,为jenkins/jenkins。 (slave镜像registry.cn-beijing.aliyuncs.com/qinyujia-test/jenkins-slave-dind-java-maven的账号密码是root/passwd。)
使用阿里云容器服务Jenkins 2.0实现持续集成之Pipeline篇(updated on 2016.12.23)
这样slave的docker源就已经配置完成。下面我们只需要修改一行Jenkins Pipeline代码就可以使用动态slave。把node中的标签由'slave-java'改成‘docker’。 这时候,您就可以变更代码触发整个持续交付流程了。

node('docker')

总结

这套持续交付的方案,最大的两个特性在于,一,使用了Jenkins Pipeline Project,通过Jenkinsfile脚本简化整个配置过程,增加了artifacts存储,加强了结果展示,UT结果看板以及stage时间统计。二,Dynamic slave,提供了动态生成slave,执行Jenkins job,销毁slave的能力。

想查看阿里云容器服务提供了哪些slave节点,请访问 https://github.com/AliyunContainerService/jenkins-slaves
想了解更多容器服务内容,请访问 https://www.aliyun.com/product/containerservice

上一篇:SQL Server 2005数据库安装


下一篇:三星又有折叠手机新专利,然而Galaxy Fold的屏幕bug还没有解决