乔克视界 乔克视界
首页
  • 运维
  • 开发
  • 监控
  • 安全
  • 随笔
  • Docker
  • Golang
  • Python
  • AIOps
  • DevOps
  • Kubernetes
  • 心情杂货
  • 读书笔记
  • 面试
  • 实用技巧
  • 博客搭建
友链
关于
收藏
  • 分类
  • 标签
  • 归档

乔克

云原生爱好者
首页
  • 运维
  • 开发
  • 监控
  • 安全
  • 随笔
  • Docker
  • Golang
  • Python
  • AIOps
  • DevOps
  • Kubernetes
  • 心情杂货
  • 读书笔记
  • 面试
  • 实用技巧
  • 博客搭建
友链
关于
收藏
  • 分类
  • 标签
  • 归档
  • 运维

    • 运维是必需品,安全是奢侈品
    • 应用获取客户端真实IP地址
    • 基于 Kubernetes 容器平台的容灾方案
    • 基于Jira的运维发布平台
      • 面临的问题
      • 为啥选择JIRA?
      • 方案设计
        • 设计思路
        • 发布流程方案
        • 涉及软件
      • Jira与Jenkins进行集成合并分支
        • Jenkins配置
        • Jira上配置
        • 建立工作流
        • 将工作流关联项目组
        • 配置webhook
      • Gitlab与Jenkins集成发布系统
        • 开发分支简要
        • Jenkins配置流水线
      • 写到最后
    • 使用Zadig从0到1实现持续交付平台
    • 使用 Generic Webhook Trigger 触发 Jenkins 多分支流水线自动化构建
  • 监控

  • 开发

  • 安全

  • 随笔

  • 博客
  • 运维
乔克
2025-07-19
目录

基于Jira的运维发布平台

fbe4a8aa76ba48da24874ff3826bef7c MD5

上线发布是运维的日常工作,常见的发布方式有:

  • 手动发布
  • Jenkins发布平台
  • Gitlab CI
  • ......

除此之外还有需要开源软件,他们都有非常不错的发布管理功能。

# 面临的问题

作为运维人员,上线发布是必不可少的一环,一个正常的发布流程是怎么样的?

  • 需求方提发布任务,走发布流程
  • 供应方执行发布上线

环节看似简单,但是中间其实是有断层的。一般企业在走上线流程都是通过一些公共渠道,比如邮件、钉钉、飞书的流程,这些都很难和运维执行上线发布平台进行关联上,而且也不够直观。所以我们就需要解决以下几个问题:

  • 流程和运维平台建立连接
  • 从发起到结束形成闭环

# 为啥选择JIRA?

JIRA优秀的项目管理,问题跟踪的工具,另外它的流程管理和看板模式也能够非常直观看到目前流程处在什么位置。另外它可以通过webhook和其他平台建立友好的连接,方便扩展。再者对于开发、测试、项目管理人员等来说Jira是他们日常的工具,使用熟练度非常高,降低了额外的学习成本。鉴于此,我们选择JIRA作为运维发布平台,争取做到一个平台做所有事。

# 方案设计

# 设计思路

充分利用Jira、Gitlab的webhook功能,以及Jenkins的灵活性。

  • Jira上更新状态触发Jenkins执行合并分支流水线
  • Gitlab上代码合并成功后触发Jenkins执行发布流水线
  • 将发布结果通过钉钉等软件通知相应的人

整体思路相对简单,难点主要集中在Jenkins获取Jira、Gitlab的数据,所幸Jenkins的插件功能非常丰富,这里就使用Generic Webhook Trigger插件,可以很灵活地获取到触发软件地信息。

# 发布流程方案

然后整理出如下地发布流程。

01f0557d0e44e081336a03a827825209 MD5

# 涉及软件

软件 功能
Jira 发布流程管理
Jenkins 执行各种流水线
Gitlab 代码仓库
Kubernetes 应用管理
Helm/kustomize 包管理
钉钉 消息通知
trivy 镜像扫描
镜像仓库 阿里云镜像仓库

