第十一篇:jenkins pipline完整流水线实现
一、实现
- gitlab+jenkins+harbor+k8s_rancher的cicd流水线
- 手动提交&gitlab触发判定
- Generic Webhook Trigger触发器
- 钉钉审批
- 构建描述自定义
- 构建名称自定义
- 钉钉通知结果
- gitlab cicd接收构建结果
二、流程图
三、效果展示
四、jenkinsfile
- 在配置pipline之前,需要先安装相关插件和配置相相关凭据,见第二篇:jenkins插件;第三篇:jenkins凭据管理
- 配置时,因为用到了参数化构建,第一次执行会报错,可手动添加参数,也可再执行一次。
pipeline {
agent any
//参数化构建,xxxxxx需根据实际情况修改
parameters {
//触发动作,用于区分是gitlab webhook自动触发还是jenkins手动触发
string(name: 'event_name', defaultValue: 'jenkins_admin', description: 'event name')
//分支名称,用于GenericTrigger-->regexpFilterText 制定匹配分支条件
string(name: 'BRANCHNAME', defaultValue: 'xxxxxx', description: '分支名称')
//用于GenericTrigger token
string(name: 'JENTRIGGERTOKEN', defaultValue: 'xxxxxx', description: '触发器token')
}
//定义用于自动安装和放置在PATH. 如果agent none指定,则忽略。例如自动安装maven、jdk、gradle等
//我这里是springboot项目,需要jdk1.8 maven打包
tools {
//maven
maven 'maven'
//jdk
jdk 'jdk1.8'
}
//环境变量
environment {
//GIT凭证ID,根据实际情况修改
GITCREDENTIALSID='1'
//harbor凭证ID,根据实际情况修改
HARBORCREDENTIALSID='2'
//rancher凭证ID,根据实际情况修改
RANCHERCREDENTIALSID='3'
//版本定义
def DATE = sh(script: "date +%Y_%m_%d", returnStdout:true).trim()
BUILD_VERSION = "${DATE}.${BUILD_NUMBER}"
//#####以下为需要根据实际项目修改#####//
//GIT地址
GIT_ADDR='http://xxxx/xxxx/xxxxx.git'
//分支名称
BRANCH_NAME='xxxxxx'
//打包命令
BUILD_SH='xxxxxx'
//harbor地址
HARBOR_ADDRES='xxxxxx'
//harbor项目地址
HARBOR_PROJECT='xxxxxx'
//rancher工作负载api地址
WORKAPIADRESS='xxxxxx'
//jenkins触发器secretToken,与参数化构建中的保持一致
JENTRIGGERTOKEN='xxxxxx'
//Dockerfile所在相对路径
DOCKERFILEWORK='xxxxxx'
}
//运行参数,不需要修改
options{
//时间戳
timestamps ()
//禁止管道的并发执行
disableConcurrentBuilds()
//构建保留配置
buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '7', numToKeepStr: '9')
}
//触发器配置,不需要修改
triggers{
GenericTrigger(
genericVariables:[
[key:'event_name',value:'$.event_name'], //触发动作 pubat or tag_pubat
[key:'user_name',value:'$.user_name'], //GitLab用户名
[key:'project_name',value:'$.project.name'], //项目名称 DevOps_Test
[key:'git_url',value:'$.project.git_http_url'], //git_url
[key:'ref',value:'$.ref'], //分支或tag信息
[key:'group_name',value:'$.project.namespace'], //GITLAB_GROUP
[key:'commits_id',value:'$.commits[0].id'] //gitlab commits id
],
token:"""${params.JENTRIGGERTOKEN}""", //gitlab webhook触发token 多个任务配置同一个token会一起触发
//causeString:'Triggered on $ref',
causeString:'Triggered on $project_name $event_name by $user_name',
printContributedVariables:true,
printPostContent:true,
//此处的BRANCHNAME是通过环境变量获取构建过程中的当前分支
regexpFilterExpression: 'refs/heads/' + BRANCHNAME,
//regexpFilterExpression:'$ref',
regexpFilterText: """refs/heads/${params.BRANCHNAME}"""
)
}
//阶段
stages {
stage('触发判定') {
steps {
script {
if (params.event_name == 'jenkins_admin') {
echo "This job is triggered by ${event_name}"
currentBuild.description = "Started by jenkins push by ${USER}"}
else {
echo "This job is triggered by Gitlab ${event_name}"
currentBuild.description = "Started by GitLab ${event_name} by ${user_name} branch:${BRANCH_NAME}"
}
}
}
}
stage('构建名称定义') {
steps {
// 自定义设置构建历史显示的名称和描述信息
script {
//设置buildName
wrap([$class: 'BuildUser']) {
// 修改构建历史展示名称
//def deploylog="${BUILD_USER} use pipeline '${JOB_NAME}(${BUILD_NUMBER})' "
//println deploylog
buildName "#${BUILD_NUMBER}-${HARBOR_PROJECT}:${BUILD_VERSION}"
//buildName '#${BUILD_NUMBER} -demo:${HARBOR_PROJECT}:${BUILD_VERSION}'
//修改Description
//buildDescription '<span style="padding-left: 160px;color: #0587d4;"> demo:${buildVersion} ${environment} ${branch} </span>'
}
}
}
}
stage('钉钉审批') {
steps {
script {
//py程序配置的Authorization
hook = registerWebhook(authToken: 'xxxxxx')
webhookId = hook.url.substring(hook.url.lastIndexOf('/') + 1)
dingtalk (
//jenkins配置里钉钉机器人ID
robot: '1',
type: 'ACTION_CARD',
title: '确认发布',
text: [
//'**是否确认发布**',
'## **<font color=blue>是否确认发布</font>**',
'',
'---',
"- 任务名称:${JOB_NAME}",
"- 构建ID:[#${env.BUILD_NUMBER}](${env.BUILD_URL})",
"- 构建人:${env.USER}",
"- 构建说明:${currentBuild.description}",
"- 持续时长:${currentBuild.durationString}"
],
btns: [
[
title: '确认',
//py中转程序外网地址
actionUrl: "http://xxxxxx/jenkins/webhook?url=${webhookId}&type=confirm&jobName=${JOB_NAME}&buildNumber=${env.BUILD_NUMBER}"
],
[
title: '取消',
//py中转程序外网地址
actionUrl: "http://xxxxxx/jenapi/jenkins/webhook?url=${webhookId}&type=cancel&jobName=${JOB_NAME}&buildNumber=${env.BUILD_NUMBER}"
]
]
)
// 30分钟没有确认 取消任务
timeout(time: 1800, unit: 'SECONDS') {
data = waitForWebhook hook
// 解析 JSON 字符串
def json = new groovy.json.JsonSlurperClassic().parseText(data)
def type = json.type
// 判断 type 的值
if (type == 'cancel') {
echo "取消"
currentBuild.result = 'ABORTED'
error('任务被取消')
}
}
}
}
}
stage('代码拉取') {
steps {
git branch: env.BRANCH_NAME, credentialsId: env.GITCREDENTIALSID, url: env.GIT_ADDR
}
}
stage('开始打包') {
steps {
sh """${BUILD_SH}"""
}
}
stage('镜像构建') {
steps {
script {
dir("${WORKSPACE}/${DOCKERFILEWORK}") {
sh 'ls'
docker.withRegistry("${HARBOR_ADDRES}", env.HARBORCREDENTIALSID) {
docker.build("${HARBOR_PROJECT}:${BUILD_VERSION}").push()
}
echo "build DockerImage success"
}
}
}
}
stage('rancher部署') {
steps {
rancherRedeploy alwaysPull: true, credential: env.RANCHERCREDENTIALSID, images: """${HARBOR_PROJECT}:${BUILD_VERSION}""", workload: env.WORKAPIADRESS
}
}
}
post {
always{
script{
println("一直执行")
}
}
success{
script{
println("成功")
updateGitlabCommitStatus name: 'cicd-build', state: 'success'
}
}
aborted{
script{
println("取消")
updateGitlabCommitStatus name: 'cicd-build', state: 'canceled'
}
}
failure{
script{
println("失败")
updateGitlabCommitStatus name: 'cicd-build', state: 'failed'
}
}
}
}
五、python脚本
- cat ding.py
启动 nohup python3 ding.py > ding.log 2>&1 &
# -*- coding: utf-8 -*-
from flask import Flask, request
from jenkinsapi.jenkins import Jenkins
import requests
app = Flask(__name__)
@app.route('/jenkins/webhook', methods=['GET'])
def post():
commitType = request.args.get('type')
webhookId = request.args.get('url')
jobName = request.args.get('jobName')
buildNumber = request.args.get("buildNumber")
# 创建Jenkins对象
## jenkins地址
jenkins_url = 'http://xxxxxx'
## jenkins用户名
username = 'admin'
## jenkins密码
password = 'xxxxxx'
jenkins = Jenkins(jenkins_url, username, password)
# 获取job对象
job = jenkins.get_job(jobName)
# 获取指定构建的信息
build_number = int(buildNumber)
build = job.get_build(build_number)
build_result = build.get_status()
# 打印构建状态
print('Job "{}" build {} status: {}'.format(jobName, buildNumber, build_result))
if build_result == 'ABORTED':
return "<h1 style='font-size: 2em;display: flex;justify-content: center;align-items: center;height: 100%;color:orange;'>任务已中止,请重新构建<h1>"
elif build_result == 'FAILURE':
return "<h1 style='font-size: 2em;display: flex;justify-content: center;align-items: center;height: 100%;color:red;'>任务构建失败,请勿重复点击<h1>"
# 组装webhook基础参数
## jenkins地址/webhook-step
url = 'http://xxxxxx/webhook-step/' + webhookId
data = {'type': commitType}
## Authorization配置,在pipline里用到,用于接口调用验证
headers = {
'Authorization': 'xxxxxx',
'Content-Type': 'application/json'
}
# 调用webhook
try:
response = requests.post(url, json=data, headers=headers)
response.raise_for_status()
except Exception as e:
logging.exception("Failed to call Jenkins webhook step service.")
return "请求失败:" + str(e)
if commitType == 'confirm':
return "<h1 style='font-size: 2em;display: flex;justify-content: center;align-items: center;height: 100%;color: green;'>已同意</h1>" if response.status_code == 200 else "<h1 style='font-size: 2em;display: flex;justify-content: center;align-items: center;height: 100%;color:orange;'>请勿重复操作</h1>"
else:
return "<h1 style='font-size: 2em;display: flex;justify-content: center;align-items: center;height: 100%;;'>已拒绝</h1>" if response.status_code == 200 else "<h1 style='font-size: 2em;display: flex;justify-content: center;align-items: center;height: 100%;color:orange;'>请勿重复操作</h1>"
# 端口配置
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8099)