3. Jenkins pipeline 语法3.1 pipeline 组成

    [作者:Surpassme]Jenkins pipeline 是基于Groovy语言实现的一种DSL(领域特定语言),用于描述整条流水线是如何进行的。流水线的主要内容包括源码拉取构建打包部署测试生成报告等步骤。

    从源码管理仓库到生成测试报告这些过程中,可以根据需要分成若干阶段,而每个阶段仅处理一件事情,而每个阶段也可以通过多个步骤来完成,因此我们可以基于这些阶段和步骤些进行抽象,形成工程化的pipeline,因此一个基本的pipeline示例如下所示:

pipeline{ agent any stages{   stage("Sample stage"){     steps {       echo "Hi,Surpass.Welcome visit my blog:https://www.cnblogs.com/surpassme/"     }   } }}

    以上示例详细解释如下所示:

  • pipeline:代表整条流水线,包含整条流水线的逻辑
  • agent:指定流水线的运行位置

流水线的中每个阶段都必须在某个地方(例如物理机、虚拟机或容器)运行,因此可能通过指定 agent部分来指定具体在哪里运行。

  • stages:流水线中多个stage的容器。该部分至少包含一个stage
  • stage:表示阶段,代表流水线的阶段,每个阶段必须有名称
  • steps:表示阶段中的一个或多个具体步骤的容器

1.steps至少包含一个步骤
2.在一个stage中仅有一个steps

    以上每一个部分都是必需的,否则Jenkins都会报错

官方参考文档:https://www.jenkins.io/doc/pipeline/steps/

3.2 pipeline 支持的命令

    [作者:Surpassme]基本的pipeline结构是无法满足日常现实多变的需求。因此,Jenkins pipeline可以通过各种指令来扩展丰富自己。以下为Jenkins pipeline支持的命令。

3.2.1 environment

    [作者:Surpassme]environment主要用于设置环境变量。可以定义在stagepipeline部分。环境变量可以分为:Jenkins内置变量自定义环境变量

3.2.1.1 Jenkins 内置环境变量

    [作者:Surpassme]在执行pipeline时,可以通过env命令来获取Jenkins的全部内置环境变量,其主要使用方法如下所示:

pipeline {    agent any    stages{        stage("PrintEnviroment"){            steps{                // method A                echo "Running ${env.BUILD_ID} on ${env.JENKINS_URL}"                // method B                echo "Running $env.BUILD_ID on $env.JENKINS_URL"                // method C                echo "Running ${BUILD_ID} on ${JENKINS_URL}"            }        }    }}

默认情况下,env中的所有环境变量都可以直接在pipeline中使用。因此上面三种方法都可以使用,但不推荐第三种方法,因为在出现变量名冲突时,排查问题非常难。

    如果需要查看env所有可用的环境变量,可以通过以下方式进行访问。http://jenkins-master-address/pipeline-syntax/globals。示例如下所示:

http://192.168.188.133:9090/pipeline-syntax/globals

    Jenkins 内置环境变量如下所示:

    以下为日常经常使用到的内置环境变量简介。如下所示:

  • BRANCH_NAME

    多分支piple 项目支持。可根据不同的分支执行不同的语句。例如当分支为release时,部署到生产环境,分支为test时,部署至测试环境。

  • BUILD_NUMBER

    构建号,同一个项目中,持续累加的数字。

  • BUILD_URL

    当前构建的URL地址,点击该URL,可以快速跳转至构建页面

  • WORKSPACE

    构建的工作空间,为绝对路径

    在调试pipeline时,也可以在pipeline 的开始阶段使用以下代码片断,打印所有env的环境变量,来进行排查问题。如下所示:

sh "printenv"

3.2.1.2 自定义环境变量

    [作者:Surpassme]当内置环境变量无法满足要求时,我们也可以定义自己的环境变量。这个时候就需要使用environment命令来自定义环境变量。示例如下所示:

pipeline {    agent any    environment{        NAME="Surpass"        AGE="28"    }    stages{        stage("PrintGolbalEnviroment"){            steps{               echo "Global Enviroment is name is ${env.NAME} age is ${env.AGE}"            }        }        stage("PrintLocalEnviroment"){            environment{                CITY="Shanghai"                FROM="Wuhan"            }            steps{                echo "Global Enviroment is name is ${env.NAME} age is ${env.AGE}"                echo "Local Enviroment is city is ${env.CITY} from is  ${env.FROM}"            }        }    }}

    [作者:Surpassme]environment 可以在pipeline中定义,属于全局环境变量,代表整个pipeline均可以使用该环境变量。也可以在stage中定义,属于局部环境变量,仅限于该阶段内部有效,外部无法使用。

    除了以上几中自定义环境变量,还可以通过script来定义全局变量,示例如下所示:

pipeline {    agent any    environment{        NAME="Surpass"        AGE="28"    }    stages{        stage("Set environment by script"){            steps{              script{                  env.SURPASS_NAME="Surpass"                  env.SURPASS_AGE=28                }            }        }        stage("PrintGolbalEnvironment"){            steps{               echo "Global Enviroment is name is ${env.NAME} age is ${env.AGE}"            }        }        stage("PrintLocalEnvironment"){            environment{                CITY="Shanghai"                FROM="Wuhan"            }            steps{                echo "Global Enviroment is name is ${env.NAME} age is ${env.AGE}"                echo "Local Enviroment is city is ${env.CITY} from is  ${env.FROM}"            }        }        stage("PrintSetEnvironment"){            steps{                echo "print global env by script ${env.SURPASS_NAME}"                echo "print global env by script ${env.SURPASS_AGE}"            }        }    }}

    在自定义环境变量,需要注意的地方如下所示:

  • 1.环境变量是不能跨pipeline进行访问的,即不同的pipeline间不能共享环境变量
  • 2.如果用户自定义的环境变量与env环境变量重名,则被重命名的环境将被覆盖

3.2.1.3 自定义全局环境变量

    [作者:Surpassme]env中的环境变量都是内置的,用户自定义的环境变量是与具体的pipeline相关的。如果需要定义全局并且跨pipeline自定义环境变量,可以这样设置。

Manage Jenkins->Configure System-> Global properties,勾选 Environment variables,添加对应的环境变量即可。

    以上自定义的全局环境变量,会被加入env环境变量列表中,后续使用时可以使用${env.SURPASS_NAME}${env.SURPASS_AGE}来获取。

3.2.2 tools

    [作者:Surpassme]可定义在pipeline或stage部分。在运行时,会自动下载并安装指定的工具和加入PATH变量中。但在agent none时,会失效

基于在线自动安装,受限于网络不可达、网络速度、网络策略等因素,一般还是建议,还是提前下载到本地,然后在Jenkins进行配置好之后,在pipeline中直接使用即可。

3.2.2.1 Go 语言环境搭建

    其操作步骤如下所示:

  • 1.下载golang SDK本地

  • 2.在Jenkins 中安装go插件

  • 3.在Jenkins中进行配置

Manage Jenkins->Configure System-> Global Tool Configuration->GO

  • 4.编写pipeline 脚本
pipeline{    agent any    tools {        go "go-1.19"    }    stages{        stage("tools demo"){            steps{                sh "go version"                sh "go env"            }        }    }}

    运行结果如下所示:

3.2.2.2 Python 语言环境搭建

    [作者:Surpassme]Python 环境很容易产生版本冲突和第三方库冲突等问题,因此Python通过会进行工程级别的环境隔离。在Jenkins中,我们可以使用插件Pyenv Pipeline Plugin(https://plugins.jenkins.io/pyenv-pipeline/)来解决此类问题。其操作步骤如下所示:

  • 1.在Jenkins中安装Python/pip/virtualenv
  • 2.安装插件Pyenv Pipeline Plugin
  • 3.编写pipeline 脚本
pipeline{    agent any    stages{        stage("python virtual env demo"){            steps{                withPythonEnv("/usr/local/bin/python3") {                    // Uses the specific python3.5 executable located in /usr/bin                    sh "python3 --version"                    sh "python3 -c \"print('hello,Surpass')\""                }            }        }    }}

    运行结果如下所示:

3.2.2.3 利用作用域实现多版本编译

    [作者:Surpassme]在实际项目中,同一份源码可能会存在多个版本的编译构建等。tools 除了支持pipeline域也支持stage域。示例如下所示:

pipeline{    agent any    stages{        stage("build with go 1.19"){            tools {                go "go-1.15"            }            steps{                echo "use go 1.15 build"            }        }        stage("build with go 1.19"){            tools {                go "go-1.19"            }            steps{                echo "use go 1.19 build"            }        }    }}

3.2.3 input

    [作者:Surpassme]input 命令可以实现pipeline中的交互操作。利用input命令,我们可以一些简单的场景。

  • 实现简易的审核流程

在部署环境前,由相应负责人员进行确认

  • 排查定位问题

因为pipeline在遇到input命令时,会暂停执行。因此,我们可以利用这个特性,使运行某个阶段前,暂停执行pipeline

3.2.3.1 常用参数

    input 命令的常用参数如下所示:

  • message

    该参数为必选参数,input命令的消息提示框消息。

  • id

    该参数为可选参数, input命令标识符,默认为stage的名称

  • submitter

    该参数为可选参数,类型为字符串类型,可以进行操作的用户ID或用户组名称,使用,分隔,在,左右不允许有空格。可以用来做权限控制。

  • submitterParameter

    该参数为可选参数,类型为字符串类型,保存input命令的实际操作者的用户名的变量名

  • ok

    该参数为可选参数,自定义确定按钮的文本

  • parameters

    该参数为可选参数,提供参数列表供input命令使用。

3.2.3.2 简易用法

    比如我们现在实现第一种简易的审核流程。示例脚本如下所示:

pipeline{    agent any    stages{        stage("input simple use"){            steps{                input message:"Confirm or Cancle ?"            }        }    }}

    当执行到input simple use阶段时,pipeline会暂停,当鼠标移动到该阶段虚线视图上时,会出现一个浮层 ,以供选择。示意图如下所示:

3.2.3.3 复杂用法

    示例脚本如下所示:

pipeline{    agent any    environment {        DEPLOY_ENV=""    }    stages{        stage("choose deploy nodes"){            steps{                script{                  DEPLOY_ENV=input(                      message: "准备部署到哪个节点?",                      parameters:[choice(                            name:'chooseNode',                            choices:["node-1","node-2","node-3"],                            description:"选择部署节点")                             ],                      ok:"确定",                      submitter:"admin,Surpass",                      submitterParameter:"approvers"                      )                }            }        }        stage("deply"){            steps{                echo "操作人员:${DEPLOY_ENV['approvers']}"                echo "部署节点:${DEPLOY_ENV['chooseNode']}"            }        }    }}

因为input返回的值需要跨阶段,因此需要将input的返回值定义为全局变量。

    input 命令返回值类型取决于返回的值个数,详情如下所示:

  • 仅返回一个值,返回值类型就是这个值的类型
  • 返回多个值,则返回值类型为Map类型

    运行结果如下所示:

3.2.4 options

    [作者:Surpassme]用于配置pipeline本身的选项,可以定义在pipelinestage中。常用的配置项如下所示:

  • buildDiscarder

    [作者:Surpassme]用于配置保存最近历史构建记录的数量。示例如下所示:

options{      buildDiscarder(logRotator(numToKeepStr:'5'))    }

此选项只能在pipeline下的 options 中使用

  • checkoutToSubdirectory

    [作者:Surpassme]Jenkins 从版本控制库中拉取源码时,默认会存放到工作空间目录中,此选项可以指定检出到工作空间的子目录中,示例如下所示:

   options{      checkoutToSubdirectory('subdir')    }
  • disableConcurrentBuilds

    [作者:Surpassme]同一个pipeline,Jenkins默认是可以同时执行的,而此选项则是禁止多个pipeline并行执行,示例如下所示:

   options {      disableConcurrentBuilds()    }
  • retry

    [作者:Surpassme]当发生失败时进行重试,可以指定整个pipeline的重试次数。示例如下所示:

   options{       retry(3)   }

需要注意的是这里的重试次数是指总次数,即包含第一次失败。当使用retry选项时,options可以放在stage中

  • timeout

    [作者:Surpassme]若pipeline执行时间过长,超出了设置的超时时间之后,则Jenkins将中止pipeline。示例如下所示:

   options{       timeout(time:1,unit:'SECONDS')   }

1.timeout时间单位有SECONDSMINUTESHOURS
2.当使用timeout选项时,options可以放在stage块中

  • timestamps

    [作者:Surpassme]为控制台输出时间戳。示例如下所示:

   options {        timestamps()    }

3.2.5 parallel

    [作者:Surpassme]在pipeline中使用parallel可以很方便的实现并行构建。

3.2.5.1 阶段并行

    示例如下所示:

pipeline{    agent any    stages{        stage("Non Parallel stage"){            steps{                echo "This is non-parallel stage"            }        }        stage("Parallel stage"){            failFast true            parallel{                stage("Parallel A"){                    steps{                        echo "Parallel A"                    }                }                stage("Parallel B"){                    steps{                        echo "Parallel B"                    }                }                stage("Parallel C"){                    steps{                        echo "Parallel C"                    }                }            }        }    }}

    在使用parallel时,注意事项如下所示:

  • parallel 块本身不允许包含agenttools
  • 默认情况下,pipeline要等parallel块下所有的阶段都运行完成,才能确定运行结果。若希望所有并行阶段中某个阶段失败后,就即将中止所有阶段,则需要在parallel同级位置加入failFast true

failFast true 简单来讲就是表示,只要有一个并行阶段运行失败,则立即退出。

3.2.5.2 步骤并行

    [作者:Surpassme]示例如下所示:

pipeline{    agent any    stages{        stage("Non-parallel steps"){            steps{                echo "Non-parallel steps"            }        }        stage("Parallel steps"){            steps{                parallel(                  parallelA:{                      echo "parallelA steps"                  },                  parallelB:{                      echo "parallelB steps"                  },                  parallelC:{                      echo "parallelC steps"                  }                )            }        }    }}

    [作者:Surpassme]阶段并行和步骤并行的主要区别如下所示:

  • 写法区别:在阶段并行时,使用的是大括号,而步骤并行时,使用的是括号
  • 运行区别:阶段并行是运行在不同的executor,而步骤并行,则是运行在同一个executorr

原文地址:https://www.jianshu.com/p/f2895e38a824

本文同步在微信订阅号上发布,如各位小伙伴们喜欢我的文章,也可以关注我的微信订阅号:woaitest,或扫描下面的二维码添加关注:

作者: Surpassme

来源: http://www.jianshu.com/u/28161b7c9995/

         http://www.cnblogs.com/surpassme/

声明:本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出 原文链接 ,否则保留追究法律责任的权利。如有问题,可发送邮件 联系。让我们尊重原创者版权,共同营造良好的IT朋友圈。