[持續交付實踐] 最後一千米,你須要一套具有質量思惟的發佈平臺!

前言

發佈是持續交付的最後一千米。
傳統上,軟件的最終發佈是個充滿壓力的過程,須要大量的手工配置、操做和團隊配合。爲了發佈的可靠性,開發人員須要準備詳盡的部署文檔,而後再把相關信息同步給運維人員執行部署,由運維人員執行一系列個性化的發佈腳本,部署完後還須要測試人員作詳盡的手工驗證。
每一個步驟裏都有不少須要人爲判斷和信息溝通的事情,稍有不慎就很會產生人爲錯誤形成系統故障,發佈時間和結果都不可預測,發佈以後忙活到凌晨,絞盡腦汁想着怎麼讓剛剛部署的應用程序可以正常工做,最後經常不得不回滾,相似這樣的場景都很常見。
要解決這樣的痛點,除了在軟件研發時採用小步迭代的方式,下降交付複雜度外,一套易用、快速、穩定、容錯力強,必要時有能力快速回滾的發佈系統必不可少,本文將重點建設微醫在發佈平臺建設過程當中的一些優秀實踐。java

核心特性

  • 覆蓋主流應用:Java、NodeJS、Python、PHP、Lua、Android、IOS等。
  • 支持分批發布和灰度發佈
  • 支持發佈暫停和恢復
  • 支持歷史版本的快速回滾
  • 支持應用重啓和中止等操做
  • 支持多實例應用日誌聚合查看
  • 支持分佈式的發佈節點
  • 發佈前質量紅線卡點
  • 發佈中實時監控分析
  • 發佈後質效度量

總體架構

 

 

 

  • 典型應用發佈流程:
    • 系統立項後,在WCP-應用管理中申請建立應用,輸入包括開發語言、jdk版本、構建工具、部署方式、產物路徑、代碼地址、應用負責人等應用基礎信息(應用信息是全部研發協做的基礎)。
    • 應用建立後,在WCP-資源管理中申請申請資源,在CMDB中自動創建應用和IP/域名之間的關係(包括測試環境、預發環境、生產環境等)。
    • 應用創建後,發佈平臺自動生成發佈job,發佈時獲取應用和資源等基礎信息,可觸發灰度發佈或分批發布的執行。
    • 發佈操做前,自動執行質量紅線檢查(主要包括pipeline執行結果以及發佈檢查表確認等),質量紅線未達標拒絕發佈。
    • 發佈操做中,自動暫停監控,灰度發佈或首批發布後,自動觸發監控。若監控失敗,中止發佈;若監控經過,可繼續發佈。
    • 發佈操做後,採集存儲發佈數據,輸出給質效看板作發佈數據度量(發佈成功率,發佈頻率,發佈時長等)。
    • 發佈出問題,可執行快速回滾等功能,並提供發佈日誌以及應用聚合日誌的查詢,以快速定位問題。
  • 發佈平臺界面:
     

發佈前質量卡點

質量是持續交付的內置特性。在發佈這最後一千米,如何經過發佈平臺自動化作好質量紅線的卡點,是發佈平臺的一個重要特性。
在Pipeline流水線中,開發代碼提交後自動觸發單元測試,、靜態代碼掃描、安全測試、集成測試, 構成了開發和測試共同參與的一套流水線。
發佈卡點是用於保障交互質量的重要手段,爲了達到持續交付的目標,咱們把研發pipeline執行結果做爲質量紅線(也能夠增長人工的發佈檢查表結果),以此方式來保障整個持續交付的順利進行。算法

 


經常使用的自動化質量卡點策略:shell

 

  • 研發流水線狀態
    • 單元測試結果
    • 單元測試覆蓋率
    • 代碼靜態檢查
    • 集成測試結果
    • 安全掃描結果
  • 發佈計劃狀態(發佈計劃管理系統)
    • 發佈時間窗口
    • 發佈評審結果等

發佈中質量監控

爲保障系統的穩定性,咱們每一個應用在監控平臺都配置有對應的撥測監控點。
如何減小發布時的告警誤報,或者避免發佈過程當中出現重大故障,這裏須要發佈平臺和監控平臺配合作一系列精密的控制策略。編程

  • 發佈場景1:
    • 描述:發佈新版本,在重啓某個實例服務時,有必定的機率會觸發該實例的應用報警,對應用開發人員會形成一些沒必要要的干擾。
    • 策略:發佈平臺在將應用編譯打包好,執行正式發佈以前,調用監控平臺API中止該應用下的監控任務,在發佈完後一樣調用監控平臺API啓用該應用下的監控任務。
  • 發佈場景2:json

    • 描述:發佈時應用實例由於各類緣由(如代碼部署出錯,新版本存在明顯BUG等),出現了系統故障。
    • 策略:採用分批發布策略,各個實例發佈完後當即觸發該實例的監控,若是監控發現異常,標識該批次發佈操做失敗,並強制停止後續批次的發佈操做,以免更多的實例出現問題。
  • 邏輯流程安全

     

    這裏須要強調的是,撥測監控覆蓋率在微醫會做爲團隊的重要指標。由於應用只有在配置監控點後,發佈平臺才能在發佈過程當中進行有效的監測和干預。

     

發佈後質效度量

質效度量是研發協做平臺的一個重要組成部分,主要質效指標將按照研發質量、研發效率、研發成本三方面進行細分。服務器

 


其中在發佈過程當中產生的數據,將會輸送給質效度量系統進行質效分析,重點包括架構

 

  • 發佈頻率
  • 發佈時長
  • 發佈成功率等。

全部團隊和研發成員能夠結合這些發佈數據指標,發現本身存在的問題和短板,並進行有效改進。app

分批發布

分批發布是批次進行應用部署,每次僅對應用的一部分實例進行升級。分批發布過程當中若是出現故障,則終止回退,待問題修復後從新發布。
這裏咱們採用了比較簡潔的批次分配算法,由於公司目前使用的是雙機房IDC,當應用進行分批發布時,首批發佈會在兩個機房中隨機各選擇一個實例執行,其餘的實例則放到了第二批發布。負載均衡

 


選擇發佈暫停,則可在首批發布完後暫停發佈,等人工確認首批發布的實例沒有問題後,再執行後續其餘實例的發佈,如此可有效保障發佈的穩定性。
發佈過程當中,可在發佈平臺中實時查看運行日誌,若發現問題,可隨時執行暫停、取消或者回滾等操做。

 

 

  • 最佳實踐

每一個實例進行部署時,須要保證沒有請求會派發到該實例,不然用戶就會看到502的錯誤。因此須要有一個「下線」的操做,把當前機器從負載均衡中摘除,而後在部署完成以後,再把本身掛回到負載均衡中,這個過程稱爲「上線」。
爲了實現該目的,可基於OpenResty自研Nginx網關對實例上下線進行實時調度,基於 OpenResty 的 Nginx 網關的實現過程比較複雜,這裏再也不詳盡展開。

問題響應

在發佈過程當中,若是出現了一些意料以外的狀況,發佈平臺也提供了一些經常使用的功能,知足開發人員定位和處理問題的須要,同時也儘可能避免開發人員直接登陸服務器操做。

  • 查看日誌

主要對接了公司統一的日誌平臺系統,可實時查看應用日誌,而且聚合了多實例的日誌信息,減小几個實例不停切換尋找問題的痛苦。

 

 

  • 重啓或中止實例

某個實例故障時,可快速重啓或停用實例。

 

 

  • 快速回滾

每一個發佈的版本發佈平臺都會有備份,當發佈新版本發現問題時,可快速回滾到歷史版本

 

 

Jenkins Pipeline

在整套發佈平臺中,Jenkins Pipeline提供了核心的構建、打包、部署以及分佈式調度的底層基礎能力,只不過爲了更靈活的調度發佈操做、管理應用與發佈任務之間關係等,咱們摒棄了Jenkins自身的UI界面,而經過發佈平臺調用Jenkins API的方式將其定位爲基礎引擎。
其中Jenkins Pipeline的共享庫特性,讓咱們經過groovy編程的方式,很好的實現了發佈腳本的版本管理,不再用發愁怎麼管理那堆凌亂的shell腳本了。
這裏只截取一部分結構代碼,Jenkins共享庫的具體使用可參見以前的系列文章。

