Jenkins pipeline 是基于Groovy语言实现的一种DSL(领域特定语言),用于描述整条流水线是如何进行的。
1. pipeline的组成
1.1最简结构
以下从pipeline最简结构----Hello World 查看Jenkins file文件的构成:
pipeline {
agent any
stages {
stage('Hello') {
steps {
echo 'Hello World'
}
}
}
}
pipeline:代表整条流水线,包含整条流水线的逻辑
stage:代表流水线的阶段。每个阶段都必须有名称。本例中,Hello就是此阶段的名称
stages:流水线中多个stage的容器。stages至少包含一个stage
steps:代表阶段中的一个或多个具体步骤(step)的容器。steps至少包含一个步骤。本例中的echo就是一个步骤。在一个stage中有且只有 一个steps
-
agent:指定流水线的执行位置(Jenkins agent)。流水线中的每个每段都必须在某个地方(物理机、虚拟机或Docker 容器)执行。agent部分即指定具体在哪里执行。
以上的每一个部门都是必需的,少任何一个,Jenkins 都会报错。
1.2 步骤(Steps)
pipeline基本结构决定pipeline整体流程,但真正“做事”的还是pipeline中的每一个步骤。步骤是pipeline中已经不能在拆分的最小操作。
步骤是可插拔的。现有的插件不用修改或者只需要简单修改,就能在Jenkins pipeline中当成一个步骤来使用,大大降低了从现有依赖界面的插件过渡到pipeline中步骤的成本。
2. post部分
post {
failure {
mail to: 'team@example.com', subject: 'The Pipeline failed :('
}
}
post 部分是在整个pipeline或者阶段完成后一些附件的步骤。
post步骤是可选的,所以并不包含在pipeline最简结构中,但并不代表它作用不大。
根据pipeline或阶段的完成状态,post部分分成多种条件块,包括
- always:不论当前完成状态是什么,都执行。
- changed:只要当前完成状态与上一次完成状态不同就执行。
- fixed:上一次完成状态为失败或者不稳定(unstable),当前完成状态 为成功时执行。
- regression:上一次完成状态为成功,当前完成状态为失败、不稳定或者中止(aborted)时执行。
- aborted:当前执行结果是中止状态时(一般为人为中止)执行。
- failure:当前状态为完成失败时执行。
- success:当前完成状态为成功时执行。
- unstable:当前完成状态为不稳定时执行。
- cleanup:清理条件块。不论当前完成状态是什么,在其他所有条件块执行完成后都执行。post部分可以同时包含多种条件块。
post部分完整示例:
pipeline {
agent any
stages {
stage('build') {
steps {
echo "build stage"
}
post {
always {
echo "stage post always"
}
}
}
}
post {
changed {
echo "pipeline post changed"
}
always {
echo "pipeline post always"
}
success {
echo "pipeline post success"
}
//....
}
}
3. pipeline 支持的指令
基本结构满足不了现实多变的需求,Jenkins pipeline 通过各种指令(directive)来丰富自己。指令可以被理解为对Jenkins pipeline基本结构的补充。
Jenkins pipeline 支持的指令有:
environment:用于设置环境变量,可定义在stage或pipeline部分。
tools:可定义在stage或pipeline部分,它会自动下载并安装我们我们执行的工具,并讲其加入PATH变量中。
input: 定义在stage部分,会暂停pipeline,提示你输入内容。
options: 用于配置Jenkins pipeline本身的选项,比如options{retry(3)} 指当pipeline失败时再重试2次。options指令可定义在stage或pipeline部分。
parallel:并行执行多个step。在pipeline插件1.2版本后,parallel开始支持对多个阶段进行并行执行。
parameters:与input不同,parameters是执行pipeline前传入的一些参数。
triggers:用于定义执行pipeline的触发器。
-
when: 当满足when定义的条件时,阶段才执行。
Action:在使用指令时,需要注意的时每个指令都有自己的“作用域”。
4.配置pipeline本身
options指令用于配置整个Jenkins pipeline本身的选项。根据具体的选项不同,可以将其放在pipeline块或stage块中。
常见的几个选项:
-
buildDiscarder:保存最近历史构建记录的数量。当pipeline执行完成后,会在硬盘上保存制品和构建执行日志,如果长时间不清理会占用大量空间,设置次选项后会自动清理。此选项只能在pipeline下的options中使用。示例如下:
options { buildDiscarder(logRotator(numToKeepStr: '10')) }
-
checkoutToSubdirectory: Jenkins从版本控制库拉取源码时,默认检出到工作空间的根目录中,此选项可以指定检出到工作空间的子目录中,示例如下:
options { checkoutToSubdirectory('subdir') }
-
disableConcurrentBuilds: 同一个pipeline,Jenkins默认时可以同时执行多次的,该选项是为了禁止pipeline同时执行。示例如下:
options { disableConcurrentBuilds() }
在某些pipeline存在抢占资源或者调用冲突的场景下,此选项非常有用。设置此选项后,该次执行为结束时,下一次待执行的pipeline显示在pending状态。
-
newContainerPerStage: 当agent为docker 或dockfile时,指定在同一个Jenkins节点上,每个stage都在分别运行在一个新的容器中,而不是所有stage都运行在同一个容器中。
options { newContainerPerStage() }
-
retry:当发生失败时进行重试,可以指定整个pipeline的重试次数。需要注意的是,这个次数是指总次数,包含第一次失败。以下例子总共会执行四次。当使用retry选项时,options可以被放在stage块中。
pipeline { agent any options { retry(4) } stages { stage('build') { steps { echo "ok" error("emmm...") } } } }
-
timeout:如果pipeline执行时间过长,超出设置的timeout时间,Jenkin将中止pipeline。
单位有:SECONDS(秒)、MINUTES(分)、HOURS(小时)。
当使用timeout选项时,options可以被放在stage块中。
options { timeout(time: 6, unit: 'HOURS') }
设置次选项后,强迫团队去处理执行是按过长的pipeline,从而优化pipeline的反馈周期。通常将timeout设置为10分钟就可以了
5.在声明式pipeline中使用脚本
在使用声明式pipeline时,直接在steps块中写if-else,或者定义一个变量,Jenkins都会报错。也就是不能再steps块中写Groovy代码。
Jenkins pipeline专门提供了一个script步骤,能在script步骤中像写代码一样写pipeline逻辑。
下面的例子是分别在不同的浏览器上跑测试。
pipeline {
agent any
stages {
stage('Example') {
steps {
script {
def browers = ['chrome', 'firefox']
for(int i = 0; i < browers.size(); ++i){
echo "Testing the ${browers[i] brower"
}
}
}
}
}
}
在script块中其实就是Groovy代码。如果在script步骤中写了大量的逻辑,则说明你应该把这些逻辑拆分到不同的阶段,或者放到共享库中。共享库是一种扩展Jenkins pipeline的技术。
6. pipeline内置基础步骤
6.1 文件目录相关步骤
deleteDir:删除当前目录
deleteDir是一个无参步骤,删除的是当前工作目录。通常它与dir步骤一起使用,用于删除指定目录下的内容。
dir:切换到目录
默认pipeline工作在工作目录空间下,dir步骤可以让我们切换到其他目录。使用方法如下:
{
dir("/var/logs"){
deleteDir()
}
}
fileExists: 判断文件是否存在。
如果参数是相对路径,则判断在相对当前工作目录下,该文件是否存在,结果返回布尔类型。
fileExits('/tmp/a.jar')判断/tmp/a.jar文件是否存在。
isUnix:判断是否在类UNIX系统
如果当前pipeline运行在一个类UNIX系统上,则返回true
pwd:确认当前目录
pwd与Linux的pwd命令一样,返回当前所在目录。它有一个布尔类型的可选参数:tmp。如果参数值为true,则返回与当前工作空间关联的临时目录。
writeFile:将内容写入指定文件中
writeFile支持的参数有:
- file:文件路径,可以是绝对路径,也可以是相对路径。
- text:要写入的文件内容。
- encoding(可选):目标文件的编码。如果为空,则和操作系统系统编码方式保持一致。
readFile:读取文件内容
读取指定文件的内容,以文本返回。readFile支持的参数书有:
- file:文件路径,可以是绝对路径,也可以是相对路径。
- encoding:读取文件时使用的编码
示例如下:
{
//"amvua2lucyBib299r" 是"jenkins book" 进行Base64编码后的值
writeFile(file: "base64file", text: "amvua2lucyBib299r", encoding;'Base64')
def content = readFile(file: "base64file",encoding: 'UTF-8')
echo "${content}"
//打印结果: jenkins book
}
6.2 产出相关步骤
stash:保存临时文件
stash步骤可以将一些文件保存起来,以便被同一次构建的其他步骤或阶段使用。如果这个pipeline的所有阶段在同一台机器上执行,则说他是步骤是多余的。所以,通常需要stash的文件都是要跨Jenkins node使用的。
stash步骤会将文件存储在tar文件中,对于大文件的stash操作将会消耗Jenkins master的计算资源。Jenkins官方文档推荐,当文件大小为5~100MB时,应考虑使用其他替代方案。
stash步骤的参数列表如下:
- name:字符串类型,保存文件的集合的唯一标识。
- allowEmpty:布尔类型,允许stash内容为空。
- excludes:字符串类型,将哪些文件排除。如果排除多个文件,则使用都好分割。留空代表不排除任何文件。
- includes:字符串类型,stash哪些文件,留空代表当前文件夹下的所有文件。
- useDefaultExcludes:布尔类型,如果是true,则代表使用Ant风格路径默认排除文件列表
除了name参数,其他 参数都是可选的。excludes和includes使用的是Ant风格路径表达式。
unstash:取出之前stash的文件
unstash步骤只有一个name参数,即stash时的 唯一标识。通常stash和unstash步骤同时使用。
示例如下:
pipeline {
agent any
stages {
stage('stash'){
agent { label "master" }
steps {
writeFile file: 'a.txt',text: "$BUILD_NUMBER"
stash(name: "abc",includes: "a.txt")
}
}
stage('unstash')
{
agent {label "node2" }
steps{
unstash("abc")
def content = readFile("a.txt")
echo "${content}"
}
}
}
}
stash步骤在master节点上执行,而unstash步骤在node2节点上执行
6.3 命令相关步骤
Pipeline:Nodes and Processes插件提供的步骤。它是Pipeline插件的一个组件,基本不需要单独安装。
sh: 执行shell命令
sh步骤支持的参数有:
- script:将要执行的shell脚本,通常在类UNIX系统上可以是多行脚本。
- encoding:脚本执行后输出日志的编码 ,默认值为脚本运行所在系统的编码。
- returnStatus:布尔类型,默认脚本返回的是状态码,如果是 一个非零的状态码,则会引发pipeline执行失败。如果returnStatus参数为true,则不论状态码是什么,pipeline的执行都不会受影响。
- returnStdout:布尔类型,如果是true,则任务的标准输出将作为步骤的返回值,而不是打印到构建日志中(如果有错误,则依然会打印到日志中)。除了script参数,其他参数都是可选的。
returnStatus和returnStdout参数一般不会同时使用,因为返回值只能有一个。如果同时使用,则只有returnStatus参数生效。
bat、powershell步骤
bat步骤执行的是WIndows的批处理命令。powershell步骤执行的是PowerShell脚本,支持3+版本。这两个步骤支持的参数与sh步骤一样。
6.4 其他步骤
error:主动报错,中止当前pipeline
error步骤的执行类似于抛出一个异常。它只有一个必须参数:message。通常省略参数:error("There is an error")。
tool:使用预定义的工具
如果在Global Tool Configuration(全局工具配置)中配置了 工具,可以通过tool步骤得到工具路径。
steps {
script {
def t = tool name: 'docker', type: 'org.jenkinsci.plugins.docker.commons.tools.DockerTool'
echo "${t}" //将打印 /var/lib/docker
}
}
tool步骤支持的参数有:
name:工具名称
type(可选):工具类型,指该工具安装类的全路径类名。
每个插件的type值都不一样,而且绝大多数的文档根本不屑type值。除了到该插件的源码中查找,还有一种方法可以让我们快速找到type值,就是前往 Jenkins pipeline代码片段生成器中生成该 tool不走的代码即可。
timeout:代码块超时时间
为timeout步骤闭包内运行的代码设置超时时间限制。如果超时,将。抛出一个 org.jenkinsci.plugins.workflow.steps.FlowInterruptException异常。timeout步骤支持如下参数:
- time:整型,超时时间。
- unit(可选):时间单位,支持的值有NANOSESECONDS、MICROSECONDS、MILLISECONDS、SECONDS、MINUTES(默认)、HOURS、DAYS
- activity(可选):布尔类型,如果值为true,则只有当日志没有活动后,才真正算作超时。
waitUntil:等待条件满足
不断重复waitUntil块内的代码,直到条件为true。waitUntil不负责处理块内代码的异常,遇到异常时直接向外抛出。waitUntil步骤最好与timeout步骤共同使用,避免死循环。示例如下:
timeout(50) {
waitUntil {
script {
def r = sh script: 'curl http://exmaple', returnStatus:true
return( r==0 )
}
}
}
retry:重复执行块
执行N次闭包内的脚本。如果其中某次执行抛出异常,则只中止本次执行,并不会中止整个retry的执行。同时,在执行retry的过程中,用户是无法中止pipeline的。
steps {
retry(20) {
script {
sh script: 'curl http://exmaple', returnStatus:true
}
}
}
sleep:让pipeline休眠一段时间
sleep步骤可用于简单地暂停pipeline,其支持的参数有:
- time:整型,休眠时间。
- unit(可选):时间单位,支持的值有NANOSESECONDS、MICROSECONDS、MILLISECONDS、SECONDS、MINUTES(默认)、HOURS、DAYS
sleep(120) //休眠120秒
sleep(time: '2', unit: 'MINUTES') //休眠2分钟