pipeline语法

pipeline

        最近在测试k8s上的ci/cd,之前的ci/cd其实也能满足目前先业务的需求,但是想尝试改进一下,优化以前的job,希望在登录ci的时候更加的简洁, 而且查找job的时候,点击一个job就能查看完整的信息,不需要job之间的来回切换,等等各种理由,😁😁。这里使用jenkins pipeline,起初测试的时候使用pipeline,没问题以后,使用jenkinsfile。

pipeline 常用介绍

清理历史build

        普通job的时候清理和保留历史job的build 很简单,勾勾就可以啦,但是pipeline就的使用一下方式,而且还的写在最前面,不然识别不了,会报错的

1
2
3
4
options {
buildDiscarder(logRotar(numToKeepStr: '8'))
disableConcurrentBuilds()
}
  • buildDiscarder: 保持构建的最大个数
  • disableConcurrentBuilds: 禁止并发构建

详细参数:

1
buildDiscarder(logRotator(numToKeepStr: '8', artifactNumToKeepStr: '8', daysToKeepStr: '8', artifactDaysToKeepStr: '7'))
  • artifactDaysToKeepStr: 发布包保留天数
  • artifactNumToKeepStr: 发布包最大保留#个构建
  • daysToKeepStr: 保持构建的天数
  • numToKeepStr: 保持构建的最大个数

gitlab事件触发

        之前的我们的ci/cd都是开发提交到某一个分支,然后jenkins会自动触发编译、发布,而且配置这个步骤也需要好几步才能实现,但在pipeline中也可以通过代码形式最这种触发器(勾子)进行配置。这样让每个项目都和jenkins进行耦合;运维人员只需要专注的维护Jenkinsfile,创建对应的项目即可。gitlab触发jenkins的构建需要依赖Gitlab插件。这里需要自行安装

  • 接受固定的分支

    1
    2
    3
    4
    5
    6
    7
    triggers {
    gitlab(triggersOnPush: true,
    triggersOnMergeRequest: true,
    branchFilterType: "NameBasedFilter",
    includeBranchesSpec: "dev,test,master",
    secretToken: "${env.git_token}")
    }
  • triggerOnPush: 当Gitlab触发push事件时,是否执行构建

  • triggerOnMergeRequest: 当Gitlab触发mergeRequest事件时,是否执行构建

  • branchFilterType: 只有符合条件的分支才会触发构建,必选,否则无法实现触发。

    • All: 所有分支
    • NameBasedFilter: 基于分支名进行过滤,多个分支名使用逗号分隔
      • includeBranchesSpec: 基于branchFilterType值,输入期望包括的分支的规则
      • excludeBranchesSpec: 基于branchFilterType值,输入期望排除的分支的规则
    • RegexBasedFilter: 基于正则表达式对分支名进行过滤
      • sourceBranchRegex: 定义期望的通过正则表达式限制的分支规则

        所以分支不阐述,其他的两个选项是最实用的,我们在正式使用的时候一定会用到这个,上面的例子是一个接受固定的几个分支

  • 匹配的方式
    1
    2
    3
    4
    5
    6
    7
    triggers {
    gitlab(triggersOnPush: true,
    triggersOnMergeRequest: true,
    branchFilterType: "RegexBasedFilter",
    sourceBranchRegex: "dev.*",
    secretToken: "${env.git_token}")
    }

        这里的git_token需要在jenkins的全局变量里面添加一个Environment variables对应的一个键值即可。

: 所有的触发器都需要先手动执行一次,让jenkins家在其中的配置,对应的指令才会生效。

  • jenkins 验证
    img

  • gitlab验证
            需要将项目回调地址写入到Gitlab钩子当中才可以。经过测试一个pipeline的job可以管理多个分支的触发,避免之前的每一个分支的job进行触发。

parameters 模块

        该模块需要安装,parameters指令提供用户在触发Pipeline时应提供的参数列表。这些用户指定的参数的值通过该params对象可用于Pipeline步骤。研发经常会有打出一个特性分支,这个分支用于hotfix,这个时候就要给研发提交一个可以选择的分支,然他们去部署到对应的环境。

  • 字符串参数
            定义一个字符串参数,用户可以在Jenkins UI上输入字符串,常见使用这个参数的场景有,用户名,收件人邮箱,文件网络路径,主机名称的或者url等

    1
    2
    3
    parameters {
    string(name: 'DEPLOY_ENV', defaultValue: 'staging', description: '')
    }
  • 布尔值参数
            定义一个布尔类型参数,用户可以在Jenkins UI上选择是还是否,选择是表示代码会执行这部分,如果选择否,会跳过这部分。一般需要使用布尔值的场景有,执行一些特定集成的脚本或则工作,或者事后清除环境,例如清楚Jenkins的workspace这样的动作

    1
    2
    3
    parameters {
    booleanParam(name: 'DEBUG_BUILD', defaultValue: true, description: '')
    }
  • 选择参数
            选择(choice)的参数就是支持用户从多个选择项中,选择一个值用来表示这个变量的值。工作中常用的场景,有选择服务器类型,选择版本号等。

    1
    2
    3
    parameters {
    choice(name: 'ENV_TYPE', choices: ['dev', 'test', 'product'], description: 'dev env test')
    }

        当然parameters模块我们用的最多的是在手动的时候我们可以手动点击进行构建部署,至于其他的目前我暂时未用到

  • 选择分支部署
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    pipeline {
    agent {label 'agent-node'}
    parameters {
    gitParameter branchFilter: 'origin/(.*)', defaultValue: 'dev', name: 'BRANCH', type: 'PT_BRANCH'
    }
    stages {
    stage('gitlib code') {
    steps{
    git branch:"${params.BRANCH}", credentialsId:'gitlabUser', url: "http://gitlab.xxlaila.cn/xxx/kxl-eureka.git"
    script {
    build_tag = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()
    }
    }
    }
    }
    }

parameters官方参考,介绍得挺详细的,辅助参考
img

  • 还可以写成
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    parameters {
    gitParameter(
    branch: '',
    branchFilter: 'origin/(.*)',
    defaultValue: 'dev',
    description: 'test code',
    name: 'BRANCH',
    quickFilterEnabled: false,
    selectedValue: 'NONE',
    sortMode: 'NONE',
    tagFilter: '*',
    type: 'PT_BRANCH')
    }

        这里有一个问题:当这里设置了可以选择分支的时候,然后在之前的自动触发就会有问题,就是在去分支拉去代码的时候就一只是dev分支,而不是其他的分支,这里仍然在探索的测试中。
编辑job可以看到
img

多分支pipeline

        按照上面的又要支持用户可以选择分支,又要适合自动触发功能。用单分支pipeline来管理项目,又要回到我们最初的模式,而在实际过程中,我们可以用到多分支同时进行开发。这样就满足了我们的实际需求。多分支任务这里不做过多的详细介绍,这里阐述两个功能点;分别是分支的扫描策略和孤儿项策略(Orphaned Item)。

分支的扫描策略

        分支扫描是jenkins根据一定的策略去代码仓库扫描分支,如果有新分支就创建一个以新分支命名的任务,如果发现分支被删除,就删除对应的jenkins任务。

        在”扫描多分支流水线触发器(Scan Multibranch Pipeline Triggers)”下有一个: Periodically if not otherwise run(没有手动触发,就定期扫描分支)。选择此项,设置一个扫描间隔时长。可以根据项目分支的频繁程度设置周期的长短,也可以在任务页面手动触发jenkins进行扫描。
img

孤儿项策略(Orphaned Item)

        该功能是在代码仓库中删除了release分支,那么在多任务页面上,该分支在jenkins上的任务也应该对应删除。什么时候删除,取决于下次分支扫描时间。如果代码仓库中的分支被删除,而jenkins上响应的任务没有被删除,那么这个任务就是所说的孤儿任务。对于分支任务的历史记录,保存多长时间设置

  • 界面配置
    img

  • pipeline 写法

    1
    2
    3
    4
    5
    6
    orphanedItemStrategy {
    discardolditems {
    daysTokeep(10)
    numToKeep(5)
    }
    }

: 这里孤儿策略pipeline 需要另外一种方式来支持,Setting up GitLab Server Configuration on Jenkins,这里没有用到这个,不做过多的阐述。github参考

多分支的自动触发

        分支的触好处是多多的,自然在多分支面前自动触发肯定也少不了。多分支的触发有两种模式,分别是前面提到的Gitlab trigger和Generic Webhook Trigger。下面分别对两种模式进行阐述和实际的测试

Generic Webhook Trigger

        Generic Webhook Trigger 插件需要提前安装,GenericTrigger触发条件是由GWT插件提供,GenericTrigger触发的条件分为5个部分。GenericTrigger官方参考

  • 从HTTP POST请求中提取参数
  • token,GWT插件用于标识jenkins项目的唯一性
  • 根据请求参数值判断是否触发Jenkins项目执行
  • 日志控制打印
  • webhook响应控制

GerenericTrigger 的写法

1
2
3
4
5
6
7
8
9
10
11
triggers {
GenericTrigger(
genericVariables:[
[key: 'ref', value: '$.ref']
],

token: env.JOB_NAME,
regexpFilterText: '$ref',
regexpFilterExpression: 'refs/heads/' + env.BRANCH_NAME
)
}

       env.BRANCH_NAME 这里指的是分支名。当然这样修改以后是不行的,是达不到自动触发的,需要自行去gitlab上添加钩子,这里经过测试流程:用户修改dev分支,push到gitlab dev分支可以触发任务的dev分支自动构建;合并到test分支,也可以触发test分支自动构建;在合并到master分支也能自动触发任务的master分支自动构建。
img

       我们要实现这块,要理解知道这个东西,首先要知道gitlab push 数据的格式,知道了gitlab push格式,我们才知道应该怎么操作,gitlab push数据的格式参考

  • 参考
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    {
    "object_kind": "push",
    "before": "95790bf891e76fee5e1747ab589903a6a1f80f22",
    "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
    "ref": "refs/heads/master",
    "checkout_sha": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
    "user_id": 4,
    "user_name": "John Smith",
    "user_username": "jsmith",
    "user_email": "john@example.com",
    "user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80",
    "project_id": 15,
    "project":{
    "id": 15,
    "name":"Diaspora",
    "description":"",
    "web_url":"http://example.com/mike/diaspora",
    "avatar_url":null,
    "git_ssh_url":"git@example.com:mike/diaspora.git",
    "git_http_url":"http://example.com/mike/diaspora.git",
    "namespace":"Mike",
    "visibility_level":0,
    "path_with_namespace":"mike/diaspora",
    "default_branch":"master",
    "homepage":"http://example.com/mike/diaspora",
    "url":"git@example.com:mike/diaspora.git",
    "ssh_url":"git@example.com:mike/diaspora.git",
    "http_url":"http://example.com/mike/diaspora.git"
    },
    "repository":{
    "name": "Diaspora",
    "url": "git@example.com:mike/diaspora.git",
    "description": "",
    "homepage": "http://example.com/mike/diaspora",
    "git_http_url":"http://example.com/mike/diaspora.git",
    "git_ssh_url":"git@example.com:mike/diaspora.git",
    "visibility_level":0
    },
    "commits": [
    {
    "id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
    "message": "Update Catalan translation to e38cb41.",
    "timestamp": "2011-12-12T14:27:31+02:00",
    "url": "http://example.com/mike/diaspora/commit/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
    "author": {
    "name": "Jordi Mallach",
    "email": "jordi@softcatala.org"
    },
    "added": ["CHANGELOG"],
    "modified": ["app/controller/application.rb"],
    "removed": []
    },
    {
    "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
    "message": "fixed readme",
    "timestamp": "2012-01-03T23:36:29+02:00",
    "url": "http://example.com/mike/diaspora/commit/da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
    "author": {
    "name": "GitLab dev user",
    "email": "gitlabdev@dv6700.(none)"
    },
    "added": ["CHANGELOG"],
    "modified": ["app/controller/application.rb"],
    "removed": []
    }
    ],
    "total_commits_count": 4
    }

        如果我们想根据不同的分支提交来触发jenkins的构建,那就应该知道post数据哪一个属性代表了不同的分支,我们可以在第四行看到

