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