Jenkins

这里写目录标题

  • 一 前言
    • 1.1 生产三要素
    • 1.2 市面上流行的其他工具
    • 1.3 Window 安装
    • 1.4 插件安装
    • 1.5 系统配置
      • image.png 1.5.1 修改密码
      • image.png 1.5.2 本地工具配置
      • 1.5.3 服务器配置
      • 1.5.4 查看系统变量
      • 1.5.5 钉钉通知配置
  • 二 实战编写
    • 2.1 SpringBoot+Jenkinsfile+Pipeline
    • 2.2 Vue+Jenkinsfile+Pipeline
  • 三 Pipeline语法基本知识
    • 3.1 Groovy知识
    • 3.2 pipeline的组成
    • 3.3 Post部分
    • 3.4 Post支持的指令
    • 3.5 环境变量
    • 3.6 拆分条件

中文官网:Jenkins
参考书籍:《Jenkins 2.x实践指南》 翟志军
构建伟大,无所不能
Jenkins是开源CI&CD软件领导者, 提供超过1000个插件来支持构建、部署、自动化, 满足任何项目的需要。

一 前言

1.1 生产三要素

在软件工程领域,生产力三要素又分别指的是什么呢?

  • 劳动力:通常将软件开发工程师、测试工程师认为是劳动力。
  • 劳动资料:从硬件、软件的角度对生产工具进行了分类。· 硬件:开发时使用的电脑、机械键盘、灵敏的鼠标、网络速度等。· 软件:IDE(如Eclipse、IntelliJ IDEA)、构建工具(如Webpack、Maven)、协作工具(如Jira)、部署工具(如Ansible、Puppet)等。
  • 劳动对象:不像制造汽车,在开发软件时,劳动对象则是看不见、摸不着的知识。

笑话一则

程序员笑话一则:程序员在椅子上打斗,经理叫他们回去,其中一位说:正在编译呢!经理回答:哦,那你们继续。

1.2 市面上流行的其他工具

参考文章:值得推荐的13个 Jenkins 替代方案
总结一句话,适合自己的才是最好的

1.3 Window 安装

  1. 下载 Jenkins.
  2. 打开终端进入到下载目录.
  3. 运行命令 java -jar jenkins.war --httpPort=8080.
  4. 打开浏览器进入链接 http://localhost:8080.
  5. 按照说明完成安装

这里采用Jar安装,安装完毕之后再设置中设置开机自启就行了,当然还有其他方式主流的是Linux上采用Docker安装

1.4 插件安装

  • Localization: Chinese (Simplified):中文翻译插件
  • Pipeline:流水线
  • Pipeline: GitHub Groovy Libraries:流水线
  • Publish Over SSH:文件推送
  • SSH plugin:服务器连接
  • Subversion Plug-in:SVN插件
  • Workspace Cleanup Plugin:工作空间清理插件
  • DingTalk:钉钉通知
  • Email Extension Plugin:Email邮件通知
  • Build Monitor View:监控视图
  • build user vars plugin:自己的变量
  • NodeJS Plugin:Node Js 插件

这里的插件可以自己选择安装,在进入系统之前可以默认选择给你推荐的的插件

1.5 系统配置

主界面

Jenkins_第1张图片 1.5.1 修改密码

Jenkins_第2张图片

Jenkins_第3张图片 1.5.2 本地工具配置

Maven,NodeJs,JDK,Git
Jenkins_第4张图片
Jenkins_第5张图片
Jenkins_第6张图片
Jenkins_第7张图片
Jenkins_第8张图片

1.5.3 服务器配置

这里主要是我们的文件推送服务器配置,这里可以有多个
Jenkins_第9张图片

1.5.4 查看系统变量

系统环境变量,在我们的编码中可能会存在编码问题这时候就需要设置系统编码,否则可能会乱码
Jenkins_第10张图片

1.5.5 钉钉通知配置

钉钉的机器人配置参考网上的文章吧,有很多自行百度吧,这里主要是预警或者消息配置,当然也可以邮件通知
Jenkins_第11张图片
Jenkins_第12张图片

二 实战编写

2.1 SpringBoot+Jenkinsfile+Pipeline

新建项目信息

  • 首先我们在Jenkins中新建Item,这里我们采用流水线的配置,填写自己的项目名称

Jenkins_第13张图片

  • 配置必要信息

Jenkins_第14张图片

  • 配置轮循方式,这里可以采用两种,定时器和检测代码变更

Jenkins_第15张图片

  • 仓库配置,我这里以Svn为例,当然也可以Git

Jenkins_第16张图片
这里我们需要记到是Svn地址或者Git地址,然后添加密码凭证
Jenkins_第17张图片
注意这个文件的名称我们需要再Svn仓库或者在Git仓库获取到该脚本文件
Jenkins_第18张图片
好了,我们的项目配置就完毕,接下里我们在项目中配置脚本文件,下面我们来看那可能配置文件的编写

Idea 配置

  • 首先我们需要再Idea中配置Jenkins文件

Jenkins_第19张图片
编写Jenkins文件,我阿里梳理一下Jenkins脚本文件中作的事情

  • Maven,调用本地Maven来进行打包
stage('Build') {
     steps {
                echo 'begin build'
                bat 'mvn clean install'
            }
 }
  • 开始上传文件到指定服务器中,这里需要SSPublish插件,这里我们可以采用Jenkins自带的Pipeline语法工具

Jenkins_第20张图片
Jenkins_第21张图片
Jenkins_第22张图片

        stage('begin upload') {
            steps {
                sshPublisher(publishers: [sshPublisherDesc(configName: 'ComPany Server', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '''
               ''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '.', remoteDirectorySDF: false, removePrefix: 'target', sourceFiles: 'target/FeedbackSystem-0.0.1-SNAPSHOT.jar')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            }
        }
  • 执行Jar运行,先备份一份文件,在监听端口是否运行,运行jar包,注意这里需要sshCommand插件
 stage('begin run') {
            steps {
                script {
                    def currentDate = new Date().format('yyyy-MM-dd-HH-mm-ss')
                    def remoteBackupsFilePath = "/jar/feedback/backups/FeedbackSystem-0.0.1-SNAPSHOT_${currentDate}.jar"
                    def logFilePath = "/jar/feedback/logs/feedback_${currentDate}.log"
                    def remoteFilePath = "/jar/feedback/FeedbackSystem-0.0.1-SNAPSHOT.jar"
                    sshCommand remote: remote, command: '''
                    if netstat -tln | grep ":9020\\s" &> /dev/null; then
                        process_id=$(lsof -t -i:9020)
                        kill ${process_id}
                    fi
                    '''
                    sshCommand remote: remote, command: "cp ${remoteFilePath} ${remoteBackupsFilePath}"
                    sshCommand remote: remote, command: "nohup /usr/java/jdk1.8.0_131/bin/java -jar ${remoteFilePath} > ${logFilePath} 2>&1 &"
                }
            }
        }
  • 成功或失败通知,注意robot是自己配置的钉钉机器人名称
post {
        success {
            echo 'success'
            wrap([$class: 'BuildUser']) {
                dingtalk(
                        robot: 'FeedBackSystem',
                        type: 'MARKDOWN',
                        title: "success: ${JOB_NAME}",
                        text: ["### 项目信息",
                               '---',
                               "- 项目名称: XXXXXXX",
                               '---',
                               "### 构建信息",
                               '---',
                               "- 构建编号: ${BUILD_ID}",
                               "- 构建状态: **${currentBuild.result}**",
                               '---',
                               "### 执行人",
                               '---',
                               "- ${BUILD_USER}",
                               "",
                               '---',
                               "### 持续时间",
                               '---',
                               "- ${currentBuild.durationString}",
                               "",
                               '---'
                        ]
                )
            }
        }
        failure {
            echo 'failure'
            wrap([$class: 'BuildUser']) {
                dingtalk(
                        robot: 'FeedBackSystem',
                        type: 'MARKDOWN',

                        title: "failure: ${JOB_NAME}",
                        text: ["### 项目信息",
                               '---',
                               "- 项目主体: XXXXXXX",
                               '---',
                               "### 构建信息",
                               '---',
                               "- 构建状态: **${currentBuild.result}**",
                               '---',
                               "### 执行人",
                               '---',
                               "- ${BUILD_USER}",
                               "",
                               '---',
                               "### 持续时间",
                               '---',
                               "- ${currentBuild.durationString}",
                               "",
                               '---'
                        ]
                )
            }
        }
    }

完整案例

def remote = [:]
remote.name = 'saltm'
remote.host = ''
remote.user = ''
remote.password = ''
remote.allowAnyHosts = true
pipeline {
    agent any
    stages {
        stage('Maven') {
            steps {
                echo 'begin Maven'
                bat 'mvn -version'
            }
        }
        stage('Build') {
            steps {
                echo 'begin build'
                bat 'mvn clean install'
            }
        }
        stage('begin upload') {
            steps {
                sshPublisher(publishers: [sshPublisherDesc(configName: 'ComPany Server', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '''
               ''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '.', remoteDirectorySDF: false, removePrefix: 'target', sourceFiles: 'target/FeedbackSystem-0.0.1-SNAPSHOT.jar')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            }
        }
        stage('begin run') {
            steps {
                script {
                    def currentDate = new Date().format('yyyy-MM-dd-HH-mm-ss')
                    def remoteBackupsFilePath = "/jar/feedback/backups/FeedbackSystem-0.0.1-SNAPSHOT_${currentDate}.jar"
                    def logFilePath = "/jar/feedback/logs/feedback_${currentDate}.log"
                    def remoteFilePath = "/jar/feedback/FeedbackSystem-0.0.1-SNAPSHOT.jar"
                    sshCommand remote: remote, command: '''
                    if netstat -tln | grep ":9020\\s" &> /dev/null; then
                        process_id=$(lsof -t -i:9020)
                        kill ${process_id}
                    fi
                    '''
                    sshCommand remote: remote, command: "cp ${remoteFilePath} ${remoteBackupsFilePath}"
                    sshCommand remote: remote, command: "nohup /usr/java/jdk1.8.0_131/bin/java -jar ${remoteFilePath} > ${logFilePath} 2>&1 &"
                }
            }
        }
    }
    post {
        success {
            echo 'success'
            wrap([$class: 'BuildUser']) {
                dingtalk(
                        robot: 'FeedBackSystem',
                        type: 'MARKDOWN',
                        title: "success: ${JOB_NAME}",
                        text: ["### 项目信息",
                               '---',
                               "- 项目名称: XXXXXX",
                               '---',
                               "### 构建信息",
                               '---',
                               "- 构建编号: ${BUILD_ID}",
                               "- 构建状态: **${currentBuild.result}**",
                               '---',
                               "### 执行人",
                               '---',
                               "- ${BUILD_USER}",
                               "",
                               '---',
                               "### 持续时间",
                               '---',
                               "- ${currentBuild.durationString}",
                               "",
                               '---'
                        ]
                )
            }
        }
        failure {
            echo 'failure'
            wrap([$class: 'BuildUser']) {
                dingtalk(
                        robot: 'FeedBackSystem',
                        type: 'MARKDOWN',

                        title: "failure: ${JOB_NAME}",
                        text: ["### 项目信息",
                               '---',
                               "- 项目主体: XXXXXX",
                               '---',
                               "### 构建信息",
                               '---',
                               "- 构建状态: **${currentBuild.result}**",
                               '---',
                               "### 执行人",
                               '---',
                               "- ${BUILD_USER}",
                               "",
                               '---',
                               "### 持续时间",
                               '---',
                               "- ${currentBuild.durationString}",
                               "",
                               '---'
                        ]
                )
            }
        }
    }
}

  • 查看运行日志

