[TOC]

0x01 基础实践

(1) Maven 构建之 Pipeline Script

描述:此处重新不在累述新建流水线任务(maven-pipeline-helloword)而是直接进行配置测试等关键项;
流程:代码拉取 -> 代码检测 -> 代码构建 -> 代码部署 -> 消息通知

Step 1. Dashboard -> maven-pipeline-helloword -> 流水线项目配置 (名称|丢弃旧的构建|参数化构建过程(Git/名称))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Git 参数
名称: git_tags
描述: Project_Release
参数类型: 标签
默认值: origin/master
排序方式: DESCENDING SMART
----------------------------
# 选项 参数
名称: deploy_option
选项:
deploy
rollback
redeploy
描述: 部署&回退&重部署

  • Step 2.流水线 Pipeline script 编写

    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
    pipeline {
    agent any
    stages {
    stage ("代码获取") {
    steps {
    checkout([$class: 'GitSCM', branches: [[name: '${git_tags}']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'b4c8b4e9-2777-44a1-a1ed-e9dc21d37f4f', url: '[email protected]:ci-cd/java-maven.git']]])
    }
    }

    stage ("代码质检") {
    steps {
    sh label: 'sonar', returnStatus: true, script: '''/usr/local/bin/sonar-scanner -Dsonar.projectName=${JOB_NAME} -Dsonar.projectKey=Hello-World -Dsonar.sources=.'''
    }
    }

    stage ("项目构建") {
    steps {
    // 此处实际上不用执行cd命令
    sh label: 'build', script: 'cd /var/lib/jenkins/workspace/maven-pipeline-helloword/ && /usr/local/maven/bin/mvn clean package -Dmaven.test.skip=true '
    }
    }

    stage ("项目部署") {
    steps {
    // 脚本为前面一章的部署脚本(两种方式)
    sh label: 'deploy', script: '/tmp/script/maven-jenkins-ci-script.sh'
    // sh "/tmp/script/maven-jenkins-ci-script.sh"
    }
    }
    }

    // 消息通知: POST阶段当所有任务执行后触发
    post {
    // 企业微信
    always {
    qyWechatNotification aboutSend: true, failNotify: true, failSend: true, mentionedId: 'ALL', mentionedMobile: '', startBuild: true, successSend: true, unstableSend: true, webhookUrl: 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=c222f3fc-f645-440a-ad24-0ce8d9626fa0'
    }

    // 钉钉
    success {
    //当此Pipeline成功时打印消息(注意此处的robot为您在Jenkins全局配置中设置钉钉的id值)
    echo 'success'
    dingtalk (
    robot: 'weiyigeek-1000',
    type: 'LINK',
    title: 'Jenkins 持续集成 - 任务名称 ${JOB_NAME}',
    text: [
    '项目构建成功',
    'Pipeline 流水线测试'
    ],
    messageUrl: 'http://www.weiyigeek.top',
    picUrl: 'https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2319834554,1372531032&fm=26&gp=0.jpg',
    atAll: true
    )
    }
    }
    }
  • Step 3.Pipeline maven-pipeline-helloword 进行选项参数构建 (Build with Parameters)-> 部署 v1.8 版本

WeiyiGeek.参数构建

WeiyiGeek.参数构建


  • Step 4.查看部署结果与信息通知
WeiyiGeek.Kubernets部署结果与消息通知

WeiyiGeek.Kubernets部署结果与消息通知


(2) Maven 构建之 Pipeline Script from SCM

描述: 我也可以将上面流水线的脚本放在我们的代码项目之中,在流水线拉取项目时候便会自动按照项目中的Jenkinsfile文件内容进行执行对于操作

  • Step 1.修改项目首页文件以及在项目根添加Jenkinsfile文件(内容将取消第一阶段的代码拉取),例如:
    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
    # (1) E:\EclipseProject\hello-world\src\main\webapp\index.jsp
    <h1>Maven - Hello World - v1.10 - Pipeline script for SCM</h1>

    # (2) 项目信息
    /e/EclipseProject/hello-world (master)
    $ ls
    Jenkinsfile pom.xml src/ target/

    # (3) Jenkinsfile : 注意内容将取消第一阶段的代码拉取
    pipeline {
    agent any
    stages {

    stage ("代码质检") {
    steps {
    sh label: 'sonar', returnStatus: true, script: '''/usr/local/bin/sonar-scanner -Dsonar.projectName=${JOB_NAME} -Dsonar.projectKey=Hello-World -Dsonar.sources=.'''
    }
    }

    stage ("项目构建") {
    steps {
    sh label: 'build', script: 'cd /var/lib/jenkins/workspace/maven-pipeline-helloword/ && /usr/local/maven/bin/mvn clean package -Dmaven.test.skip=true '
    }
    }

    stage ("项目部署") {
    steps {
    // 脚本为前面一章的部署脚本
    sh label: 'deploy', script: '/tmp/script/maven-jenkins-ci-script.sh'
    }
    }
    }

    // 消息通知: POST阶段当所有任务执行后触发
    post {
    // 企业微信
    always {
    qyWechatNotification aboutSend: true, failNotify: true, failSend: true, mentionedId: 'ALL', mentionedMobile: '', startBuild: true, successSend: true, unstableSend: true, webhookUrl: 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=c222f3fc-f645-440a-ad24-0ce8d9626fa0'
    }

    // 钉钉
    success {
    //当此Pipeline成功时打印消息 (注意此处的robot为您在Jenkins全局配置中设置钉钉的id值)
    echo 'success'
    dingtalk (
    robot: 'weiyigeek-1000',
    type: 'LINK',
    title: 'Jenkins 持续集成 - 任务名称 ${JOB_NAME}',
    text: [
    '项目构建成功',
    'Pipeline 流水线测试'
    ],
    messageUrl: 'http://www.weiyigeek.top',
    picUrl: 'https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2319834554,1372531032&fm=26&gp=0.jpg',
    atAll: true
    )
    }
    }
    }
  • Step 2.上传修改的项目到Gitlab内部私有仓库中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    $ git add .
    $ git commit -m "v1.10 pipeline script for SCM"
    # [master 3f9b6e8] v1.10 pipeline
    # 2 files changed, 26 insertions(+), 1 deletion(-)
    # create mode 100644 Jenkinsfile
    $ git push
    # To http://gitlab.weiyigeek.top/ci-cd/java-maven.git
    # 0f50b10..3f9b6e8 master -> master
    $ git tag -a "v1.10" -m "v1.10 Pipelinescript for SCM "
    $ git push origin v1.10
    # To http://gitlab.weiyigeek.top/ci-cd/java-maven.git
    # * [new tag] v1.10 -> v1.10
  • Step 3.任务项目流水线设置 -> 选择 Pipeline script from SCM -> git -> 输入 Repository URL 和 Credentials -> 指定分支 Branches to build (以及Jenkinsfile 拉取的文件名实现自动构建集成)

WeiyiGeek.Pipeline script from SCM

WeiyiGeek.Pipeline script from SCM

  • Step 4.项目构建参数输入 -> v1.10 | deploy -> 进行构建 -> 查看流水线
WeiyiGeek.PIPE 流水线

WeiyiGeek.PIPE 流水线

  • Step 5.查看部署结果http://10.10.107.202:30089/结果正常;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Maven - Hello World - v1.10 - Pipeline script for SCM

    访问时间: Tue Jan 26 2021 14:54:35 GMT+0800 (中国标准时间)

    Server : Apache Tomcat/8.5.61 | 10.244.1.217

    Client : 10.244.0.0 | 10.244.0.0

    Document_Root : /usr/local/tomcat/webapps/ROOT/

    URL : 10.10.107.202/index.jsp


(3) Jenkins pipeline 之 邮件(Email)发信管理

描述: 如果利用 Freestyle 的原生Job我们可以很好的进行Job邮件发信,而在与 Jenkins 流水线中需要Extended E-mail Notification的方式进行实现(此处只是简单说明建议采用钉钉或者企业微信的方式更加及时方便);

下面提供两种格式进行参考:

  • (1) Scripted Pipeline
    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
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    node ("master"){
    parameters {string(name:'Jenkins',defaultValue:'Hello',description:'How should I greet the world')}
    echo "${params.nane} 你好!"
    // gitlab 流水线通知
    gitlabCommitStatus {
    stage('第1步拉代码'){
    echo "拉代码"
    git credentialsId: '03fd8295-c536-4871-9794-1c37394676e0', url: '[email protected]:wangxu/ops.git'
    }
    stage('第2步编译'){
    echo "编译"
    // sh "source /etc/profile && /usr/local/maven/bin/mvn clean compile"
    }
    stage('第3步打包'){
    echo "打包,有一个mail模块是系统级别的,需要sudo"
    // sh "sudo /usr/local/maven/bin/mvn package"
    // echo "完成后 修改一下权限,否则下一次麻烦"
    // sh "sudo chown -R jenkins: ."
    // sh "find -name '*SNAPSHOT.jar' "
    }
    stage('第四步单元测试'){
    echo "单元测试"
    //sh "sudo /usr/local/maven/bin/mvn test"
    }
    stage("放到下载服务器上"){
    echo "发送文件"
    // sh "sudo cp ./account-email/target/account-email-1.0.0-SNAPSHOT.jar /home/admin/webserver/html/download && sudo chown -R admin: /home/admin/webserver/html/download"
    }
    }
    stage("发送邮件"){
    def git_url = '[email protected]:wangxu/ops.git'
    def branch = 'dev'
    def username = 'Jenkins'
    echo "Hello Mr.${username}"
    echo "GIT路径: ${git_url}"

    echo "发送邮件"
    emailext body: """
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>${ENV, var="JOB_NAME"}-第${BUILD_NUMBER}次构建日志</title>
    </head>
    <body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4" offset="0">
    <table width="95%" cellpadding="0" cellspacing="0" style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">
    <tr>
    <td>(本邮件由程序自动下发,请勿回复!)</td>
    </tr>
    <tr>
    <td>
    <h2><font color="#FF0000">构建结果 - ${BUILD_STATUS}</font></h2>
    </td>
    </tr>
    <tr>
    <td><br />
    <b><font color="#0B610B">构建信息</font></b>
    <hr size="2" width="100%" align="center" />
    </td>
    </tr>
    <tr><a href="${PROJECT_URL}">${PROJECT_URL}</a>
    <td>
    <ul>
    <li>项目名称:${PROJECT_NAME}</li>
    <li>GIT路径:<a href="[email protected]:wangxu/ops.git">[email protected]:wangxu/ops.git</a></li>
    <li>GIT分支: master</li>
    <li>构建编号:${BUILD_NUMBER}</li>
    <li>触发原因:${CAUSE}</li>
    <li>构建日志:<a href="${BUILD_URL}console">${BUILD_URL}console</a></li>
    </ul>
    </td>
    </tr>
    <tr>
    <td>
    <b><font color="#0B610B">变更信息:</font></b>
    <hr size="2" width="100%" align="center" />
    </td>
    </tr>
    <tr>
    <td>
    <ul>
    <li>上次构建成功后变化 : ${CHANGES_SINCE_LAST_SUCCESS}</a></li>
    </ul>
    </td>
    </tr>
    <tr>
    <td>
    <ul>
    <li>上次构建不稳定后变化 : ${CHANGES_SINCE_LAST_UNSTABLE}</a></li>
    </ul>
    </td>
    </tr>
    <tr>
    <td>
    <ul>
    <li>历史变更记录 : <a href="${PROJECT_URL}changes">${PROJECT_URL}changes</a></li>
    </ul>
    </td>
    </tr>
    <tr>
    <td>
    <ul>
    <li>变更集:${JELLY_SCRIPT,template="html"}</a></li>
    </ul>
    </td>
    </tr>
    <!--
    <tr>
    <td>
    <b><font color="#0B610B">Failed Test Results</font></b>
    <hr size="2" width="100%" align="center" />
    </td>
    </tr>
    <tr>
    <td>
    <pre style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">${FAILED_TESTS}</pre>
    <br />
    </td>
    </tr>

    <tr>
    <td>
    <b><font color="#0B610B">构建日志 (最后 100行):</font></b>
    <hr size="2" width="100%" align="center" />
    </td>
    </tr>-->
    <!-- <tr>
    <td>Test Logs (if test has ran): <a
    href="${PROJECT_URL}ws/TestResult/archive_logs/Log-Build-${BUILD_NUMBER}.zip">${PROJECT_URL}/ws/TestResult/archive_logs/Log-Build-${BUILD_NUMBER}.zip</a>
    <br />
    <br />
    </td>
    </tr> -->
    <!--
    <tr>
    <td>
    <textarea cols="80" rows="30" readonly="readonly" style="font-family: Courier New">${BUILD_LOG, maxLines=100,escapeHtml=true}</textarea>
    </td>
    </tr>-->
    <hr size="2" width="100%" align="center" />

    </table>


    </body>
    </html>
    """, subject: '$PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS!', to: '[email protected]'
    }
    }
  • (2) Declarative Pipeline
    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
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    pipeline{
    agent{label 'master'}
    environment {
    git_url = '[email protected]:wangxu/ops.git'
    git_key = '03fd8295-c536-4871-9794-1c37394676e0'
    git_branch = 'master'
    }

    stages {
    stage('下载代码') {
    steps {
    git branch: "${git_branch}",credentialsId: "${git_key}", url: "${env.git_url}"
    }
    }
    }

    post {
    //always部分 pipeline运行结果为任何状态都运行
    always{
    script{
    emailext body:
    ''' <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>${ENV, var="JOB_NAME"}-第${BUILD_NUMBER}次构建日志</title>
    </head>
    <body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4" offset="0">
    <table width="95%" cellpadding="0" cellspacing="0" style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">
    <tr>
    <td>(本邮件由程序自动下发,请勿回复!)</td>
    </tr>
    <tr>
    <td>
    <h2><font color="#FF0000">构建结果 - ${BUILD_STATUS}</font></h2>
    </td>
    </tr>
    <tr>
    <td><br />
    <b><font color="#0B610B">构建信息</font></b>
    <hr size="2" width="100%" align="center" />
    </td>
    </tr>
    <tr><a href="${PROJECT_URL}">${PROJECT_URL}</a>
    <td>
    <ul>
    <li>项目名称:${PROJECT_NAME}</li>
    <li>GIT路径:<a href="[email protected]:wangxu/ops.git">[email protected]:wangxu/ops.git</a></li>
    <li>GIT分支: master</li>
    <li>构建编号:${BUILD_NUMBER}</li>
    <li>触发原因:${CAUSE}</li>
    <li>构建日志:<a href="${BUILD_URL}console">${BUILD_URL}console</a></li>
    </ul>
    </td>
    </tr>
    <tr>
    <td>
    <b><font color="#0B610B">变更信息:</font></b>
    <hr size="2" width="100%" align="center" />
    </td>
    </tr>
    <tr>
    <td>
    <ul>
    <li>上次构建成功后变化 : ${CHANGES_SINCE_LAST_SUCCESS}</a></li>
    </ul>
    </td>
    </tr>
    <tr>
    <td>
    <ul>
    <li>上次构建不稳定后变化 : ${CHANGES_SINCE_LAST_UNSTABLE}</a></li>
    </ul>
    </td>
    </tr>
    <tr>
    <td>
    <ul>
    <li>历史变更记录 : <a href="${PROJECT_URL}changes">${PROJECT_URL}changes</a></li>
    </ul>
    </td>
    </tr>
    <tr>
    <td>
    <ul>
    <li>变更集:${JELLY_SCRIPT,template="html"}</a></li>
    </ul>
    </td>
    </tr>
    <!--
    <tr>
    <td>
    <b><font color="#0B610B">Failed Test Results</font></b>
    <hr size="2" width="100%" align="center" />
    </td>
    </tr>
    <tr>
    <td>
    <pre style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">$FAILED_TESTS</pre>
    <br />
    </td>
    </tr>

    <tr>
    <td>
    <b><font color="#0B610B">构建日志 (最后 100行):</font></b>
    <hr size="2" width="100%" align="center" />
    </td>
    </tr>-->
    <!-- <tr>
    <td>Test Logs (if test has ran): <a
    href="${PROJECT_URL}ws/TestResult/archive_logs/Log-Build-${BUILD_NUMBER}.zip">${PROJECT_URL}/ws/TestResult/archive_logs/Log-Build-${BUILD_NUMBER}.zip</a>
    <br />
    <br />
    </td>
    </tr> -->
    <!--
    <tr>
    <td>
    <textarea cols="80" rows="30" readonly="readonly" style="font-family: Courier New">${BUILD_LOG, maxLines=100,escapeHtml=true}</textarea>
    </td>
    </tr>-->
    <hr size="2" width="100%" align="center" />

    </table>


    </body>
    </html>
    ''',
    subject: '$PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS!', to: '[email protected]'
    }
    }

    success {
    //当此Pipeline成功时打印消息
    echo 'success'
    }
    failure {
    //当此Pipeline失败时打印消息
    echo 'failure'
    }
    unstable {
    //当此Pipeline 为不稳定时打印消息
    echo 'unstable'
    }
    aborted {
    //当此Pipeline 终止时打印消息
    echo 'aborted'
    }
    changed {
    //当pipeline的状态与上一次build状态不同时打印消息
    echo 'changed'
    }
    }
    }

