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用户提供了大量的灵活性性和可扩展性,如果脚本中有大量的逻辑处理则推荐使用

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

声明式Pipeline&多分支

构建部署模式是模仿线上环境,故使用多分支。
主要分为三个文件:

  • 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}方式调用,避免获取不到。
测试了下BRANCH_NAME这个变量,在多分支中好像才能获取到对应的分支。 -- 先遗留这个细节问题,反正多分支没事。

node { 
    config = readYaml file: 'config.yml' 
    // 读取config.yml中的配置,调用方式 config.name 获取值
} 

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
        IMAGE_WHOLE_NAME = "${config.IMAGE_NAME}:${IMAGE_TAG}"      // 完整的镜像名称
        OUTPUT_URL_TEXT="${env.BUILD_URL}consoleText"                         // 文本url
    }

    // 流水线阶段
    stages {

        stage("下载代码"){
            steps{
              git credentialsId: 'gitee_admin', url: "${config.GIT_URL}"
            }   
        }

        stage("编译"){
            steps{
                sh "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('构建结果')
                echo "${env.PIPELINE_CONFIG}"
            }
        }
        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页面构建图片

触发邮件截图