PS:这里没有具体的软件部署

# Jira与Jenkins进行集成合并分支

# Jenkins配置

Jenkins的配置主要有两部分,如下:

  • 配置Jenkins ShareLibrary功能
  • 编写Jira触发相应的Jenkinsfile

(1)Jenkins上配置ShareLibarary

系统配置-->系统配置-->Global Pipeline Libraries

60d1df9e861ac50b62582ded558e0d8b MD5

(2)创建流水线,配置Webhook以及添加Jenkinsfile

  • 配置触发器

先配置一个变量和正则

e70c089b27eb7103dd8fc83a2c712b0f MD5

再配置一个Token即可

460c46941b9b58e0d024b9e8f25f296a MD5

  • 配置流水线,添加对于的Jenkinsfile

ab37451ff3c59ae1649ebac7dcec55ae MD5

(3)Jenkinsfile的主要逻辑如下

PS:下面仅列出大致的框架,并没有详细的代码

  • 获取Jira的配置信息进行解析
  • 根据不同信息执行不同的操作
  • 合并分支主要是通过调Gitlab的API接口完成
#!groovy

@Library('lotbrick') _

def gitlab = new org.devops.gitlab()
def tool = new org.devops.tools()
def dingmes = new org.devops.sendDingTalk()

pipeline {
    agent { node { label "master"}}

    environment {
        DINGTALKHOOK = "https://oapi.dingtalk.com/robot/send?access_token=xxxx"   
    }

    stages{

        stage("FileterData"){
            steps{
                script{
                    response = readJSON text: """${webHookData}"""

                    // println(response)

                    env.eventType = response["webhookEvent"]

                    if (eventType == "jira:issue_updated"){
                        // 获取状态值
                        env.jiraStatus = response['issue']['fields']['status']['name']
                        env.gitlabInfos = response['issue']['fields']['customfield_10219']
                        infos = "${gitlabInfos}".split("\r\n")
                        for (info in infos){
                            prName = "$info".split("/")[0]
                            // brName = "$info".split("/")[1]
                            brName = info - "${prName}/"
                            println(prName)
                            println(brName)
                            if (jiraStatus == "已发布(UAT)"){
                                println('进行合并PRE分支操作')
                            }else if (jiraStatus == "已发布(PROD)"){
                                println('进行合并PROD分支操作')
                            }else if (jiraStatus == "已完成"){
								println('进行分支打Tag并删除原分支')
                            }else{
                                println("查无此项")
                            }
                        }
                    }
                }
            }
        }
    }
    // 构建后的操作
	post {
		failure {
			script{ 
				println("failure:只有构建失败才会执行")
				dingmes.SendDingTalk("分支合并失败 ❌")
			}
		}
		aborted {
            script{
				println("aborted:只有取消构建才会执行")
				dingmes.SendDingTalk("分支合并取消 ❌","暂停或中断")
            }
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

以上Jenkins上配置基本完成。

# Jira上配置

Jira上的主要配置如下:

  • 建立工作流
  • 工作流关联项目
  • 配置项目触发Webhook

# 建立工作流

f1cccf394abb87142c49890d5e44e06e MD5

# 将工作流关联项目组

dc35cbf89fcb62b897f1cf8d8dfec680 MD5

# 配置webhook

设置-->系统-->网络钩子

00f04d89a3d882d56f41290375435156 MD5

上面配置完成后,即完成Jira上配置,然后就可以在对应项目的看板上查看所以待发布的项目,如下:

b6756ee2719dbf0aac635b0f2f21fc13 MD5

然后进行拖拽或者点击发布按钮,即可改变状态,触发流水线进行相应的操作了。

# Gitlab与Jenkins集成发布系统

# 开发分支简要

这里主要使用的是功能分支开发模式,主要分为以下几个分支:

  • DEV分支:开发环境分支
  • TEST分支:测试环境分支
  • UAT分支:联调环境分支
  • PRE分支:预发布环境分支
  • MASTER分支:生产环境分支

代码合并路线是:DEV->TEST->UAT->PRE->MASTER

然后根据不同的分支判断执行不同环境的部署。

# Jenkins配置流水线

(1)配置Webhook插件参数

accd3882da1061b0ccb87cadf3a7e12e MD5

获取Gitlab分支

43dfc050de3d7c009f61e18f40f2fafa MD5

定义gitlab push条件,不是任何改动都需要触发流水线

13a7e310cfd26f7da032024516ba0dd6 MD5

8587790e5e816c5373925c07343b081e MD5

7b1de31c8217ff260abd0f5f5b63b947 MD5

定义过滤正则表达式

70c5e085899551f31244e048494c3283 MD5

这样就只有commit的时候才会触发流水线。

(2)配置Jenkinsfile

def labels = "slave-${UUID.randomUUID().toString()}"

// 引用共享库
@Library('jenkins_shareLibrary')

// 应用共享库中的方法
def tools = new org.devops.tools()

def branchName = ""

// 获取分支
if ("${gitlabWebhook}" == "gitlabPush"){
    branchName = branch - "refs/heads/"
	currentBuild.description = "构建者${userName} 分支${branchName}"
}

pipeline {
    agent {
        kubernetes {
            label labels
            yaml """
apiVersion: v1
kind: Pod
metadata:
  labels:
    some-label: some-label-value
spec:
  volumes:
  - name: docker-sock
    hostPath:
      path: /var/run/docker.sock
      type: ''
  - name: maven-cache
    persistentVolumeClaim:
      claimName: maven-cache-pvc
  containers:
  - name: jnlp
    image: registry.cn-hangzhou.aliyuncs.com/rookieops/inbound-agent:4.3-4
  - name: maven
    image: registry.cn-hangzhou.aliyuncs.com/rookieops/maven:3.5.0-alpine
    command:
    - cat
    tty: true
    volumeMounts:
    - name: maven-cache
      mountPath: /root/.m2
  - name: docker
    image: registry.cn-hangzhou.aliyuncs.com/rookieops/docker:19.03.11
    command:
    - cat
    tty: true
    volumeMounts:
    - name: docker-sock
      mountPath: /var/run/docker.sock
  - name: sonar-scanner
    image: registry.cn-hangzhou.aliyuncs.com/rookieops/sonar-scanner:latest
    command:
    - cat
    tty: true
  - name: kustomize
    image: registry.cn-hangzhou.aliyuncs.com/rookieops/kustomize:v3.8.1
    command:
    - cat
    tty: true
  - name: kubedog
    image: registry.cn-hangzhou.aliyuncs.com/rookieops/kubedog:v0.5.0
    command: ['cat']
    tty: true
  - name: trivy
    image: registry.cn-hangzhou.aliyuncs.com/rookieops/trivy:v2
    command: ['cat']
    tty: true
    volumeMounts:
      - name: docker-sock
        mountPath: /var/run/docker.sock
"""
        }
    }

    environment {
        auth = 'joker'
    }

    options {
        timestamps()    // 日志会有时间
        skipDefaultCheckout()    // 删除隐式checkout scm语句
        disableConcurrentBuilds()    //禁止并行
        timeout(time:1, unit:'HOURS') //设置流水线超时时间
    }

    stages {
        // 拉取代码
        stage('GetCode') {
            steps {
                checkout([$class: 'GitSCM', branches: [[name: "${gitBranch}"]],
                    doGenerateSubmoduleConfigurations: false,
                    extensions: [],
                    submoduleCfg: [],
                    userRemoteConfigs: [[credentialsId: '83d2e934-75c9-48fe-9703-b48e2feff4d8', url: "${gitUrl}"]]])
            }
        }

        // 单元测试和编译打包
        stage('Build&Test') {
            steps {
                container('maven') {
                    script {
                        tools.PrintMes('编译打包', 'blue')
                    }
                }
            }
        }
        // 代码扫描
        stage('CodeScanner') {
            steps {
                container('sonar-scanner') {
                    script {
						tools.PrintMes('代码扫描', 'blue')
                    }
                }
            }
        }
        // 构建镜像
        stage('BuildImage') {
            steps {
                container('docker') {
					script {
						tools.PrintMes('构建镜像', 'blue')
					}
				}
            }
        }
		
		// 镜像扫描
		stage('Vulnerability Scanner') {
            steps {
                container('trivy') {
                    script{
						tools.PrintMes('镜像扫描', 'blue')
					}            
                }
            }
        }
		
		// 推送镜像
		stage('Push Image') {
            steps {
                container('docker') {
                    script{
						tools.PrintMes('推送镜像', 'blue')
					}            
                }
            }
        }
		
        // 部署
        stage('Deploy DEV') {
			when {
				branchName 'dev'
			}
            steps {
                container('kustomize'){
					script{
						tools.PrintMes('部署DEV环境','blue')
					}
				}
            }
        }
		stage('Deploy TEST') {
			when {
				branchName 'test'
			}
            steps {
                container('kustomize'){
					script{
						tools.PrintMes('部署TEST环境','blue')
					}
				}
            }
        }
		stage('Deploy UAT') {
			when {
				branchName 'uat'
			}
            steps {
                container('kustomize'){
					script{
						tools.PrintMes('部署UAT环境','blue')
					}
				}
            }
        }
		stage('Deploy PRE') {
			when {
				branchName 'pre'
			}
            steps {
                container('kustomize'){
					script{
						tools.PrintMes('部署PRE环境','blue')
					}
				}
            }
        }
		stage('Deploy PROD') {
			when {
				branchName 'master'
			}
            steps {
                container('kustomize'){
					script{
						tools.PrintMes('部署PROD环境','blue')
					}
				}
            }
        }
		
		// 跟踪应用启动情况
		stage('Check App Start') {
			steps{
				container('kubedog'){
					script{
						tools.PrintMes('跟踪应用启动', 'blue')
					}
				}
			}
		}
		
        // 接口测试
        stage('InterfaceTest') {
            steps {
                sh 'echo "接口测试"'
            }
        }

    }
    // 构建后的操作
    post {
        success {
            script {
                println('success:只有构建成功才会执行')
                currentBuild.description += '\n构建成功!'
                dingmes.SendDingTalk("构建成功 ✅")
            }
        }
        failure {
            script {
                println('failure:只有构建失败才会执行')
                currentBuild.description += '\n构建失败!'
                dingmes.SendDingTalk("构建失败 ❌")
            }
        }
        aborted {
            script {
                println('aborted:只有取消构建才会执行')
                currentBuild.description += '\n构建取消!'
                dingmes.SendDingTalk("构建失败 ❌","暂停或中断")
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261

(3)在Gitlab上配置钩子

settings->webhook

afe2769b207b11d9198ca5c01c345622 MD5

到这里,Gitlab和Jenkins集成就差不多完成了,后面就是具体的调试以及配置了。

# 写到最后

道路千万条,适合自己才最好。

上面是根据工作的实际情况做的运维发布,整体思路还有实现方式并不复杂,主要是充分利用各个软件地webhook能力,以及充分利用Jenkins灵活的插件功能,使得从创建发布计划和执行发布进行打通。

个人觉得还是有必要记录一下,也希望能帮助到有这方面需要的人。

上次更新: 2025/07/19, 11:33:23
基于 Kubernetes 容器平台的容灾方案
使用Zadig从0到1实现持续交付平台

← 基于 Kubernetes 容器平台的容灾方案 使用Zadig从0到1实现持续交付平台→

最近更新
01
读《额尔古纳河右岸》有感
07-19
02
读《命运》有感
07-19
03
读《月亮与六便士》有感
07-19
更多文章>
Theme by Vdoing | Copyright © 2019-2025 乔克 | MIT License | 渝ICP备20002153号 |
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式