Jenkins pipeline
Jenkins Pipeline介绍
- Pipeline 是Jenkins 2.X核心特性,帮助Jenkins实现从CI到CD与DevOps的转变。
- Pipeline 简而言之,就是一套运行于Jenkins上的工作流框架,将原本独立运行于单个或者多个节点的任务连接起来,实现单个任务难以完成的复杂流程编排与可视化。
什么是Jenkins Pipeline
- Jenkins Pipeline是一组插件,让Jenkins可以实现持续交付管道的落地和实施。
- 持续交付管道(CD Pipeline)是将软件从版本控制阶段到交付给用户或客户的完整过程的自动化表现。
- 软件的每一次更改(提交到源代码管理系统)都要经过一个复杂的过程才能被发布。
- Pipeline提供了一组可扩展的工具,通过Pipeline Domain Specific Language(DSL) syntax可以达到Pipeline as Code的目的。
- Pipeline as Code:Jenkinsfile 存储在项目的源代码库
Why Pipeline?
本质上,Jenkins 是一个自动化引擎,它支持许多自动模式。 Pipeline向Jenkins中添加了一组强大的工具, 支持用例 简单的CI到全面的CD pipeline。通过对一系列的相关任务进行建模, 用户可以利用pipeline的很多特性:
- 代码:Pipeline以代码的形式实现,通常被检入源代码控制,使团队能够编辑,审查和迭代其CD流程。
- 可持续性:Jenkins重启或者中断后都不会影响Pipeline Job。
- 停顿:Pipeline可以选择停止并等待人工输入或批准,然后再继续Pipeline运行。
- 多功能:Pipeline支持现实世界的复杂CD要求,包括fork/join子进程,循环和并行执行工作的能力。
- 可扩展:Pipeline插件支持其DSL的自定义扩展以及与其他插件集成的多个选项。
Pipeline与Freestyle区别
- Job调度方式
- pipeline:通过结构化pipeline 语法进行调度,易于理解与阅读
- freestyle:通过jenkins api或者cli进行调度
- Job显示形式
- pipline:提供上帝视角(全局视图)
- freestyle:没有视图
Pipeline两种语法类型
在Jenkins中编辑流水线时,有两种不同的语法样式:脚本语法(scripted syntax)和声明式语法(declarative syntax)。
脚本语法(scripted syntax)
脚本语法(scripted syntax)是Jenkins最开始实现的流水线即代码方式。这是一种命令式风格,也就是在流水线脚本中定义逻辑和程序流程。它也依赖与Groovy语言和结构,特别是对于错误检查和异常处理来说。
1 | node('woker_node') { |
优点:
- 更少的代码段落和弱规范要求
- 更强大的程序代码能力
- 更像编写代码程序
- 传统的流水线即代码模型,用户熟悉并向后兼容
- 更灵活的自定义代码操作
- 能够构建更复杂的工作流和流水线
缺点:
- 普遍要求更高的编程水平
- 语法检查受限于Groovy语言及环境
- 和传统Jenkins模型有很大差异
- 与声明式流水线的实现相比,同一工作流会更复杂
声明式语法(declarative syntax)
声明式语法(declarative syntax)是Jenkins提供的一种新的选择。声明式风格的流水线代码被编排在清晰的段落中,相对于只关注实现逻辑,这些流水线的主要区域描述(或“声明”)了我们所期望的流水线的状态和输出。
1 | pipeline { |
优点:
- 更结构化,贴近传统的Jenkins Web表单形式
- 更强大的声明内容能力,高可读性
- 可以通过Blue Ocean图形化界面自动生成
- 段落可映射到常见的Jenkins概念,比如通知
- 更友好的语法检查和错误识别
- 提升流水线的一致性
缺点:
- 对迭代逻辑支持较弱(相比程序而言)
- 仍在开发完善中(对于传统Jenkins中的部分功能缺乏支持)
- 更严格的结构(更难实现自定义流水线代码)
- 目前对于复制的流水线和工作流难以胜任
Pipeline语法详解
声明时语法(Declarative pipeline)
- 必须包含一个pipeline块内,具体来说是:pipeline {}
- 基本的部分是”steps”,steps告诉Jenkins要做什么
- 语句分类具体包含Sections,Directives,Steps,赋值几大类
Declarative语句树
agent:定义pipeline执行节点
参数:
- 必须出现的指令
- any:可以在任意agent上执行pipeline
- none:pipeline将不分配全局agent,每个stage分配自己的agent
- label:指定运行节点的Label
- node:自定义运行节点配置
- 指定 label
- 指定 customWorkspace
- docker:控制目标节点上的docker运行相关内容
代码示例
1
2
3
4
5
6
7
8pipeline {
agent {
node {
label "myslave"
customWorkspace "myWorkspace"
}
}
}
stages:包含一个或多个stage的序列,pipeline的大部分工作在此执行
- 必须出现的指令
- 无参数
- 每个pipeline代码区间中必须只有一个stages
stage:包含在stages中,pipeline完成的所有实际工作都需要包含到stage中
- 必须出现的指令
- 无参数
- 需要定义stage的名字
steps:
- 必须出现的指令
- 无参数
- 具体执行步骤,包含在stage代码区间中
示例代码
1
2
3
4
5
6
7
8stages {
stage('git pull souce code') {
steps {
echo "sync updated code"
git "https://github.com/xxx/xxx.git"
}
}
}post:定义pipeline或stage运行结束时的操作
参数:
- 不是必须出现的指令
- always:无论pipeline运行的完成状态如何都会运行
- changed:只有当前pipeline运行的状态与先前完成的pipeline的状态不同时,才能运行
- failure:仅当当前pipeline处于“失败”状态时才运行
- success:仅当当前pipeline具有”成功“状态时才运行
- unstable:只有当前pipeline具有”不稳定“状态才运行
- aborted:只有当前pipeline处于“中止”状态时才能运行
示例代码
1
2
3
4
5
6
7
8
9post {
success {
echo 'goodbye pipeline success!'
sleep 2
}
always {
echo 'always say goodbye'
}
}
environment:定义pipeline或stage运行时的环境变量
参数:
- 不是必须出现的指定
- 无参数
示例代码
1
2
3
4
5
6
7
8
9
10environment {
hlw = 'hello world'
}
stages {
stage('print environment_1') {
steps {
echo hlw
}
}
}
options:定义pipeline的专属属性
参数:
- 不是必须出现的指令
- buildDiscarder:保持构建的最大个数
- disableConcurrentBuilds:不允许并行执行pipeline任务
- timeout:pipeline超时时间
- retry:失败后,重试整个pipeline的次数
- timestamps:预定义由pipeline生成的所有控制台输出时间
- skipStagesAfterUnstable:一旦构建状态进入了”Unstable”状态,就跳过此stage
示例代码
1
2
3
4
5options {
timeout(time: 30, unit: 'SECONDS')
buildDiscarder(logRotator(numToKeepStr: '2'))
retry(5)
}
parameters:定义pipeline的专有参数列表
参数:
- 不是必须出现的指令
- 支持数据类型,booleanParam, choice, credentials, file, text, password, run, string
- 类似参数化构建的选项
示例代码:
1
2
3
4
5
6
7
8
9
10parameters {
string(name: 'PERSON', defaultValue: 'Jenkins', description: '输入的文本参数')
}
stages {
stage('Test Parameters') {
steps {
echo "Hello ${params.PERSON}"
}
}
}
triggers:定义了pipeline自动触发的方式
参数
- 不是必须出现的指令
- cron:接收一个cron风格的字符串来定义pipeline触发的常规间隔
- pollSCM:接收一个cron风格的字符串来定义Jenkins检查SCM源更改的常规间隔;如果存在新的更改,则pipeline将被重新触发
示例代码
1
2
3triggers {
pollSCM('H */4 * * 1-5')
}
脚本语法(Scripted pipeline)
- 基于Groovy语法指定个一种DSL语言
- 灵活性更高
- 可扩展性更好
- Script pipeline与Declarative pipeline程序构成方式有雷同之处,基本语句也有相似之处
Script语句树
流程控制之 - if/else
1
2
3
4
5
6
7
8
9node {
stage('example') {
if (ev.BRABCH_NAME == 'master') {
echo 'I only execute on the master branch'
} else {
echo 'I execute else where'
}
}
}流程控制之 - try/catch
1
2
3
4
5
6
7
8
9
10node {
echo "This is test stage which run on the slave agent"
try {
echo "This is in the try block"
}catch(exc) {
echo "Something failed,I'm in the catch block"
}finally {
echo "Finally, I'm in the finally block"
}
}Groovy语句控制环境变量与运行命令
- 定义maven工具变量的名称与位置
- 定义java工具变量的名称与位置
- 工具设置位置“Manage Jenkins(系统管理)” -> “Global Tool Configuration(全局工具配置)”
Groovy语句控制环境变量与运行命令
- script 代码中引用、配置环境变量
- script 代码中引用maven工具与java工具
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15node('master') {
git(url: 'git@github.com:xxx/xxx.git', branch:'master')
}
stage('maven编译打包') {
node('master') {
sh ". ~/.bash_profile"
// 定义mvn环境,与上面Jenkins里配置的一致
def mvnHome = tool 'maven'
def jdkHome = tool 'jdk1.8'
env.PATH = "${mvnHome}/bin:${env.PATH}"
env.PATH = "${jdkHome}/bin:${env.PATH}"
sh "mvn clean install"
sh "mvn target/iWeb.war target/ROOT.war"
}
}
Jenkins Pipeline Job
Jenkins中创建一个pipeline任务(Job)
流水线 中选择PipelineScript
在Pipeline模块中添加示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17pipeline {
agent any
stages {
stage('Hello') {
steps {
echo 'Hello World'
}
}
}
post {
always {
echo 'Say Goodbay'
}
}
}执行构建的结果
再查看Build的控制台输出的日志:
Pipeline Script from SCM
上面介绍了,是将pipeline script是直接写在Jenkins Job中的,这种方式步骤少,代码量少还可以便于管理,如果步骤多,代码量大的话就会容易出错,这时我们可以将该部分的代码单独管理。
- Pipeline的代码可以放入Git源码仓库进行管理
- 在Jenkins Pipeline任务“流水线” 中选择“Pipeline script from SCM”,然后添加Git源码地址,在Script Path中填写需要运行Jenkinsfile文件所在的脚本路径
Github地址:https://github.com/jinglv/pipeline-script
新建一个Jenkins pipeline Job
Job设置页面,流水线 中选择Pipeline script from SCM
添加Github地址,及Jenkinsfile路径
保存并执行,查看执行结果
再查看Build的控制台输出的日志: