学习 Jenkins 的Pipeline(流水线)

934 阅读7分钟

Jenkins PipeLine

我正在参加「掘金·启航计划」

Jenkins 流水线是一套插件,将持续交付的实现和实施集成到 Jenkins 中。

PipeLine 也就是构建流水线,对于程序员来说,最好的解释就是:使用代码来空值项目的构建、测试、部署等。

使用它的好处有很多,包括但不限于:

  • 使用 Pipeline 可以非常灵活的控制整个构建过程
  • 可以清楚的知道每个构建阶段使用的时间,方便构建的优化
  • 构建出错,使用 stageView 可以快速定位出错的阶段
  • 一个 job 可以搞定整个构建,方便管理和维护等

image.png

定义流水线使用的是 Groovy 脚本,保存脚本的方式有两种:

  1. 直接写在Jenkins工程中
  2. 存放在代码仓库项目目录下的 Jenkinsfile 中。通常认为在 Jenkinsfile 中定义并检查代码控制是最佳实践

image.png

上图中的 Pipeline Script 即定义流水线直接写在 Jenkins 工程中,Pipeline script from SCM 就是从代码仓库项目的 Jenkinsfile 中读取流水线。

Pipeline 语法

Jenkinsfile 能使用两种语法进行编写:声明式脚本化

声明式和脚本化的流水线从根本上是不同的。声明式流水线的是 Jenkins 流水线新推出的特性:

  • 相比脚本化的流水线语法,它提供更丰富的语法特性
  • 更容易编写和读取流水线代码

声明式流水线基础

pipeline {
    agent any
    stages {
        stage('Build') {
            steps { }
        }
        stage('Test') {
            steps { }
        }
        stage('Deploy') {
            steps { }
        }
    }
}

脚本化流水线基础

node {
    stage('Build') {
        //
    }
    stage('Test') {
        //
    }
    stage('Deploy') {
        //
    }
}

声明式 Pipeline 的基本语法(Sections、Directives、Steps、赋值语句)

声明式 Pipeline 的基本语法和表达式遵循与 Groovy 语法相同的规则,但有例外

  1. 声明式 Pipeline 必须包含在固定格式 pipeline {} 块内
  2. 每个声明语句必须独立一行,行尾无需使用分号
  3. 块只能由 Sections(章节)Directives(指令)Steps(步骤)或者赋值语句组成
  4. 属性引用语句被视为无参数方法调用。例:input 被视为 input()

下文介绍声明式的 Pipeline 的语法

Sections

声明式流水线的 Sections 通常包含一个或多个 DirectivesSteps

agent

指定了整个流水线或特定的部分, 将会在Jenkins环境中执行的位置,这取决于 agent 区域的位置。该部分必须在 pipeline 块的顶层被定义, 但是 stage 级别的使用是可选的。

参数说明
any在任何可用的代理上执行流水线或阶段。如:agent any
none在 pipeline 块的顶部没有全局变量,该参数将会被分配到整个流水线的运行中并且每个 stage 部分都需要包含他自己的 agent 部分。如:agent any
label在提供了标签的 Jenkins 环境中可用的代理上执行流水线或阶段。
docker使用给定的容器执行流水线或阶段。
dockerfile执行流水线或阶段, 使用从源代码库包含的 Dockerfile 构建的容器。

post

post 定义一个或多个 steps,这些阶段根据流水线或阶段的完成情况而运行。post 支持以下破石头-condition 块中的其中之一:alwayschangedfailuresuccessunstableaborted。这些条件块允许在 post 部分的步骤的执行取决于流水线或阶段的完成状态。

Condition说明
always无论流水线或阶段的完成状态如何,都允许在 post 部分运行该步骤
changed只有流水线或阶段的完成状态与他之前的运行不同时,才允许在 post 部分运行该步骤
failure只有当前流水线或阶段的完成状态为“failure”,才允许在 post 部分运行该步骤,通常 web UI 是红色的
success只有当前流水线或阶段的完成状态为“success”,才允许在 post 部分运行该步骤,通常 web UI 是蓝色或绿色
unstable只有当前流水线或阶段的完成状态为"unstable",才允许在 post 部分运行该步骤,通常由于测试失败,代码违规等造成。通常web UI是黄色。
aborted只有当前流水线或阶段的完成状态为"aborted",才允许在 post 部分运行该步骤,通常由于流水线被手动的aborted。通常web UI是灰色。
pipeline {
    agent any
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'
            }
        }
    }
    post { 
        always { 
            echo 'I will always say Hello again!'
        }
        failure {
            echo 'pipeline is failure'
        }
        success {
            echo 'pipeline is success'
        }
    }
}

stages

包含一系列一个或多个 stage 指令,stages 部分是流水线描述的大部分“work”的位置。建议 stages 至少包含一个 stage 指令用于连续交付过程的每个离散部分,比如构建、测试和部署。

pipeline {
    agent any
    stages { 
        stage('Example') {
            steps {
                echo 'Hello World'
            }
        }
    }
}

steps

steps 块定义在 stage 指令中被执行。可以定义一系列一个 step 或多个 step

  • 按照惯例,post 部分应该放在流水线的底部
pipeline {
    agent any
    stages {
        stage('Example') {
            steps { 
                echo 'Hello World'
            }
        }
    }
}

Directive(指令)

stage

stage 指令在 stages 块中定义并且要包含一个 steps 块、一个可选的 agent 块或者其他特定阶段的指令

pipeline {
    agent any
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'
            }
        }
    }
}

environment(环境变量)

environment 创建一个 键-值对序列,该序列将被定义为所有步骤的环境变量,或者是特定于阶段的步骤, 这取决于 environment 指令在流水线内的位置。

该指令支持一个特殊的助手方法 credentials() ,该方法可用于在Jenkins环境中通过标识符访问预定义的凭证。对于类型为 "Secret Text"的凭证, credentials() 将确保指定的环境变量包含秘密文本内容。

  • 顶层流水线块中使用的 environment 指令将适用于流水线中的所有步骤
  • 在一个 stage 中定义的 environment 指令只会将给定的环境变量应用于 stage 中的步骤
  • environment 块有一个助手方法 credentials() 定义,该方法可以在 jenkins 环境中用于通过标识符访问预定义的凭证
pipeline {
    agent any
    enviroment {
        name = "DBAA"
    }
    stages {
        stage('Start') {
            echo "环境变量中name的值为 ${name}"
        }
    }
}

options

options 指令允许从流水线内部配置特定于流水线的选项。

选项说明
buildDiscarder为最近的流水线运行的特定数量保存组件和控制台输出。如:options { buildDiscarder(logRotator(numToKeepStr: '1')) }
disableConcurrentBuilds不允许同时执行流水线。 可被用来防止同时访问共享资源等。如:options { disableConcurrentBuilds() }
overrideIndexTriggers允许覆盖分支索引触发器的默认处理。
skipStagesAfterUnstable一旦构建状态变得 UNSTABLE,跳过该阶段。如:options { skipStagesAfterUnstable() }
skipDefaultCheckoutagent 指令中,跳过从源代码控制中检出代码的默认情况。
checkoutToSubdirectory在工作空间的子目录中自动地执行源代码控制检出。如:options { checkoutToSubdirectory('foo') }
retry在失败时, 重新尝试整个流水线的指定次数。如:options { retry(3) }
timeout设置流水线运行的超时时间, 在此之后,Jenkins将中止流水线。如:options { timeout(time: 1, unit: 'HOURS') }
timestamps预谋所有由流水线生成的控制台输出,与该流水线发出的时间一致。如:options { timestamps() }

parameters

parameters 指令提供了一个用户在触发流水线时应该提供的参数列表。这些用户指定参数的值可通过 params 对象提供给流水线步骤。

可用参数说明
string字符串类型的参数。如:string(name: 'branch', defaultValue: 'master', description: '分支名')
booleanParam布尔参数。如:booleanParam(name: 'isFile', defaultValue: true, description: '是否为文件')
text文本参数。如:text(name: 'name', defaultValue: 'xiaoming', description: '输入你的名字')
choice选项参数。如:choice(name: 'ENV_TYPE', choices: ['test', 'dev', 'product'], description: '环境选项')
password密码参数。如:password(name: 'PASSWORD', defaultValue: '123456', description: '密码')

triggers(触发器)

triggers 指令定义了流水线被重新触发的自动化方法。对于集成了源(Github 或 BitBucket)的流水线,可能不需要 triggers。

触发器说明
cron接收 cron 样式的字符串来定义要重新触发流水线的常规间隔。如:triggers { cron('H */4 * * 1-5') }
pollSCM接收 cron 样式的字符串来定义一个固定的间隔,在这个间隔中,Jenkins 会检查新的源代码更新。如果存在更改, 流水线就会被重新触发。如:triggers { pollSCM('H */4 * * 1-5') }
upstream接受逗号分隔的工作字符串和阈值。 当字符串中的任何作业以最小阈值结束时,流水线被重新触发。如:triggers { upstream(upstreamProjects: 'job1,job2', threshold: hudson.model.Result.SUCCESS) }

