BlueOcean实战多分支pipeline构建(Jenkins)

把jenkinsfile的语法给你讲的明明白白!!!

Posted by Naah on Sunday, Dec 22,2019 14:52:00

最近,将公司的CI/CD从jenkins传统配置更改成了jenkins最新的声明式流水线(Declarative Pipeline),这样就可以使用jenkins的清爽的Pipeline界面Blue Ocean,向jenkins的老旧繁杂界面say goodbye!

之前看网上写的一些文章,这给我读的,脑瓜子疼!明明一个很简单的东西,要搞的那么复杂,害得我踩坑踩了很久!!!

在这里记录下,也给广大码农同胞一条捷径吧!

1 环境准备

  1. jenkins
  2. git
  3. vscode(可选,主要用jenkins代码高亮,代码提示,校验插件)

1.1 配置jenkins插件镜像源

这里我不得不说一句,清华牛B!

因为清华的镜像源真的是无所不有!竟然连jenkins的插件镜像源都有,着实惊艳了我!

如果你不替换镜像源,可能会导致Blue Ocean安装失败

  1. 进入jenkins的系统管理
  2. 进入插件管理
  3. 点击高级
  4. 在下方的升级站点URL中输入
https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json
  1. 点击提交
  2. 重启jenkins

1.2 安装Blue Ocean

  1. 进入jenkins的系统管理
  2. 进入插件管理
  3. 点击可选插件
  4. 搜索Blue Ocean
  5. 安装

2 Declarative Pipeline语法

在这里,我将说明一些常用的标签块,基本可以满足大家大部分的构建场景,剩下的冷门的大家可以在BlueOcean中编辑或去官网查看

jenkins pipeline语法中文文档

默认所有二级标签块是无序的

2.1 pipeline(一级)

jenkinsfile的最外层,所有的步骤都要写在这个块里面 pipeline文档

pipeline{
}

2.2 agent(二级)

指定jenkins的执行节点,默认就是any就可以了 agent文档

pipeline{
    agent any
}

2.3 environment(二级)

就看这个名,你还能不知道是干啥的?环境变量!!!一个非常有用的东西,贯穿整个pipeline

使用的时候通过这种格式使用:$变量名 environment文档

pipeline{
    environment{
        // 下方的变量都是举例说明,用户可以自定义
        // 版本,只改这个就行!!!
        version = '0.1'
    
        // docker镜像名
        docker_app_name = 'demo'
        // docker私库地址
        docker_base_url = '192.168.1.100:1234'
        // docker路径
        docker_base_path = 'service'
    }
}

2.4 tools(二级)

这个标签块就是用来声明你在jenkins中安装的插件的

例如下方就是用来声明jenkins中配置的maven插接件 tools文档

pipeline{
    tools {
        // 两个单引号之间放你在插件中配置的name
        // 插件 '配置中的插件名'
        maven 'maven3.6.1'
    }
}

2.5 stages(二级)

真正由你来自定义的构建流程的标签块,之后的stage和``就放在这个代码块里的

其中要包括至少一个stage stages文档

pipeline{
    stages {
        stage('Maven Build'){
        }
    }
}

2.5.1 stage(三级及以下)

终于进入这个高潮阶段了!我们将在这里定义我们的构建流程!

定义至少一个stage stage文档

pipeline{
    stages {
        // 括号内是我们给这个流程定义一个名字
        stage('Maven Build'){
            steps {
                // 打印 不要跟linux的sh混淆 跟linux的sh并不通用
                echo 'Maven Building....'
            }
        }
        stage('Test'){
            steps {
                echo 'Testing....'
            }
        }
        stage('Sonar Check'){
            steps {
                echo 'Sonar Checking....'
            }
        }
        stage('Docker Build'){
            steps {
                echo 'Docker Building....'
            }
        }
        stage('Rancher Deploy'){
            steps {
                echo 'Rancher Deploying....'
            }
        }
    }
}

2.5.2 steps(四级及以下)

这个标签快就是用来让我们在流程中编写执行的步骤

定义至少一个steps steps文档

pipeline{
    stages {
        stage('Maven Build'){
            // 步骤,用来定义流程内的具体实现
            steps {
                // 打印
                echo 'Maven Building1....'
            }
            steps {
                // 打印
                echo 'Maven Building2....'
            }
        }
    }
}

2.5.3 when(四级及以下)

这个标签块跟我们写程序时用的if异曲同工!用来控制该节点是否运行

该标签块有几个条件,我们这里以branch举例, 其他的可以去看when文档

pipeline{
    stages {
        stage('Maven Build'){
            when {
                // 当分支是develop时执行
                branch 'develop'
            }
            steps {
                echo 'Maven Building....'
            }
        }
        stage('Test'){
            when {
                // 当分支是test时执行
                branch 'test'
            }
            steps {
                echo 'Testing....'
            }
        }
    }
}

2.5.4 parallel(四级及以下)

这个标签块很迷惑人,叫做并行

当我第一次看到这个语法的时候,没有在意,以为是多线程的概念,然而我错了!

这个标签块可以配合when来达到多环境同流程不同构建的目的 parallel文档

pipeline{
    stages {
        stage('Docker Build') {
            parallel {
                stage('Dev') {
                    when {
                        branch 'develop'
                    }
                    steps {
                        echo 'Dev Building....'
                    }
                }
                stage('Test') {
                    when {
                        branch 'test'
                    }
                    steps {
                        echo 'Test Building....'
                    }
                }
            }
         }
        stage('Rancher Deploy'){
            echo 'Rancher Deploying....'
        }
    }
}

2.5.5 post(二级)

这个标签块就是jenkins中原来的构建后执行,大家可以自由发挥,我这里主要用来钉钉通知

这里我以成功失败总是举例,还有其他条件,大家可以去看post文档

pipeline{
    post {
      failure {
        echo '构建失败'
        script{
          jenkins_projectname=env.JOB_NAME.substring(0,env.JOB_NAME.indexOf('/'))
          log_url=env.JENKINS_URL+"blue/organizations/jenkins/"+jenkins_projectname+"/detail/"+env.BRANCH_NAME+"/"+env.BUILD_NUMBER+"/pipeline#"
        }
        echo "${log_url}"
        dingTalk(accessToken: "$jenkins_dingding_token", imageUrl: 'http://www.iconsdb.com/icons/preview/soylent-red/x-mark-3-xxl.png',
        message: "构建失败", jenkinsUrl: "${log_url}")
      }
    
      success {
        echo '构建成功'
        script{
          jenkins_projectname=env.JOB_NAME.substring(0,env.JOB_NAME.indexOf('/'))
          log_url=env.JENKINS_URL+"blue/organizations/jenkins/"+jenkins_projectname+"/detail/"+env.BRANCH_NAME+"/"+env.BUILD_NUMBER+"/pipeline#"
        }
        dingTalk(accessToken: "$jenkins_dingding_token", imageUrl: 'http://icons.iconarchive.com/icons/paomedia/small-n-flat/1024/sign-check-icon.png',
        message: "构建成功",jenkinsUrl: "${log_url}")
      }
 
      always {
        echo '开始构建'
        script{
          jenkins_projectname=env.JOB_NAME.substring(0,env.JOB_NAME.indexOf('/'))
          log_url=env.JENKINS_URL+"blue/organizations/jenkins/"+jenkins_projectname+"/detail/"+env.BRANCH_NAME+"/"+env.BUILD_NUMBER+"/pipeline#"
        }
        dingTalk(accessToken: "$jenkins_dingding_token", imageUrl: 'http://icon-park.com/imagefiles/loading7_gray.gif',
         message: "开始构建", jenkinsUrl: "${log_url}")
      }
    }

}

3 构建CI/CD流程

3.1 编写Jenkinsfile

在我们的项目根目录下创建Jenkinsfile文件

下面的代码包含,maven打包sonar检查检查钉钉通知docker打包RancherWebhook部署钉钉构建通知

这些在我的文章里有讲,感兴趣可以去看看

  1. sonar检查
  2. RancherWebhook
  3. jenkins钉钉通知