Jenkins_第23张图片

  • 机器人

Jenkins_第24张图片

2.2 Vue+Jenkinsfile+Pipeline

前面的新建项目的步骤我就不多说了,与上面一样,我们来看看脚本文件的编写

def remote = [:]
remote.name = 'saltm'
remote.host = ''
remote.user = ''
remote.password = ''
remote.allowAnyHosts = true
pipeline {
		agent any
		stages {
			stage('Npm Install') {
				steps {
					bat 'node -v'
					bat 'npm install'
				}
			}
			stage('Npm Build') {
				steps {
					bat 'npm run build:prod'
				}
			}
			stage ("Linux Clear Dist") {
            steps {
							 script {
								sshCommand remote: remote, command: 'rm -rf /jar/html/dist/'
							 }
            }
        }
			stage ("Upload Dist") {
						steps {
							sshPublisher(publishers: [sshPublisherDesc(configName: 'FeedBackWeb', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: 'dist', remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
						}
				}
		}
		post {
        success {
            echo 'success'
            wrap([$class: 'BuildUser']) {
                dingtalk(
                        robot: 'FeedBackSystem',
                        type: 'MARKDOWN',
                        title: "success: ${JOB_NAME}",
                        text: ["### 项目信息",
                               '---',
                               "- 项目名称: xxxxxx",
                               '---',
                               "### 构建信息",
                               '---',
                               "- 构建编号: ${BUILD_ID}",
                               "- 构建状态: **${currentBuild.result}**",
                               '---',
                               "### 执行人",
                               '---',
                               "- ${BUILD_USER}",
                               "",
                               '---',
                               "### 持续时间",
                               '---',
                               "- ${currentBuild.durationString}",
                               "",
                               '---'
                        ]
                )
            }
        }
        failure {
            echo 'failure'
            wrap([$class: 'BuildUser']) {
                dingtalk(
                        robot: 'FeedBackSystem',
                        type: 'MARKDOWN',
                        title: "failure: ${JOB_NAME}",
                        text: ["### 项目信息",
                               '---',
                               "- 项目主体: xxxxx",
                               '---',
                               "### 构建信息",
                               '---',
                               "- 构建状态: **${currentBuild.result}**",
                               '---',
                               "### 执行人",
                               '---',
                               "- ${BUILD_USER}",
                               "",
                               '---',
                               "### 持续时间",
                               '---',
                               "- ${currentBuild.durationString}",
                               "",
                               '---'
                        ]
                )
            }
        }
    }
}

当然上面只是一种方式,实现的方式还有多种,下面我们来介绍一些基本的概念

三 Pipeline语法基本知识

3.1 Groovy知识

参考网站:初探Groovy——无缝兼容Java的脚本语言

3.2 pipeline的组成

Jenkins pipeline其实就是基于Groovy语言实现的一种DSL(领域特定语言),用于描述整条流水线是如何进行的,流水线的内容包括执行编译、打包、测试、输出测试报告等步骤。

pipeline {
    agent any
    
    stages {
        stage('Build') {
            steps {
                sh 'mvn clean package' // 使用 Maven 构建项目
            }
        }
        
        stage('Test') {
            steps {
                sh 'mvn test' // 运行单元测试
            }
        }
        
        stage('Deploy') {
            steps {
                sh 'mvn deploy' // 部署构件到远程仓库或服务器
            }
        }
    }
}

  • pipeline:代表整条流水线,包含整条流水线的逻辑
  • stage部分:阶段,代表流水线的阶段,每个阶段都必须有名称。本例中,build就是此阶段的名称。
  • stages部分:流水线中多个stage的容器。stages部分至少包含一个stage。
  • steps部分:代表阶段中的一个或多个具体步骤(step)的容器。steps部分至少包含一个步骤,本例中,echo就是一个步骤。在一个stage中有且只有一个steps。
  • agent部分:指定流水线的执行位置(Jenkins agent)。流水线中的每个阶段都必须在某个地方(物理机、虚拟机或Docker容器)执行,agent部分即指定具体在哪里执行。
  • 以上每一个部分(section)都是必需的,少一个,Jenkins都会报错。

3.3 Post部分

post部分包含的是在整个pipeline或阶段完成后一些附加的步骤,post部分是可选的,所以并不包含在pipeline最简结构中。但这并不代表它作用不大,根据pipeline或阶段的完成状态,post部分分成多种条件块,包括:

  • always:不论当前完成状态是什么,都执行。
  • changed:只要当前完成状态与上一次完成状态不同就执行。
  • fixed:上一次完成状态为失败或不稳定(unstable),当前完成状态为成功时执行。
  • regression:上一次完成状态为成功,当前完成状态为失败、不稳定或中止(aborted)时执行。•
  • aborted:当前执行结果是中止状态时(一般为人为中止)执行。
  • failure:当前完成状态为失败时执行。
  • success:当前完成状态为成功时执行。
  • unstable:当前完成状态为不稳定时执行。
  • cleanup:清理条件块。不论当前完成状态是什么,在其他所有条件块执行完成后都执行。
pipeline {
    agent any
    
    stages {
        // 定义流水线的阶段
    }
    
    post {
        success {
            // 当流水线成功完成时执行的操作
            // 例如发送邮件通知或触发其他流水线
        }
        
        failure {
            // 当流水线失败时执行的操作
            // 例如发送错误报告或执行回滚操作
        }
        
        always {
            // 无论流水线成功还是失败,都会执行的操作
            // 例如清理临时文件或更新报告
        }
    }
}

3.4 Post支持的指令

  1. agent:指定在哪个节点上运行流水线,可以是任意节点或特定的代理节点。
  2. stages:定义流水线的阶段,每个阶段可以包含一个或多个步骤。
  3. steps:在阶段中定义具体的步骤,用于执行构建、测试、部署等操作。
  4. stage:定义流水线的一个阶段,用于逻辑上划分流水线,并可在 Jenkins 界面中显示可视化进度。
  5. script:在流水线中执行一段 Groovy 脚本代码,可以用于编写自定义逻辑。
  6. input:在流水线中暂停执行,等待用户输入或批准继续执行。
  7. environment:定义环境变量,可以在流水线中传递和使用数据。
  8. parameters:定义流水线的参数,允许用户在执行时提供输入。
  9. options:定义流水线的选项,如超时设置、并行度控制等。
  10. when:根据条件控制流水线的执行路径,可以基于环境变量、构建状态等条件进行条件判断。
  11. post:定义在流水线执行完成后执行的操作,如成功、失败、始终执行等。
  12. parallel:在流水线中并行执行多个步骤或阶段,提高执行效率。

在使用指令时,需要注意的是每个指令都有自己的“作用域”。如果指令使用的位置不正确,Jenkins将会报错。

3.5 环境变量

环境变量可以被看作是pipeline与Jenkins交互的媒介,比如,可以在pipeline中通过BUILD_NUMBER变量知道构建任务的当前构建次数。环境变量可以分为Jenkins内置变量和自定义变量,比如在上面我们发送邮件的时候就用到了jenkins的环境变量

  • BUILD_NUMBER:构建号,累加的数字。在打包时,它可作为制品名称的一部分,比如server-2.jar。BRANCH_NAME:多分支pipeline项目支持。当需要根据不同的分支做不同的事情时就会用到,比如通过代码将release分支发布到生产环境中、master分支发布到测试环境中。
  • BUILD_URL:当前构建的页面URL。如果构建失败,则需要将失败的构建链接放在邮件通知中,这个链接就可以是BUILD_URL。
  • GIT_BRANCH:通过git拉取的源码构建的项目才会有此变量。

这里可以参考全局变量参考
Jenkins_第25张图片

3.6 拆分条件

自动化是指pipeline按照一定的规则自动执行,而这些规则被称为pipeline触发条件。

时间触发

时间触发是指定义一个时间,时间到了就触发pipeline执行。在Jenkins pipeline中使用trigger指令来定义时间触发。tigger指令只能被定义在pipeline块下,Jenkins内置支持cron、pollSCM,upstream三种方式

Jenkins trigger cron语法

  • MINUTE:一小时内的分钟,取值范围为0∼59。
  • HOUR:一天内的小时,取值范围为0∼23。
  • DOM:一个月的某一天,取值范围为1∼31。
  • MONTH:月份,取值范围为1∼12。
  • DOW:星期几,取值范围为0∼7。0和7代表星期天。
  • *:匹配所有的值• M-N:匹配M 到N 之间的值。
  • M-N/X or*/X:指定在M 到N 范围内,以X值为步长。
  • A,B,· · ·,Z:使用逗号枚举多个值。

轮询代码仓库:pollSCM

轮询代码仓库是指定期到代码仓库询问代码是否有变化,如果有变化就执行。
Jenkins_第26张图片

你可能感兴趣的:(运维,jenkins,运维,学习,后端,java)