GVKun编程网logo

Jenkins Pipeline 参数详解(jenkins的pipeline)

17

本文将介绍JenkinsPipeline参数详解的详细情况,特别是关于jenkins的pipeline的相关信息。我们将通过案例分析、数据研究等多种方式,帮助您更全面地了解这个主题,同时也将涉及一些关

本文将介绍Jenkins Pipeline 参数详解的详细情况,特别是关于jenkins的pipeline的相关信息。我们将通过案例分析、数据研究等多种方式,帮助您更全面地了解这个主题,同时也将涉及一些关于CI/CD:Jenkins Pipeline 实践、Gitlab+Jenkins学习之路(十)之Jenkins按角色授权和Pipeline、Gitlab_ansible_jenkins 三剑客⑤jenkins Pipeline-job 的使用、Jenkins Pipeline的知识。

本文目录一览:

Jenkins Pipeline 参数详解(jenkins的pipeline)

Jenkins Pipeline 参数详解(jenkins的pipeline)

Pipeline 是什么

Jenkins Pipeline 实际上是基于 Groovy 实现的 CI/CD 领域特定语言(DSL),主要分为两类,一类叫做 Declarative Pipeline,一类叫做 Scripted Pipeline

Declarative Pipeline 体验上更接近于我们熟知的 travis CI 的 travis.yml,通过声明自己要做的事情来规范流程,形如:

pipeline {
    agent any
    stages {
        stage(''Build'') {
            steps {
                //
            }
        }
        stage(''Test'') {
            steps {
                //
            }
        }
        stage(''Deploy'') {
            steps {
                //
            }
        }
    }
}

 

而 Scripted Pipeline 则是旧版本中 Jenkins 支持的 Pipeline 模式,主要是写一些 groovy 的代码来制定流程:

node {  
    stage(''Build'') {
        //
    }
    stage(''Test'') {
        //
    }
    stage(''Deploy'') {
        //
    }
}

 

一般情况下声明式的流水线已经可以满足我们的需要,只有在复杂的情况下才会需要脚本式流水线的参与。

过去大家经常在 Jenkins 的界面上直接写脚本来实现自动化,但是现在更鼓励大家通过在项目中增加 Jenkinsfile 的方式把流水线固定下来,实现 Pipeline As Code,Jenkins 的 Pipeline 插件将会自动发现并执行它。

语法

Declarative Pipeline 最外层有个 pipeline 表明它是一个声明式流水线,下面会有 4 个主要的部分: agentpoststagessteps,我会逐一介绍一下。

Agent

agent 主要用于描述整个 Pipeline 或者指定的 Stage 由什么规则来选择节点执行。Pipeline 级别的 agent 可以视为 Stage 级别的默认值,如果 stage 中没有指定,将会使用与 Pipeline 一致的规则。在最新的 Jenkins 版本中,可以支持指定任意节点 (any),不指定 (none),标签 (label),节点 (node),dockerdockerfile 和 kubernetes 等,具体的配置细节可以查看文档,下面是一个使用 docker 的样例:

agent {
    docker {
        image ''myregistry.com/node''
        label ''my-defined-label''
        registryUrl ''https://myregistry.com/''
        registryCredentialsId ''myPredefinedCredentialsInJenkins''
        args ''-v /tmp:/tmp''
    }
}

 

Tips:

  • 如果 Pipeline 选择了 none,那么 stage 必须要指定一个有效的 agent,否则无法执行
  • Jenkins 总是会使用 master 来执行 scan multibranch 之类的操作,即使 master 配置了 0 executors
  • agent 指定的是规则而不是具体的节点,如果 stage 各自配置了自己的 agent,需要注意是不是在同一个节点执行的

Stages && Stage

Stages 是 Pipeline 中最主要的组成部分,Jenkins 将会按照 Stages 中描述的顺序从上往下的执行。Stages 中可以包括任意多个 Stage,而 Stage 与 Stages 又能互相嵌套,除此以外还有 parallel 指令可以让内部的 Stage 并行运行。实际上可以把 Stage 当作最小单元,Stages 指定的是顺序运行,而 parallel 指定的是并行运行。

接下来的这个 case 很好的说明了这一点:

pipeline {
    agent none
    stages {
        stage(''Sequential'') {
            stages {
                stage(''In Sequential 1'') {
                    steps {
                        echo "In Sequential 1"
                    }
                }
                stage(''In Sequential 2'') {
                    steps {
                        echo "In Sequential 2"
                    }
                }
                stage(''Parallel In Sequential'') {
                    parallel {
                        stage(''In Parallel 1'') {
                            steps {
                                echo "In Parallel 1"
                            }
                        }
                        stage(''In Parallel 2'') {
                            steps {
                                echo "In Parallel 2"
                            }
                        }
                    }
                }
            }
        }
    }
}

 

除了指定 Stage 之间的顺序关系之外,我们还可以通过 when 来指定某个 Stage 指定与否:比如要配置只有在 Master 分支上才执行 push,其他分支上都只运行 build

stages {
  stage(''Build'') {
    when {
      not { branch ''master'' }
    }
    steps {
      sh ''./scripts/run.py build''
    }
  }
  stage(''Run'') {
    when {
      branch ''master''
    }
    steps {
      sh ''./scripts/run.py push''
    }
  }
}

 

还能在 Stage 的级别设置 environment,这些就不展开了,文档里有更详细的描述。

Steps

steps 是 Pipeline 中最核心的部分,每个 Stage 都需要指定 Steps。Steps 内部可以执行一系列的操作,任意操作执行出错都会返回错误。完整的 Steps 操作列表可以参考 Pipeline Steps Reference,这里只说一些使用时需要注意的点。

  • groovy 语法中有不同的字符串类型,其中 ''abc'' 是 Plain 字符串,不会转义 ${WROKSPACE} 这样的变量,而 "abc" 会做这样的转换。此外还有 '''''' xxx '''''' 支持跨行字符串,""" 同理。
  • 调用函数的 () 可以省略,使得函数调用形如 updateGitlabCommitStatus name: ''build'', state: ''success'',通过 , 来分割不同的参数,支持换行。
  • 可以在声明式流水线中通过 script 来插入一段 groovy 脚本

Post

post 部分将会在 pipeline 的最后执行,经常用于一些测试完毕后的清理和通知操作。文档中给出了一系列的情况,比较常用的是 alwayssuccess 和 failure

比如说下面的脚本将会在成功和失败的时候更新 gitlab 的状态,在失败的时候发送通知邮件:

post {
  failure {
    updateGitlabCommitStatus name: ''build'', state: ''failed''
    emailext body: ''$DEFAULT_CONTENT'', recipientProviders: [culprits()], subject: ''$DEFAULT_SUBJECT''
  }
  success {
    updateGitlabCommitStatus name: ''build'', state: ''success''
  }
}

 

每个状态其实都相当于于一个 steps,都能够执行一系列的操作,不同状态的执行顺序是事先规定好的,就是文档中列出的顺序。

Shared Libraries

同一个 Team 产出的不同项目往往会有着相似的流程,比如 golang 的大部分项目都会执行同样的命令。这就导致了人们经常需要在不同的项目间复制同样的流程,而 Shared Libraries 就解决了这个问题。通过在 Pipeline 中引入共享库,把常用的流程抽象出来变成一个的指令,简化了大量重复的操作。

在配置好 lib 之后,Jenkins 会在每个 Pipeline 启动前去检查 lib 是否更新并 pull 到本地,根据配置决定是否直接加载。

所有的 Shared Libraries 都要遵循相同的项目结构:

(root)
+- src                     # Groovy source files
|   +- org
|       +- foo
|           +- Bar.groovy  # for org.foo.Bar class
+- vars
|   +- foo.groovy          # for global ''foo'' variable
|   +- foo.txt             # help for ''foo'' variable
+- resources               # resource files (external libraries only)
|   +- org
|       +- foo
|           +- bar.json    # static helper data for org.foo.Bar

目前我们的使用比较低级,所以只用到了 vars 来存储全局的变量。

vars 下的每一个 foo.groovy 文件都是一个独立的 namespace,在 Pipeline 中可以以 foo.XXX 的形式来导入。比如我们有 vars/log.groovy

def info(message) {
    echo "INFO: ${message}"
}

def warning(message) {
    echo "WARNING: ${message}"
}

 

那么 Jenkinsfile 中就可以这样调用:

// Jenkinsfile
steps {
  log.info ''Starting''
  log.warning ''Nothing to do!''
}

 

大家可能已经注意到了,在 groovy 文件中,我们可以直接像在 steps 中一样调用已有的方法,比如 echo 和 sh 等。

我们也能在 groovy 文件中去引用 Java 的库并返回一个变量,比如:

#!/usr/bin/env groovy
import java.util.Random;

def String name() {
  def rand = new Random()
  def t = rand.nextInt(1000)
  return String.valueOf(t)
}

 

这样就能够在 JenkinsFile 中去设置一个环境变量:

// Jenkinsfile
environment {
  NAME = random.name()
}

 

除了定义方法之外,我们还能让这个文件本身就能被调用,只需要定义一个 call 方法:

#!/usr/bin/env groovy

def call() {
  sh "hello, world"
}

 

还能够定义一个新的 section,接受一个 Block:

def call(Closure body) {
    node(''windows'') {
        body()
    }
}

 

这样可以让指定的 Body 在 windows 节点上调用:

// Jenkinsfile
windows {
    bat "cmd /?"
}

 

常用技巧

发送邮件通知

主要使用 emailext,需要在 Jenkins 的配置界面事先配置好,可用的环境变量和参数可以参考文档 Email-ext plugin

emailext body: ''$DEFAULT_CONTENT'',  recipientProviders: [culprits(),developers()], subject: ''$DEFAULT_SUBJECT''

结果同步到 gitlab

同样需要配置好 gitlab 插件,在 Pipeline 中指定 options

// Jenkisfile
options {
  gitLabConnection(''gitlab'')
}

然后就可以在 post 中根据不同的状态来更新 gitlab 了:

// Jenkisfile
failure {
  updateGitlabCommitStatus name: ''build'', state: ''failed''
}
success {
  updateGitlabCommitStatus name: ''build'', state: ''success''
}

 

文档参考:Build status configuration

构建过程中可用的环境变量列表

Jenkins 会提供一个完整的列表,只需要访问 <your-jenkins-url>/env-vars.html/ 即可,别忘了需要使用 "${WORKSPACE}"

在 checkout 前执行自定义操作

在 Multibranch Pipeline 的默认流程中会在 checkout 之前和之后执行 git clean -fdx,如果在测试中以 root 权限创建了文件,那么 jenkins 会因为这个命令执行失败而报错。所以我们需要在 checkout 之前执行自定义的任务:

#!/usr/bin/env groovy

// var/pre.groovy
def call(Closure body) {
  body()
  checkout scm
}

在 Jenkinsfile 中配置以跳过默认的 checkout 行为:

// Jenkisfile
options {
  skipDefaultCheckout true
}

在每个 stage 中执行自定义的任务即可:

// Jenkisfile
stage(''Compile'') {
  agent any
  steps {
    pre {
      sh ''pre compile''
    }
    sh ''real compile''
  }
}

总结

Jenkins 作为使用最为广泛的 CI/CD 平台,网上流传着无数的脚本和攻略,在学习和开发的时候一定要从基本出发,了解内部原理,多看官方的文档,不要拿到一段代码就开始用,这样才能不会迷失在各式各样的脚本之中。

更重要的是要结合自己的业务需求,开发和定制属于自己的流程,不要被 Jenkins 的框架限制住。比如我们是否可以定义一个自己的 YAML 配置文件,然后根据 YAML 来生成 Pipeline,不需要业务自己写 Pipeline 脚本,规范使用,提前检查不合法的脚本,核心的模块共同升级,避免了一个流程小改动需要所有项目组同步更新。这是我现在正在做的事情,有机会再跟大家分享~

CI/CD:Jenkins Pipeline 实践

CI/CD:Jenkins Pipeline 实践

目录

  • 1 Jenkins2.X Pipeline
    • 1.0 CI/CD
    • 1.1 pipeline 简介
    • 1.2 Jenkinsfile
    • 1.3 流水线语法
      • 1.3.1 脚本式流水线
      • 1.3.2 声明式流水线
    • 1.4 Blue Ocean
    • 1.5 流水线的优势
  • 2 多分支流水线实践
    • 2.1 配置简述
    • 2.2 新建project
    • 2.3 脚本编写
      • 2.3.1 代码拉取
      • 2.3.2 Maven打包
      • 2.3.3 设置环境变量
      • 2.3.4 构建镜像
      • 2.3.5 测试
        • 2.3.5.1 启动服务以及环境容错处理
        • 2.3.5.2 测试
        • 2.3.5.3 发布Html报告
      • 2.3.6 推送镜像到仓库
      • 2.3.7 部署到远程机器上
      • 2.3.8 构建后操作
        • 2.3.8.1 环境清理
        • 2.3.8.2 发送邮件
      • 2.3.9 配置
  • 3 完整流水线脚本
  • 4总结

1 Jenkins2.X Pipeline

1.0 CI/CD

持续集成 (Continuous integration)是一种软件开发实践,即团队开发成员经常集成它们的工作,通过每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽早地发现集成错误。

持续部署(continuous deployment)是通过自动化的构建、测试和部署循环来快速交付高质量的产品。某种程度上代表了一个开发团队工程化的程度,毕竟快速运转的互联网公司人力成本会高于机器,投资机器优化开发流程化相对也提高了人的效率,让 engineering productivity 最大化。

1.1 pipeline 简介

pipeline,即流水线,是jenkins2.X的新特性,是jenkins官方推荐使用的持续集成方案。与传统的自由风格项目不同,它是通过jenkins DSL 编写代码来实现。相比于之前用户只能通过Web界面进行配置的方式来定义Jenkins任务,现在通过使用jenkins DSL 和 Groovy 语言编写程序,用户可以定义流水线并执行各种任务。

1.2 Jenkinsfile

那么重点来了,流水线代码写在哪里呢?
答案就是Jenkinsfile。
在jenkins 2 中,流水线配置可以从jenkins中剥离出来。在自由风格等项目中,任务的配置都是以配置文件的形式保存在jenkins的服务器上的,这意味这所有的配置的变更都依赖于jenkins的web界面。当配置中使用了许多插件时,去维护他人构建的配置就十分繁琐。在流水线项目中,可以在Web界面中编写流水线脚本,也可以将脚本以文本形式保存在外部的版本控制系统中,这个文本就是Jenkinsfile。
对于git项目,推荐使用多分支流水线。将任务配置和流水线信息保存在Jenkinsfile中,把jenkinsfile保存在项目的根目录下。不同的项目和分支都会有自己的Jenkinsfile,其内容各不相同。这样一个多分支流水线 project 可以对不同的分支代码进行持续集成和持续部署。
使用Jenkinsfile可以像管理项目中代码一样通过文件的形式来管理jenkins任务,支持历史追溯和差异对比等功能。

1.3 流水线语法

流水线语法有两种:一种是脚本式流水线,另一种是声明式流水线。下面对两种语法进行简单介绍。更详细的内容请查阅官方文档。

1.3.1 脚本式流水线

流水线代码就是 Groovy 脚本,其中插入了部分针对jenkins的DSL步骤。这种方式几乎没有结构上的约束,程序流程也基于Groovy语法结构实现。这种方式更加灵活,但是需要会使用Groovy。

Jenkinsfile (Scripted Pipeline)
    
  node {
    stage('Example') {
        if (env.BRANCH_NAME == 'master') {
            echo 'I only execute on the master branch'
        } else {
            echo 'I execute elsewhere'
        }
    }
}

1.3.2 声明式流水线

相对于脚本式流水线的灵活,声明式流水线比较严谨。它的结构更加清晰,更加接近自由风格类型的项目。同时,清晰的结构有助于错误检查,上手简单,对Blue Ocean(下面会讲到)的支持也好。官方推荐使用这种语法格式。
通过下面的示例,可以看到一个stage就是一个阶段,每个阶段内是步骤和相关配置。agent 标识了阶段在哪个节点上卖弄执行。相关语法会在下面结合实际项目进行讲解。
如果声明式流水线不能满足我们的需求,那么我们可以在声明式脚本中使用脚本式流水线脚本,具体方法在下一章中。

Jenkinsfile (Declarative Pipeline)
    
  pipeline {
    agent none 
    stages {
        stage('Example Build') {
            agent { docker 'maven:3-alpine' } 
            steps {
                echo 'Hello, Maven'
                sh 'mvn --version'
            }
        }
        stage('Example Test') {
            agent { docker 'openjdk:8-jre' } 
            steps {
                echo 'Hello, JDK'
                sh 'java -version'
            }
        }
    }
}

在本文的实践环节中,会使用声明式语法来演示一个多分支流水线的project。

1.4 Blue Ocean

Blue Ocean 是 jenkins2 中全新的可视化界面(需求安装Blue Ocean插件)。它为流水线的每一个阶段都添加了图形化展示,可以查看每一个阶段的状态和进展,对每个阶段、每个任务都有点选式日志查看的功能,十分清晰。

在这里插入图片描述

1.5 流水线的优势

  1. 灵活性:通过脚本的方式要比只依赖于WEB界面配置的方式要灵活。
  2. 可追溯性:jenkinsfile管理在源码下,可对配置进行历史追溯和差异比对。
  3. 清晰性:结构清晰,便于排错。
  4. 可恢复性:可以基于某一个版本的配置进行重新运行。

2 多分支流水线实践

2.1 配置简述

已经安装pipeline相关插件。实践的对象是一个git项目。一共三个分支:maste、test、script。每个分支下都有一个Jenkinsfile。
三个分支的配置基本相同,主要步骤如下:
拉取代码—>maven打包—>构建镜像—>测试—>推送harbor仓库—>部署发布

在这里插入图片描述


上述基本完成了一个项目的持续集成和持续部署

2.2 新建project

新建一个多分支流水线project,源码管理选择git,然后填上项目的git地址。如果权限正常的话,在保存后Jenkins会扫描项目的所有分支下的Jenkinsfile,自动创建流水线并执行。

在这里插入图片描述


在这里插入图片描述

查看新增的多分支流水线(我这里每个分支都已经构建过多次了)。

在这里插入图片描述


点击左侧的打开Blue Ocean进入可视化界面。

在这里插入图片描述

2.3 脚本编写

2.3.1 代码拉取

持续集成的源头就是获取最新的代码。让我们看下在pipeline 脚本中是如何做的。

    stage('Git Pull') {
      steps {
        git(url: 'https://gitee.com/GJXing/luckymoney.git', branch: 'master',credentialsId: '6cde17d1-5480-47df-9e08-e0880762b496')
        echo 'pull seccess'
      }
    }
  • url: 源代码地址
  • branch:git分支
  • credentialsId:凭证(username and password)

echo 是输出信息,提示拉取成功,此处可有可无。

2.3.2 Maven打包

项目是一个SpringBoot的Java项目,通过maven构建jar包。

    stage('Maven Build') {
      steps {
        sh 'mvn clean install'
      }
 }

sh 就是执行shell 命令
注意:在test分支中做了testNG测试,此时执行 ‘mvn clean install’ 会在打包时进行测试。如果test分支此阶段不想做测试,则打包时忽略测试

    stage('Maven Build') {
      steps {
        sh 'mvn clean install -Dmaven.test.skip=true'
      }
    }

将jar包保存为“制品”
假设我们想保存script分支构建得到的jar包,那么可以通过

archive 'target/luckymoney-0.0.1-SNAPSHOT.jar'

进入Blue Ocean 查看制品

在这里插入图片描述

2.3.3 设置环境变量

在进行下一阶段前,先解决一个比较重要的问题,在流水线中如何配置环境变量。

environment {
  IMAGE_NAME = 'harbor.guojiaxing.red/public/springbootdemo'
  CONTAINER_NAME = 'luckymoney'
}

声明式流水线的环境变量配置是声明在environment块中。这里我声明了两个环境变量,一个是镜像名称,一个是容器名称。这两个变量在后面的阶段中会反复使用。
environment 可以声明在pipeline块下作用于整个配置,也可以声明在一个stage下只作用于一个阶段。
环境变量的调用方式与shell一样,如:${CONTAINER_NAME}

2.3.4 构建镜像

pipeline 针对docker做了分装,其自己定义了一套语法规则来进行容器的操作。
例如:

build(image[,args])

使用当前目录的Dockerfile,运行docker build来创建一个镜像并打上标签。

Image.run([args,command])

使用docker run来运行一个镜像,同时返回一个容器。

Image.pull()

运行 docker pull

除了以上列出的几个,还有其他的许多方法。但是我并不愿意使用这些方法。原因有两个,一个是docker 命令本身就不复杂,使用起来就比较方便。另一个原因是使用docker命令比二次分装的方法更加灵活。

下面正式开始进行镜像的构建

stage('Build Image') {
  steps {
    sh '''VERSION=$(date +%Y%m%d%H%M%s)
          echo "$(date +%Y%m%d%H%M%s)" > ${WORKSPACE}/VERSION
          echo "building image: ${IMAGE_NAME}_${BRANCH_NAME}:${VERSION}"
          docker build -t ${IMAGE_NAME}_${BRANCH_NAME}:${VERSION} .'''
	}
}

镜像由镜像名称和镜像TAG构成。
对于镜像名称,由于是多分支流水线,这里采用基础镜像名称+分支名称组合的形式。
tag的作用就是表明版本,此处采用构建时间,精确到秒。将构建时间保存在文件中,之后所有的阶段都读取该文件来获取版本。当然我们还可以选择构建号或自定义等变量作为tag(如下),但是这种方式在唯一性方面不如时间方式。此处可根据实际业务进行选择。

sh ' docker build -t ${IMAGE_NAME}_${BRANCH_NAME}:${BUILD_NUMBER} .'

2.3.5 测试

在每次构建后,我们都应该对当前版本的代码做自动化测试,通过冒烟等测试来评估当前版本的质量。保证我们要发布版本的质量是过关的,如果任何一个测试步骤失败则后续阶段不会继续,应用也不会发布。这也是持续集成的重要思想。

2.3.5.1 启动服务以及环境容错处理

严格的说,服务的启动不应该和测试放在一个阶段,应该在一个单独的阶段中执行,此处为了方便进行了简写。
了解docker就会知道,同名的容器没有被删除,那么容器是无法启动的。如果由于上次构建失败或其他原因导致目标容器名称的容器存在,那么在容器启动前进行环境清理就很由必要。

steps {
  script {
    try {
      sh '''environmental_clean(){
            docker_ps=`docker ps | grep ${CONTAINER_NAME}_${BRANCH_NAME}`
            docker_psa=`docker ps -a | grep ${CONTAINER_NAME}_${BRANCH_NAME}`

            if [[ 0 -eq $docker_ps ]];
            then
                #容器未启动
                echo "容器${CONTAINER_NAME}_${BRANCH_NAME}未启动"
            else
                echo "停止容器"
                docker stop ${CONTAINER_NAME}_${BRANCH_NAME}
            fi

            if [[ 0 -eq $docker_psa ]];
            then
                echo "容器${CONTAINER_NAME}_${BRANCH_NAME}不存在"
            else
                echo "删除容器"
                docker rm ${CONTAINER_NAME}_${BRANCH_NAME}
            fi
            }
            #docker 环境清理
            environmental_clean'''
    }
    catch (exc) {
        echo '环境不需要清理'
    }
}

脚本很简单,通过判断docker进程是否存在来停止或删除容器。
重点是通过script块在声明式代码中引用了脚本式的代码。为什么要这样做呢?
前面已经讲过了这是一个容错处理,如果环境不干净就清理,但如果环境中无目标容器名称的容器从在,在‘docker_ps=docker ps | grep ${CONTAINER_NAME}_${BRANCH_NAME}’执行完后会返回-1,与shell脚本不同,pipeline规定当返回结果不是表示成功时,流水线就会终止,此处为了流水线可以继续走下去,用try catch做异常处理。
如果不借助脚本式语法,声明式流水线可以在sh步骤开始加入 ‘set +e’,则在出现错误后不会停止。此处主要是为了演示如何在声明式代码中引用脚本式的代码。

启动容器

sh '''VERSION=$(cat ${WORKSPACE}/VERSION)
docker run -dit --name=${CONTAINER_NAME}_${BRANCH_NAME} ${IMAGE_NAME}_${BRANCH_NAME}:${VERSION}'''

2.3.5.2 测试

流水线不仅可以串行也可以并行。在测试阶段我们就可以进行并行操作。

在这里插入图片描述


如图所示,在test分支的Test阶段,ui自动化、接口自动化、性能测试、单元测试等都可以并行执行。
在pipeline脚本中 parallel 块内的 stage 块会并行执行。

parallel {
  stage('接口自动化测试') {
         steps {
            sh  '''echo "进行接口自动化测试"
                   mvn clean test
                   echo "自动化测试完成" '''
      	  }
       }

  stage('UI自动化测试') {
    steps {
      echo 'ui test'
    }
  }
 }

2.3.5.3 发布Html报告

将html报告进行发布。
在testNG执行完后会在项目test-output目录下生成report.html,将报告发布。安装 HTML Publisher plugin 插件。

post {
  always {
    publishHTML([
      allowMissing: false,
      alwaysLinkToLastBuild: false,
      keepAll: true,
      reportDir: "test-output",
      reportFiles: "report.html",
      reportName:"testNg report"
  ])
    }
  }

post块的作用是在所有步骤最后执行,可以作用于整个pipeline或一个阶段内。always表示不管是成功还是失败都执行。

同样在制品中查看

在这里插入图片描述


点击报告查看(出现样式问题点击这里)

在这里插入图片描述

2.3.6 推送镜像到仓库

代码测试完成后就可以将镜像推送到远程仓库了。
仓库是我自己搭建的harbor。

stage('Push to Harbor') {
  steps {
    withCredentials(bindings: [usernamePassword(credentialsId: 'e8dc0fa7-3547-4b08-8b7b-d9e68ff6c18f', passwordVariable: 'password', usernameVariable: 'username')]) {
      sh 'docker login -u $username -p $password harbor.guojiaxing.red'
    }

    sh '''export VERSION=$(cat ${WORKSPACE}/VERSION)
      echo "docker push ${IMAGE_NAME}_${BRANCH_NAME}:${VERSION}"
      docker push ${IMAGE_NAME}_${BRANCH_NAME}:${VERSION}'''
  }

pipeline中的鉴权方式是withCredentials(),这需要事先在jenkins配置中保存凭据。

2.3.7 部署到远程机器上

安装 SSH Pipeline Steps 插件。配置username and password 凭据。

stage('ssh deploy') {
  steps {
    script {                 
        def remote = [:]
        remote.name = 'gjx_server'
        remote.host = 'www.guojiaxing.red'
        remote.allowAnyHosts = true
        withCredentials([usernamePassword(credentialsId: 'gjx-server', passwordVariable: 'password', usernameVariable: 'username')]) {
            remote.user = "${username}"
            remote.password = "${password}"
        }
        sshCommand remote: remote, command: "docker pull ${IMAGE_NAME}_${BRANCH_NAME}:${BUILD_NUMBER}"            
    }
}

ssh pipeline 更多的操作请查阅官方文档

2.3.8 构建后操作

2.3.8.1 环境清理

在作用于pipeline全局的post中进行环境清理

always {
      script {
        try{
            sh '''environmental_clean(){
                docker_ps=`docker ps | grep ${CONTAINER_NAME}_${BRANCH_NAME}`
                docker_psa=`docker ps -a | grep ${CONTAINER_NAME}_${BRANCH_NAME}`

                if [[ 0 -eq $docker_ps ]];
                then
                    #容器未启动
                    echo "容器${CONTAINER_NAME}_${BRANCH_NAME}未启动"
                else
                    echo "停止容器"
                    docker stop ${CONTAINER_NAME}_${BRANCH_NAME}
                fi

                if [[ 0 -eq $docker_psa ]];
                then
                    echo "容器${CONTAINER_NAME}_${BRANCH_NAME}不存在"
                else
                    echo "删除容器"
                    docker rm ${CONTAINER_NAME}_${BRANCH_NAME}
                fi
                }
                #docker 环境清理
                environmental_clean
                export BUILD_NUMBER=$(cat ${WORKSPACE}/BUILD_NUMBER)
                docker rmi ${IMAGE_NAME}_${BRANCH_NAME}:${BUILD_NUMBER}'''
        }
         catch (exc) {
              echo '镜像不需要删除'
          }
      }

2.3.8.2 发送邮件

安装 Email Extension Plugin 插件并完成邮件配置
在作用于pipeline全局的post中编写邮件配置

failure {
       emailext (
       subject: "Failed: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' 自动化测试结果",
       body: '''<body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4" offset="0">
                   <table width="95%" cellpadding="0" cellspacing="0">
                       <tr>
                               <td><br />
                               <b><font color="#0B610B">构建信息</font></b>
                               <hr size="2" width="100%" align="center" /></td>
                           </tr>
                           <tr>
                               <td>
                                   <ul> 
                                       <li>构建名称:${JOB_NAME}</li>
                                       <li>构建结果: <span> ${BUILD_STATUS}</span></li> 
                                       <li>构建编号:${BUILD_NUMBER}</li>
                                       <li>构建地址:<a href="${BUILD_URL}">${BUILD_URL}</a></li>                   
                                       <li>GIT 分支:${BRANCH_NAME}</li>
                                       <li>变更记录: ${CHANGES,showPaths=true,showDependencies=true,format="<pre><ul><li>提交ID: %r</li><li>提交人:%a</li><li>提交时间:%d</li><li>提交信息:%m</li><li>提交文件:<br />%p</li></ul></pre>",pathFormat="         %p <br />"}
                                   </ul>
                               </td>
                           </tr>
                       </table>
                   </body>
                   </html>
                   ''',
       to: "XXX802003@qq.com",
       from: "XXX34093915@163.com",
       attachLog: true,
       compressLog: true
   )
   }
   success {
       emailext (
       subject: "SUCCESSFUL: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' 自动化测试结果",
       body: '''<body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4" offset="0">
                   <table width="95%" cellpadding="0" cellspacing="0">
                       <tr>
                               <td><br />
                               <b><font color="#0B610B">构建信息</font></b>
                               <hr size="2" width="100%" align="center" /></td>
                           </tr>
                           <tr>
                               <td>
                                   <ul> 
                                       <li>构建名称:${JOB_NAME}</li>
                                       <li>构建结果: <span> ${BUILD_STATUS}</span></li> 
                                       <li>构建编号:${BUILD_NUMBER}</li>
                                       <li>构建地址:<a href="${BUILD_URL}">${BUILD_URL}</a></li>                   
                                       <li>GIT 分支:${BRANCH_NAME}</li>
                                       <li>变更记录: ${CHANGES,showPaths=true,showDependencies=true,format="<pre><ul><li>提交ID: %r</li><li>提交人:%a</li><li>提交时间:%d</li><li>提交信息:%m</li><li>提交文件:<br />%p</li></ul></pre>",pathFormat="         %p <br />"}
                                   </ul>
                               </td>
                           </tr>
                       </table>
                   </body>
                   </html>
                   ''',
       to: "XXX802003@qq.com",
       from: "xxx34093915@163.com",
       attachLog: true,
       compressLog: true
   )
   }

failure 为构建失败时执行,success 为构建成功时执行。
查看邮件:

在这里插入图片描述

2.3.9 配置

  options {
    disableConcurrentBuilds()
    timeout(time: 1, unit: 'HOURS')
  }
  triggers {
    pollSCM('H/15 * * * *')
  }

disableConcurrentBuilds() 表示多分支不允许同时构建
timeout(time: 1, unit: ‘HOURS’) 表示超时时间,1小时超时则构建失败
pollSCM(‘H/15 * * * *’) 表示每15分钟 轮询扫描源代码,如果有修改则构建,无修改则不构建。

3 完整流水线脚本

script分支下Jenkinsfile

在这里插入图片描述

4总结

通过此次 Jenkins Pipeline 实践,对比于传统的自由风格项目,感受到流水线工程的强大和方便之处,也加深了我对CI/CD的理解。
此次从代码拉取到打包到测试再到部署,使用了maven、docker、testNG和harbor等工具,基本上覆盖了一般业务的部署流程。对于一门新技术或工具的学习,我认为具体用到什么内容就学什么内容,没有必要把一个工具或技术完完全全掌握,一般也很难全部掌握。正所谓用到的才是有用的。即使如此,此次实践也覆盖了pipeline 80%以上的语法内容。
pipeline很强大、很好用,并且它还在不断的完善中,随着时间推移,未来其在CI/CD领域一定会有更大的作用。

Gitlab+Jenkins学习之路(十)之Jenkins按角色授权和Pipeline

Gitlab+Jenkins学习之路(十)之Jenkins按角色授权和Pipeline

  • 一、Jenkins按角色授权

当一个公司的开发分为多个组别,或者是多个项目等等。用于公司内部测试,让开发人员自行构建测试,此时不可能让所有的开发都在公用一个构建,这样变得很混乱,为了解决这一问题,jenkins也提供了角色授权的机制。每个开发有着对应的账号和权限,可以自行新建,构建,测试,发布等。角色授权需要安装的插件:Role-based Authorization Strategy

(1)系统配置
系统管理-->Configure Global Security-->访问控制-->授权策略-->Role-Base Strategy

(2)新建用户
系统管理-->管理用户-->新建用户
这里创建一个java用户和php用户,用于区分php和java项目的构建

(3)用户关联自己相关job,无法查看其他的job

建立项目角色,并对该用户进行项目的授权(包括Job的构建,取消,配置,创建,删除等等)
注意此处的添加,一个是Role是随意填写,最好规范点。Pattern是正则匹配,比如此处匹配的是所有包含Java开头的项目和php开头的项目

系统管理-->Manage and Assign Roles --> Manage Roles-->Project roles

 

配置好项目的权限,使用java用户或php用户登录是没有新建项目的权限,如需新增这样的权限,需要在Global roles中进行配置,新增一个角色(比如新增一个可以创建新项目的权限角色:new job,可登录查看的角色read):

 

 

 然后系统管理-->Manage and Assign Roles -->Assign Roles进行添加对应的权限

使用php登录查看,可以看到php用户可以看到对应php开头的项目,但是没有新建项目的权限:

  • 二、Jenkins的Pipeline(代码流水线管理)

Pipeline(代码流水线管理)

Pipeline名词顾名思义就是流水线的意思,因为公司可能会有很多项目。如果使用jenkins构建完成后,开发构建项目需要一项一项点击,比较麻烦。所以出现pipeline名词。

(1)下载pipeline。这样只需要构建一个项目,就会帮我们完成所有相关项目

搜索插件pipeline 

等待安装完成 

 

(2)新建一个pipeline的项目

 

配置pipeline项目,使用Pipeline script脚本: 

node {
    stage ''build php-deploy''
    build ''php-deploy''
    stage ''build java''
    build ''java''
}

为了解决各个项目的依赖关系关系,使用pipeline的脚本进行控制。
第一步构建php项目,第二步构建java项目。Pipeline的脚本语法在Pipeline Syntax中有展示

Pipeline Syntax的使用,如图:

 

立即构建p1的项目,如图:

 

代码流水线的功能就介绍到此!

Gitlab_ansible_jenkins 三剑客⑤jenkins Pipeline-job 的使用

Gitlab_ansible_jenkins 三剑客⑤jenkins Pipeline-job 的使用

Pipeline-job 的使用

创建 Pipeline 任务

找到 root 用户的 id

编写 pipeline 脚本

#!groovy

 

pipeline{

       agent {node {label ''master''}}

 

       environment {

              PATH="/bin/:sbin:usr/bin:usr/sbin:/usr/local/bin"

       }

 

       parameters {

              choice(

                     choices: ''dev\nprod'',

                     description: ''choose deploy environment'',

                     name: ''deploy_env''

                     )

              string (name: ''version'', defaultValue: ''1.0.0'', description: ''build version'')

       }

 

       stages {

              stage("checkout test repo") {

                     steps {

                           sh ''git config --global http.sslVerify false''

                           dir ("${env.WORKSPACE}") {

                                  git branch: ''master'', credentialsId: "6bdd72fd-dcc1-4977-9978-4982aae37dc9", url: ''https://root@gitlab.example.com/root/test-repo.git''

                           }

                     }

              }

              stage("Print env variable") {

                     steps {

                           dir ("${env.WORKSPACE}") {

                                  sh """

                                  echo "[INFO] print env variable"

                                  echo "current deployment environment is $deploy_env" >> test.properties

                                  echo "the build is $version" >> test.properties

                                  echo "[INFO] Done..."

                                  """

                           }

                     }

              }

              stage("check test properties") {

                     steps{

                           dir ("${env.WORKSPACE}") {

                                  sh """

                                  echo "[INFO] check test properties"

                                  if [ -s test.properties ]

                                  then

                                         cat test.properties

                                         echo "[INFO] done..."

                                  else

                                         echo "test.properties is empty"

                                  fi

                                  """

 

                                  echo "[INFO] build finished..."

                           }

                     }

              }

       }

}

 

 

编译结果:

Jenkins 与基础应用集成

打开 shell 模块编辑

编写测试脚本

#!/bin/sh

user=`whoami`

 

if [ $user == ''deploy'' ]

then

       echo "hello, my name is $user"

else

       echo "sorry, i am not $user"

fi

 

ip addr

 

cat /etc/system-release

 

free -m

 

df -h

 

py_cmd=`which python`

 

$py_cmd --version

 

带参数的界面

 

添加参数选项

 

脚本

 

#!/bin/sh

 

echo "current deploy environment is $deploy_env"

echo "the build is $version"

echo "the password is $pass"

 

if $bool

then

       echo "request is appoved"

else

       echo "request is rejected"

fi

 

 

编译结果

Jenkins 和 git 的集成

 

Jenkins Pipeline

Jenkins Pipeline

jenkins + pipeline 构建自动化部署


pipeline {
   agent any
   tools { 
        maven ''maventool''
   }
   stages {
      stage (''初始化'') {
            steps {
                sh ''''''
                    echo "PATH = ${PATH}"
                    echo "M2_HOME = ${M2_HOME}"
                '''''' 
            }
        }
     stage(''拉取代码'') { // for display purposes
       steps {
           git branch: ''release-2.0'', credentialsId: ''7cb3c8ca-b916-49eb-95fc-be3b243c3a93'', url: ''http://url:9080/Shtel-PaaS/Shtel-PaaS-DevOps/paas-devops-pipeline.git''
           sh ''git checkout v2.0.1''     
        }
     }
     stage(''maven构建'') {
       steps {
           // Run the maven build
           sh "mvn clean -U package -Dmaven.test.skip=true"
       }
     }
     stage(''拉取部署剧本'') { // for display purposes
       steps {
           sh "rm -rf paas-svc-k8s-deploy-playbook"
           
           dir("paas-svc-k8s-deploy-playbook") {
               git branch: ''dev'', credentialsId: ''7cb3c8ca-b916-49eb-95fc-be3b243c3a93'', url: ''http://url:9080/configserver/paas-svc-k8s-deploy-playbook.git''
            //   sh ''git checkout v0.1''
           }
        }
     }
     stage(''拉取配置集''){  // for desplay purposes
      steps {
        sh "rm -rf deploy_config_set"
        dir("deploy_config_set"){
          git branch: ''dev'', credentialsId: ''7cb3c8ca-b916-49eb-95fc-be3b243c3a93'', url: ''http://url:9080/configserver/deploy_config_set''
        }
      }
     }
     stage(''放置发布包'') {
       steps {
           sh "cp target/*.tar.gz paas-svc-k8s-deploy-playbook/roles/paas-svc/files"
       }
     }
     stage(''根据包,修改脚本中的版本号''){
      steps{
         sh "sh paas-svc-k8s-deploy-playbook/roles/paas-svc/templates/set_version.sh"
      }
     }
     stage(''执行部署'') {
       steps {
         ansiblePlaybook credentialsId: ''paas-ce-77'', extras: ''--extra-vars=\''@./deploy_config_set/paas-devops-pipeline/dev.yml\'' -e "env=dev"'', installation: ''ansible-playbook'', inventory: ''paas-svc-k8s-deploy-playbook/inventory/inventory'', playbook: ''paas-svc-k8s-deploy-playbook/playbooks/deploy-dev-k8s.yml''
       }
     }
   }
}



# -----------------------------------------------------------------------------------------------------


pipeline {
   agent any
   tools { 
        maven ''maventool''
   }
   stages {
      stage (''初始化'') {
            steps {
                sh ''''''
                    echo "PATH = ${PATH}"
                    echo "M2_HOME = ${M2_HOME}"
                '''''' 
            }
        }
     stage(''拉取devops-common代码'') {
       steps {
           sh "rm -rf *"
           git branch: ''release-2.0'', credentialsId: ''7cb3c8ca-b916-49eb-95fc-be3b243c3a93'', url: ''http://url:9080/Shtel-PaaS/Shtel-PaaS-DevOps/paas-devops-common.git''
           sh ''git checkout v2.0.1'' 
        }
     }
     stage(''maven构建devops-common包'') {
       steps {
           sh "mvn clean deploy -DaltDeploymentRepository=paas::default::http://url:8082/repository/maven-snapshots/"
       }
     }

   }
}

今天关于Jenkins Pipeline 参数详解jenkins的pipeline的讲解已经结束,谢谢您的阅读,如果想了解更多关于CI/CD:Jenkins Pipeline 实践、Gitlab+Jenkins学习之路(十)之Jenkins按角色授权和Pipeline、Gitlab_ansible_jenkins 三剑客⑤jenkins Pipeline-job 的使用、Jenkins Pipeline的相关知识,请在本站搜索。

本文标签: