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

  1. 云栖社区>
  2. 容器服务Docker&Kubernetes>
  3. 博客>
  4. 正文

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

嫀嫀 2016-11-28 10:06:02 浏览39847

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项目的视觉感受:
1.jpg

使用容器服务部署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。
2.jpg

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

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

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来测试集群的连通性。
8.jpg
9.jpg

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可以不用重新下载依赖包。
10.jpg
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。)
11.jpg
这样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