0x02 进阶实践

(1) Sonarqube 代码质量检测之 Pipeline Script from SCM

实验需求: 拉取代码并指定Tag、采用sonarqube进行代码质量检测并进行构建

Tips : sonarQube 是sonarQube扫描插件工具, 该工具除了前面直接下载二进制执行文件到Jenkins种(登陆jenkins页面–>系统管理–>全局工具配置)进行配置; 还可以通过自动化构建工具安装,不管是maven项目还是gradle项目都提供了安装sonarQube扫描工具的插件。;

1
2
3
plugins {
id "org.sonarqube" version "2.7"
}

实验流程:

  • Step 0.按照前面的流程在Jenkins中下载并配置好SonarQube并且在SonarQube中进行URL设置
    1
    2
    3
    4
    5
    6
    7
    8
    # 关键项:
    # (1) Sonarqube 通用配置项
    Server base URL : http://sonar.weiyigeek.top:9000/

    # (2) Dashboard -> 全局配置 -> SonarQube servers
    # 环境变量允许将SonarQube服务器配置注入构建环境变量
    Server URL : http://sonar.weiyigeek.top:9000
    Server authentication token : 可以参照下面的Step2步骤
WeiyiGeek.Jenkins && Sonarqube

WeiyiGeek.Jenkins && Sonarqube

  • Step 1.并生成项目标识符名称(somarqube-test)并获取令牌somarqube-test: 4354fc222bee3c2bad2d812b087dabab943a7ab0

  • Step 2.将该Token添加到Jenkins凭据之中Dashboard -> 凭据 -> 系统 -> 全局凭据 (unrestricted)选择Secret类型文本 -> Secret(输入上面的Token) -> 描述:somarqube-test-api;

    1
    Secret text	6810ea0d-e76a-40cf-9373-5040ed6b5456	somarqube-test-api	Secret text	somarqube-test-api
WeiyiGeek.somarqube-test-api

WeiyiGeek.somarqube-test-api

  • Step 3.创建一个流水线项目somarqube-test-pipeline -> 编写Pipeline Script脚本如下(非常值得注意涵盖的知识较多)
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
pipeline {
agent any
/* 该块中的变量将写入到Linux环境变量之中作为全局变量,在shell可通过变量名访问,而在script pipeline脚本中通过env.变量名称访问. */
environment {
GITLAB_URL = '[email protected]:ci-cd/java-maven.git'
// 代码质量检测项目名称
SONARQUBE_PROJECTKEY = 'somarqube-test';
// 代码质量检测反馈超时时间
SONARQUBE_TIMEOUT = '10';
// 版本号
//RELEASE_VERSION="v1.1"
// 默认操作方式
//PREJECT_OPERATION="deploy"
}

/* 全局参数, 在shell可通过变量名访问,而在script pipeline脚本中通过params.参数名称访问. */
parameters {
// Jenkins -> 原生 Build With Parameters 支持 (BuleOcean不支持gitParameter)
// gitParameter name: 'RELEASE_VERSION',
// type: 'PT_BRANCH_TAG',
// branchFilter: 'origin/(.*)',
// defaultValue: 'master',
// selectedValue: 'DEFAULT',
// sortMode: 'DESCENDING_SMART',
// description: 'Message: 请选择部署的Tags版本?'
choice(name: 'SONARQUBE', choices: ['False','True'],description: 'Message: 是否进行代码质量检测?')
string(name: 'RELEASE_VERSION', defaultValue: "", description: 'Message: 请选择部署的Tags版本?')
choice(name: 'PREJECT_OPERATION', choices: ['deploy', 'rollback', 'redeploy'], description: 'Message: 选择项目操作方式?')
}

stages {
stage ('代码拉取') {
//注意: 此处得input不能包含在steps中并且只有局部stage块中可用调用,调用方式 ${RELEASE_VERSION} ${PREJECT_OPERATION}
// input {
// message "Title : ${JOB_NAME} 项目构建参数"
// ok "完成提交"
// parameters {
// string(name: 'RELEASE_VERSION', defaultValue: '', description: 'Message: 请选择部署的Tags版本?')
// choice(name: 'PREJECT_OPERATION', choices: ['deploy', 'rollback', 'redeploy'], description: 'Message: 选择项目操作方式?')
// }
// }
steps {
// (1) git项目拉取
git branch: "${params.RELEASE_VERSION}", credentialsId: 'b4c8b4e9-2777-44a1-a1ed-e9dc21d37f4f', url: "${env.GITLAB_URL}"

// (2) 超时时间设置 5 分钟
timeout(time: 1, unit: 'MINUTES') {
script {
// (3) 存储可用版本
def RELEASE=sh label: 'release',returnStdout: true, script: """\
git tag -l --column | tr -d ' ' > tag ;
export a="\$(sed "s#v#, v#g" tag)'";
echo [\${a#,*}]
"""

// 由于此处是groovy的变量此处得input通过script块包含只有局部stage块中可用调用(故此处不采用)
// (4) 定义操作变量(RELEASE_VERSION 与 PREJECT_OPERATION) 注意此处无法影响shell中的变量
//def RELEASE_VERSION=input message: "${JOB_NAME} 项目版本:\n ${RELEASE}", ok: '完成提交', parameters: [ string(name: 'RELEASE_VERSION', description: 'Message: 请选择部署的Tags版本?')];
//def PREJECT_OPERATION=input message: "${JOB_NAME} 项目构建参数", ok: '完成提交', parameters: [choice(name: 'PREJECT_OPERATION', choices: ['deploy', 'rollback', 'redeploy'], description: 'Message: 选择项目操作方式?')];

// (5) 输出变量与操作
// 例如: RELEASE_VERSION: v1.11 , PREJECT_OPERATION: redeploy. SONARQUBE: True
echo "RELEASE_VERSION: ${RELEASE_VERSION} , PREJECT_OPERATION: ${PREJECT_OPERATION}. SONARQUBE: ${params.SONARQUBE}"

// (6) 切换分支版本(其实上面有了branch: "${params.RELEASE_VERSION}"此处显得多余)
sh label: 'checkout_version', script: '[ -n "${RELEASE_VERSION}" ] && git checkout ${RELEASE_VERSION} || { echo -e "切换至指定的tag的版本 tag:${RELEASE_VERSION} 不存在或为空,请检查输入的tag!" && exit 1; }'
}
}
}
}

stage ('代码检测') {
// (7) 判断是否进行代码质量检测
when {
// expression { return params.SONARQUBE == "True" }
anyOf {
environment name: 'SONARQUBE', value: 'True'
environment name: 'PREJECT_OPERATION', value: 'deploy'
environment name: 'PREJECT_OPERATION', value: 'redeploy'
}
}

steps {
script {
// (8) Tool 后的名称一定是对应全局工具配置中sonar扫描器的名字进行代码质量检测
def sonarqubeScanner = tool 'sonarqubescanner';
withSonarQubeEnv(credentialsId: '6810ea0d-e76a-40cf-9373-5040ed6b5456') {
// 注意:可以将sonarQube的属性定义在这里,也可以定义在项目文件中然后在这里引用配置文件
sh "${sonarqubeScanner}/bin/sonar-scanner " +
"-Dsonar.projectName=${JOB_NAME} " +
"-Dsonar.projectKey=${SONARQUBE_PROJECTKEY} " +
"-Dsonar.projectVersion=${RELEASE_VERSION} " +
"-Dsonar.sourceEncoding=utf8 " +
"-Dsonar.sources=. " +
"-Dsonar.language=java " +
"-Dsonar.java.binaries=."
}

// (9) 暂停10s等待sonarqube扫描反馈
sh "sleep ${SONARQUBE_TIMEOUT}"
// 方式1
// timeout(1) {
// def qg = waitForQualityGate()
// if (qg.status != 'OK') {
// error "未通过Sonarqube的代码质量阈检查,请及时修改!failure: ${qg.status}"
// } else {
// echo "successful"
// }
// }

// 方式2
timeout(time: 1, unit: 'MINUTES') {
waitForQualityGate abortPipeline: true
}
}
}
}

// (10) 项目构建打包处理
stage ("项目构建") {
steps {
sh label: 'build', script: '/usr/local/maven/bin/mvn clean package -Dmaven.test.skip=true '
}
}

// (11) 进行开发测试部署
stage ("开发测试部署") {
steps {
echo "后续添加......"
}
}
}

// (12) 全部阶段完成后执行进行工作空间的清理以及消息通知;
post {
always {
echo 'One way or another, I have finished'
deleteDir() /* clean up our workspace */
}
success {
echo 'I succeeeded!'
}
unstable {
echo 'I am unstable :/'
}
failure {
echo 'I failed :('
}
changed {
echo 'Things were different before...'
}
}
}
  • Step 4.项目运行以及代码拉取结果反馈
    1
    2
    3
    4
    5
    6
    # [email protected]:ci-cd/java-maven.git— Print Message <1s
    # Git 4s
    # 查看可用标签
    # git tag -l --column | tr -d ' ' > tag ; export a="$(sed "s#v#, v#g" tag)'"; echo [${a#,*}] — release<1s
    # RELEASE_VERSION: v1.11 , PREJECT_OPERATION: deploy. SONARQUBE: True— Print Message <1s
    # [ -n "${RELEASE_VERSION}" ] && git checkout ${RELEASE_VERSION} || { echo -e "切换至指定的tag的版本 tag:${RELEASE_VERSION} 不存在或为空,请检查输入的tag!" && exit 1; }— checkout_version <1s
