Jenkins初识Pipeline之声明式-项目构建

Jenkins是Devops技术栈的核心之一。CI/CD离不开编写Pipeline脚本。 Pipeline分为 声明式、脚本式。

二者的选择

Jenkins是使用Java实现的,所以在很早的时候就引入了groovy作为DSL,管理员可以使用groovy来实现一些自动化和高级的管理功能。因为groovy引擎已经集成到Jenkins,所以在pipeline一开始很自然就使用groovy来编写Jenkinsfile。但是groovy毕竟是一种语言,对于没有太多编程经验的小白学习成本有些高,这个时候声明式的pipeline就出现了,主要是为了降低入门的难度
区别如下:

  • 声明式pipeline,官方鼓励声明式编程模型,比较适合没有编程经验的初学者.
  • 脚本式pipeline,是基于groovy的DSL语言实现的,为Jenkins用户提供了大量的灵活性性和可扩展性,如果脚本中有大量的逻辑处理则推荐使用

声明式Pipeline&多分支

构建部署模式是模仿线上环境,故使用多分支。

本文主要介绍官网主推也较为简单的声明式。 声明式官方文档参考: https://www.jenkins.io/zh/doc/book/pipeline/syntax/
学习Jenkins的时候参考的博客,力推讲的非常好: https://wiki.eryajf.net/pages/3298.html#_1-框架介绍。

环境准备

首先安装插件 Generic Webhook Trigger ,Pipeline 安装后重启Jenkins。

配置Jenkins发邮件功能。

系统管理->系统设置->Jenkins Location

  • 首先配置系统管理员邮箱地址名称

  • 配置发送邮箱验证信息,点击最底下可以测试。

可以发送出测试邮箱就配置完毕了。为脚本中发送构建通知做准备。

配置Jenkins WebHook功能。

功能背景: 在Dev环境,开发同学会频繁在本地修改代码并提交到git,提交后就要部署到服务器上。但是每次手工点太过麻烦,而且有的开发同学对Jenkins也没有绝对的权限,运维一般也不会管dev环境。 WebHook功能代码提交后,触发git的webhook调用Jenkins完成构建。

配置Jenkins

不同Jenkins界面可能存在差异
系统管理-> 管理用户 -> 点击超级管理员(guopeihua) -> 设置 添加 Api Token
把token复制下来,在gitee上会用

系统管理->安全全局配置->跨站请求伪造保护 不勾选

配置Gitee

仓库页 -> 管理->webhook

添加webhook
http://api-bj.top:8080/jenkins/generic-webhook-trigger/invoke?token=hello-word

  • Jenkins外网ip地址
  • generic-webhook-trigger/invoke 固定的webhook触发地址
  • ?token=xxxxxx token值与jenkins项目中Pipeline的值对应,否则会触发失败。

Jenkins项目配置

项目主要分为三个文件:

  • Dockerfile # build image 使用
  • config.yml # 定义jenkins构建时使用的变量,使得jenkinsfile更加灵活
  • Jenkinsfile # 具体构建部署的逻辑步骤

Jenkins Job配置截图如下:

Dockerfile

FROM adoptopenjdk/openjdk11:alpine-jre
ADD target/spring-boot-helloworld-*-SNAPSHOT.jar /applications/spring-boot-helloworld.jar
ENTRYPOINT ["/bin/sh","-c","/opt/java/openjdk/bin/java -jar /applications/spring-boot-helloworld.jar --server.port=80"]

config.yml

GIT_URL: "https://gitee.com/guopeihua/spring-boot-hello-world.git" # git地址
DEPLOY_NAME: "hello-deploy" # 控制器名称
CONTAINER_NAME: "hello" # 容器名称
IMAGE_NAME: "img_hello"  # 镜像前缀

Jenkinsfile

这里的一些系统级别的变量尽量使用${env.BRANCH_NAME}方式调用,避免获取不到。

def project_name="${JOB_NAME}".split("/")[1]  // 多分支的项目名称
def branch_name="${JOB_NAME}".split("/")[2]  // 多分支的分支名称
def scmUrl = scm.getUserRemoteConfigs()[0].getUrl()   // 获取项目 git地址

