DevOps实战系列【第十章】:详解Jenkins Pipeline基本概念和语法

个人亲自录制全套DevOps系列实战教程手把手教你玩转DevOps全栈技术

流水线基本概念

官方中文手册:
https://www.jenkins.io/zh/doc/book/pipeline
我们最好在结合英文文档去看,因为翻译过来的中文比较乱。

Jenkins pipeline是一套插件,它支持实现和集成 continuous delivery pipelines 到Jenkins,即实现CD持续部署的功能。

Jekins的流水线插件会在安装Jenkins时通过“建议安装”的方式自动安装上,如果大家建议安装时失败了,可以单独下载Pipeline plugin去安装一下。

流水线提供了一组可扩展的工具,通过 Pipeline domain-specific language (DSL) syntax,翻译过来就是Pipeline有自己的语法DSL,基于groovy语法,对从简单到复杂的交付流水线进行建模。

Jenkins流水线的定义,需要创建一个Jenkinsfile文本文件,该文件除了可以直接定义在Jenkins的Job中还可以被提交到代码仓库管理(如:gitlab),这样流水线将会作为我们开发项目的一部分,像其他代码一样进行跟踪管理,所以建议使用gitlab管理jenkinsfile的方式去实施Jenkins的流水线。

说白了:jenkins 流水线 就是通过一个file文件来实现jenkins job的功能。

流水线的特点:为什么选择流水线?通过jenkins的配置不好吗?

  • 流水线可以通过代码仓库管理,方便我们进行迭代、审核等;
  • 流水线文件独立于jenkins的job,而配置方式比较难实现独立
  • 流水线可以在流程阶段做更丰富的操作,比如暂停,审核等交互式操作;
  • 流水线支持更多的插件扩展
    在这里插入图片描述

图中是一个通过管道(pipeline流水线)实现的从开发到生产的过程,其中每一个步骤都可以通过Jenkins的流水线的DSL语法去建模。

概念:SCM=>软件配置管理,他有很多实现工具,比如我们常用的gitlab就是一个SCM工具,而在本文中提到SCM我们默认使用的就是gitlab。

Jenkins流水线的语法有2种:

  • 脚本式:2014年12月
  • 声明式:2017年2月,为更简便易用而生

实际中我们的jenkinsfile中一般会混用两种语法。


流水线语法概念

  • pipeline:是用户定义的一个CD流水线模型,就是说他是我们定义的jenkinsfile的跟节点,流水线起始于pipeline块 。pipeline块中一般会包含stages块去完成一个应用程序构建、 测试和部署的阶段。
  • node(节点):它是Jenkins环境的一个机器并且能够执行流水线文件。
  • stage(阶段):stage 块中定义了不同的任务,比如 “Build”, “Test” 和 “Deploy” 阶段, 这些不同的任务可以通过多种插件去可视化展示或者也可以展示Jenkins执行pipeline的进度和状态。
  • step(步骤):是stage块中的一个任务,他可以告诉Jenkins去做什么,比如在Build节点,在这个任务中可以让Jenkins去执行make命令,注意需要使用sh "make"方式执行,不能直接通过make去执行。

注意:stages块和steps块在声明式和脚本是语法中都可以使用。


声明式语法举例:创建一个Jenkinsfile

// pipeline块作为jenkinsfile的根节点
pipeline {
	agent any      // 指定可以在任何空闲的代理上执行以下的stages块,代理指的是我们jenkins服务器,如果是集群的话就是其中的某个节点(声明式中必须要有agent)
	stages {       // 通过stages包括多个stage
    	stage('Build') {     //通过stage定义一个任务,这个任务我们取名叫做”Build“,即代表构建阶段要执行的步骤
        	steps { // 通过steps包括多个step,而每个step就是我们要执行的任务的具体内容,比如执行一个shell脚本等
            	echo "hello build"
        	}
    	}
    	stage('Test') { // 定义测试阶段
     	   steps { // 测试阶段需要执行的步骤
         	   echo "hello test"
       	 }
    	}
    	stage('Deploy') { // 定义部署阶段
     	   steps { //部署阶段需要执行的步骤
         	   echo "hello deploy"
        	}
    	}
	}
}

脚本式语法举例:创建一个Jenkinsfile

