作者:乔克

公众号:运维开发故事

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

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

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

面临的问题

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

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

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

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

为啥选择JIRA?

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

方案设计

设计思路

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

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

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

发布流程方案

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

涉及软件

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

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

Jira与Jenkins进行集成合并分支

Jenkins配置

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

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

(1)Jenkins上配置ShareLibarary

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



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

  • 配置触发器

先配置一个变量和正则



再配置一个Token即可

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

(3)Jenkinsfile的主要逻辑如下

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

  • 获取Jira的配置信息进行解析
  • 根据不同信息执行不同的操作
  • 合并分支主要是通过调Gitlab的API接口完成
  1. #!groovy
  2. @Library('lotbrick') _
  3. def gitlab = new org.devops.gitlab()
  4. def tool = new org.devops.tools()
  5. def dingmes = new org.devops.sendDingTalk()
  6. pipeline {
  7. agent { node { label "master"}}
  8. environment {
  9. DINGTALKHOOK = "https://oapi.dingtalk.com/robot/send?access_token=xxxx"
  10. }
  11. stages{
  12. stage("FileterData"){
  13. steps{
  14. script{
  15. response = readJSON text: """${webHookData}"""
  16. // println(response)
  17. env.eventType = response["webhookEvent"]
  18. if (eventType == "jira:issue_updated"){
  19. // 获取状态值
  20. env.jiraStatus = response['issue']['fields']['status']['name']
  21. env.gitlabInfos = response['issue']['fields']['customfield_10219']
  22. infos = "${gitlabInfos}".split("\r\n")
  23. for (info in infos){
  24. prName = "$info".split("/")[0]
  25. // brName = "$info".split("/")[1]
  26. brName = info - "${prName}/"
  27. println(prName)
  28. println(brName)
  29. if (jiraStatus == "已发布(UAT)"){
  30. println('进行合并PRE分支操作')
  31. }else if (jiraStatus == "已发布(PROD)"){
  32. println('进行合并PROD分支操作')
  33. }else if (jiraStatus == "已完成"){
  34. println('进行分支打Tag并删除原分支')
  35. }else{
  36. println("查无此项")
  37. }
  38. }
  39. }
  40. }
  41. }
  42. }
  43. }
  44. // 构建后的操作
  45. post {
  46. failure {
  47. script{
  48. println("failure:只有构建失败才会执行")
  49. dingmes.SendDingTalk("分支合并失败 ")
  50. }
  51. }
  52. aborted {
  53. script{
  54. println("aborted:只有取消构建才会执行")
  55. dingmes.SendDingTalk("分支合并取消 ","暂停或中断")
  56. }
  57. }
  58. }
  59. }

以上Jenkins上配置基本完成。

Jira上配置

Jira上的主要配置如下:

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

建立工作流

将工作流关联项目组

配置webhook

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

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



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

Gitlab与Jenkins集成发布系统

开发分支简要

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

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

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

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

Jenkins配置流水线

(1)配置Webhook插件参数

获取Gitlab分支



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





定义过滤正则表达式



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

