作者:喬克

公眾號:運維開發故事

上線釋出是運維的日常工作,常見的釋出方式有:

  • 手動釋出
  • 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介面完成
#!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("分支合併取消 ","暫停或中斷")
}
}
}
}

以上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

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("構建失敗 ","暫停或中斷")
}
}
}
}

(3)在Gitlab上配置鉤子

settings->webhook

到這裡,Gitlab和Jenkins整合就差不多完成了,後面就是具體的除錯以及配置了。

寫到最後

道路千萬條,適合自己才最好。

上面是根據工作的實際情況做的運維釋出,整體思路還有實現方式並不複雜,主要是充分利用各個軟體地webhook能力,以及充分利用Jenkins靈活的外掛功能,使得從建立釋出計劃和執行釋出進行打通。

個人覺得還是有必要記錄一下,也希望能幫助到有這方面需要的人。