WeiyiGeek.项目运行以及代码拉取

WeiyiGeek.项目运行以及代码拉取

  • Step 5.代码检测阶段查看(重点), SonarQube analysis api 接口URL(http://sonar.weiyigeek.top:9000/api/ce/task?id=AXdd7NO4mUDNtrevcJJD)
    1
    2
    3
    4
    # sonarqubescanner— Use a tool from a predefined Tool Installation <1s
    # /usr/local/sonar//bin/sonar-scanner -Dsonar.projectName=somarqube-test-pipeline -Dsonar.projectKey=somarqube-test -Dsonar.projectVersion=v1.11 -Dsonar.sourceEncoding=utf8 -Dsonar.sources=. -Dsonar.language=java -Dsonar.java.binaries=.— Shell Script7s
    # sleep 10 — Shell Script10s
    # true— Wait for SonarQube analysis to be completed and return quality gate status <1s
WeiyiGeek.代码检测阶段查

WeiyiGeek.代码检测阶段查

  • Step 6.综合效果
WeiyiGeek.最终效果

WeiyiGeek.最终效果


(2) Gitlab 自动触发构建之 Pipeline Script from SCM

实验需求:Gitlab 上传自动触发Jenkins构建并通过BlueOcan进行控制构建, 以及与 Gitlab 流水线状态同步

实验流程:

  • Step 1.此处假设您已安装配置Gitlab Authentication plugin、GitLab Plugin这两个插件
  • Step 2.到 Gitlab私有仓库中进行生成项目API Access Token -> 用户设置 -> 访问令牌 -> 输入您的应用程序的名称 -> 选择相应到期时间 -> 范围: 授予对API的完全读/写访问权,包括所有组和项目、容器注册表和包注册表 -> 然后创建个人访问令牌;
  • Step 3.得到api Token(kWL_9Fw_nvbxTkpDb9X6)后在Jenkins中添加全局凭据 -> Dashboard -> 凭据 -> 系统 -> 全局凭据 (unrestricted) -> 选择凭据类型为Gitlab API Token -> 然后确定即可 -> 他会自动生成一个类似于UUID的一个字符串

    1
    2
    # Value
    f49076a8-11e4-4351-a88a-143cfab6555b GitLab API token (gitlab_admin_api) GitLab API token gitlab_admin
    WeiyiGeek.Gitlab-API-Token

    WeiyiGeek.Gitlab-API-Token

  • Step 4.或者直接在全局配置的 Gitlab -> Enable authentication for ‘/project’ end-point -> 应用保存;

WeiyiGeek.Gitlab-Token-配置

WeiyiGeek.Gitlab-Token-配置

  • Step 5.在Dashboard -> Gitlab-Pipeline Job 中 -> 构建触发器 -> 勾选Build when a change is pushed to GitLab. -> 重新生成打开的合并请求为On push to source branch -> Comment (regex) for triggering a build 可以在提交Jenkins build字符串进行触发构建编译;
  • Step 6.Jenkins 生成 Api Token -> 面板 _> 用户设置 -> API Token 生成 (APl令牌提供了一种进行经过身份验证的CLI或REST API调用的方法。注意每六个月需要重新生成一次) 11112e147020668570e571fa438439cc60
    Tips: 每次重新启动Jenkins时,未使用的遗留令牌的创建日期将被重置,这意味着日期可能不准确。
WeiyiGeek.Jenkins-API-Token

WeiyiGeek.Jenkins-API-Token

  • Step 7.在Gitlab -> java-maven 项目 -> 设置 -> WebHooks -> 地址为是前面Build when a change is pushed to GitLab. GitLab webhook URL中的地址 http://jenkins.weiyigeek.top:8080/project/Gitlab-Pipeline-> 输入 Secret Token -> 选择Push events Trigger -> add Webhook -> 最后进行选择push events测试 -> Hook executed successfully: HTTP 200
    • 注意事项:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      #补充说明
      Gitlab Project -> helloworld -> Webhook设置

      # Gitlab 设置
      URL :Jenkins Job
      Secret Token
      Trigger
      Push events : This URL will be triggered by a push to the repository (每次提交都将触发-如不需要则不要勾选即可)
      Tag push events : This URL will be triggered when a new tag is pushed to the repository (非常建议设置tag后才会触发) - 一般开启该事件选项后不建议开启Push evnts
      Comments : This URL will be triggered when someone adds a comment (评论触发)

      # 成功 > 控制台输出 #32
      # 2021-2-23 上午11:38 CST
      # Started by GitLab push by Gitlab WeiyiGeek
WeiyiGeek.GitLab webhook URL

WeiyiGeek.GitLab webhook URL

Tips : 此处需要设置允许来自钩子和服务的对本地网络的请求。
Tips : 注意请根据您的Jenkins站点启用SSL(建议内网也需要注意的)

  • Step 8.此处先使用Pipeine Script脚本然后应用保存然后上传v1.11版本到Gitlab,查看是否自动触发Build;
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
pipeline {
agent any
// 全局选项
options {
gitLabConnection('Private-Gitlab')
gitlabBuilds(builds: ['代码拉取', '代码检测', '项目构建','测试部署','成品归档'])
timeout
}

environment {
GITLAB_URL = '[email protected]:ci-cd/java-maven.git'
// 代码质量检测项目名称
SONARQUBE_PROJECTKEY = 'Hello-World';
// 代码质量检测反馈超时时间
SONARQUBE_TIMEOUT = '10';
// 版本号
//RELEASE_VERSION="v1.1"
// 默认操作方式
//PREJECT_OPERATION="deploy"
// 企业微信WebHookURL
QYWX_WEBHOOK = 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=c222f3fc-f645-440a-ad24-0ce8d9626fa0'
}

/* 全局参数, 在shell可通过变量名访问,而在script pipeline脚本中通过params.参数名称访问. */
parameters {
string(name: 'RELEASE_VERSION', defaultValue: "master", description: 'Message: 请选择部署的Tags版本?',trim: 'True')
choice(name: 'PREJECT_OPERATION', choices: ['deploy', 'rollback', 'redeploy'], description: 'Message: 选择项目操作方式?')
choice(name: 'SONARQUBE', choices: ['False','True'],description: 'Message: 是否进行代码质量检测?')
}

triggers {
gitlab(triggerOnPush: true, triggerOnMergeRequest: true, branchFilterType: 'All')
}

stages {
stage ('代码拉取') {
steps {
// (1) git项目拉取
echo "${env.GITLAB_URL} -- ${params.RELEASE_VERSION}"
// (2) 超时时间设置 5 分钟
timeout(time: 1, unit: 'MINUTES') {
script {
try {
echo "${env.GITLAB_URL} -- ${params.RELEASE_VERSION}"

// 方式1: git branch: 'v1.11', credentialsId: '43287e62-ce5b-489a-9c11-cedf38e16e92', url: "${env.GITLAB_URL}"
// 方式2:
checkout([$class: 'GitSCM', branches: [[name: "${params.RELEASE_VERSION}"]], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'b4c8b4e9-2777-44a1-a1ed-e9dc21d37f4f', url: "${env.GITLAB_URL}"]]])
updateGitlabCommitStatus name: '代码拉取', state: 'success'
} catch(Exception err) {
updateGitlabCommitStatus name: '代码拉取', state: 'failed'
// 显示错误信息但还是会进行下一阶段操作
echo err.toString() /* hudson.AbortException: Couldn't find any revision to build. Verify the repository and branch configuration for this job. */
unstable '拉取代码失败'
// 构建停止
error "[-Error] : 代码拉取失败\n [-Msg] : ${err.getMessage()} "

}
// (3) 存储可用版本
def RELEASE=sh label: 'release',returnStdout: true, script: """\
git tag -l --column | tr -d ' ' > tag ;
export a="\$(sed "s#v#, v#g" tag)'";
echo [\${a#,*}]
"""

// (4) 输出变量与操作
// 例如: RELEASE_VERSION: v1.11 , PREJECT_OPERATION: redeploy. SONARQUBE: True
echo "RELEASE_VERSION: ${params.RELEASE_VERSION} , PREJECT_OPERATION: ${params.PREJECT_OPERATION}. SONARQUBE: ${params.SONARQUBE}"

// (5) 切换数据库版本
//sh label: 'checkout_version', script: '[ -n "${RELEASE_VERSION}" ] && git checkout ${RELEASE_VERSION} || { echo -e "切换至指定的tag的版本 tag:${RELEASE_VERSION} 不存在或为空,请检查输入的tag!" && exit 1; }'
sh "git branch"
}
}
}
}

stage ('代码检测') {
// (6) 判断是否进行代码质量检测
when {
expression { return params.PREJECT_OPERATION != "rollback" }
anyOf {
environment name: 'SONARQUBE', value: 'True'
environment name: 'PREJECT_OPERATION', value: 'deploy'
environment name: 'PREJECT_OPERATION', value: 'redeploy'
}
}

steps {
script {

// (7) Tool 后的名称一定是对应全局工具配置中sonar扫描器的名字进行代码质量检测
def sonarqubeScanner = tool 'sonarqubescanner';
withSonarQubeEnv(credentialsId: '6810ea0d-e76a-40cf-9373-5040ed6b5456') {
// 注意:可以将sonarQube的属性定义在这里,也可以定义在项目文件中然后在这里引用配置文件
sh "${sonarqubeScanner}/bin/sonar-scanner " +
"-Dsonar.projectName=${JOB_NAME} " +
"-Dsonar.projectKey=${SONARQUBE_PROJECTKEY} " +
"-Dsonar.projectVersion=${RELEASE_VERSION} " +
"-Dsonar.sourceEncoding=utf8 " +
"-Dsonar.sources=. " +
"-Dsonar.language=java " +
"-Dsonar.java.binaries=."
}

// (8) 暂停10s等待sonarqube扫描反馈
sleep time: "${env.SONARQUBE_TIMEOUT}", unit: 'NANOSECONDS'
sh "sleep ${SONARQUBE_TIMEOUT}"
// 方式1
// timeout(1) {
// def qg = waitForQualityGate()
// if (qg.status != 'OK') {
// error "未通过Sonarqube的代码质量阈检查,请及时修改!failure: ${qg.status}"
// } else {
// echo "successful"
// }
// }
try {
// 方式2
timeout(time: 1, unit: 'MINUTES') {
waitForQualityGate abortPipeline: true
}

updateGitlabCommitStatus name: '代码检测', state: 'success'


} catch(Exception err) {
updateGitlabCommitStatus name: '代码检测', state: 'failed'
// 显示错误信息但还是会进行下一阶段操作
unstable '代码检测失败'
}
}
}
}

// (10) 项目构建打包处理
stage ("项目构建") {
when {
expression { return params.PREJECT_OPERATION != "rollback" }
}
steps {
gitlabCommitStatus(connection: gitLabConnection('Private-Gitlab'),name: "项目构建") {
sh script: '/usr/local/maven/bin/mvn clean package -Dmaven.test.skip=true'
}

}
}

// (11) 进行开发测试部署
stage ("测试部署") {
steps {
script {
try {
sh label: 'deploy', script: '/tmp/script/jenkins-pipeline-gitlab-k8s.sh'
updateGitlabCommitStatus name: '测试部署', state: 'success'
}catch(Exception err) {
echo err.toString() /* hudson.AbortException: Couldn't find any revision to build. Verify the repository and branch configuration for this job. */
unstable '部署失败'
updateGitlabCommitStatus name: '测试部署', state: 'failed'

// 构建停止
error "[-Error] : 测试部署失败\n [-Msg] : ${err.getMessage()} "
}

}
}
}

stage('成品归档') {
steps {
script {
try {
archiveArtifacts artifacts: "target/*.war",fingerprint: true, followSymlinks: false, onlyIfSuccessful: true
updateGitlabCommitStatus name: '成品归档', state: 'success'
} catch (Exception err) {
updateGitlabCommitStatus name: '成品归档', state: 'failed'
echo err.toString() /* hudson.AbortException: Couldn't find any revision to build. Verify the repository and branch configuration for this job. */

}
}
}
}
}



