day11 Jenkins Pipeline语法-Jenkins基于Gitlab的授权认证 (4.3.1-4.4)

发布时间 2023-12-04 21:13:21作者: ikubernetesi

一、Jenkins Pipeline语法上

Jenkins Pipeline 语法

Jenkins 有多种方式实现交付流水线。其中,Jenkins Pipeline 是一种比较流行的方式,它提供一个DSL(Domain Specific Language的缩写,)来描述交付流水线。

官网地址:https://www.jenkins.io/doc/book/pipeline/syntax/

1、什么是Jenkins Pipeline

Jenkins Pipeline 是一种基于Groovy编写的DSL,它可以描述交付流水线。Pipeline 支持串行和并行执行,可以将多个任务组合成一个流水线。Pipeline 支持将上下文传递给不同的阶段,是的阶段之间的数据共享变得更加容器。

Pipeline提供三种编写Pipeline的方式

  • Declarative Pipeline:基于YAML 编写的声明式语言,可以曾容易描述流水线。
  • Scripted Pipeline: 基于Groovy编写的脚本语言,它是一种灵活的方式描述交付流水线。
  • Jenkinsfile:是一种将Pipeline 脚本保存为Jenkins源代码挂你系统中的文件。

2、Declarative Pipeline(声明式)流水线

  • 最外层必须由pipline{do something} 来进行包裹
  • 不需要分号作为分隔符,每个语句必须在一行内
  • 不能直接使用groovy语句(例如循环判断等),需要被script{}包裹

下面是一个简单的Pipeline脚本示例

pipeline{   // 最外层必须由pipeline包裹
    agent any // agent表示再哪个节点执行
    stages{
        stage("build"){
            steps{ // 具体执行步骤
                echo "Build..."
            }
        }
        stage("test"){
            steps{
                echo "Test..."
            }
        }
        stage("depoly"){
            steps{
                echo "Deployment..."
            }
        }
    }
    post{ // 最后执行
        success{ // 测试成功时执行(需要安装 Email Extension 插件)
            emailext body: 'Build succeeded!', subject: 'Build Success', to: 'xx@example.com'
        }
        failure{ // 失败时会执行
            emailext body: 'Build failed!', subject: 'Build Failure', to: 'xx@example.com'
        }
    }
}

在示例中,我们使用三个阶段:build、test 和deploy

每个阶段都是一个stage 块。在每个阶段中,我们使用Jenkins提供的一些API执行任务,例如sh命令来执行shell脚本或者jenkins提供的其他插件

2.3 声明时核心概念

  1. pipline:声明其内如为一个声明式的pipeline 脚本
  2. agent:执行节点(job运行的slave 或者master节点)
  3. stages:阶段组合,包裹所有的阶段(例如:打包,部署等各个阶段)
  4. stage:阶段,被stages包裹,一个stages 可以有多个stage
  5. steps:步骤,为每个阶段的最小执行单元,被stage包裹
  6. post:执行构建后的操作,根据构建结果执行对应的操作

2.2.1 pipeline

作用域:应用于全局最外层,表明脚本为声明式pipeline
是否必须:必须
参数:无

2.2.2 agent

作用域:可用在全局与stage内
是否必须:是
参数:any、node、label、node、docker、dockerfile

参考示例
//运行在任意的节点上
agent any

//全局不指定运行节点,由各自stage来决定
agent none

//运行在指定标签的机器上,具体标签名称由agent 配置决定
agent {label 'master'}

//node参数可以扩展节点信息
agent { 
     node {
         label 'master'
         customWorkspace 'xxx'
    } 
}