tools

定义自动安装和放置 PATH 的工具的一部分。如果 agent none 指定,则忽略该操作。

支持的工具有 mavenjdkgradle

pipeline {
    agent any
    tools {
        maven 'apache-maven-3.0.1' 
    }
    stages {
        stage('Example') {
            steps {
                sh 'mvn --version'
            }
        }
    }
}

input

stage 的 input 指令允许你使用 input step 提示输入。在应用了 options 后,进入 stage 的 agent 或评估 when 条件前,stage 将暂停。如果 input 被批准,stage 将会继续。作为 input 提交的一部分的任何参数都将在环境中用于其他 stage 。

配置项说明
message必需的。这将在用户提交 input 时呈现给用户
idinput 的可选标识符,默认为 stage 名称
okinput 表单上的“ok”按钮的可选文本
submitter可选的以逗号分隔的用户列表或允许提交 input 的外部组名。默认允许任何用户。
submitterParameter环境变量的可选名称。如果存在,用 submitter 名称设置。
parameters提示提交者提供的一个可选的参数列表。
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."
            }
        }
    }
}

image.png

when

when 指令允许流水线根据给定的条件决定是否应该执行阶段。when 指令必须包含至少一个条件,如果 when 指令包含多个条件,所有的子条件必须返回 true,阶段才能执行。这与子条件在 allOf 条件下嵌套的情况相同(可看下面的 allOf 条件)。

内置条件说明
branch当正在构建的分支与模式给定的分支匹配时,执行这个阶段。如:when { branch 'master' }。注意:该条件只使用于多分支流水线
environment当指定的环境变量是给定的值时,执行这个步骤。如:when { environment name: 'DEPLOY_TO', value: 'production' }
expression当指定的 Groovy 表达式为 true 时,执行这个阶段。如:when { expression { return params.DEBUG_BUILD } }
not当嵌套条件时错误时,执行这个阶段,必要包含一个条件。如:when { expression { return params.DEBUG_BUILD } }
allOf当所有的嵌套条件都正确时,执行这个阶段,必须包含至少一个条件,例如: when { allOf { branch 'master'; environment name: 'DEPLOY_TO', value: 'production' } }
anyOf当至少有一个嵌套条件为真时,执行这个阶段,必须包含至少一个条件,例如: when { anyOf { branch 'master'; branch 'staging' } }
在进入 stage 的 agent 前评估 when如果 beforeAgent 被设置为 true, 那么就会首先对 when 条件进行评估 , 并且只有在 when 条件验证为真时才会进入 agent 。

Parallel(并行)

声明式流水线的阶段可以在他们内部声明多嵌套 stage,它们将并行执行。注意:一个阶段必须有一个并且只能有一个 steps 或 parallel 的阶段。

pipeline {
    agent any
    stages {
        stage('Non-Parallel Stage') {
            steps {
                echo 'This stage will be executed first.'
            }
        }
        stage('Parallel Stage') {
            when {
                branch 'master'
            }
            failFast true
            parallel {
                stage('Branch A') {
                    agent {
                        label "for-branch-a"
                    }
                    steps {
                        echo "On Branch A"
                    }
                }
                stage('Branch B') {
                    agent {
                        label "for-branch-b"
                    }
                    steps {
                        echo "On Branch B"
                    }
                }
            }
        }
    }
}

Steps

声明式流水线可以使用所有在流水线步骤引用中记录的步骤,它包含一个完整的步骤列表,其中添加了下面列出的步骤,这些步骤仅仅只能在声明式流水线中使用。

script

script 步骤需要 scripted-pipeline 块并在声明式流水线中执行。

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"
                    }
                }
            }
        }
    }
}

可能出现的问题

Jenkins 构建执行 shell 脚本提示 premission-denied

原因:jenkins 用户的权限不够

解决办法:

  1. 修改 jenkins 的用户权限
vi /etc/sysconfig/jenkins

# jenkins 文件,修改如下配置
JENKINS_USER="root"
  1. 修改 jenkins 的用户与组
cd /var/lib
chown -R root:root jenkins

# 重启 jenkins 服务
service jenkins restart

参数化构建功能选了git参数的分支后,拿不到分支变量

解决办法:

不勾选【轻量级检出】

image.png