// (12) POST阶段当所有任务执行后触发进行工作空间的清理以及消息通知;
post {
always {
echo 'One way or another, I have finished'
qyWechatNotification aboutSend: true, failNotify: true, failSend: true, mentionedMobile: '', startBuild: true, successSend: true, unstableSend: true, webhookUrl: 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=c222f3fc-f645-440a-ad24-0ce8d9626fa0'
deleteDir() /* clean up our workspace */

}
success {
echo 'I succeeeded!'
}
unstable {
echo 'I am unstable - 不稳定 :/'
}
failure {
echo 'I failed :('
}
changed {
echo 'Things were different before - 以前的情况不一样...'
}
}
}

/tmp/script/jenkins-pipeline-gitlab-k8s.sh

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
#!/bin/bash
# Description: Jenkins CI & Kubernetes & Gitlab -> Deploy or Rollback or Redeploy Java Maven Project
DATE=$(date +%Y%m%d-%H%M%S)
WAR_PATH="/nfs/data4/war"
WEBROOT_PATH="/nfs/data4/webapps"
WEB_DIR="${JOB_NAME}-${DATE}-${RELEASE_VERSION}"
WAR_DIR=" ${WAR_PATH}/${WEB_DIR}"
WAR_NAME="${WEB_DIR}.war"
K8S_MATER="[email protected]"
K8S_MATER_PORT="20211"
echo "${RELEASE_VERSION} --------------- ${PREJECT_OPERATION}"