// 该节点和声明式中的agent是一个意思,表示在任意jenkins节点上执行以下stages,他不需要一个顶级的pipeline块,而是直接执行节点或任一节点执行以下阶段
node {
	// 在脚本式语法中stage块是可选的,即可以直接使用steps来定义步骤,但是如果定义了stage,该stage将可以在jenkins的图像化界面中展示
	stage('Build') { 
   		// 其实展示的就是"Build"名字,而对于声明式语法stage是一定要有的,如果不想展示图形化可以专门使用脚本式语法,然后不添加stage即可。            
		echo "hello build"           
	}
	stage('Test') { 
 	   // 这个里边定义的和声明式就一样了,定义当前步骤的steps
    	echo "hello test"      
	}
	stage('Deploy') { 
 	   echo "hello deploy"   
	}
}

语法详解

官方地址:https://www.jenkins.io/zh/doc/book/pipeline/syntax/

1.注释:Jenkinsfile文件中可以使用两种注释方式

  • // 注释一行内容
  • /* … 注释框选内容 … */

2.使用环境变量:
jenkins提供了很多内置的环境变量都可以在Jenkinsfile中使用,可以通过如下地址查看:
http://10.10.1.199:9078/pipeline-syntax/globals
使用方法:类似访问Groovy的map属性,关键字是env:${env.BUILD_ID}

pipeline {
	agent any
	stages {
 		stage('Example') {
     		steps {
         		echo "Running ${env.BUILD_ID} on ${env.JENKINS_URL}"
     		}
 		}
	}
}

注意:可以通过printenv或env将已有的环境变量输出

3.设置变量:声明式和脚本式有所不同
声明式语法:environment指令(块)

pipeline {
	agent any
	// 当前文件全局生效
	environment { 
	CC = 'clang'
	}
	stages {
		stage('Example') {
			// 局部生效,所有stage内的steps中可以使用
    		environment { 
      		  DEBUG_FLAGS = '-g'
    		}
    		steps {
     		   sh 'printenv'
    		}
		}
	}
}

脚本式语法:需要使用一个step为withEnv的命令来定义变量

node {
	/* 
	withEnv中设置的变量只能在当前块中生效\
	就是将环境变量添加到PATH中
	*/
	withEnv(["PATH+MAVEN=${tool 'M3'}/bin"]) {
		sh 'mvn -B verify'
	}
}

${tool ‘M3’}:其中tool是Jenkinsfile的工具命令,就是要使用一个工具,完整写法如:tool name: ‘maven3.8.6’, type: ‘maven’,其中name是我们配置的jenkins全局工具中的名称,而类型就是工具的名字,此处M3来自官网,即他定义了一个全局工具叫M3,此处是给环境变量设置了maven的bin目录,以便sh中的mvn命令得以正常执行,当然也可以直接不设置变量,通过sh “${tool ‘M3’}/bin/mvn -B verify”,注意是双引号,单引号不能执行插值语法

4.动态设置变量:
所有的脚本执行完后都会有返回状态returnStatusreturnStdout

