Pipeline As Code With Jenkins2.0

2018-03-20 09:25:36 +08:00
 hansonwang99


Jenkins2.0 Pipeline 导入

Pipeline as Code 是 Jenkins 2.0 版本的精华所在,是帮助 Jenkins 实现从 CI 到 CD 华丽转身的关键工具。

所谓 Pipeline,简单来说,就是一套运行于 Jenkins 上的工作流框架,将原本独立运行于单个或者多个节点的任务连接起来,实现单个任务难以完成的复杂发布流程

Pipeline 的实现方式是一套 Groovy DSL(类似 Gradle),我也正在学习和实践,任何发布流程都可以表述为一段 Groovy 脚本,并且 Jenkins 支持从代码库直接读取脚本,从而实现了 Pipeline as Code 的理念。


为什么要使用 Jenkins2.0 Pipeline

这里主要结合我自己对传统 Jenkins Job 使用的一些痛点来说:

我想这些理由应该足以让我们把目光转向 Jenkins2.0 的 Pipeline !

Pipeline 的功能和优点:

  1. 持久性:在 jenkins 的 master 按计划和非计划的重启后,pipeline 的 job 仍然能够工作,不受影响。其实理解起来也很简单,jenkins 的 master 和 agent 通过 ssh 连接,如果你知道 nohup 或 disown 的话,就可以理解为啥 master 的重启不会影响 agent 上的 job 继续运行。
  2. 可暂停性:pipeline 基于 groovy 可以实现 job 的暂停和等待用户的输入或批准然后继续执行。 3. 更灵活的并行执行,更强的依赖控制,通过 groovy 脚本可以实现 step,stage 间的并行执行,和更复杂的相互依赖关系。
  3. 可扩展性:通过 groovy 的编程更容易的扩展插件。
  4. 设计 Pipeline = 设计代码,很优雅
  5. As Code:集中管理 CI 脚本、用代码库来管理脚本、从代码库直接读取脚本,从而可以将项目 CI 迅速拉起来!

Pipeline 原理与流程

Pipeline 为用户设计了三个最最基本的概念:

一个典型的 Stage View 如下图所示:

从图中可以十分方便地看到哪些 Stage 通过,哪些 Stage 失败,以及构建的时间。

Jenkins2.0 的 Pipeline 搭建使用的是 Groovy 脚本,通过 Groovy 脚本实现工作流管理的步骤如下:

实际上更常用的是 MultiBranch Pipeline,上面的图中截图没有包含,但与普通 Pipeline 基本类似。

上图的实例脚本如下:

node { 
    stage('Checkout Code') { // for display purposes 
        // Get some code from a GitHub repository 
        git 'https://github.com/jglick/simple-maven-project-with-tests.git' 
    }
 
    stage('Build') { 
        // Run the maven build 
        if (isUnix()) { 
            sh "'${MAVEN_HOME}/bin/mvn' -Dmaven.test.failure.ignore clean package" 
        } else { 
            bat(/"${MAVEN_HOME}\bin\mvn" -Dmaven.test.failure.ignore clean package/) 
        } 
    } 
 
    stage('Unit test') { 
        junit '**/target/surefire-reports/TEST-UT.xml' 
        archive 'target/*.jar' 
    } 
} 

构建过程的 stage View 如下:

很明显可以看出,这里显示的和 Groovy 脚本中格式化的代码是一致的,会实时显示各个工作流的执行进度和结果,直观易懂。鼠标移上去,能看到日志信息的缩略图,单击可以调到对应 stage 的 console 中。

总而言之,一切都是那么地优雅!


Jenkins2.0 Pipeline 关键 DSL 语法及示例

在这里总结一下 Pipeline 中的关键 DSL 语法,利用 Groovy 对其进行组合可以完成任何一项复杂的 CI/CD 流程,熟悉它们大有裨益。

归档文件,举例:

archiveArtifacts 'target/*.jar'

执行 windows 平台下的批处理文件,如

bat "call example.bat"

触发构建一个 jenkins job,如

build 'TEST_JOB'

从 SCM 系统中 checkout repo,如:

checkout([$class: 'SubversionSCM', additionalCredentials: [], excludedCommitMessages: '', excludedRegions: '', excludedRevprop: '', excludedUsers: '', filterChangelog: false, ignoreDirPropChanges: false, includedRegions: '', locations: [[credentialsId: '30e6c1e5-1035-4bdd-8a44-05ba8f885158', depthOption: 'infinity', ignoreExternalsOption: true, local: '.', remote: 'svn://xxxxxx']], workspaceUpdater: [$class: 'UpdateUpdater']]) 

从 workspace 中删除当前目录

切换目录,如

dir('/home/jenkins') { // 切换到 /home/jenkins 目录中做一些事情
    // some block
}

打印信息,如 echo 'hello world'

利用 Jenkins 发送邮件,内容、主题全都可以自定义,如

emailext body: 'Subject_test', subject: 'Subject_test', to: 'hansonwang99@163.com.cn'
// 邮件的正文 body,主题 subject,收件人 to 等可以进行自定义

抛出一个错误信号,可以自行在代码里抛出,如 error 'read_error'

检查工作空间某个路径里是否存在某个 file,举例:

fileExists '/home/test.txt'  // 检查是否存在 test.txt

等待外界用户的交互输入,举例:

input message: '', parameters: [string(defaultValue: '默认值', description: '版本号', name: 'version')] // 在某一步骤,等待用户输入 version 参数才能往下执行

用于判断当前任务是否运行于 Unix-like 节点上,举例:

def flag = isUnix()
if( flag == false ) { // 可以据此进行判断
	echo "not run on a unix node !"
}

调用一个外部 groovy 脚本,举例:

load 'D:\\jenkins\\workspace\\test.groovy'

分配节点给某个任务运行,举例:

node('节点标签') { // 在对应标签的节点上运行某项任务
	Task()
}

并行地执行任务,可以说是最实用高效的工具了,举例:

parallel(   //并行地执行 android unit tests 和 android e2e tests 两个任务
	'android unit tests': {
		runCmdOnDockerImage(androidImageName, 'bash /app/ContainerShip/scripts/run-android-docker-unit-tests.sh', '--privileged --rm')
	},
	'android e2e tests': {
	runCmdOnDockerImage(androidImageName, 'bash /app/ContainerShip/scripts/run-ci-e2e-tests.sh --android --js', '--rm')
	}
)

设置 Job 的属性,举例:

properties([parameters([string(defaultValue: '1.0.0', description: '版本号', name: 'VERSION')]), pipelineTriggers([])]) // 为 job 设置了一个 VERSION 参数

从工作空间中读取文件,举例:

def editionName = readFile '/home/Test/exam.txt'

重复 body 内代码 N 次,举例:

retry(10) {
    // some block
}

执行 shell 脚本,如:sh "sh test.sh"

延时,如延时 2 小时:sleep time: 2, unit: 'HOURS'

创建任务的 stage,举例:

stage('stage name') {
    // some block
}

存放文件为后续构建使用,举例:

dir('target') {
	stash name: 'war', includes: 'x.war'
}

将 stash 步骤中存放的文件在当前工作空间中重建,举例:

def deploy(id) {
    unstash 'war'
    sh "cp x.war /tmp/${id}.war"
}

时间限制,举例

timeout(time: 4, unit: 'SECONDS') {
    // some block
}

用于在控制台加时间戳,举例:

timestamps {
    // some block
}

创建文件,举例:

touch file: 'TEST.txt', timestamp: 0

解压文件,举例:

unzip dir: '/home/workspace', glob: '', zipFile: 'TEST.zip'

检查给定的文件是否包含一个有效的 Declarative Pipeline,返回 T 或者 F

validateDeclarativePipeline '/home/wospace'

等待,直到条件满足

waitUntil {
    // some block
}

使用凭据

withCredentials([usernameColonPassword(credentialsId: 'mylogin', variable: 'USERPASS')]) {
    sh '''
      set +x
      curl -u $USERPASS https://private.server/ > output
    '''
}

设置环境变量,注意近本次运行有效!

withEnv(['MYTOOL_HOME=/usr/local/mytool']) {
    sh '$MYTOOL_HOME/bin/start'
}

写文件到某个路径

writeFile file: '/home/workspace', text: 'hello world'

写 JSON 文件,用法基本同上

创建 zip 文件

zip dir: '/home/workspace', glob: '', zipFile: 'TEST.zip'

自定义工作空间,在其中做一些工作,效果类似于 Dir 命令,举例:

ws('/home/jenkins_workspace') {
    // some block
}

后记

作者更多的原创文章在此


1791 次点击
所在节点    推广
1 条回复
lfzyx
2018-03-20 11:30:36 +08:00
我还是安利下 gocd 吧 https://lfzyx.org/devops/gocd.html

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/439590

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX