目录

  • 一、简介
  • 二、Groovy
  • 2.1 HelloWorld
  • 2.2 Pipeline script from SCM
  • 三、Jenkinsfile
  • 3.1 拉取代码
  • 3.2 代码质量检测
  • 3.3 构建代码
  • 3.4 制作镜像并发布镜像仓库
  • 3.5 部署到目标服务器
  • 3.6 完整的Jenkinsfile
  • 3.7 参数配置
  • 3.8 通过参数构建
  • 四、添加邮件通知
  • 4.1 配置Jenkins邮件配置
  • 4.2 生成Pipeline语法
  • 4.3 邮件发送内容
  • 完整Jenkinsfile文件


一、简介

通过前面的文章,我们已经实现了Jenkins 自由风格部署项目,每个步骤流程都要通过不同的方式设置,并且构建过程中整体流程是不可见的,无法确认每个流程花费的时间,并且问题不方便定位问题。

Jenkins的Pipeline可以让项目的发布整体流程可视化,明确执行的阶段,可以快速的定位问题。并且整个项目的生命周期可以通过一个Jenkinsfile文件管理,而且Jenkinsfile文件是可以放在项目中维护。

所以Pipeline相对自由风格或者其他的项目风格更容易操作。
下面我们就来介绍一下使用Jenkins Pipeline构建项目。

二、Groovy

2.1 HelloWorld

使用Jenkins Pipeline部署项目,需要编写Jenkinsfile文件,Jenkinsfile文件使用groovy语言进行编写的。
下面是groovy编写的HelloWorld程序:

pipeline {
    agent any

    stages {
        stage('Hello') {
            steps {
                echo 'Hello World'
            }
        }
    }
}

jenkins pipeline for循环 并发执行sh jenkins pipeline when_jenkins

点击应用 保存,返回项目点击立即构建

jenkins pipeline for循环 并发执行sh jenkins pipeline when_Jenkins_02


jenkins pipeline for循环 并发执行sh jenkins pipeline when_ci/cd_03

2.2 Pipeline script from SCM

但是我们并不在Jenkins这里编写脚本,而是把Jenkinsfile文件放在项目里,Jenkins从Git仓库拉去代码执行Jenkinsfile文件中的脚本。
需要将Jenkins流水线定义为Pipeline script from SCM,输入Git仓库的地址。

jenkins pipeline for循环 并发执行sh jenkins pipeline when_jenkins_04

在项目中新建一个Jenkinsfile文件:

pipeline {
    agent any

    stages {
        stage('拉取Git代码') {
            steps {
                echo '拉取Git代码'
            }
        }

        stage('检测代码质量') {
            steps {
                echo '检测代码质量'
            }
        }

        stage('构建代码') {
            steps {
                echo '构建代码'
            }
        }

        stage('制作自定义镜像并发布Harbor') {
            steps {
                echo '制作自定义镜像并发布Harbor'
            }
        }

        stage('基于Harbor部署工程') {
            steps {
                echo '基于Harbor部署工程'
            }
        }
    }
}

然后通过Jenkins进行构建,可以看到每个步骤构建的过程。

三、Jenkinsfile

上面的脚本只是输出了日志,现在我们将一一实现上面的每个步骤。

jenkins pipeline for循环 并发执行sh jenkins pipeline when_Jenkins_05


Jenkins为我们提供了生成Groovy脚本的方法。

3.1 拉取代码

进入流水线语法,选择checkout,填入Git地址,点击生成流水线语法即可。

jenkins pipeline for循环 并发执行sh jenkins pipeline when_运维_06


jenkins pipeline for循环 并发执行sh jenkins pipeline when_ci/cd_07

checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[url: 'https://gitee.com/L1692312138/jenkins-demo.git']]])

我们将这个脚本直接粘贴到上面拉取Git代码的地方。

3.2 代码质量检测

执行脚本进行Sonar代码质量检测

/var/jenkins_home/sonar-scanner/bin/sonar-scanner -Dsonar.sources=./ -Dsonar.projectname=${JOB_NAME} - Dsonar.projectKey=${JOB_NAME} -Dsonar.java.binaries=target/ -Dsonar.login=31388be45653876c1f51ec02f0d478e2d9d0e1fa

Sonar可以在此处生成Token:

jenkins pipeline for循环 并发执行sh jenkins pipeline when_Jenkins_08


jenkins pipeline for循环 并发执行sh jenkins pipeline when_Jenkins_09

sh '/var/jenkins_home/sonar-scanner/bin/sonar-scanner -Dsonar.sources=./ -Dsonar.projectname=${JOB_NAME} -Dsonar.projectKey=${JOB_NAME} -Dsonar.java.binaries=target/ -Dsonar.login=7d66af4b39cfe4f52ac0a915d4c9d5c513207098'

3.3 构建代码

执行shell脚本进行Mavne打包(注意:Jenkins安装Mavne的位置)

/var/jenkins_home/apache-maven-3.8.6/bin/mvn clean package -DskipTests

jenkins pipeline for循环 并发执行sh jenkins pipeline when_jenkins_10

sh '/var/jenkins_home/apache-maven-3.8.6/bin/mvn clean package -DskipTests'

3.4 制作镜像并发布镜像仓库