# 部署
deploy () {
# 1.上传Maven打包的war包到master之中
scp -P ${K8S_MATER_PORT} ${WORKSPACE}/target/*.war [email protected]:${WAR_PATH}/${WAR_NAME}

# 2.解压&软链接
ssh -p ${K8S_MATER_PORT} ${K8S_MATER} "unzip ${WAR_PATH}/${WAR_NAME} -d ${WAR_DIR} && \
rm -rf ${WEBROOT_PATH} && \
ln -s ${WAR_PATH}/${WEB_DIR} ${WEBROOT_PATH} && \
kubectl delete pod -l app=java-maven"
}

# 回退
rollback () {
History_Version=$(ssh -p ${K8S_MATER_PORT} ${K8S_MATER} "find ${WAR_PATH} -maxdepth 1 -type d -name ${JOB_NAME}-*-${RELEASE_VERSION}")
ssh -p ${K8S_MATER_PORT} ${K8S_MATER} "rm -rf ${WEBROOT_PATH} && \
ln -s ${History_Version} ${WEBROOT_PATH} && \
kubectl delete pod -l app=java-maven"
}


# 重部署
redeploy () {
# 如果是以前部署过则删除以前部署的项目目录(包括多个文件夹)否则重新部署;
if [[ "v${GIT_COMMIT}" = "v${GIT_PREVIOUS_SUCCESSFUL_COMMIT}" ]];then
echo -e "曾经部署过 ${RELEASE_VERSION} 版本,现在正在重新部署!"
$(ssh -p ${K8S_MATER_PORT} ${K8S_MATER} "find ${WAR_PATH} -d -maxdepth 1 -type d -name ${JOB_NAME}-*-${RELEASE_VERSION} && find ${WAR_PATH} -d -maxdepth 1 -type f -name ${JOB_NAME}-*-${RELEASE_VERSION}.war")
# ssh -p ${K8S_MATER_PORT} ${K8S_MATER} "rm -rf ${History_Version}"
fi
# 物理如何都要重新部署
deploy
}

# 部署 & 回退 入口(坑-==两边没有空格)
if [[ "${PREJECT_OPERATION}" = "deploy" ]]; then
# 坑 (防止字符串为空)
# if [[ "v${GIT_COMMIT}" = "v${GIT_PREVIOUS_SUCCESSFUL_COMMIT}" ]];then
# echo -e "您已经部署过 ${RELEASE_VERSION} 版本"
# exit 1
# else
deploy
# fi
elif [[ "${PREJECT_OPERATION}" = "rollback" ]];then
rollback
elif [[ "${PREJECT_OPERATION}" = "redeploy" ]];then
redeploy
else
echo -e "无任何操作!停止执行脚本"
exit 127
fi


  • Step 9.功能分析之Git与Gitlab拉取指定分支并切换分支
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #在“源代码管理”部分中:
    1. 单击Git

    2. 输入您的存储库URL,例如[email protected]:gitlab_group/gitlab_project.git
    #在高级设置,设置名称,以origin和的Refspec到+refs/heads/*:refs/remotes/origin/* +refs/merge-requests/*/head:refs/remotes/origin/merge-requests/*

    3. 在“分支说明符”中输入:
    # 对于单存储库工作流: origin/${gitlabSourceBranch}
    # 对于分叉的存储库工作流: merge-requests/${gitlabMergeRequestIid}

    4.在其他行为中:
    # 点击添加下拉按钮
    # 从下拉列表中选择合并,然后再构建
    # 将存储库名称设置为origin
    # 将“分支”设置为合并为${gitlabTargetBranch}

    # 补充:标签时构建
    (1) 在GitLab Webhook配置中,添加“标签推送事件”
    (2) 在“源代码管理”下的作业配置中:
    1.选择“高级...”并添加“ `+refs/tags/*:refs/remotes/origin/tags/*` ”作为参考规格
    2.您还可以使用“分支说明符”来指定需要构建的标签(例如“ refs / tags / $ {TAGNAME}”示例)

简单示例:

1
2
3
4
5
6
// 方式1.
git branch: "${params.RELEASE_VERSION}", credentialsId: 'b4c8b4e9-2777-44a1-a1ed-e9dc21d37f4f', url: "${env.GITLAB_URL}"
// git branch: 'v1.11', credentialsId: '43287e62-ce5b-489a-9c11-cedf38e16e92', url: '[email protected]:ci-cd/java-maven.git'

//方式2.
checkout([$class: 'GitSCM', branches: [[name: "origin/${params.RELEASE_VERSION}"]], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'b4c8b4e9-2777-44a1-a1ed-e9dc21d37f4f', url: '${env.GITLAB_URL}']]])

  • Step 9.功能分析之 Jenkins 同步到 Gitlab 流水线之中,并且从Gitlab中可以直接进入Jenkins Job页面查看构建情况;
    1
    2
    3
    4
    # 语法参考: 包含在 steps 块之中
    gitlabCommitStatus(connection: gitLabConnection('Private-Gitlab'), name: '${JOB_NAME}') {
    sh label: 'build', script: '/usr/local/maven/bin/mvn clean package -Dmaven.test.skip=true'
    }
WeiyiGeek.jenkins与Gitlab流水线

WeiyiGeek.jenkins与Gitlab流水线

  • Step 10.功能分析之 Jenkins 中成品进行归档, 注意其路径为相对路径及其您生成的项目打包文件格式文件和Gitlab Relase 发布
    1
    2
    3
    4
    5
    6
    7
    8
    # (1) 成品归档当前路径为 ${WORKSPACE} 变量路径;
    archiveArtifacts artifacts: 'target/*.jar', fingerprint: true, followSymlinks: false, onlyIfSuccessful: true # 对于war项目则'target/*war'

    # (2) 获取归档成品的URL(注意为了后续不丢失建议采用专门的服务器进行存储或者在jenkins将此次Job编译进行完整留存)
    http://jenkins.weiyigeek.top:8080/job/Gitlab-Pipeline/187/artifact/target/hello-world.war

    # (3) 项目 -> 发布 (Release) -> 新建发布 -> 编写相关资源 -> 发布资源
    # 推荐格式: https://host/namespace/project/releases/:release/downloads/:filepath
WeiyiGeek.Jenkins归档与Release发布

WeiyiGeek.Jenkins归档与Release发布

Tips : Gitlab 项目 Release 自动发布 GitLab CI 工具: https://gitlab.com/gitlab-org/release-cli/-/blob/master/docs/index.md#usage 后面有时间再扩充;

  • Step 11.同样访问我们的K8s部署的Pod查看是否部署成功URL: http://10.10.107.202:30089/;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Maven - Hello World - v1.13 - Declarative Pipeline jobs for Gitlab WebHook Trigger

    访问时间: Tue Feb 02 2021 14:15:24 GMT+0800 (中国标准时间)

    Server : Apache Tomcat/8.5.61 | 10.244.0.245

    Client : 10.244.0.1 | 10.244.0.1

    Document_Root : /usr/local/tomcat/webapps/ROOT/

    URL : 10.10.107.202/index.jsp
WeiyiGeek.Jenkins & gitlab 自动触发

WeiyiGeek.Jenkins & gitlab 自动触发


0x03 入坑与出坑

问题1.在BlueOcean中流水线使用的输入类型不支持。请使用 经典 Jenkins 参数化构建。
问题原因: 在BlueOcean中不支持选择下拉而只支持文本参数;
文本参数:

  • git_tags 默认值 描述信息
  • deploy_option 默认值(deploy 、rollback、redeploy) 描述信息(部署&回退&重部署)
WeiyiGeek.BlueOcean输入

WeiyiGeek.BlueOcean输入


问题2.添加Webhook测试时显示Hook execution failed: URL ‘http://jenkins.weiyigeek.top:8080/project/Gitlab-Pipeline' is blocked: Requests to the local network are not allowed
错误信息:

1
Hook execution failed: URL 'http://jenkins.weiyigeek.top:8080/project/Gitlab-Pipeline' is blocked: Requests to the local network are not allowed

解决办法: 允许来自钩子和服务的对本地网络的请求。
操作流程: 管理中心 -> 设置 -> 网络 -> 勾选 允许Webhook和服务对本地网络的请求 -> 然后输入 钩子和服务可以访问的本地IP地址和域名。

WeiyiGeek.外发请求设置

WeiyiGeek.外发请求设置


问题3.Jenkinsfile 编写过程中遇到的情况以及解决办法

  • 1.字符串插值处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #设置环境变量
    environment {
    STATIC_VAR = "静态变量"
    def dynamic_var = ""
    }

    # 可选的步骤参数
    parameters {
    string(name: 'RELEASE_VERSION', defaultValue: "master", description: 'Message: 请选择部署的Tags版本?', trim: 'True')
    choice(name: 'PREJECT_OPERATION', choices: ['deploy', 'rollback', 'redeploy'], description: 'Message: 选择项目操作方式?')
    }

    #使用环境变量
    echo "${env.STATIC_VAR}"
    echo "${params.PREJECT_OPERATION}"

    #动态设置环境变量
    dynamic_var = "动态设置环境变量"
    println(dynamic_var)
  • 2.凭据处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # Secret 文本,带密码的用户名,Secret 文件
    withCredentials([string(credentialsId: 'ce228573-ad3d-40e8-a3bf-b9de510080db', variable: 'GITLAB_TOKEN')]) {
    echo GITLAB_TOKEN # println(GITLAB_TOKEN)
    }

    # 其他凭据类型
    # SSH User Private Key
    withCredentials([sshUserPrivateKey(credentialsId: '5e2f46d9-6725-4399-847f-dafb3f29d0ce', keyFileVariable: 'key', passphraseVariable: 'pass', usernameVariable: 'user')]) {
    # //SSH User Private Key
    }
    # Certificate
    withCredentials([certificate(aliasVariable: 'aliase', credentialsId: '6795a628-97a9-4a18-8dcc-1c913e6901d4', keystoreVariable: 'private', passwordVariable: 'pass')]) {
    # // some block
    }
  • 3.额外处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    # 处理故障
    try {
    sh script: "./test.sh"
    timeout(time: 1, unit: 'MINUTES') {
    waitForQualityGate abortPipeline: true
    }
    updateGitlabCommitStatus name: '代码检测', state: 'success'
    } catch(Exception err) {
    updateGitlabCommitStatus name: '代码检测', state: 'failed'
    unstable '代码检测失败'
    }

    # 使用多个代理

    # 高级脚本式流水线

    # 并行执行