(2)配置Jenkinsfile

  1. def labels = "slave-${UUID.randomUUID().toString()}"
  2. // 引用共享库
  3. @Library('jenkins_shareLibrary')
  4. // 应用共享库中的方法
  5. def tools = new org.devops.tools()
  6. def branchName = ""
  7. // 获取分支
  8. if ("${gitlabWebhook}" == "gitlabPush"){
  9. branchName = branch - "refs/heads/"
  10. currentBuild.description = "构建者${userName} 分支${branchName}"
  11. }
  12. pipeline {
  13. agent {
  14. kubernetes {
  15. label labels
  16. yaml """
  17. apiVersion: v1
  18. kind: Pod
  19. metadata:
  20. labels:
  21. some-label: some-label-value
  22. spec:
  23. volumes:
  24. - name: docker-sock
  25. hostPath:
  26. path: /var/run/docker.sock
  27. type: ''
  28. - name: maven-cache
  29. persistentVolumeClaim:
  30. claimName: maven-cache-pvc
  31. containers:
  32. - name: jnlp
  33. image: registry.cn-hangzhou.aliyuncs.com/rookieops/inbound-agent:4.3-4
  34. - name: maven
  35. image: registry.cn-hangzhou.aliyuncs.com/rookieops/maven:3.5.0-alpine
  36. command:
  37. - cat
  38. tty: true
  39. volumeMounts:
  40. - name: maven-cache
  41. mountPath: /root/.m2
  42. - name: docker
  43. image: registry.cn-hangzhou.aliyuncs.com/rookieops/docker:19.03.11
  44. command:
  45. - cat
  46. tty: true
  47. volumeMounts:
  48. - name: docker-sock
  49. mountPath: /var/run/docker.sock
  50. - name: sonar-scanner
  51. image: registry.cn-hangzhou.aliyuncs.com/rookieops/sonar-scanner:latest
  52. command:
  53. - cat
  54. tty: true
  55. - name: kustomize
  56. image: registry.cn-hangzhou.aliyuncs.com/rookieops/kustomize:v3.8.1
  57. command:
  58. - cat
  59. tty: true
  60. - name: kubedog
  61. image: registry.cn-hangzhou.aliyuncs.com/rookieops/kubedog:v0.5.0
  62. command: ['cat']
  63. tty: true
  64. - name: trivy
  65. image: registry.cn-hangzhou.aliyuncs.com/rookieops/trivy:v2
  66. command: ['cat']
  67. tty: true
  68. volumeMounts:
  69. - name: docker-sock
  70. mountPath: /var/run/docker.sock
  71. """
  72. }
  73. }
  74. environment {
  75. auth = 'joker'
  76. }
  77. options {
  78. timestamps() // 日志会有时间
  79. skipDefaultCheckout() // 删除隐式checkout scm语句
  80. disableConcurrentBuilds() //禁止并行
  81. timeout(time:1, unit:'HOURS') //设置流水线超时时间
  82. }
  83. stages {
  84. // 拉取代码
  85. stage('GetCode') {
  86. steps {
  87. checkout([$class: 'GitSCM', branches: [[name: "${gitBranch}"]],
  88. doGenerateSubmoduleConfigurations: false,
  89. extensions: [],
  90. submoduleCfg: [],
  91. userRemoteConfigs: [[credentialsId: '83d2e934-75c9-48fe-9703-b48e2feff4d8', url: "${gitUrl}"]]])
  92. }
  93. }
  94. // 单元测试和编译打包
  95. stage('Build&Test') {
  96. steps {
  97. container('maven') {
  98. script {
  99. tools.PrintMes('编译打包', 'blue')
  100. }
  101. }
  102. }
  103. }
  104. // 代码扫描
  105. stage('CodeScanner') {
  106. steps {
  107. container('sonar-scanner') {
  108. script {
  109. tools.PrintMes('代码扫描', 'blue')
  110. }
  111. }
  112. }
  113. }
  114. // 构建镜像
  115. stage('BuildImage') {
  116. steps {
  117. container('docker') {
  118. script {
  119. tools.PrintMes('构建镜像', 'blue')
  120. }
  121. }
  122. }
  123. }
  124. // 镜像扫描
  125. stage('Vulnerability Scanner') {
  126. steps {
  127. container('trivy') {
  128. script{
  129. tools.PrintMes('镜像扫描', 'blue')
  130. }
  131. }
  132. }
  133. }
  134. // 推送镜像
  135. stage('Push Image') {
  136. steps {
  137. container('docker') {
  138. script{
  139. tools.PrintMes('推送镜像', 'blue')
  140. }
  141. }
  142. }
  143. }
  144. // 部署
  145. stage('Deploy DEV') {
  146. when {
  147. branchName 'dev'
  148. }
  149. steps {
  150. container('kustomize'){
  151. script{
  152. tools.PrintMes('部署DEV环境','blue')
  153. }
  154. }
  155. }
  156. }
  157. stage('Deploy TEST') {
  158. when {
  159. branchName 'test'
  160. }
  161. steps {
  162. container('kustomize'){
  163. script{
  164. tools.PrintMes('部署TEST环境','blue')
  165. }
  166. }
  167. }
  168. }
  169. stage('Deploy UAT') {
  170. when {
  171. branchName 'uat'
  172. }
  173. steps {
  174. container('kustomize'){
  175. script{
  176. tools.PrintMes('部署UAT环境','blue')
  177. }
  178. }
  179. }
  180. }
  181. stage('Deploy PRE') {
  182. when {
  183. branchName 'pre'
  184. }
  185. steps {
  186. container('kustomize'){
  187. script{
  188. tools.PrintMes('部署PRE环境','blue')
  189. }
  190. }
  191. }
  192. }
  193. stage('Deploy PROD') {
  194. when {
  195. branchName 'master'
  196. }
  197. steps {
  198. container('kustomize'){
  199. script{
  200. tools.PrintMes('部署PROD环境','blue')
  201. }
  202. }
  203. }
  204. }
  205. // 跟踪应用启动情况
  206. stage('Check App Start') {
  207. steps{
  208. container('kubedog'){
  209. script{
  210. tools.PrintMes('跟踪应用启动', 'blue')
  211. }
  212. }
  213. }
  214. }
  215. // 接口测试
  216. stage('InterfaceTest') {
  217. steps {
  218. sh 'echo "接口测试"'
  219. }
  220. }
  221. }
  222. // 构建后的操作
  223. post {
  224. success {
  225. script {
  226. println('success:只有构建成功才会执行')
  227. currentBuild.description += '\n构建成功!'
  228. dingmes.SendDingTalk("构建成功 ")
  229. }
  230. }
  231. failure {
  232. script {
  233. println('failure:只有构建失败才会执行')
  234. currentBuild.description += '\n构建失败!'
  235. dingmes.SendDingTalk("构建失败 ")
  236. }
  237. }
  238. aborted {
  239. script {
  240. println('aborted:只有取消构建才会执行')
  241. currentBuild.description += '\n构建取消!'
  242. dingmes.SendDingTalk("构建失败 ","暂停或中断")
  243. }
  244. }
  245. }
  246. }

(3)在Gitlab上配置钩子

settings->webhook

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

写到最后

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

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

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

基于Jira的运维发布平台的设计与实现的更多相关文章

  1. 不看好运维竖井产品模式,优云打造融合化运维PaaS平台

    2018年1月13号中国双态运维用户大会上,优云软件总裁刘东海接受了36Kr记者的专访,期间谈到了新时代下的企业运维模式,新兴技术和传统运维的融合以及优云未来的发展方向等问题.以下为访谈实录: 优云软 ...

  2. 建设DevOps统一运维监控平台,全面的系统监控 Zabbix VS Nagios VS Open-Falcon OR Prometheus

    前言 随着Devops.云计算.微服务.容器等理念的逐步落地和大力发展,机器越来越多,应用越来越多,服务越来越微,应用运行基础环境越来多样化,容器.虚拟机.物理机不一而足.面对动辄几百上千个虚拟机.容 ...

  3. 开源运维自动化平台-opendevops

    开源运维自动化平台-opendevops 简介 官网 | Github|  在线体验 CODO是一款为用户提供企业多混合云.自动化运维.完全开源的云管理平台. CODO前端基于Vue iview开发. ...

  4. 详解Linux运维工具:运维流程管理、运维发布变更、运维监控告警

     概述 应用上线后,运维工作才刚开始,具体工作可能包括:升级版本上线工作.服务监控.应用状态统计.日常服务状态巡检.突发故障处理.服务日常变更调整.集群管理.服务性能评估优化.数据库管理优化.随着应用 ...

  5. 13. Redis监控运维云平台CacheCloud

    13. Redis监控运维云平台CacheCloud13.1 CacheCloud是什么13.1.1 现有问题13.1.2 CacheCloud基本功能13.2 快速部署13.2.1 CacheClo ...

  6. 【原创】基于Docker的CaaS容器云平台架构设计及市场分析

    基于Docker的CaaS容器云平台架构设计及市场分析 ---转载请注明出处,多谢!--- 1 项目背景---概述: “在移动互联网时代,企业需要寻找新的软件交付流程和IT架构,从而实现架构平台化,交 ...

  7. ylbtech-KeFuYunWei(服务运维考核系统)-数据库设计

    ylbtech-DatabaseDesgin:ylbtech-KeFuYunWei(服务运维考核系统)-数据库设计 DatabaseName:KEFUYUNWEI Model:Admin 用户后台管理 ...

  8. Docker搭建Jenkins+Gogs+Maven/Gradle——代码自动化运维部署平台(三)

    一.简介 1.CI/CD CI/CD 是一种通过在应用开发阶段引入自动化来频繁向客户交付应用的方法.CI/CD 的核心概念是持续集成.持续交付和持续部署.作为一个面向开发和运营团队的解决方案,CI/C ...

  9. 基于 ANSIBLE 自动化运维实践

    摘要:运维这个话题很痛苦,你做任何的产品都离不开运维.不管你用什么语言.什么平台.什么技术,真正能够决定你产品成熟度的很有可能就是你运维的能力.取自 云巴 CEO 张虎在 ECUG 大会上的分享. 云 ...

随机推荐

  1. python进阶(16)深入了解GIL锁(最详细)

    前言 python的使用者都知道Cpython解释器有一个弊端,真正执行时同一时间只会有一个线程执行,这是由于设计者当初设计的一个缺陷,里面有个叫GIL锁的,但他到底是什么?我们只知道因为他导致pyt ...

  2. Printer Queue UVA - 12100

    The only printer in the computer science students' union is experiencing an extremely heavy workload ...

  3. JAVAEE_Servlet_24_HttpSession实现原理

    关于JavaWeb中的HttpSession (一) * Session表示会话,不止存在于JavaWeb之中,只要是Web开发都会存在这种机制 * Session包:javax.servlet.ht ...

  4. Day16_89_通过反射机制获取所有构造方法

    通过反射机制获取某个特定的构造方法 * 代码 import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; publ ...

  5. 基于.Net Core 5.0 Worker Service 的 Quart 服务

    前言 看过我之前博客的人应该都知道,我负责了相当久的部门数据同步相关的工作.其中的艰辛不赘述了. 随着需求的越来越复杂,最近windows的计划任务已经越发的不能满足我了,而且计划任务毕竟太弱智,总是 ...

  6. 829. Consecutive Numbers Sum

    Given a positive integer N, how many ways can we write it as a sum of consecutive positive integers? ...

  7. 2.1.1- css产生的原因

    CSS的发展历程 从HTML被发明开始,样式就以各种形式存在.不同的浏览器结合它们各自的样式语言为用户提供页面效果的控制.最初的HTML只包含很少的显示属性.随着HTML的成长,为了满足页面设计者的要 ...

  8. Linux中常见的150个命令(干货)

    目录 线上查询及帮助命令 文件和目录操作命令 查看文件和内容处理命令 文件压缩及解压缩命令 信息显示命令 搜索文件命令 进程管理相关命令 用户管理命令 基础网络操作命令 深入网络操作命令 有关磁盘与文 ...

  9. Portswigger web security academy:Server-side request forgery (SSRF)

    Portswigger web security academy:Server-side request forgery (SSRF) 目录 Portswigger web security acad ...

  10. python通过字符串定义函数名

    记录python里的一个有意思的小技巧:通过字符串定义函数名称. import sys m=sys.modules[__name__] def temp(x): return x+1 setattr( ...