持續交付探索與實踐(一):交付流水線的設計

g發表於2021-12-28

一、前言

移動網際網路時代,對於質量和效率的追求是永恆不變的目標,持續交付能力的建設則是提升效能和質量的重要的手段之一,自Jez Humble等人提出持續交付的理念以來,已經過去了10餘年,隨著微服務架構與雲端計算、容器化等新興技術的發展,持續交付的概念又重新回到了大家的視野,各類相關的工具、產品和服務如雨後春筍般湧現。

雖然有了先驅者提出的先進思想和各大廠商的優秀工具,但是不同公司、不同產品、不同的技術棧、不同的開發與部署策略註定了持續交付的實施是因人而異的,很難有一種實踐方式或工具能做到對所有業務場景的完美契合。

所以,打造一套符合自身產品和業務特性的持續交付系統和最佳實踐方案,就成了我們不斷探索的方向,接下來我將介紹一下我們部門在持續交付系統建設方面所進行的實踐。

二、為什麼要建立交付流水線?

交付流水線

20世紀初福特開創了首個汽車生產流水線,帶來了汽車生產效率的革命性提升,最終讓福特汽車開進了千家萬戶;軟體行業也是如此,要想實現持續、快速的交付,交付流水線是核心。它就像一個故事的主線,貫穿整個研發流程,銜接並展示了從程式碼提交、構建、部署、測試直至最終釋出的整個過程,為團隊提供狀態視覺化和即時反饋的能力。

所謂狀態視覺化,就是能直觀的看到分支、專案、版本等維度的進展及相應的問題,提升資訊的透明度以便更好的對整個專案進行把控;而即時反饋,主要是為了將整個研發過程中不必要的損耗降至最低,比如研發提交新的程式碼後立即觸發相應的構建,分支提測後立即通知測試進行介入等。通過流水線將關聯事項緊密連線,可以最大程度的減少過程等待;同時,交付流水線通過與一系列工具鏈的結合,我們還相當於無形之中建立了一套統一的標準和流程。

隨著業務的不斷髮展,我們的持續交付流水線主要經歷了以下幾個階段。

三、交付流水線的架構演進

1)1.0時代:基於UI配置的流水線

平臺建設初期,我們主要是通過jenkins的UI配置job,這種方式主要是通過jenkins web頁面配置的方式來定義構建流程,同時結合相應的外掛來實現一定流水線控制,比如通過multi job來實現多個子job的並行和串聯,通過trigger外掛傳遞當前構建的引數並觸發下一個job,如:

這種方式在我們平臺建設初期應對簡單的業務流程是完全夠用的,但隨著接入業務線的增加,該方式存在的問題和痛點逐漸暴露,比如:

  • 每個業務線需要獨立配置相應的流程,當基礎流程發生變化時,所有的業務線都需要進行相應的調整,配置工作量較大
  • 另外,由於對應的構建命令和指令碼都是都是直接配置並應用在對應的job中的,新的配置應用會直接覆蓋舊有的配置,若出現問題無法便捷的回滾
  • 最後,複雜的流程配置較為繁瑣,比如要用到multi job等一系列外掛來進行序列、並行流程控制

2)2.0時代:基於jenkins pipeline的流水線管控

為了解決上述痛點,我們引入了jenkins pipeline。所謂Pipeline,就是一套執行於Jenkins上的工作流框架,將原本獨立執行於單個或多個節點的任務連線起來,實現單個任務難以完成的複雜釋出流程。

jenkins pipeline

Pipeline的實現方式是一套基於Groovy的DSL(類似Gradle),任何釋出流程都可以表述為一段Groovy指令碼,並且支援從程式碼庫直接讀取,我們當時比較典型的構建pipeline結構示例如下:

pipeline {
    agent {
        label 'pipeline'
    }
    stages {
        stage('程式碼掃描&單元測試') {
            agent {
                label 'sonar'
            }
            steps {
                parallel '程式碼檢測':{
                    script{
                        //省略...
                    }
                },
                '單元測試':{
                    script {
                        //省略...
                    }
                }
            }
        }
        stage('自動部署') {
            when {
                expression { env.skips.indexOf('deploy')<0 }
            }
            agent {
                label 'make'
            }
            steps {
                    script {
                        //省略...
                    }
            }
        }
        stage('介面自動化') {
            when {
                expression { env.skips.indexOf('iface')<0 }
            }
            agent {
                label 'junit'
            }
            steps {
                    script {
                        //省略...
                    }
            }
        }
    }
    post {
        always {
            script {
                sh "curl -X POST 'http://***.net/ci/api/commmon/fast_result/' -d 'env=${env.env}&branch=${env.ref}'"
            }
        }
    }
}

說明:

  • Jenkinsfile : 是Pipeline的定義檔案,由Stage,Node,Step組成,一般存放於程式碼庫根目錄下
  • Stage : 一個Pipeline可以劃分為若干個Stage,每個Stage代表一組操作。
  • Node: 一個Node就是一個Jenkins節點,或者是Master,或者是Agent,是執行Step的具體執行期環境。
  • Step: Step是最基本的操作單元,小到建立一個目錄,大到構建一個Docker映象,由各類Jenkins Plugin提供。

最終的構建流水線如下:

可以看到,基於這種方式能很方便的定義不同構建步驟的執行順序、串聯或並行狀態、以及對應構建的所執行機器節點,相比1.0時代基於web化的配置主要有以下優點:

  • 工作流配置程式碼化:改變原先手動配置每個jenkins job的方式,通過程式碼倉庫的jenkinsfile控制整個構建流程,方便通過gitlab等版本控制系統去管理配置,無論是後續配置更新、回滾、還是批量複製建立都更加靈活方便,做到真正的pipeline as code;
  • 工作流配置可下發:基於jenkinsfile的定製化流程配置職責可下發給對應的業務團隊,對於持續交付平臺來說只是讀取不同倉庫的jenkinsfile執行統一的pipeline job即可,避免了原先集中化配置任務帶來的管理維護成本高的問題;
  • 支援更復雜的工作流:有著更靈活的構建流程控制,方便順序/並行執行,執行節點分配等。相比普通job,不用再去配置一堆前置、後置條件及對應的multijob。

3)3.0時代:自研的可編排、視覺化事務流

隨著公司業務不斷的發展,各業務線研發管控流程的不斷優化、細化,我們發現Pipeline流水線也逐漸顯現出了它的侷限性,已經無法滿足各業務線日益增長的定製化需求了,比如:

1)自動化流程中穿插人工稽核節點:針對涉及金錢及核心業務的需求我們往往需要更嚴格的稽核流程,有時需要在自動化節點間增加額外的人工Code review確認節點

2)流程錯誤重跑希望只跑部分節點:當流程中的某個節點執行失敗後,基於效率考慮我們只希望重跑特定的節點而非重新執行整個pipeline

3)下游節點關聯上游節點狀態:比如冒煙測試失敗後,我們希望將整個流程自動打回到提測前的狀態

針對以上場景,如果基於jenkins pipeline來實現會有較大成本,而且也不便於擴充;為此,實現一套具有高度可定製化和可擴充性的交付流水線便被提上了日程,最終我們決定自研流水線底層元件並設計上層流水線模型,替代 Jenkins pipeline外掛,以提供更加靈活的流水線編排和排程能力,核心模組設計方案如下:

上述方案通過定義基礎事務並細化事務關係配置實現了對事務的精細化編排,賦予了事務流更高的定製化能力。對於不同的業務線,無非就是基於基礎事務傳入不同的構建引數,同時結合業務自身要求對各類事務的構建順序、構建關係進行定製化編排即可,較好的滿足了各業務線差異化、特性化的研發交付流程

如果說1.0時代通過UI配置流水線的方式是雕刻版印刷術的話,那麼3.0時代就是活字印刷術,每一類構建任務就好比一個個的字塊,通過配置中心的事務編排能力就能實現不同的排列組合。很好的做到了可編排、可複用。

四、交付過程的視覺化應用—讓風險可以被看見

在完成上述的交付流水線改造後,我們又在此基礎上封裝了各個維度的自定義檢視,並通過打通交付過程中所涉及到的其他外部系統,最終形成了目前的這套視覺化的持續交付平臺。系統部分截圖如下:

卡片化的整體檢視,方便觀察各需求對應分支的整體進展及相關核心資料,整個流水線按照我們的研發流程分為5個環節——研發、測試、整合、灰度、釋出,每個環節對應實際研發流程中的特定階段

結合各類篩選條件能較直觀的看到哪些分支有質量問題阻斷、對應的需求總體進展如何、某個版本有哪些需求進度偏慢,做到資訊透明度的提升以便更好的對整個專案的進度和質量進行把控。

流水線化的環節檢視,方便跟進對應環節的交付進展及對應的交付質量、交付效率情況,環節檢視主要展示元素有:

  • 流水線檢視:重新設計的類blue ocean的檢視,相比原生外掛所提供的功能,我們能更方便的定製展示樣式和互動方式,比如展示每個節點的構建時長、日誌檢視改為點選對應節點後彈窗展示等;
  • 質量校驗閥:針對所有需求設定對應的質量閥,達到一定的質量標準才允許通過。比如單元/介面通過率達到100%,程式碼規範問題為0,增量程式碼健康度>80等,能直觀的看到當前分支存在哪些阻斷問題;
  • 需求資訊:展示需求相關的關鍵資訊,如專案管理平臺需求單、分支對應的gitlab merge request、釋出系統對應的申請單等等,方便檢視相關資訊並及時跟進;
  • 流程操作:流程中需要人工節參與的環節所對應的操作項,如測試驗收通過/不通過、程式碼稽核通過/不通過等,通過打通研發流程中涉及的其他第三方系統,使得對應的操作入口統一,無需多系統切換。

五、交付流程中的多平臺打通—打破組織間的牆

另外,整個研發交付過程中也會涉及到多個組織、多套系統,我們通過打通交付過程中所涉及到的其他外部系統,形成了整個研發流程的一站式體驗;
比如,我們通過打通gitlab和專案管理平臺,使得研發同學可以直接在系統上進行分支建立、合入以及技術方案的稽核等;通過打通灰度系統和eflow流程系統,能直接在系統上進行灰度釋出提單、灰度策略的調整以及結果檢視等等。
通過上述實踐,使得這套系統從單純的交付流水線平臺逐漸轉變為了綜合性的研發協作平臺

六、結語

通過建立以上標準化、視覺化的交付流水線並搭建相應的協作平臺,我們主要解決了如下幾類問題:

  • 質量標準不透明,無法及時反饋

從程式碼提交到最終的釋出上線,會經歷多個環節,每個環節又存在多項測試驗收等動作,每個業務每個環節的質量標準也不盡相同。原先結果的收集彙總和通知反饋都是對應的需求跟進人或版本負責人進行人工收集和反饋,存在資訊不透明、反饋不及時的問題。現在通過結合流程的質量閥建立相應的質量標準,並結合大盤的視覺化展示和及時通知,有效的解決了溝通層面的低效以及資訊傳遞過程中的損耗,也能較直觀的看到版本、需求、分支等維度的進展,方便PM及相關負責人及時跟進並解決相關問題,從而促進了整體交付效率的提升。

  • 環節流轉不夠緊密,需要人工參與

對於整個交付流程來說,只要有人工參與的地方就勢必會存在效率方面的損耗。我們通過結合上面提到的質量標準,通過系統自動計算當前的質量資料來判定是否可以進行環節的流轉,比如是否達到提測條件、是否達到合入整合分支條件,從而儘可能的排除人為因素導致的流程流轉中的耗時,做到更加的準確和及時,大大減少了過程等待。

  • 交付過程不順暢,涉及多個站點

我們通過打通交付過程中涉及到的其他外部系統,使得原本需要去不同平臺操作的動作直接在持續交付平臺上就可以完成,無需在多個系統之間頻繁切換,形成了一站式體驗,使得整個交付過程中的操作體驗更加的順暢

以上就是我們在持續交付流水線設計方面所進行的一些改進和實踐。當然,這套系統和實踐方案不一定適合所有的公司,這裡主要是分享一下在此過程中碰到的問題、痛點以及相應的改進思路,希望能給想建設或正在著手建設持續交付平臺的朋友們提供一些有益的參考。

下一篇預告:    持續交付實踐(二):自動化工具鏈建設

相關文章