pipeline {
    // 任意主机运行pipeline
    agent any 

    // 编译的工具
    tools {
        maven 'mvn-3.3.9'
    }

    // 定义全局变量
    environment {
        // PIPELINE_CONFIG
        // BRANCH_NAME 分支名称
        // JOB_BASE_NAME  job名称
        // JOB_NAME   job 全称
        // WORKSPACE 构建目录
        // BUILD_NUMBER 构建次数
        // BUILD_URL 构建的url 加上 consoleText 直达文本页面
        BUILD_DATE = sh (script: 'date "+%m%d"', returnStdout: true).trim()  // 获取今天的时间 月日
        IMAGE_TAG = "${env.BRANCH_NAME}.${BUILD_DATE}.${env.BUILD_NUMBER}"  // 镜像tag
        OUTPUT_URL_TEXT="${env.BUILD_URL}consoleText"                         // 文本url
    }

    triggers {
        GenericTrigger (
            // 构建时的标题
            causeString: 'Triggered by $ref',
            // 获取POST参数中的变量,key指的是变量名,通过$ref来访问对应的值,value指的是JSON匹配值(参考Jmeter的JSON提取器)
            // ref指的是推送的分支,格式如:refs/heads/master
            genericVariables: [[key: 'ref', value: '$.ref'],[key: 'repositoryURL', value: '$.repositoryURL'], [key: 'branch', value: '$.branch']],
            // 打印获取的变量的key-value,此处会打印如:ref=refs/heads/master
            printContributedVariables: true,
            // 打印POST传递的参数
            printPostContent: true,
            // regexpFilterExpression与regexpFilterExpression成对使用
            // 当两者相等时,会触发对应分支的构建
            regexpFilterExpression: '^refs/heads/(master|production)$',
            regexpFilterText: '$ref',
            // 与webhook中配置的token参数值一致
            token: "${project_name}"
    )
}

    // 流水线阶段
    stages {
        stage("下载代码"){
            steps{
                git credentialsId: 'gitee_admin', url: "${scmUrl}"
                script {
                    config = readYaml file: 'config.yml'  // 读取config.yml
                    IMAGE_WHOLE_NAME =   "${config.IMAGE_NAME}:${IMAGE_TAG}"  // 定义完整的镜像名称
                }
            }   
        }

        stage("编译"){
            steps{
                sh "/usr/local/maven/bin/mvn clean package -Dfile.encoding=UTF-8 -DskipTests=true;"
            }
        }

        stage("构建镜像"){
            steps {
                sh """
                  cd ${WORKSPACE}
                  docker build -t ${IMAGE_WHOLE_NAME} .  
                """
            }
        }

        stage("部署镜像"){
            steps {
                sh """
                  kubectl set image deployment/${config.DEPLOY_NAME} ${config.CONTAINER_NAME}=${IMAGE_WHOLE_NAME}
                  sleep 5;
                """
            }
        }


        stage('判断镜像触发') {
            steps {
              script {
                  _image = sh(script: "kubectl get deploy ${config.DEPLOY_NAME} -o jsonpath='{..image}'", returnStdout: true).trim()
                  if ( _image == "${IMAGE_WHOLE_NAME}" ) {
                      echo "${_image} Trigger successfully."
                      // mail bcc: '', body: "${IMAGE_WHOLE_NAME} image触发成功!", cc: '', from: 'guopeihua@ele-cloud.com', replyTo: '', subject: '镜像触发成功', to: '17611443879@163.com'
                  }else{
                      echo "${_image} Triggered failed."
                      mail bcc: '', body: "${IMAGE_WHOLE_NAME} image触发失败 退出构建!", cc: '', from: 'guopeihua@ele-cloud.com', replyTo: '', subject: '镜像触发失败', to: '17611443879@163.com'
                      sh 'exit 1'
                    }
                }
            }
        }

    }

    post{
        always{
            // 始终都会执行
            script{
                println('构建结果')
            }
        }
        success{
            mail bcc: '', body: "job ${env.JOB_NAME}第${env.BUILD_NUMBER}次构建成功! \n deploy_name: ${config.DEPLOY_NAME} \n URL: ${OUTPUT_URL_TEXT} ", cc: '', from: 'guopeihua@ele-cloud.com', replyTo: '', subject: '[构建成功]', to: '17611443879@163.com'
            script{
             println('success--构建成功!')
             echo "项目构建地址为: ${OUTPUT_URL_TEXT}"
            }
        }
        failure{
            mail bcc: '', body: "job ${env.JOB_NAME}第${env.BUILD_NUMBER}次构建失败! \n deploy_name: ${config.DEPLOY_NAME} \n URL: ${OUTPUT_URL_TEXT} ", cc: '', from: 'guopeihua@ele-cloud.com', replyTo: '', subject: '[构建失败]', to: '17611443879@163.com'
            script{
             println('failure--构建失败!')
            }
        }
        aborted{
            mail bcc: '', body: "job ${env.JOB_NAME}第${env.BUILD_NUMBER}次构建终止! \n deploy_name: ${config.DEPLOY_NAME} \n URL: ${OUTPUT_URL_TEXT}", cc: '', from: 'guopeihua@ele-cloud.com', replyTo: '', subject: '[构建终止]', to: '17611443879@163.com'
            script{
             println('aborted--构建终止!')
            }
        }
    }
}

构建的结果

Jenkins页面构建图片

触发邮件截图

Pipeline 补充 公共Jenkinsfile

整体思路: Jenkins 修改pipeline读取类型,gitee创一个库专门存放Jenkinsfile逻辑
操作如下:

config.yml 增加配置

# Jenkinsfile 存放的公共库路径
pipeline_template: docs/jenkins/Jenkinsfile
GIT_URL: "https://gitee.com/guopeihua/spring-boot-hello-world.git" # git地址
DEPLOY_NAME: "hello-deploy" # 控制器名称
CONTAINER_NAME: "hello" # 容器名称
IMAGE_NAME: "img_hello"  # 镜像前缀

Jenkinsfile gitee路径如下:

Jenkins Job配置

其他配置不变

posted @ 2021-12-04 19:13  郭培华  阅读(396)  评论(0)    收藏  举报