1
"ref": "refs/heads/master",

注释: 也可以通过IDEA工具提交的时候看到提交的选项。可以看到我们用ref可以很好的区分不同分支,这里就是为什么要填写ref的原因。我们可以通过pipeline代码的生成器来生成

  • pipeline 代码生成器
    1
    2
    3
    triggers {
    GenericTrigger causeString: 'Generic Cause', genericVariables: [[defaultValue: '', key: 'ref', regexpFilter: '', value: '$.ref']], printContributedVariables: true, printPostContent: true, regexpFilterExpression: '\'refs/heads/\' + evn.BRANCH_NAME', regexpFilterText: '$ref', token: 'env.JOB_NAME'
    }

img
img

: token参数的作用是标识一个pipeline在jenkins中的唯一性,这个参数的重要性就得提起GWT插件的原理。当jenkins收到generic-webhook-trgger/invoke接口的请求时,会将请求代理给GWT插件处理,GWT插件内容会从jenkins实例对象中取出所有的参数化jenkins项目,包括pipeline,然后进行遍历。如果我们在参数化项目中Generic Trigger配置token的值与webhook请求时的token一致,就会触发改项目。如果多个参数化项目的token一样,则都会进行触发,所以这里的token最好时JOB_NAME项目名,因为这个是在项目或者是在为服务领域他都是唯一的。

  • 参数介绍:
    • regexpFilterText: 需要进行匹配的key,例子中,使用从post body中提取的ref变量值。
    • regexpFilterExpression: 正则表达式;如果regexpFilterText参数符合regexpFilterExpression参数的正则表达式,则触发执行。
    • printPostContent: 布尔值,将webhook请求信息打印到日志上
    • printContributedVariables: 布尔值,将提取后的变量名及变量值打印出来
    • causeString: 字符串型,触发原因,可以直接应用提取后的变量,如 causeString: ‘Triggered on $msg’
    • Silent response: 布尔型,在正常情况下,当webhook请求成功后,GWT插件会返回HTTP 200状态码和触发结果给对方调用,但是当Silentresponse设置为true时,就只返回HTTP 200状态码,不反悔触发结果

        上面的看的出来,我们只要是提交了分支都可以进行触发构建,但是呢,在实际生产中,我们定义了dev——>test——master 分支,就是只想要这几个进行触发构建,其他的不进行触发,让开发自己去点击。

  • 指定分支构建

    1
    2
    3
    triggers {
    GenericTrigger causeString: 'Triggered on $msg', genericVariables: [[defaultValue: '', key: 'ref', regexpFilter: '', value: '$.ref']], printContributedVariables: true, printPostContent: true, regexpFilterExpression: '\'refs/heads/(dev|test|master)\'', regexpFilterText: '$ref', token: 'env.JOB_NAME'
    }
  • 多分支Gitlab trigger
            多分支的Gitlab trigger和我们前面介绍的gitlab事件触发一样的,没有任何区别,这里我测试了一个job,没有任何问题。同时新建了一个分支,jenkins会自动的扫描新建一个以分支为名的任务,进行自动触发。当我删除了某一个分支,就会触发自动扫描,然后查看分支为删除。

  • 删除分支
    img
    img

  • 整体效果图
    img

        这里介绍一下部署这块,根据branch来进行判断,不同的branch部署到不同的环境,当设定的值不在branch范围内,就需要人为的制定部署环境。当人员三分钟内没有来进行环境部署的选择,系统就会断开,对该分支标记为结束。

完整文件
推荐学习参考地址

坚持原创技术分享,您的支持将鼓励我继续创作!
0%