注意:groovy是可以支持三种引号

  • 单引号:和我们java中定义字符串一样,不支持解析插值语法,即无法获取${变量名}的值
  • 双引号:可以进行字符串的拼接,且可以执行插值表达式(${变量名})
  • 三引号:支持字符串换行,注意:三引号也分三个单引号和三个双引号,意思和单引号、双引号一样,只不过是这里可以换行,如['''或"""]

注意:我们知道shell脚本执行结果是0或非0,而Jenkinsfile中采用了groovy中使用shell脚本获取返回值状态或标准输出的语法:

  • 获取标准输出的shell脚本
  • 获取执行状态的shell脚本
  • 无需返回值的shell脚本

// 获取标准输出
// 第一种
result = sh returnStdout: true ,script: "<shell command>"
// 返回结果末尾会有空格,所以trim()一下
result = result.trim()
// 第二种
result = sh(script: "<shell command>", returnStdout: true).trim()
// 第三种
sh "<shell command> > commandResult"
result = readFile('commandResult').trim()

// 获取执行状态
// 第一种
result = sh returnStatus: true ,script: "<shell command>"
result = result.trim()
// 第二种
result = sh(script: "<shell command>", returnStatus: true).trim()
// 第三种
sh '<shell command> > status'
def r = readFile('status').trim()

//无需返回值,仅执行shell命令
//最简单的方式
sh '<shell command>'

动态定义变量Jenkinsfile脚本如下:

pipeline {
   agent any 
   environment {
       // 使用 returnStdout
       CC = """${sh(
               returnStdout: true,
               script: 'echo "clang"'
       )}""" 
       // 使用 returnStatus
       EXIT_STATUS = """${sh(
               returnStatus: true,
               script: 'exit 1'
       )}"""
   }
   stages {
       stage('Example') {
           environment {
               DEBUG_FLAGS = '-g'
           }
           steps {
               // 输出所有变量
               sh 'printenv'
           }
       }
   }
}

5.凭证:
jenkins中我们可以创建与第三方进行鉴权的凭证,比如我们之前创建的凭证ID有:gitlab、sonar-key
在这里插入图片描述
用法:在environment块中使用credentials('凭证ID')为变量赋值,然后通过在脚本中使用变量进行鉴权

pipeline {
	agent any
	environment {
 	   // 基于用户名和密码方式的凭证赋值,可以使用$USERNAME_PASSWORD_KEY使用凭证,内容格式为[username:passwd]
    	// 用户名密码的凭证,除了会赋值外,jenkins还会自动创建2个变量,名字为当前定义的变量后加"_USR"和"_PSW",即可以单独获取用户名和密码
	     USERNAME_PASSWORD_KEY = credentials('gitlab')
 	   // 基于Secret文本方式的凭证赋值,可以通过$SONAR_KEY使用凭证
    	SONAR_KEY = credentials('sonar-key')
	}
	stages {
 	   stage('Example stage 1') {
     	   steps {
         	   // 
        	}
    	}
	}
}

注意:如果要生成其他凭证,比如ssh秘钥或证书,那么需要使用jenkins提供的【片段生成器】,选用withCredentials: Bind credentials to variables来生成脚本
地址:http://10.10.1.199:9078/job/p1/pipeline-syntax/
在这里插入图片描述

// 注意:keyFileVariable可以用来接收我们配置的ssh的证书信息,比如我指定了私钥内容,可以通过指定keyFileVariable='ABC',然后通过$ABC来获取私钥内容
withCredentials([sshUserPrivateKey(credentialsId: 'gitlab-ssh', keyFileVariable: '')]) {
	// some block
}

6.字符串插值:使用groovy语法

// 单引号、双引号、三引号都可以
def singlyQuoted = 'Hello'
def doublyQuoted = "World"

echo 'Hello Mr. ${singlyQuoted}'  // 注意:单引号不能解析表达式,将会原样输出
echo "I said, Hello Mr. ${doublyQuoted}"

7.参数:配置完的参数都会作为params内置变量的属性被使用。

  • 声明式语法:可以通过在parameters指令中定义参数
  • 脚本式语法:可以通过properties步骤,类似withEnv

pipeline {
   agent any
   // 指定参数
   parameters {
       // 参数名=Greeting, 值=Hello
       stringname: 'Greeting', defaultValue: 'Hello', description: 'How should I greet the world?')
   }
   stages {
       stage('Example') {
           steps {
               // 使用内置params属性引用参数
               echo "${params.Greeting} World!"
           }
       }
   }
}

8.故障处理

  • 声明式:使用post指令支持对多个条件进行处理,**包括:always、unstable、success、failure和changed **
  • 脚本式:使用groovy内置语法try…catch…finally

pipeline {
   agent any
   stages {
       stage('Test') {
           steps {
               sh 'make check'
           }
       }
   }
   // 后处理阶段
   post {
       // 不管前边阶段什么结果都执行
       always {
           echo "always is printed!"
       }
       // 前边阶段失败才会执行
       failure {
           // mail to: team@example.com, subject: 'The Pipeline failed :('
           echo "failure is printed!"
       }
       // 前边阶段成功才会执行
       success {
           echo "success!"
       }
   }
}
node {
   stage('Test') {
       try {
           sh 'make check'
       }
       finally {
           // 通过junit插件实现生成xml测试报告到target目录
           junit '**/target/*.xml'
       }
   }
}

9.多代理指定:agent指令,前边我们只配置过agent:any,并且都在顶级pipeline块中配置的,属于全局作用域

一个Jenkinsfile还可以给不同的stage阶段配置不同的agent代理。这样不同的阶段可以被不同的jenkins节点去执行。

注意:jenkins集群或单机都可以指定label标签:系统管理->节点管理

pipeline {
// 该属性为必填属性,所以全局先定义成不启用jenkins节点
	agent none
	stages {
 	   stage('Build') {
     	   // 指定Build阶段使用任何一个jenkins节点都可执行
        	agent any
        	steps {
				checkout scm
            	sh 'make'
            	// stash 是一个暂存命令插件,可以将内容暂存到jenkins master节点,通过unstash取出存储的内容
            	stash includes: '**/target/*.jar', name: 'app' 
        	}
		}
    	stage('Test on Linux') {
			// 指定Test on Linux阶段,只能使用被标签为linux的jenkins节点执行
        	agent { 
         	   label 'linux'
        	}
        	steps {
            	unstash 'app' 
            	sh 'make check'
        	}
        	post {
            	always {
                	junit '**/target/*.xml'
            	}
        	}
    	}
    	stage('Test on Windows') {
        	// 指定Test on Windows阶段,只能使用被标签为windows的jenkins节点执行
        	agent {
            	label 'windows'
        	}
        	steps {
            	unstash 'app'
            	bat 'make check' 
        	}
        	post {
            	always {
                	junit '**/target/*.xml'
            	}
        	}
    	}
	}
}

脚本式可以使用node指令:如果要指定标签,则通过node(‘label’){…}

10.步骤step的参数简化写法:就是如果一个step语句可以省略参数外部的括号
在groovy中创建map的方式如[key1:value1, key2:value2],而step中多数命令都是以map作为参数:

省略参数外部的所有括号

git([url: 'git://example.com/amazing-project.git', branch: 'master'])

// ==> 简化方式
git url: 'git://example.com/amazing-project.git', branch: 'master'

只有一个参数时,除了括号参数名也可省略

sh([script: 'echo hello'])

// ==> 简化方式
sh 'echo hello'

11.并发执行:脚本式语法的高级应用,作为基于groovy的语法,脚本式语法几乎可以直接使用groovy的所有语法而无需修改

优化:第9个案例中,Test on Linux和Test on Windows两个阶段是串行执行,我们此处改为并发执行

指令:parallel

stage('Test') {
	// 并发执行,此处指定的linux是一个parallel的一个分支而已,随意取名,不必和jenkins节点的label相同
	parallel linux: {
    	// 脚本式语法选择linux标签的jenkins节点执行
    	node('linux') {
        	checkout scm
        	try {
            	unstash 'app'
            	sh 'make check'
        	}
        	finally {
            	junit '**/target/*.xml'
        	}
    	}
	},
	// parallel的另一个并发执行的分支
	windows: {
    	node('windows') {
        	/* .. snip .. */
    	}
	}
}

12.options:可以设置一些jenkins底层对于当前配置此属性块的pipeline的属性限制

比如:当前流水线失败后是否可以重试、是否打印时间戳、是否设置超时时间等

pipeline {
	agent any
	// 设置1小时超时时间,超过1小时该job还没运行完则终止执行,也可以配置在stage阶段块内部,但设置项较少,参考官网看一下
	options {
    	timeout(time: 1, unit: 'HOURS') 
	}
	stages {
    	stage('Example') {
        	steps {
            	echo 'Hello World'
        	}
    	}
	}
}

13.input:可以在执行某个stage阶段时,通过图形化界面与用户交互,即确认后才能继续执行流水线任务,当然也可以取消执行。

pipeline {
	agent any
	stages {
    	stage('Example') {
        	input {
            	message "Should we continue?"
            	ok "Yes, we should."
            	submitter "alice,bob"
            	parameters {
                string(name: 'PERSON', defaultValue: 'Mr Jenkins', description: 'Who should I say hello to?')
            	}
        	}
        	steps {
            	echo "Hello, ${PERSON}, nice to meet you."
        	}
    	}
	}
}

14.when:指令允许流水线根据给定的条件决定是否应该执行stage阶段。

参考官网:https://www.jenkins.io/zh/doc/book/pipeline/syntax/#when

15.script:声明式中如果想嵌入脚本,即执行定义变量、方法等groovy语法,需要使用script指令来嵌入

pipeline {
	agent any
	stages {
    	stage('Example') {
        	steps {
            	echo 'Hello World'
            	// 执行脚本式语法脚本
            	script {
                	def browsers = ['chrome', 'firefox']
                	for (int i = 0; i < browsers.size(); ++i) {
                    	echo "Testing the ${browsers[i]} browser"
                	}
            	}
        	}
    	}
	}
}

16.tools:可以将我们配置到全局工具中的工具导入到环境变量PATH中,方便我们下文直接使用命令,否则就需要使用绝对路径。

目前只支持:mavenjdkgradle

pipeline {
	agent any
	tools {
    	// 注意"maven3.8.6"是我们全局工具中定义的名字
    	maven "maven3.8.6"
	}
	stages {
    	stage('Example') {
        	steps {
            	// 此处可以直接使用maven的命令而无需指定绝对路径
            	sh 'mvn --version'
        	}
    	}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

上树的蜗牛儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值