[toc]
介绍
Pipeline在Unix/Linux系统中经常用到,Pipeline将一个命令/程序/进程的输出发送到另一个命令/程序/进程,以进行进一步处理。比如:cat test.txt | grep test1。Jenkins 中的Pipeline借用了Unix/Linux中的 Pipeline思路,实现像流水线一样来调度Jenkins任务,通过Jenkinsfile来描述整个持续集成流程。
Jenkins pipeline语法形式
Jenkinsfile支持两种语法形式:
- Scripted pipeline - 脚本式流水线语法,基于 Groovy语言构建的通用 DSL(Domain-specific language,领域特定语言
- Declarative pipeline - 声明式流水线语法,在v2.5之后引入,支持结构化方式,提供了更丰富的语法特性。
DSL是专注于某个应用领域的计算机语言。和Python、Java等这种通用语言(General-purpose Language, GPL)不同的是,DSL是一种为了特定领域而设计的开发语言,比如Web 应用使用的HTML、可扩展标记语言XML、SQL语言等。
本篇文章主要记录声明式流水线语法
Declarative pipeline
声明式流水线语法必须包含在一个 pipeline块内:
pipeline {
/* Declarative Pipeline */
}
pipeline块中主要由Sections, Directives, Steps,或者赋值语句组成。
pipeline {
agent any
stages {
stage('begin') {
steps {
echo 'Hello pipeline'
}
}
}
post {
always {
echo 'say goodbay'
}
}
}
Sections
Sections包括agent、stages、steps和post。
agent
agent 是指定流水线运行在那个环境之中。目前主要有如下几种agent:
- any:在任何环境中运行
pipeline {
agent any
}
- none:表示该 Pipeline 脚本没有全局的 agent 配置。当顶层的 agent 配置为 none 时,每个 stage 部分都需要包含它自己的 agent。
pipeline {
agent none
stages {
stage('Stage For Build'){
agent any
}
}
}
- node:指定运行在某个标签的节点之上
pipline {
agent {
node {
label "myslave"
customWorkspace "myWorkspace"
}
}
}
- docker:运行在 docker 之上相当于 dockerfile,可以直接使用 docker 字段指定外部镜像即可,可以省去构建的时间。比如使用 maven 镜像进行打包,同时可以指定 args:
pipeline {
agent{
docker{
image 'maven:3-alpine'
label 'my-defined-label'
args '-v /tmp:/tmp'
}
}
}
- Dockerfile:从某个 dockerfile 创建的 docker 容器中运行。使用从源码中包含的 Dockerfile 所构建的容器执行流水线或 stage。此时对应的 agent 写法如下:
pipeline {
agent {
dockerfile {
filename 'Dockerfile.build'
dir 'build'
label 'my-defined-label'
additionalBuildArgs '--build-arg version=1.0.2'
}
}
}
- k8s:运行在某一个 k8s 集群之中
pipeline {
agent {
kubernetes {
label podlabel
yaml """
kind: Pod
metadata:
name: jenkins-agent
spec:
containers:
- name: kaniko
image: gcr.io/kaniko-project/executor:debug
imagePullPolicy: Always
command:
...
"""
}
}
}
stages
stages 主要是用来定义某一个流水线阶段的。其中还包含 stage、steps、script等。其中 stage 代表多个步骤,一般一个stages中包含多个stage;steps 代表的是具体执行的内容以及 script 代表具体执行的脚本。
pipeline{
agent any
stages{
stage("first stage"){
steps("first steps"){
echo "Hello World!"
script {
def browsers = ['chrome', 'firefox']
for (int i = 0; i < browsers.size(); ++i) {
echo "Testing the ${browsers[i]} browser"
}
}
}
}
}
}
steps
Steps 部分在给定的 stage 指令中执行的一个或多个步骤,比如在 steps 定义执行一条 shell 命令:
pipeline {
agent any
stages {
stage('Example') {
steps {
echo 'Hello World'
}
}
}
}
或者使用sh字段执行多条指令
pipeline {
agent any
stages {
stage('Example') {
steps {
sh """
echo 'Execute building...'
mvn clean install
"""
}
}
}
}
注意:
在sh中进行变量可以分为两种情况:
1.调用jenkins 的变量(jenkins系统和pipeline定义的变量)使用:${变量名}
2.调用sh 中定义的变量使用:\${变量名}
在声明式的pipeline中默认无法使用脚本语法,但是pipeline提供了一个脚本环境入口:script{},通过使用script来包裹脚本语句,即可使用脚本语法。
判断处理:
pipeline {
agent any
stages {
stage('stage 1') {
steps {
script{
if ( "1" == "1" ) {
echo "lalala"
}else {
echo "oooo"
}
}
}
}
}
}
异常处理,异常捕获只能捕获逻辑错误,代码错误将直接报错。
pipeline {
agent any
stages {
stage('stage 1') {
steps {
script{
try {
sh 'exit 1'
}
catch (exc) {
echo 'Something failed'
}
}
}
}
}
}
post
Post 一般用于流水线结束后的进一步处理,比如错误通知等。Post 可以针对流水线不同的结果做出不同的处理,就像开发程序的错误处理,比如 Python 语言的 try catch。Post 可以定义在Pipeline 或 stage 中,目前支持以下条件:
- always:不管运行结果怎么样都运行
- success:只有当流水线运行结果成功时才运行
- changed:只有当前 Pipeline 或 stage 的完成状态与它之前的运行不同时,才允许在该post 部分运行该步骤
- fixed:当本次 Pipeline 或 stage 成功,且上一次构建是失败或不稳定时,允许运行该post 中定义的指令
- regression:当本次 Pipeline 或 stage 的状态为失败、不稳定或终止,且上一次构建的状态为成功时,允许运行该 post 中定义的指令
- failure:只有当流水线运行失败时才运行
- aborted:只有当流水线取消运行时才运行
- unstable:只有当流水线运行不稳定时才运行
配置示例如下:
pipeline{
agent any
stages{
stage("first stage"){
steps("first steps"){
echo "Hello World!"
script {
def browsers = ['chrome', 'firefox']
for (int i = 0; i < browsers.size(); ++i) {
echo "Testing the ${browsers[i]} browser"
}
}
}
}
}
post {
always {
echo "运行成功"
}
}
}
Directives
Directives可用于一些执行stage时的条件判断或预处理一些数据,和Sections一致,Directives不是一个关键字或指令,而是包含了 environment、options、parameters、triggers、stage、tools、input、when 等配置。
Environment
Environment 主要用于在流水线中配置的一些环境变量,根据配置的位置决定环境变量的作用域。可以定义在 pipeline 中作为全局变量,也可以配置在 stage 中作为该 stage 的环境变量。
pipeline{
agent any
environment {
VERSION = "1.0.0"
}
stages{
stage("first stage"){
steps("first steps"){
echo "Hello World!"
echo "${VERSION}"
script {
def browsers = ['chrome', 'firefox']
for (int i = 0; i < browsers.size(); ++i) {
echo "Testing the ${browsers[i]} browser"
}
}
}
}
}
post {
always {
echo "运行成功"
}
}
}
credentials
在Environment中支持特殊方法credentials(),可以用于在 Jenkins 环境中通过标识符访问预定义的凭证。对于类型为 Secret Text 的凭证,credentials()可以将该 Secret 中的文本内容赋值给环境变量。对于类型为标准的账号密码型的凭证,指定的环境变量为 username 和 password,并且也会定义两个额外的环境变量,分别为 MYVARNAME_USR 和 MYVARNAME_PSW。
假如需要定义个变量名为 CC 的全局变量和一个名为 AN_ACCESS_KEY 的局部变量,并且用 credentials 读取一个 Secret 文本,可以通过以下方式定义:
pipeline {
agent any
environment { // Pipeline 中定义,属于全局变量
CC = 'xxx'
}
stages {
stage('Example') {
environment { // 定义在 stage 中,属于局部变量
AN_ACCESS_KEY = credentials('my-prefined-secret-text')
}
steps {
sh 'printenv'
}
}
}
}
Options
option指令用于配置整个jenkins pipeline本身的选项,根据具体的选项不同,可以将其放在pipeline块或者stage块中。
options 指令允许从流水线内部配置特定于流水线的选项。 流水线提供了许多这样的选项, 比如 buildDiscarder,但也可以由插件提供, 比如 timestamps.
具体参数如下:
buildDiscarder
作用域:只能在pipline块使用
buildDiscardr 保存最近历史构建记录的数量。当pipeline执行完成后,会在硬盘上保存记录,发布的内容和构建执行日志,如果长时间不清理会占用大量空间,设置此选项后会保存配置的数量。
例子:
pipeline {
agent any
options {
buildDiscarder(logRotator(numToKeepStr: '3')) //保留上次构建记录
}
stages {
stage('test-a') {
steps {
echo 'test'
}
}
}
}
disableConcurrentBuilds
作用域:pipeline块
同一个pipeline,Jenkins默认可以同时点击多次,并发执行。这样会消耗资源,并造成发布失败等问题。
配置并发锁定,可以在同时只允许一个执行,在和代码仓库进行联动的时候容易遇到,同时分支进行多次提交。
options {
disableConcurrentBuilds()
}
newContainerPerStage
作用域:pipeline块
当agent为docker或dockerfile时,指定在同一个Jenkins节点上,每个stage都分别运行在一个新的容器中,而不是所有stage都运行在同一个容器中。
options {
newContainerPerStage()
}
retry
作用域:pipeline块或者stage块
当发生失败时进行重试,可以指定整个pipeline的重试次数。需要注意的是,这个次数是指总次数,包括第一次失败。
options {
retry(3)
}
timeout
作用域:stage块
如果pipeline执行时间过长,超出设置的timeout时间,Jenkins将中止pipeline。
以下例子中以小时为单位,还可以把SECONDS(秒),MINUTES(分钟)为单位。
options {
timeout(time: 1, unit: 'HOURS')
}
timestamps
作用域:stage块或者steps块
注意:需要安装Timestamper插件
在stage块声明,每个步骤执行完,日志里都会打印执行时间。
options {
timestamps()
}
在steps块执行,包含内的每个步骤执行完,日志里都会打印执行时间。
timestamps {
echo ‘这是第一个被执行的 stage.’
}
Triggers
在 Pipeline 中可以用 triggers 实现自动触发流水线执行任务,可以通过 Webhook、Cron、pollSCM 和 upstream 等方式触发流水线。
cron
接受一个cron风格的字符串来定义Pipeline触发的常规间隔。
# 每分钟运行一次
pipeline {
agent any
triggers {
cron('* * * * *')
}
stages {
stage('Example') {
steps {
echo 'Hello World'
}
}
}
}
pollSCM
接受一个cron风格的字符串来定义Jenkins检查SCM源更改的常规间隔。如果存在新的更改,则Pipeline将被重新触发。
pipeline {
agent any
triggers {
pollSCM('H */4 * * 1-5')
}
stages {
stage('Example') {
steps {
echo 'Hello World'
}
}
}
}
Input
Input 字段可以实现在流水线中进行交互式操作,比如选择要部署的环境、是否继续执行某个阶段等。
配置 Input 支持以下选项:
- message:必选,需要用户进行 input 的提示信息,比如:“是否发布到生产环境?”;
- id:可选,input 的标识符,默认为 stage 的名称;
- ok:可选,确认按钮的显示信息,比如:“确定”、“允许”;
- submitter:可选,允许提交 input 操作的用户或组的名称,如果为空,任何登录用户均可提交 input;
- parameters:提供一个参数列表供 input 使用。
假如需要配置一个提示消息为“还继续么”、确认按钮为“继续”、提供一个 PERSON 的变量的参数,并且只能由登录用户为 alice 和 bob 提交的 input 流水线:
pipeline {
agent any
stages {
stage('Example') {
input {
message "还继续么?"
ok "继续"
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."
}
}
}
}
when
when 指令允许 Pipeline 根据给定的条件确定是否执行该阶段。该 when 指令必须至少包含一个条件。如果 when 指令包含多个条件,则所有子条件必须为 stage 执行返回 true。这与子条件嵌套在一个 allOf 条件中相同(见下面的例子)。
branch
当正在构建的分支与给出的分支模式匹配时执行,例如:when { branch 'master' }。
请注意,这仅适用于多分支 Pipeline且branch匹配的分支为pipeline文件所在分支。
pipeline {
agent any
stages {
stage('Example Build') {
steps {
echo 'Hello World'
}
}
stage('Example Deploy') {
when {
branch 'master'
}
steps {
echo 'Deploying'
}
}
}
}
environment
当指定的环境变量设置为给定值时执行,例如: when { environment name: 'DEPLOY_TO', value: 'production' }
pipeline {
agent any
environment {
DEPLOY_TO = "xxx"
}
stages {
stage('Example Build') {
steps {
echo 'Hello World'
}
}
stage('Example Deploy') {
when {
environment name: 'DEPLOY_TO', value: 'production'
}
steps {
echo 'Deploying'
}
}
}
}
not
当嵌套条件为false时执行。必须包含一个条件。例如:when { not { branch 'master' } }
pipeline {
agent any
stages {
stage('Example Build') {
steps {
echo 'Hello World'
}
}
stage('Example Deploy') {
when {
not {
branch 'master'
}
}
steps {
echo 'Deploying'
}
}
}
}
allOf
当所有嵌套条件都为真时执行。必须至少包含一个条件。例如:when { allOf { branch 'master'; environment name: 'DEPLOY_TO', value: 'production' } }
pipeline {
agent any
environment {
DEPLOY_TO = "production"
}
stages {
stage('Example Build') {
steps {
echo 'Hello World'
}
}
stage('Example Deploy') {
when {
allOf {
branch 'master';
environment name: 'DEPLOY_TO', value: 'production'
}
}
steps {
echo 'Deploying'
}
}
}
}
anyOf
当至少一个嵌套条件为真时执行。必须至少包含一个条件。例如:when { anyOf { branch 'master'; branch 'staging' } }
pipeline {
agent any
environment {
DEPLOY_TO = "production"
}
stages {
stage('Example Build') {
steps {
echo 'Hello World'
}
}
stage('Example Deploy') {
when {
anyOf {
branch 'master';
environment name: 'DEPLOY_TO', value: 'production'
}
}
steps {
echo 'Deploying'
}
}
}
}
parallel
通过将阶段设置为parallel来表明该stage为并行运行。
- 一个stage只能有一个steps或者parallel
- 嵌套的stages里不能使用parallel
- parallel不能包含agent或者tools
- 通过设置failFast 为true表示:并行的job中如果其中的一个失败,则终止其他并行的stage
- 在并行执行过程中,所有job都同时开始运行,只有当其中一个job开始报错后,如果failFast 为true,对未执行完成的job进行终止跳过。需要考虑时间原因。
pipeline {
agent any
stages {
stage('Non-Parallel Stage') {
steps {
echo 'Non-parallel'
}
}
stage('Parallel Stage') {
failFast true
parallel {
stage('parallel 1') {
steps {
echo "parallel 1"
}
}
stage('parallel 2') {
steps {
echo "parallel 2"
}
}
}
}
}
}
流水线语法生成器
流水线每一个插件都会有自己的 pipeline 语句,而Jenkins提供了上千种插件,由于插件太多所以语法方面不可能完全都记住,所以Jenkins提供了流水线语法生成器,可以通过配置关键项来生成对应插件的流水线代码。