import groovy.json.JsonSlurper
def call(Map map) {
pipeline {
agent any
parameters {
//java應用參數
string(name: 'BUILD_TOOL', defaultValue: 'maven', description: '構建工具')
string(name: 'MAVEN_VERSION', defaultValue: 'maven3', description: 'maven構建工具版本')
string(name: 'GRADLE_VERSION', defaultValue: 'Gradle3.3', description: 'gradle構建工具版本')
string(name: 'WAR_RELATIVE_PATH', defaultValue: '', description: 'war包地址')
string(name: 'WAR_STD_NAME', defaultValue: '', description: 'war包地址')
string(name: 'POM_RELATIVE_PATH', defaultValue: '/pom.xml', description: 'pom文件地址')
string(name: 'HAS_TEMPLATES', defaultValue: 'false', description: '是否有模板文件')
string(name: 'TEMPLATES_RELATIVE_PATH', defaultValue: '', description: '模板文件路徑')
string(name: 'JETTY_VERSION', defaultValue: '', description: 'jetty版本')
string(name: 'GRADLE_TASK', defaultValue: 'war', description: 'gradleTask打包方式')
......
}
tools {
gradle "${params.GRADLE_VERSION}"
jdk "${params.LANGUAGE_VERSION}"
maven "${params.MAVEN_VERSION}"
}
stages {
stage('部署正式環境') {
steps {
script {
def pmap = [:]

try {
//應用參數傳遞
pmap.put('BUILD_TOOL', BUILD_TOOL.trim())
pmap.put('WAR_RELATIVE_PATH', WAR_RELATIVE_PATH.trim())
pmap.put('WAR_STD_NAME', WAR_STD_NAME.trim())
pmap.put('POM_RELATIVE_PATH', POM_RELATIVE_PATH.trim())
pmap.put('HAS_TEMPLATES', HAS_TEMPLATES.trim())
pmap.put("TEMPLATES_RELATIVE_PATH", TEMPLATES_RELATIVE_PATH.trim())
pmap.put('JETTY_VERSION', JETTY_VERSION.trim())
pmap.put('GRADLE_TASK', GRADLE_TASK.trim())
......

} catch (MissingPropertyException ex) {
println("Catching the MissingPropertyException " + ex.messageWithoutLocationText)
......
}
pmap = Utils_EnvConfig(pmap)
//發佈前監控調度
Utils_Monitor(pmap.isRestartMonitor,monitorApiDomain,appIpName,monitorTimeOut,true)
switch (ACTION) {
case "package":
java_package(pmap)
break
case "copy":
java_copy(pmap)
break
case "start":
java_start(pmap)
break
case "rollback":
java_rollback(pmap)
break
case "restart":
java_start(pmap)
break
case "stop":
java_start(pmap)
break
case "kill":
java_start(pmap)
break
case "backup":
java_backup(pmap)
break
default:
echo "pipeline do nothing please choose one step (package,copy,start,rollback,restart,stop,kill)"
}
//發佈後監控調度
Utils_Monitor(pmap.isRestartMonitor, monitorApiDomain, appIpName, monitorTimeOut, false)
}
}
}
}
}
}

結語

一套好的發佈平臺能夠充當最後的守衛角色,對交付給線上用戶的產品進行最後的檢查,將未達到要求的軟件版本擋於門外,一套完善的自動化發佈平臺也每每會比制訂各種書面上的發佈制度更爲有效。這套發佈平臺從19年年初開始重構,從原有一個單純驅動shell腳本操做的發佈工具,逐漸進化成內嵌大量質量和效率特性的發佈平臺,過程收穫良多。這裏一方面得益於持續交付的先進工程理念,另外一方面也是站在了Jenkins Pipeline以及內部積累的大量成熟基礎設施之上,讓咱們在開發時事半功倍。固然這套平臺也還有很多能夠繼續增強的功能,好比灰度發佈的能力、基於更多質量指標對發佈先後智能分析的能力等等,這些都在規劃和進行之中。中秋節將至,有閒餘時間碼篇文章分享給你們,祝中秋節快樂吧。

相關文章
相關標籤/搜索