//使用指定运行的容器
pipeline{
   agent none
   stages{
       stage('build Test'){
              agent {docker 'maven:3-alpine'}
              steps{
                   echo "Build Test"
                   }     
                }
       stage('Example Test'){
              agent {docker 'openjdk:8-jre'}
              steps{
                  echo "Exmaple Test"
                }
             }
      

2.3 stages

作用域:全局或者stage阶段内,每个作用域内只能使用一次
是否必须:全局必须
参数:无

参考示例:
pipeline{
    agent any
    stages{
        stage("first stage"){
            stages{  //嵌套在stage里
                stage("inside"){
                    steps{
                        echo "inside"
                    }
                }
                stage("inside_two"){
                    steps{
                        echo "inside_two"
                    }
                }
            }
        }
        stage("stage2"){
            steps{
                echo "outside"
            }
        }
    }
}

2.2.4 stage

作用域:被stages 包裹,作用在自己的stage包裹范文内
是否必须:必须
参数:需要一个string 参数,表示此阶段的工作内容
备注:stage内部可以嵌套stages,内部可单独制定运行的agent

2.2.5 steps

作用域:被stage 包裹,作用在stage内部
是否必须:必须
参数:无

2.2.6 post (可选)

作用域:作用在pipeline借宿或者stage结束后
条件:always、changed、failure、success、unstable、aborted

2.2.7 parameters (可选)

  • 构建用户需要提供的参数
  • 这些参数可以通过params提供给流水线的steps使用,有 **字符串** 类型和 **boolean** 类型
      **string**:字符串类型,`parameters { string(name: ‘DEPLOY_ENV’, defaultValue: ‘staging’, description: ‘’) }`
      **booleanParam**:布尔参数,`parameters { booleanParam(name: ‘DEBUG_BUILD’, defaultValue: true, description: ‘’) }`
      **text**:文本参数,包含多行 `parameters { text(name: ‘DEPLOY_TEXT’, defaultValue: ‘One\nTwo\nThree\n’, description: ‘’) }`
      **choice**:选择类型的参数,`parameters { choice(name: ‘CHOICES’, choices: [‘one’, ‘two’, ‘three’], description: ‘’) }`
      **password**:password参数,`parameters { password(name: ‘PASSWORD’, defaultValue: ‘SECRET’, description: ‘A secret password’) }`

示例:

pipeline{
    agent any
    parameters {
        string(name: 'P1', defaultValue: 'it is p1', description: 'it is p1')
        booleanParam(name: 'P2', defaultValue: true, description: 'it is p2')
    }
    stages{
        stage("stage1"){
            steps{
                echo "$P1"
                echo "$P2"
            }
        }
    }
}

2.2.8 triggers

自动化触发运行pipeline的方法

示例:每两分钟触发一次job

pipeline{
    agent any
    triggers{cron("*/2 * * * *")}
    stages{
        stage("Build Test"){
            steps{
                echo "hello world"
            }
        }
    }
}

2.2.9 inout (可选)

指令允许暂时中断 pipeline执行,等待用户输入,根据用户输入进行下一步动作
pipeline {
    agent any
    stages {
        stage('Example') {
            input {
                message "Should we continue?"
                ok "Yes, Do it."
                submitter "alice,bob"
                parameters {
                    string(name: 'PERSON', defaultValue: 'Mr Jenkins', description: 'Who should I say hello to?')
                }
            }
            steps {
                echo "Hello, ${PERSON}, nice to meet you."
            }
        }
    }
}

2.2.10  when (可选)

根据when指令的判断结果来决定是否执行后面的阶段
一个when指令至少包含一个条件,当有多个条件时,所有的子条件必须返回true,这个stage才会运行

branch:当正在构建的分支与模式给定的分支匹配时,执行这个阶段, 例如: `when { branch ‘master’ }`。注意,这只适用于多分支流水线。
environment:当指定的环境变量是给定的值时,执行这个步骤, 例如: `when { environment name: ‘DEPLOY_TO’, value: ‘production’ }`
expression:当指定的Groovy表达式评估为true时,执行这个阶段, 例如: `when { expression { return params.DEBUG_BUILD } }`
not:当嵌套条件是错误时,执行这个阶段,必须包含一个条件,例如: `when { not { branch ‘master’ } }`
allOf:当所有的嵌套条件都正确时,执行这个阶段,必须包含至少一个条件,例如: `when { allOf { branch ‘master’; environment name: ‘DEPLOY_TO’, value: ‘production’ } }`
anyOf:当至少有一个嵌套条件为真时,执行这个阶段,必须包含至少一个条件,例如: `when { anyOf { branch ‘master’; branch ‘staging’ } }`

单条件判断

pipeline{
    agent any
    parameters{
        string(name:"deploy_env",defaultValue:"test",description:"")
    }
    stages{
        stage("Build Test"){
         when{
            environment name:"deploy_env",value:"prod"
              }
        steps{
            echo "hello wrold"
        }
        }   
    }
}

build 结果:

第一次build时,deploy_env的值是 test,stage “Build Test” 被 skipped
第二次build时,参数deploy_env设置为 prod,执行 stage “Build Test”

多条件判断:

pipeline{
    agent any
    parameters{
        string(name:"deploy_env",defaultValue:"test",description:"")
        string(name:"branch",defaultValue:"test",description:"分支")
    }
    stages{
        stage("Build Test"){
            when{
                environment name:"branch",value:"master"
                environment name:"deploy_env",value:"prod"
            }
            steps{
                echo "Hello world"
            }
        }
    }
}

需满足 branch:master,deploy_env:prod 才会执行流水线。

3、Scripted Pipeline (脚本式)流水线

脚本管道和声明管道一样,是建立在底层管道子系统之上的。与Declarative不同,Scripted Pipeline实际上是一个使用Groovy构建的通用DSL。

Groovy语言提供的大多数功能都可供Scripted Pipeline的方式使用,这意味着它可以是一个非常有扩展性和灵活性的工具,可以用来编写连续交付管道。

stage('Build&Tag&Push&Deploy'){
    //把选择的项目信息转为数组
    def selectedProjects = "${project_name}".split(',')

    for(int i=0;i<selectedProjects.size();i++){
        //取出每个项目的名称
        def currentProjectName = selectedProjects[i];
        //定义镜像名称
        def imageName = "${currentProjectName}:${tag}"
                //定义newTag
                def newTag = sh(returnStdout: true,script: 'echo `date +"%Y%m%d%H%M%S"_``git describe --tags --always`').trim()
        //编译,构建本地镜像
        sh "sed -i 's#ACTIVEPROFILE#${springProfilesActive}#g' Dockerfile"
        sh "mvn clean package -Dmaven.test.skip=true dockerfile:build"
        container('docker') {
            //给镜像打标签
            sh "docker tag ${imageName} ${harbor_url}/${harbor_project_name}/${currentProjectName}:${newTag}"
            //登录Harbor,并上传镜像
            withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')])
            {
                //登录
                sh "docker login -u ${username} -p ${password} ${harbor_url}"
                //上传镜像
                sh "docker push ${harbor_url}/${harbor_project_name}/${currentProjectName}:${newTag}"
            }

        //删除本地镜像
        sh "docker rmi -f ${imageName}" 
        sh "docker rmi -f ${harbor_url}/${harbor_project_name}/${currentProjectName}:${newTag}"
        }
        def deploy_image_name = "${harbor_url}/${harbor_project_name}/${currentProjectName}:${newTag}"
        
        //基于Helm的方式部署到K8S 
        container('helm3') {
            withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
            sh """
                helm repo add --username=${username} --password=${password} aliharborrepo http://harbor-ali-test.xxxx.com:8088/chartrepo/sparkx
            """
            }
            withCredentials([file(credentialsId: 'b8fca5a2-8c91-4456-99aa-071723aae7fe', variable: 'KUBECONFIG')]) {
            sh """
                mkdir -p /root/.kube/ && echo $KUBECONFIG >/root/.kube/config
                echo "Helm应用配置信息确认..."
                helm upgrade --install --dry-run --debug ${currentProjectName} --namespace devops aliharborrepo/javaAliTest \
                    --set replicaCount=${replicas} \
                    --set image.repository=${deploy_image_name} \
                    --set service.type=ClusterIP \
                    --set springActive=${springProfilesActive} \
                    --set ingress.enabled=${isIngress}
                echo "应用部署..."
                helm upgrade --install ${currentProjectName} --namespace devops aliharborrepo/javaAliTest \
                    --set replicaCount=${replicas} \
                    --set image.repository=${deploy_image_name} \
                    --set service.type=ClusterIP \
                    --set springActive=${springProfilesActive} \
                    --set ingress.enabled=${isIngress}
                """
            }
        }
    }
}

 

二、Jenkins Pipeline语法下

4、Declarative pipeline和Scripted pipeline的比较

共同点:

两者都是pipeline代码的持久实现,都能够使用pipeline内置的插件或者插件提供的steps,两者都可以利用共享库扩展。

区别:

两者不同之处在于语法和灵活性。
Declarative pipeline:对用户来说,语法更严格,有固定的组织结构,容易生成代码段,使其成为用户更理想的选择。
Scripted pipeline:更加灵活,因为Groovy本身只能对结构和语法进行限制,对于更复杂的pipeline来说,用户可以根据自己的业务进行灵活的实现和扩展。

 

5、交付流水线性能

随着交付流水线的复杂度越来越高,需要优化交付流水线的性能成为了一个时刻需要关注的问题。
下面是一些常见的优化策略:

5.1 并行执行

使用并行执行可以大大缩短交付流水线的执行时间。Pipeline可以很容易地支持并行执行。

例如:我们可以将测试阶段并行执行

stage('Test') {
    parallel (
        "test1" : { sh 'mvn test -Dtest=Test1' },
        "test2" : { sh 'mvn test -Dtest=Test2' },
        "test3" : { sh 'mvn test -Dtest=Test3' }
    )
}

 在这个示例中,我们使用了 parallel块 来并行执行。
在parallel块内,我们定义了三个分支来执行测试。分支的名称是任意的,它们将被用作日志输出。每个分支都有自己的命令来执行测试。

5.2 缓存依赖

使用缓存可以避免在每个阶段中重新下载依赖项。

例如,如果一个项目使用Maven,我们可以在build阶段前缓存Maven仓库:

pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                script {
                    def mvnHome = tool 'Maven-3.8.2'
                    env.M2_HOME = mvnHome
                    sh "${mvnHome}/bin/mvn -B -Dmaven.repo.local=$HOME/.m2/repository clean package"
                }
            }
        }
    }
    post {
        success {
            cleanWs()
        }
    }
}

在这个示例中,我们使用了Maven插件的tool方法来定义Maven的版本。然后,我们将M2_HOME设置为我们定义的Maven的路径。
最后,我们在Maven命令中使用-Dmaven.repo.local选项来指定Maven仓库的位置。

5.3 删除不必要的阶段

一些阶段可能不必要并且会大大降低交付流水线的性能

例如:可能只是在提交代码时执行build 和test阶段,而不是每次构建时执行

示例:

```groovy
pipeline {
    agent any
    stages {
        stage('Build') {
            when {
                changeset "src/**"
            }
            steps {
                sh 'mvn clean install'
            }
        }
        stage('Test') {
            when {
                changeset "src/**"
            }
            steps {
                sh 'mvn test'
            }
        }
        stage('Deploy') {
            when {
                changeset "src/**"
            }
            steps {
                sh './deploy.sh'
            }
        }
    }
    post {
        success {
            cleanWs()
        }
    }
}

示例中,我们build、test和deploy阶段之前添加了when模块。当检测到代码库中的更改时,这些阶段才会被执行。

6总结:

Declarative Pipeline 语法要求更严,需使用Jenkins预定义的DSL结构,使用简单,

Scripetd Pipeline受限很少,限制主要在Groovy的结构和语法

三、Jenkins基于Gitlab的授权认证