cp ./target/*.jar ./deploy/
cd ./deploy
docker build -t ${JOB_NAME}:${tag} .
docker login -u ${harborUser} -p ${harborPasswd} ${harborHost}
docker tag ${JOB_NAME}:${tag} ${harborHost}/${harborRepo}/${JOB_NAME}:${tag}
docker push ${harborHost}/${harborRepo}/${JOB_NAME}:${tag}

jenkins pipeline for循环 并发执行sh jenkins pipeline when_运维_11


jenkins pipeline for循环 并发执行sh jenkins pipeline when_docker_12

sh '''cp ./target/*.jar ./deploy/
   cd ./deploy
   docker build -t ${JOB_NAME}:${tag} .'''

   sh '''docker login -u ${harborUser} -p ${harborPasswd} ${harborHost}
   docker tag ${JOB_NAME}:${tag} ${harborHost}/${harborRepo}/${JOB_NAME}:${tag}
   docker push ${harborHost}/${harborRepo}/${JOB_NAME}:${tag}'''

3.5 部署到目标服务器

连接到目标服务器,执行目标服务器准备好的deploy.sh脚本

deploy.sh $harbor_url $harbor_project_name $JOB_NAME $tag $container_port $host_port

jenkins pipeline for循环 并发执行sh jenkins pipeline when_jenkins_13


jenkins pipeline for循环 并发执行sh jenkins pipeline when_Jenkins_14

sshPublisher(publishers: [sshPublisherDesc(configName: '131', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'deploy.sh $harbor_url $harbor_project_name $JOB_NAME $tag $container_port $host_port', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])

注意:在之前的文章中已经准备了deploy.sh脚本和配置了目标服务器

3.6 完整的Jenkinsfile

pipeline {
    agent any
    environment{
        harborHost = '192.168.153.131:80'
        harborRepo = 'helloworld'
        harborUser = 'admin'
        harborPasswd = 'Harbor12345'
    }

    // 存放所有任务的合集
    stages {

        stage('Pull Code') {
            steps {
                checkout([$class: 'GitSCM', branches: [[name: '${tag}']], extensions: [], userRemoteConfigs: [[url: 'https://gitee.com/L1692312138/jenkins-demo.git']]])
            }
        }

//         stage('Sonar Scan') {
//             steps {
//                 sh '/var/jenkins_home/sonar-scanner/bin/sonar-scanner -Dsonar.sources=./ -Dsonar.projectname=${JOB_NAME} -Dsonar.projectKey=${JOB_NAME} -Dsonar.java.binaries=target/ -Dsonar.login=7d66af4b39cfe4f52ac0a915d4c9d5c513207098'
//             }
//         }

        stage('Maven Build') {
            steps {
                sh '/var/jenkins_home/apache-maven-3.8.6/bin/mvn clean package -DskipTests'
            }
        }

        stage('Push Harbor') {
            steps {
                sh '''cp ./target/*.jar ./deploy/
                cd ./deploy
                docker build -t ${JOB_NAME}:${tag} .'''

                sh '''docker login -u ${harborUser} -p ${harborPasswd} ${harborHost}
                docker tag ${JOB_NAME}:${tag} ${harborHost}/${harborRepo}/${JOB_NAME}:${tag}
                docker push ${harborHost}/${harborRepo}/${JOB_NAME}:${tag}'''
            }
        }

        stage('Publish Over SSH') {
            steps {
                sshPublisher(publishers: [sshPublisherDesc(configName: '131', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "deploy.sh $harborHost $harborRepo $JOB_NAME $tag $container_port $host_port", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            }
        }
    }
}

因为博主的机器是arm64架构,sonar-scanner不支持这个架构,所以注释了。

jenkins pipeline for循环 并发执行sh jenkins pipeline when_运维_15

3.7 参数配置

在Jenkinsfile中使用了$符号取值,我们需要配置这些变量。

  1. Git 参数 tag
  2. 字符参数 container_port
  3. 字符参数 host_port

jenkins pipeline for循环 并发执行sh jenkins pipeline when_docker_16

jenkins pipeline for循环 并发执行sh jenkins pipeline when_ci/cd_17

3.8 通过参数构建

jenkins pipeline for循环 并发执行sh jenkins pipeline when_jenkins_18

jenkins pipeline for循环 并发执行sh jenkins pipeline when_ci/cd_19

可以看到构建成功了
去Harbor也可以看到镜像已经上传

jenkins pipeline for循环 并发执行sh jenkins pipeline when_运维_20

目标服务器Docker容器也已经运行了

jenkins pipeline for循环 并发执行sh jenkins pipeline when_运维_21

Jenkins Pipeline构建成功!

四、添加邮件通知

在项目构建成功后,发送邮件通知构建结果

4.1 配置Jenkins邮件配置

下载Jenkins插件 Email Extension Plugin

jenkins pipeline for循环 并发执行sh jenkins pipeline when_Jenkins_22

进入QQ邮箱设置:

开启POP3/SMTP服务

jenkins pipeline for循环 并发执行sh jenkins pipeline when_jenkins_23


jenkins pipeline for循环 并发执行sh jenkins pipeline when_jenkins_24

邮箱

POP3服务器(端口995)

SMTP服务器(端口465或587)

qq.com

pop.qq.com

smtp.qq.com

生成授权码

jenkins pipeline for循环 并发执行sh jenkins pipeline when_Jenkins_25


在Jenkins设置中配置 系统管理员邮件地址

jenkins pipeline for循环 并发执行sh jenkins pipeline when_Jenkins_26

在Jenkins设置中配置邮件通知

jenkins pipeline for循环 并发执行sh jenkins pipeline when_ci/cd_27

点击 Test Configuration 测试配置是否成功

jenkins pipeline for循环 并发执行sh jenkins pipeline when_docker_28

4.2 生成Pipeline语法

jenkins pipeline for循环 并发执行sh jenkins pipeline when_jenkins_29


jenkins pipeline for循环 并发执行sh jenkins pipeline when_docker_30

post {
      always {
        emailext body: '${FILE,path="email.html"}', subject: '【构建通知】:$PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS!', to: 'xxxx@qq.com'
      }
    }

4.3 邮件发送内容

在项目中新建email.html文件:

<!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>
        本邮件由系统自动发出,无需回复!<br/>
        各位同事,大家好,以下为${PROJECT_NAME }项目构建信息</br>
        <td><font color="#CC0000">构建结果 - ${BUILD_STATUS}</font></td>
    </tr>
    <tr>
        <td><br />
            <b><font color="#0B610B">构建信息</font></b>
            <hr size="2" width="100%" align="center" /></td>
    </tr>
    <tr>
        <td>
            <ul>
                <li>项目名称 : ${PROJECT_NAME}</li>
                <li>构建编号 : 第${BUILD_NUMBER}次构建</li>
                <li>触发原因: ${CAUSE}</li>
                <li>构建状态: ${BUILD_STATUS}</li>
                <li>构建日志: <a href="${BUILD_URL}console">${BUILD_URL}console</a></li>
                <li>构建  Url : <a href="${BUILD_URL}">${BUILD_URL}</a></li>
                <li>工作目录 : <a href="${PROJECT_URL}ws">${PROJECT_URL}ws</a></li>
                <li>项目  Url : <a href="${PROJECT_URL}">${PROJECT_URL}</a></li>
            </ul>

            <h4><font color="#0B610B">失败用例</font></h4>
            <hr size="2" width="100%" />
            $FAILED_TESTS<br/>

            <h4><font color="#0B610B">最近提交</font></h4>
            <hr size="2" width="100%" />
            <ul>
                ${CHANGES_SINCE_LAST_SUCCESS, reverse=true, format="%c", changesFormat="<li>%d [%a] %m</li>"}
            </ul>
            详细提交: <a href="${PROJECT_URL}changes">${PROJECT_URL}changes</a><br/>

        </td>
    </tr>
</table>
</body>
</html>

jenkins pipeline for循环 并发执行sh jenkins pipeline when_Jenkins_31


jenkins pipeline for循环 并发执行sh jenkins pipeline when_docker_32

完整Jenkinsfile文件

pipeline {
    agent any
    environment{
        harborHost = '192.168.153.131:80'
        harborRepo = 'helloworld'
        harborUser = 'admin'
        harborPasswd = 'Harbor12345'
    }

    // 存放所有任务的合集
    stages {

        stage('Pull Code') {
            steps {
                checkout([$class: 'GitSCM', branches: [[name: '${tag}']], extensions: [], userRemoteConfigs: [[url: 'https://gitee.com/L1692312138/jenkins-demo.git']]])
            }
        }

//         stage('Sonar Scan') {
//             steps {
//                 sh '/var/jenkins_home/sonar-scanner/bin/sonar-scanner -Dsonar.sources=./ -Dsonar.projectname=${JOB_NAME} -Dsonar.projectKey=${JOB_NAME} -Dsonar.java.binaries=target/ -Dsonar.login=7d66af4b39cfe4f52ac0a915d4c9d5c513207098'
//             }
//         }

        stage('Maven Build') {
            steps {
                sh '/var/jenkins_home/apache-maven-3.8.6/bin/mvn clean package -DskipTests'
            }
        }

        stage('Push Harbor') {
            steps {
                sh '''cp ./target/*.jar ./deploy/
                cd ./deploy
                docker build -t ${JOB_NAME}:${tag} .'''

                sh '''docker login -u ${harborUser} -p ${harborPasswd} ${harborHost}
                docker tag ${JOB_NAME}:${tag} ${harborHost}/${harborRepo}/${JOB_NAME}:${tag}
                docker push ${harborHost}/${harborRepo}/${JOB_NAME}:${tag}'''
            }
        }

        stage('Publish Over SSH') {
            steps {
                sshPublisher(publishers: [sshPublisherDesc(configName: '131', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "deploy.sh $harborHost $harborRepo $JOB_NAME $tag $container_port $host_port", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            }
        }
    }
    post {
      always {
        emailext body: '${FILE,path="email.html"}', subject: '【构建通知】:$PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS!', to: 'liush99@foxmail.com'
      }
    }
}