一.简介
都是在推送代码后,再切换到Jenkins界面,手动点击构建。显然不够自动化,应该让每次代码变动后,就自动进行构建才对。对于pipeline触发条件,一般从时间触发和事件触发。
二.时间触发
时间触发是指定义一个时间,时间到了就触发pipeline执行。在pipeline中,使用trigger指令来定义时间触发,只能定义在pipeline块下。支持cron pollSCM upstream三种方式。其它方式可以通过插件来实现。
定时触发
定时执行就像cronjob,一到时间点就执行。它的使用场景通常是执行一些周期性的job,比如每晚构建。
pipeline {
agent any
triggers {
cron('0 0 * * *')
}
stages {
stage('Nightly build') {
steps {
echo "这是一个耗时的构建,每天凌晨执行"
}
}
}
}
Jenkins tigger cron语法采用UNIX cron语法(有细微差别)。一条cron包含5个字段,使用空格或者Tab分隔,格式为:分,时,日,月,周
- MINUTE:一小时内的分钟,取值范围为0~59
- HOUR:一天内的小时,0~23
- DOM:一个月的某几天,1~31
- MONTH:月份,取值1~12
- DOW:星期几,取值0~7 0和7代表周日
还可以使用以下特殊字符,一次性指定多个值
- *匹配所有
- M-N,匹配M到N之间的值
- M-N/X or */x 指定到N范围内,以X值为步长
- A,B,...,Z 使用逗号枚举多值
在一些大型组织中,会同时存在大量的同一时刻执行的定时任务,比如N个半夜零点 0 0 * * * 执行的任务。这样会产生负载不均衡。在Jenkins tigger cron语法中使用H字符来解决这一问题,H代表hash。对于没必要精确到零点0分执行的任务,cron可以这样写: H 0 * * *,代表零点0分支59分之前任何一个时间点执行。
需要注意的是,H应用在DOM,一个月的某一天字段时会有不准确的情况,因为10月有31天,而2月却是28天。
Jenkins trigger cron还设计了一些人性化的别名:@yearly、@annually、@monthly、@weekly、@daily、@midnight和@hourly。
例如@hourly与 H * * * *相同,代表一小时内的任何时间;@midnight实际上代表在半夜12:00到凌晨2:59之前的某个时间。其它别名很少有应用场景。
轮询代码仓库
轮询代码仓库是指定期到代码仓库询问代码是否有变化,如果有变化就执行。有读者会问:那多久轮询一次?笔者的回答是:越频繁越好。
因为构建的间隔时间越长,在一次构建内就可能会包含多次代码提交。当构建失败时,你无法马上知道那一次代码提交导致了构建失败。总之,越不频繁集成,得到的持续集成的好就越少。
triggers {
pollSCM('H/1 * * * *')
}
这种一般用于特殊情况,比如外网的代码仓库无法调用内网的jenkins。则需要用这种方式。
三.事件触发
事件触发就是发生了某个事件就触发pipeline执行,这个事件可以是你能想到的任何事件,比如手动在界面上触发、其它job主动触发、HTTP API Webhook触发等。
由上游任务触发
当B任务的执行依赖A任务的执行结果时,A就被称为B的上游任务。
在Jenkins 2.22及以上版本中,trigger指令开始支持upstream类型的触发条件。upstream的作用就是能让B pipeline自行决定依赖哪些上游任务。
triggers {
upstream(upstreamProjects: 'job1,job2', threshold: hudson.model.Result.SUCCESS)
}
当upstreamProjects参数接收多个任务时,使用逗号分隔。threshold参数是指上游任务的执行结构是什么值时触发。hudson.model.Result是一个枚举,包括以下值:
- ABORTED 任务被手动中止
- FAILURE 构建失败
- SUCCESS 构建成功
- UNSTABLE 存在一些错误,但不至于构建失败
- NOT_BUILT 在多阶段构建时,前面阶段的问题导致后面阶段无法执行
注意:这种需要手动构建当前任务一次,让jenkins加载pipeline后,trigger指令才生效
gitlab通知触发
gitlab通知触发是指当gitlab发现源代码有变化时,触发jenkins执行构建。
由gitlab主动通知进行构建的好处是显而易见的,这样很容易就解决了我们之前提到的轮询代码仓库时“多久轮询一次”的问题,实现每一次代码变化都对应一次构建。
1.安装jenkins插件
安装Generic Webhook Trigger Plugin、git、Gitlab API Plugin、GitLab Plugin插件,注意不是gitlab hook插件(已废弃)
2.在gitlab创建一个项目,test-a,地址http://1.1.1.1/book/test-a
3.在jenkins上创建pipelien项目,可以同名称test-a。正常在不使用pipeline进行这个触发配置的时候,也可以用页面进行配置,勾选相当于开始接收外界发来的请求。
这里要注意,上面标注的URL是固定输出的信息,实际项目地址要看WEB栏,这个才是真实地址的
4.生成个人API的Token,用于安全验证
5.在gitlab项目的设置里,配置钩子
URL填入如下
http://账号名:刚才生成的Token@Jenkins的地址/job/test-a/build?job=test-a&token=随机写个项目token,这里随便打
URL直接粘贴了我的
http://zhuhaoran:119db4649f157c0a25f7d2b541d3f4d7da@10.0.12.9:8080/job/test-a/build?job=test-a&token=t8vcxwuza023ehzcftzr5a74vkpto6xr
现在网络上可能有各种配置,可能老版本适用,但我用的2.220就各种用不了,最后从官网找到这个能用的配置。
为什么这么配置:
gitlab代码有更新,就会通过上面这个url,将一些请求和相关内容通过post方式传给Jenkins。Jenkins发现你的test-a项目开启了这个触发功能,就会根据pipeline的配置进行相应处理,符合条件后就会触发执行。
如果只粘贴Jenkins web配置中显示的地址+Token,会报错403问题。这是因为如果没指定账号密码,gitlab只能通过匿名用户去访问Jenkins去传参。
但现在大多全局安全配置里,是Role-Based Strategy插件方式管理的
往上都说403要这样,我感觉是真的蠢,这样会不安全,而且插件管理和这个只能选择一个。
6.编写pipeline,要保存执行一下这个job让配置生效,具体的参数含义在末尾
pipeline {
agent any
triggers {
gitlab(triggerOnPush: true,
triggerOnMergeRequest: true,
branchFilterType: "All",
secretToken: "t8vcxwuza023ehzcftzr5a74vkpto6xr")
}
stages {
stage('pull') {
steps {
echo '拉取代码'
}
}
}
}
会发现这里自动勾上了,这是因为pipeline其实就是配置的这个选项,但版本化管理会更好。
7.在gitlab上点击一下触发,看是否jenkins job被触发了
8.然后在gitlab项目中,随意修改个文件,看是否也能自动触发
9.参数含义
triggerOnPush: 当Gitlab触发push事件时,是否执行构建
triggerOnMergeRequest: 当Gitlab触发mergeRequest事件时,是否执行构建
branchFilterType: 只有符合条件的分支才会触发构建,必选,否则无法实现触发。
All: 所有分支
NameBasedFilter: 基于分支名进行过滤,多个分支名使用逗号分隔
includeBranchesSpec: 基于branchFilterType值,输入期望包括的分支的规则
excludeBranchesSpec: 基于branchFilterType值,输入期望排除的分支的规则
RegexBasedFilter: 基于正则表达式对分支名进行过滤
sourceBranchRegex: 定义期望的通过正则表达式限制的分支规则
secretToken: 指定这个job_name的token验证字符
如果只允许master分支push后才触发,就如下配置,token使用了全局变量,这样多个项目都可以用一个token,比较方便(内网比较适合)
triggers {
gitlab(triggersOnPush: true,
triggersOnMergeRequest: true,
branchFilterType: "NameBasedFilter",
includeBranchesSpec: "master",
secretToken: "${env.git_token}")
}
用正则匹配,适合对分支号进行规则定义的项目
triggers {
gitlab(triggersOnPush: true,
triggersOnMergeRequest: true,
branchFilterType: "RegexBasedFilter",
sourceBranchRegex: "dev.*",
secretToken: "${env.git_token}")
}
四.通用触发接口
GWT
前文中,我们讲到安装GitLab插件后,GitLab系统就可以发送Webhook触发Jenkins项目的执行。那是不是说其他系统想触发Jenkins项目执行,也需要找一个插件或者开发一个插件来实现呢?
有了Generic Webhook Trigger插件就不需要了,安装Generic Webhook Trigger插件(下文使用GWT简称)后, Jenkins会暴露一个API: <JENKINS URL>/generic-webhook-triggerlinvoke
,即由GWT插件来处理此API的请求。
GWT插件接收到JSON或XML的HTTP POST请求后,根据我们配置的规则决定触发哪个Jenkins项目。下面我们先感受一下,然后再详细介绍GWT各参数的含义,现在我们创建一个普通的pipeline项目。
代码如下:
pipeline {
agent any
triggers {
GenericTrigger(
genericVariables:[
[key: 'ref', value: '$.ref']
],
token:'secret',
causeString: 'Triggered on $ref',
printContri butedVariables: true,
printPostContent: true
)
}
stages {
stage('Some step') {
steps {
sh "echo $ref"
sh "printenv"
}
}
}
}
执行一次job后才会生效,然后发起一次HTTP POST请求
curl -X POST -H "Content-Type: application/json" -d '{ "ref": "refs/heads/master" }' -vs http://192.168.23.11:8667/jenkins/generic-webhook-trigger/invoke?token=secret
接着,我们就看到pipeline被触发执行了。日志如下:
Triggered on refs/heads/master
省略git操作细节部分
Seen branch in repository origin/dev
Seen branch in repository origin/ master
Seen 2 remote branches
Obtained Jenkinsfile from 6b2c6a36ac5fade7448596a5fc67ee33be4bab95
Running in Durability level: MAX_ SURVIVABILITY
Generi cWebhookEnvironmentContributor
Received :
{ "ref": "refs/heads/master" }
Contributing variables:
ref = refs/heads/master
GenericTrigger触发条件由GWT插件提供。此触发条件可以说是GWT的所有内容。将GenericTrigger触发条件分为5部分,这样更易于理解各参数的作用。
- 从HTTP POST请求中提取参数值
- token,GWT插件用于标识Jenkins项目的唯一性
- 根据请求参数值判断是否触发Jenkins项目的执行
- 日志打印控制
- Webhook响应控制
提取参数
一个HTTP POST请求可以从三个维度提取参数,即POST body、URL参数和header。
GWT插件提供了三个参数分别对这三个维度的数据进行提取。
- genericVariables :提取POST body中的参数。
genericVariables:[
[key: 'ref', value: '$.ref'] ,
[key: 'before'
value: ' $.before',
expressionType:' JSONPath
regexpFilter:
defaultValue:
]
]
- value: JSONPath表 达式,或者XPath 表达式,取决于expressionType参数值,用于从POST body中提取值。
- key :从POST body中提取出的值的新变铭,可用于pipeline其他步骤。
- expressionType :可选, value的表达式类型, 默认为JSONPath。当请求为XML内容时,必须指定XPath值。
- defaultValue:可选,当提取不到值,且defaultValue不为空时,则使用defaultValue作为返回值。
- regexpFilter :可选,过滤表达式,对提取出来的值进行过滤。其实就是string.replaceAll ( regexpFilter ,"");。string是从HTTP请求中提取出来的值。
2.genericRequestVariables :从URL参数中提取值。
- key :提取出的值的新变量名,可用于pipeline其他步骤。
- regexpFilter :对提取出的值进行过滤。
3.genericHeaderVariables :从HTTP header中提取值。
genericHeaderVariables的用法与genericRequestVariables-样,区别是它是从HTTP header中提取值的。
触发某个具体项目
上面可以看到GenericTrigger方法有一个token参数
GenericTrigger(
token: 'secret',
)
}
token参数的作用是标识一个pipeline在Jenkins中的唯一性(当然,没有人阻止你让所有的pipeline使用同-个token)。
为什么需要这个参数呢?这要从GWT插件的原理说起。当Jenkins接收到generic-webhooktriggerlinvoke接口的请求时,会将请求代理给GWT插件处理。GWT插件内部会从Jenkins实例对象中取出所有的参数化Jenkins项目,包括pipeline然后进行遍历。
如果在参数化项目中GenericTrigger配置的token的值与Webhook请求时的token的值一致,则触发此参数化项目。如果多个参数化项目的token值一样,则它们都会被触发。
pipeline的token可以被设置为Jenkins的项目名
比如:
triggers {
GenericTrigger(
//省略
token: env.JOB_NAME,
//省略
)
}
过滤请求值
上节所说的不完全正确。GWT并不只是根据token值来判断是否触发,还可以根据我们提取出的值进行判断。
示例如下:
GenericTrigger(
genericVariables:[
[key: ' refValue',value: '$.ref']
],
token: env.JOB_NAME,
regexpFilterText:'$refValue',
regexpFilterExpression: 'refs/heads/(master|dev)'
)
- regexpilterText: 需要进行匹配的key。例子中,我们使用从POST body中提取出的refValue变量值。
- regexpFilterExpression :正则表达式。
如果regexpFilterText参数的值符合regexpilterExpression参数的正则表达式,则触发执行。
控制打印内容
打印日志有助于调试。GWT插件提供了三个参数。
- printPostContent :布尔值, 将Webhook请求信息打印到日志上。
- printContributedVariables:布尔值,将提取后的变量名及变量
- 值打印出来。
- causeString :字符串类型,触发原因可以直接引用提取后的变量,如causeString : 'Triggered on $msg'。
控制响应
GWT插件最近才加入的一个参数:
silenResponse: 布尔类型,在正常情况下当Webhook请求发布成功后,GWT插件会返回HTTP 200状态码和触发结果给调用方。但是当设置为true时,就只返回HTTP 200状态码,不返回触发结果。