Jenkinsfile 举例:

pipeline {
  agent any
  environment {
    //版本,只改这个就行!!!
    version = '0.1'

    //docker镜像名
    docker_app_name = 'demo'
    //docker私库地址
    docker_base_url = '192.168.1.100:1234'
    //docker路径
    docker_base_path = 'service'
    //docker线上地址
    docker_ali_base_url = 'registry.cn-hangzhou.aliyuncs.com'
    //docker线上路径
    docker_ali_base_path = 'demo-service'

    //sonar_scanner的home路径
    sonar_scanner_home = tool name: 'SonarScanner4'
    //sonar project名
    sonar_app_name = 'demo'
    //sonar服务地址
    sonar_url = 'http://192.168.1.200'
    //sonar钉钉机器人token
    sonar_dingding_token = '111111111111111111111111111111111111'
    //sonar钉钉通知服务地址
    sonar_dingding_url = '192.168.1.300'
    
    
    //rancher webhook地址
    rancher_notify_url = '192.168.1.400'
    rancher_uat_notify_url = 'webhook-demo.rancher.ocom'
    //webhook配置名
    rancher_app_name = 'demo'
    //webhook配置token
    rancher_token = '2222222222222222222222222'
    //rancher命名空间
    rancher_namespace = 'default'
    //rancher的服务类型
    rancher_workload = 'deployment/demo'
    //rancher容器名
    rancher_container_name = 'demo'
    //镜像仓库类型
    rancher_repo_name = 'custom'

    //jenkins钉钉机器人token
    jenkins_dingding_token = '3333333333333333333333333'

  }

  tools {
    maven 'maven3.6.1'
  }

  stages {
    
    // 开发环境maven打包
    stage('Maven Build'){
      when {
          branch 'develop'
      }
      steps{
        sh 'mvn clean package dependency:copy-dependencies'
      }
    }

    // 开发环境sonar检查
    stage('Sonar Check') {
      when {
          branch 'develop'
      }
      steps {
        echo 'Sonar Checking....'
        withSonarQubeEnv('sonarqube') {
          sh "${sonar_scanner_home}/bin/sonar-scanner -Dsonar.projectVersion=$version"
        }

        // sonar的钉钉通知
        sh(label: 'Sonar DingDing Notify', script:
        '''
        #!/bin/bash
        sonarreport=$(curl -s http://$sonar_dingding_url?projectname=$sonar_app_name)
        message_url="http://icons.iconarchive.com/icons/paomedia/small-n-flat/1024/sign-check-icon.png"
        strB="ERROR"
        case $sonarreport in
            *"$strB"*) message_url=\"http://www.iconsdb.com/icons/preview/soylent-red/x-mark-3-xxl.png\" ;;
            *) message_url=\"http://icons.iconarchive.com/icons/paomedia/small-n-flat/1024/sign-check-icon.png\" ;;
        esac

        echo $message_url
        curl -s "https://oapi.dingtalk.com/robot/send?access_token=$sonar_dingding_token" \\
           -H "Content-Type: application/json" \\
           -d "{
                \\"msgtype\\": \\"link\\",
                 \\"link\\": {
                          \\"title\\":\\"sonarqube代码质量报告:$sonar_app_name\\",
                          \\"text\\": \\"$sonarreport\\",
                          \\"picUrl\\": \\"$message_url\\",
                          \\"messageUrl\\":\\"$sonar_url/dashboard?id=$sonar_app_name\\"
                }
            }"
        '''
        )
      }
    }
    
    
    stage('Docker Build') {
      parallel {
      
        //开发环境构建docker
        stage('Dev') {
          when {
              branch 'develop'
          }
          steps {
            echo 'Docker Building....'
            sh(label: 'Docker Build', script:
            '''
            docker build -t $docker_base_url/$docker_base_path/$docker_app_name:$version .
            docker push $docker_base_url/$docker_base_path/$docker_app_name:$version
            '''
            )
          }
        }
        
        //测试环境构建docker
        stage('Test') {
          when {
              branch 'test'
          }
          steps {
            echo 'Docker Building....'
            sh(label: 'Docker Build', script:
            '''
            docker pull $docker_base_url/$docker_base_path/$docker_app_name:$version
            docker tag $docker_base_url/$docker_base_path/$docker_app_name:$version $docker_ali_base_url/$docker_ali_base_path/$docker_app_name:$version
            docker push $docker_ali_base_url/$docker_ali_base_path/$docker_app_name:$version
            '''
            )
          }
        }
      }
    }

    stage('Test') {
      steps {
        echo 'Testing..'
      }
    }
    stage('Rancher Deploy') {
      parallel {
        // 开发环境部署
        stage('Dev') {
          when {
              branch 'develop'
          }
          
          // rancher webhook
          steps {
            echo 'Rancher Deploying....'
            sh(label: 'Rancher Deploy', script:
            '''
            #!/bin/bash
            curl -X POST "http://$rancher_notify_url/hooks/$rancher_app_name?token=$rancher_token&ns=$rancher_namespace&workload=$rancher_workload&container=$rancher_container_name&repo_type=$rancher_repo_name" -H \'Content-Type: application/json\' -H \'cache-control: no-cache\' \\
              -d "{
                \\"push_data\\": {
                    \\"tag\\": \\"$version\\"
                },
                \\"repository\\": {
                    \\"repo_url\\": \\"$docker_base_url\\",
                    \\"name\\": \\"$docker_app_name\\",
                    \\"namespace\\": \\"$docker_base_path\\"
                }
              }"
             '''
             )
          }
        }

        //测试环境部署
        stage('Test') {
          when {
              branch 'test'
          }
          steps {
            echo 'Rancher Deploying....'
            sh(label: 'Rancher Deploy', script:
            '''
            #!/bin/bash
            curl -X POST "http://$rancher_uat_notify_url/hooks/$rancher_app_name?token=$rancher_token&ns=$rancher_namespace&workload=$rancher_workload&container=$rancher_container_name&repo_type=$rancher_repo_name" -H \'Content-Type: application/json\' -H \'cache-control: no-cache\' \\
              -d "{
                \\"push_data\\": {
                    \\"tag\\": \\"$version\\"
                },
                \\"repository\\": {
                    \\"repo_url\\": \\"$docker_ali_base_url\\",
                    \\"name\\": \\"$docker_app_name\\",
                    \\"namespace\\": \\"$docker_ali_base_path\\"
                }
              }"
             '''
             )
          }
        }
      }
    }

  }
  
  post {
    failure {
      echo '构建失败'
      dingTalk(accessToken: "$jenkins_dingding_token", imageUrl: 'http://www.iconsdb.com/icons/preview/soylent-red/x-mark-3-xxl.png',
      message: "构建失败", jenkinsUrl: "${env.BUILD_URL}")
    }

//     success {
//       echo '构建成功'
//       dingTalk(accessToken: "$jenkins_dingding_token", imageUrl: 'http://icons.iconarchive.com/icons/paomedia/small-n-flat/1024/sign-check-icon.png',
//       message: "项目[${env.JOB_NAME}#${env.BUILD_NUMBER}]构建成功",jenkinsUrl: "${env.BUILD_URL}")
//     }

//     always {
//       echo '开始构建'
//       dingTalk(accessToken: "$jenkins_dingding_token", imageUrl: 'http://icon-park.com/imagefiles/loading7_gray.gif',
//        message: "项目[${env.JOB_NAME}#${env.BUILD_NUMBER}]开始构建", jenkinsUrl: "${env.BUILD_URL}")
//     }
  }
}

3.2 Jenkinsfile的高亮、提示和校验

这个点,我将在另一篇文章中进行说明Jenkinsfile的高亮、提示和校验

3.3 创建PipeLine

1.进入jenkins

2.点击左侧的Blue Ocean

3.点击Create a new Pipeline

4.输入git地址等信息,创建项目

5.pipline就会自动构建,你可以点进去看看呦!

下面是网上拿的图进行简单展示