• 其一:業務層面,重點是中臺規劃和建設
  • 其二:技術層面,重點是雲原生解決方案,包括了微服務,DevOps和容器雲

當然,如果你是傳統的軟件開發框架技術,或者傳統的基於虛擬機的PaaS平臺也可以上DevOps實踐,但是我們更加推薦的還是基於微服務和容器雲技術來實踐DevOps。

對於DevOps基本概念的理解

首先看下DevOps的定義:

DevOps(英文Development和Operations的組合)是一組過程、方法與系統的統稱,用於促進開發(應用程序/軟件工程)、技術運營和質量保障(QA)部門之間的溝通、協作與整合。它的出現是由於軟件行業日益清晰地認識到,爲了按時交付軟件產品和服務,開發和運營工作必須緊密合作。

對於DevOps的提出已經很多年,其主要的推動來自兩個方面:

  • 業務和需求驅動下,推動敏捷方法論,更快的發佈和交付。
  • 技術和運維部門需要銜接,在PaaS和容器技術發展下進一步推動自動化。

雖然是研發,測試QA,運維交付三方交融的地方爲DevOps的內容,但是可以看到DevOps本身更多的是要解決兩大類協同和自動化的問題

其一是開發部門和QA的協同

實際上該問題基於多年前已有的每日構建,持續集成方法論就可以解決。在DevOps裏面我們也看到這部分協同更多的是敏捷研發最佳實踐進行融合。

其二是開發部門和運維的協同

該問題的解決當前由雲平臺,微服務架構,容器技術發展推動的自動化發佈和監控運維。注意在持續集成的時候我們只解決了自動化部署問題,但是沒有解決持續交付的問題。而在DevOps裏面需要同時解決持續集成和麪向用戶的持續交付能力。

問題目標和解決關鍵點技術的對應

把上面思路理清楚後,對於DevOps要做或需要解決的事情也就更加明確了:

  • 實現自動化部署和資源動態擴展-Docker容器雲+K8s集羣管理
  • 實現不同環境遷移和持續交付 - Docker容器雲+持續集成流水線
  • 能夠滿足更加高頻率的發佈節奏 - 敏捷開發,持續集成,自動化流水線
  • 能將QA和QC部分檢驗和審查工作自動銜接到發佈和交付過程 - 自動化測試,自動化監控

以上實際上就是我們經常談到的DevOps關鍵內容。但是如果一個企業要完全實踐DevOps的話就不不僅僅是簡單的工具技術使用,還涉及到研發過程改進,組織文化等一系列的問題。而國內本身也給出了一個DevOps成熟度模型。

DevOps成熟度模型

整個DevOps成熟度模型可以算作是企業實踐DevOps過程的一個參考基準,該文檔本身由阿里,騰訊,華爲,中興,平安等大的企業共同參與完成,其總體架構包括了三大部分,即過程,應用設計,風險管理和組織結構。而裏面的過程管理本身涵蓋的子過程和活動要求又最多。整個過程本身按持續生命週期階段分爲了敏捷開發管理,持續交付,技術運營三個方面的內容。

敏捷開發管理

在這裏完全可以參考Scrum敏捷開發方法論最佳實踐進行。裏面的核心對象是產品,需求,項目,迭代版本,任務,缺陷。而這些都將和持續集成和交付發生關聯。

如果做過CMMI或敏捷項目管理的可以看到實際上在當前DevOps成熟度模型中的敏捷開發管理還是相當粗的,而且是將軟件工程域內容和過程管理內容融合在了一起,同時也可以看到其核心還是基於scrum敏捷項目管理方法論而展開,只是在這個過程中更加強調了業務場景驅動和價值交付的重要性。

DevOps持續集成和交付本身就是爲了更加敏捷響應需求,快速短週期的迭代交付,因此和敏捷方法論配合是自然的事情。同時我們也可以看到要實現敏捷, 需求必須細粒度化,需求本身需要體現業務價值 ,核心就是基於業務場景分析出來的用戶故事和用戶故事地圖。

而對於敏捷開發或敏捷項目管理,如果要用一句話來總結的話就是

基於業務場景和用戶故事驅動的短週期迭代和項目團隊高效協同,以實現快速的業務價值交付。 而這個目標下我們經常談到的可視化看板,站立會議,燃盡圖等也僅僅是實現上述目標的工具。

也正是這個原因我們看到,要進入到敏捷開發過程管理,形成基於業務場景形成具備優先級劃分的細粒度用戶故事至關重要,這將直接是我們後面迭代計劃安排,測試用例編寫,交付交付的基礎。

持續交付

注意不是我們傳統的持續集成,而變成了持續交付。對於持續交付即包含了產品從需求開發開始,一直到最終客戶的發佈交付過程,是一個完整的端到端流程。對於整個過程的支撐我們看到核心仍然是持續集成和容器雲的集成來完成,而整個集成過程又通過流水線進一步自動化。

技術運營

注意是技術運營而不是運維,雖然在技術運營裏面也包括了運維監控類的內容。但是當升級爲技術運營後進一步體現了通過對發佈產品各自運營手段來持續的促進產品優化改進工作。這個也是我們在原來各類方法論中很少提及到的點。

注:對於DevOps成熟度模型,後面專門寫一篇文章來談,在此不再展開。

持續集成過程回顧

在談DevOps持續交付這個過程域之前,準備把原來敏捷開發或軟件工程裏面的持續集成最佳實踐再做下回顧,要知道早在10多年前我們就已經在實踐持續集成,每日構建,自動化測試和冒煙測試,因此對於持續集成並不是新鮮事物。唯一我們看到的是在 DevOps最佳實踐裏面將持續集成過程變得更加靈活,自動化和可編排了。

對於持續集成的過程,我們以每天進行一次持續集成爲例來進行說明

  1. 所有開發人員,每天在下班前Check in自己開發完成並單元測試通過的代碼
  2. 統一的配置庫環境服務器(CI服務器),Update到最新的源代碼文件
  3. 基於自動化編譯配置文件,執行自動化的代碼編譯,並輸出編譯完成二進制包
  4. 將相關部署文件根據自動化執行腳本安裝和部署到我們的測試服務器
  5. 安裝部署成功後,執行自動化測試腳本,輸出自動化測試報告
  6. 測試人員介入進行功能點的黑盒測試,並反饋測試缺陷和跟蹤缺陷在下次集成版本中解決

以上是我們最早使用的持續集成過,也沒有和Docker容器相結合,對於自動部署過程也是通過自定義腳本來完成部署。而現在我們可以看到在DevOps裏面的持續集成更加強調了 容器化技術的結合和整個持續集成過程的可配置性。

簡單理解,就是上面提到的6個步驟,你是可以自己靈活組合和可視化編排的。

在傳統的單體應用架構下,我們最終是打包成一個大的部署包,而實際在編譯構建中存在諸多的依賴關係和構建順序,而這些內容都是我在構建腳本中通過配置文件或代碼進行控制的。

而在新的微服務架構模式下,各個微服務模塊相互獨立開來,都可以獨立構建打包,但是模塊之間的關聯關係依然是存在的。也就是說從一個包含了多個微服務模塊的大的應用來說,我們在大構建過程中仍然有依賴和先後關係。

即: 流水線編排本身應該分層,頂層爲產品或達應用,下層爲各個獨立微服務模塊

在整個持續集成的過程中一定會涉及到環境的遷移問題,比如SIT測試完成後需要遷移到UAT環境,而UAT環境測試完成後需要遷移到生產環境。

不論是最早的持續集成最佳實踐,還是當前的結合容器鏡像的DevOps過程實踐,我們始終在強調 一次構建多地部署,即最終的環境遷移是基於二進制部署包或容器鏡像進行的 。在後續的多個測試環境和生產環境,不能再執行編譯構建和打包動作。

一般考慮在配置庫update到最新代碼後,就可以進行代碼靜態檢查,檢查完成後再進行編譯和打包,代碼靜態檢查問題不用影響到後續的直接構建過程。在部署完成後再進行自動化的單元測試操作,按道理自動化單元測試或冒煙測試不通過應該影響到後續的人工介入測試。

持續集成實際上全新的產品開發和交付只是其一,更加重要的是用於後續的變更管理和變更版本,補丁版本的開發和交付上線。這個過程本身的效率和自動化程度真正體現了開發,質量和運維人員的協同度。

流水線設計實踐和優化改進

對於DevOps流水線可視化設計,主要是由各類任務串聯起來,而對於任務本身又分爲兩種類型,一種是自動化任務,一種是人工執行任務。具體如下:

  • 自動化任務:包括了代碼靜態檢查,構建,打包,部署,單元測試,環境遷移,自定義腳本運行等。
  • 人工任務:人工任務主要包括了檢查審覈,打標籤基線,組件包製作等類似工作。

而通常我們看到的流水線基本都由上述兩類任務組合編排而成,一個流水線可以是完全自動化執行,也可以中間加入了人工干預節點,在人工干預處理後再繼續朝下執行。比如流水線中到了測試部署完成後,可以到測試環境人工驗證環節,只有人工驗證通過再流轉到遷移發佈到生產環境動作任務。

DevOps流水線實際上和我們原來經常談到的持續集成最佳實踐是相當類似的,較大的一個差異點就在於引入了容器化技術來實現自動化部署和應用託管。但是在DevOps實踐中,是否必須馬上將項目切換到微服務架構框架模式,反而不是必須的。

在整個DevOps流水線中,我們實際上強調個一個關鍵點在於 一套Docker鏡像文件+多套環境配置+多套構建版本標籤 做法。以確保我們最終構建和測試通過的版本就是我們部署到生產環境的版本。

構建操作只有一次,而後面到測試環境,到UAT環境,到生產環境,都屬於是鏡像的環境遷移和部署。而不涉及到需要再次重新打包的問題。這個是持續集成,也是DevOps的基本要求。

今天談DevOps流水線編排,主要是對流水線編排本身的靈活性進一步思考。

構建操作: 構建我們通常採用Maven進行自動化構建,構建完成輸出一個或多個Jar包或War包。

注意常規方式下構建完執行進行部署操作,部署操作一般就是將構建的結果拷貝到我們的測試環境服務器,同時對初始化腳本進行啓動等。而在DevOps下,該操作會變成兩個操作,即一個打包,一個部署。打包是將構建完成的內容製作爲鏡像,部署是將鏡像部署到具體的資源池和指定集羣。

打包操作: 實際上即基於構建完成的部署包來生成鏡像。該操作一般首先基於一個基礎鏡像文件基礎上進行,在基礎鏡像文件上拷貝和寫入具體的部署包文件,同時在啓動相應的初始化腳本。

構建操作和打包操作的松耦合

那麼首先要考慮構建操作和打包操作如何松耦合開,打包操作簡單來就是就是一個鏡像製作,需要的是構建操作產生的輸出。我們可以對其輸出和需要拷貝的內容在構建的時候進行約定。而打包任務則是一個標準化的鏡像製作任務,我們需要考慮的僅僅是基於1)基於哪個基礎鏡像 2)中間件容器默認目錄設置 3)初始化啓動命令。即在實際的打包任務設計的時候,我們不會指定具體的部署包和部署文件,這個完全由編排的時候由上游輸入。

難點在哪?實際上在於我們dockerfile文件中往往增加了非標準化的自定義運行腳本,導致了鏡像製作過程和你最終打包生成二進制文件產生了很強的關聯性。而要做到徹底解耦,就需要對這部分進一步進行標準化。

部署操作: 部署操作相當更加簡單,重點就是將鏡像部署到哪個資源池,哪個集羣節點,初始化的節點配置等。具體部署哪個鏡像不要指定,而是由上游任務節點輸入。

DevOps持續集成和研發測試過程協同

流水線設計屬於持續交付過程域中的一個關鍵內容,其核心還是爲了持續集成服務。那麼我們首先回顧下流水線作業解決什麼問題?

簡單來說就是 解決自動化的持續集成問題 ,那麼持續集成本身又包括哪些關鍵內容?即我們常說的自動化編譯構建,自動化部署,自動化測試。而在轉到和容器技術結合後可以看到在編譯構建完成後自動化部署進行了拆分,即部署動作變化爲了首先是自動化的打包即製作容器鏡像,然後纔是自動化部署,部署部署基於部署包的,而是基於製品庫中的容器鏡像的。

說這麼多,可以看到,在DevOps中的流水線設計更多的是 解決開發人員的自動化問題,即開發只需要check in自測通過的代碼到配置管理庫,那麼後續的所有事情都自動化完成 ,最終部署到測試環境供測試人員測試。開發人員不用關心編譯構建過程,不用關心測試服務器包括測試環境數據庫,中間件的安裝部署等一系列的問題。

同時由於一系列的自動化操作,也讓代碼從檢入到交付測試的週期縮短了,在週期縮短後交付頻度也可以進一步提升以加快迭代速度。

今天再談流水線設計,主要原因是 原來更多是從技術層面開發人員自動化在談,但是卻少考慮研發管理開發和測試過程協同問題。

在DevOps最佳實踐裏面分爲了研發管理,持續交付和技術運營幾個關鍵的過程域。

但是在實踐的過程中,最容易出現問題的不是單個技術點,而是跨域的協同問題,或者說 研發過程管理和持續集成交付本身就是密不可分的兩個部分 ,我們只是爲了容易理解和學習將其劃分爲了不同的過程域而已。

因此 流水線設計需要理清研發過程管理和自動化持續集成間的協同關係 。具體兩者的協同關係我們用下圖來進行說明。

要明白任何一次新的編譯構建部署完成後都涉及到測試人員測試,測試人員測試出問題後又會提交Bug,開發人員修改Bug後Check in代碼,等待下一次打包部署以形成多次迭代。整個過程最好方式就是要儘量 減少大量的人工溝通協同,而是應該通過工具鏈協同 來完成。

對於傳統的持續集成,一般最佳實踐爲每天晚上進行自動化構建和冒煙測試,而對於當期的DevOps過程,在設計完流水線後,可以手工去啓動流水線作業,也可以自動去執行流水線,流水線執行時間頻度也可以進一步縮短。

假設我們每2個小時自動化的執行一次流水線作業,我們以此場景來做進一步梳理。因爲對於大部分企業來說再高頻的構建集成也不需要代碼有變動就馬上觸發構建打包的程度。

流水線增加啓動檢查節點

雖然2小時執行一次流水線,但是在執行前先進行啓動前檢查,如果在最近2個小時內沒有新代碼check in,那麼不執行流水線。其次,如果上一次流水線執行實例還處於待處理或未關閉狀態的時候,同樣也不執行流水線作業而直接跳過。

是否需要人工驗證

流水線打包部署包括兩個方面,一個是新功能提交或新bug解決,只有這種情況才需要人工驗證。因此一次流水線執行如果沒有新需求或Bug狀態變化,那麼應該直接跳過人工驗證節點並關閉。反之,則應該跳轉到待人工驗證環節。

需求和缺陷的管理

要注意到新需求和新缺陷都應該提交,而且都有狀態,需求細分到具體的需求功能點,同時測試人員提交的缺陷應該對應到具體的需求功能點上面。一個需求開發完成後,需求本身也到待驗證狀態,但是一個待驗證的需求是否能夠關閉則必須是該需求下面所有的bug都解決完成後才能夠關閉。

需求和缺陷狀態的變化

開發人員首先是將需求或缺陷完成,並在本機進行自測通過,然後將代碼check in到配置管理庫。同時手工將需求或缺陷狀態處理到待部署狀態。在流水線啓動後,如果整個構建打包和部署成功,則在成功完成應用部署後,將待部署狀態的需求或bug轉到待驗證狀態。在部署完成後測試人員可以看到待驗證的bug或需求,那麼他進入當前的測試環境一定是最新的可以進行缺陷驗證的環境。

應用部署和環境遷移

實際上我一直在思考如何理清這兩者的關係,包括在流水線節點設計的時候是同種類型的一個節點還是不同的兩個節點?應用來說是直接將編譯打包後的鏡像執行進行部署,前面有編譯構建操作;而對於環境遷移來說則是直接從製品庫裏面使用已有鏡像進行環境部署。

流水線模板和流水線實例應該是兩個不同的概念,一個流水線模板每次運行都會產生一個實例,而每次實例都會形成一次構建版本號。即每次打包後形成的鏡像入庫也會附帶上這麼一個流水線實例號。那麼我們的應用部署操作本身就簡單的,對於應用部署活動節點編排在流水線上面,作用是進行應用部署,但是本質是從製品庫拿到對應鏡像去部署?如何拿到對應鏡像,基於流水線實例號就可以很好的進行對接上。

環境遷移-從SIT到UAT環境

在環境遷移中,我們設置兩個環境,一個SIT環境供內部測試人員測試,一個UAT環境供客戶方進行UAT測試,那麼在SIT測試完成後可以將SIT環境的鏡像包遷移到UAT環境進行部署。那麼並不是每一次流水線作業都涉及到環境遷移操作,而實際上需要測試人員去判斷當前的測試版本是否需要遷移到UAT環境去供用戶測試。

同時測試人員在當前測試問題全部修復並關閉後,可以對當前版本設置配置基線。其次,對於用戶測試提交的問題一般並不會在我們自己的缺陷管理系統進行Bug記錄,因此用戶反饋問題給測試人員,測試人員幫忙錄入到缺陷管理系統,同時缺陷修改完成後測試先要驗證沒有問題,再通知用戶進行驗證,只有用戶驗證通過後缺陷才能夠最終關閉。

變更驅動的版本開發和流水線設計

對於一個變更,如果只涉及到一個微服務模塊的變動,那麼相當來說整個持續集成過程是很簡單的,我們也很容易在DevOps上完成這個流水線設計並執行。但是如果涉及多個整個過程就複雜了很多。

我們舉例來說,現在接收到一個或多個用戶變更需求,經過需求分析後發現實際影響三個微服務模塊都需要進行配套變更才能夠完成。那麼我們可以規劃一個研發小版本來解決,即首先該需求就會拆分,並對應到三個微服務模塊變更的任務。

在這種場景下變更驅動場景下,我們可以保留原來的大而全的頂層流水線,但是對應沒有代碼變化版本不再執行編譯構建操作。

一個大應用涉及多個微服務,務必要執行無變更不重新編譯構建原則。但是我們實際看到在很多DevOps實踐中,一個變更版本開發,往往也會對沒有變動的微服務重新構建打包。

即大流水線執行到子流水線的時候自動跳過一些子流水線的執行。當然我們也可以重新規劃一個新的頂層流水線,只選擇有變更的三個微服務模塊進行編排設計,同時根據依賴關係定義好三個模塊本身的編譯構建順序。

那我們整個頂層流水線執行的時候就會將三個變更模塊全部編譯構建並打包部署,然後驅動後面的自動化測試,人工測試和驗證。整個需求的實現,缺陷的修改過程應該是完整可視的。簡單來說,基於這個變更小版本,提交的幾個需求變更點當前已經實現了幾個,究竟還有多少缺陷在處理,我們應該一清二楚的瞭解到。

DevOps過程和容器雲協同

對於微服務架構框架選擇Spring Cloud框架來實現,不同的業務組件模塊單獨打包,同時將業務組件的對外接口API通過註解的方式快速的暴露爲Rest API接口。同時對於單個業務組件模塊我們進一步實現展現層和後端邏輯+服務層的分離,每一個業務組件獨立構建爲兩個部署包,並且可以獨立部署,通過這種分離後我們在開發上也實現前後端角色的分離。

代碼放置在外網的GitHub代碼倉庫,通過Maven來實現代碼的構建,同時在Jenkins中本身集成Maven,對於構建完成的部署包會進入到Jenkins本身的部署包倉庫管理。

這個完成後進一步啓用Jenkins來調用底層的Docker命令生成Docker鏡像文件,這個鏡像則是我們後續做自動部署和持續集成的關鍵鏡像文件。

我們知道Docker本身僅僅是容器,是資源調度的單位,在當前開源解決方案中用的比較多的是通過Kubernetes實現容器集羣管理和資源調度能力。因此在這個步驟完成後即對接到Kubernetes來實現Docker鏡像文件的自動化部署和動態調度。

在持續集成中有一個重點就是環境遷移,注意每次遷移的都應該是相同的鏡像文件,而對於和環境相關的配置則單獨進行配置或放置到OS的環境變量中。只有這樣才能夠保證最終上線的部署包就是我們最終開發和測試完成的部署包而沒有任何變動。

如果在動態部署的時候啓用了多個節點,那麼還需要提供負載均衡能力。要注意的是Kubernetes本身也提供負載均衡和虛擬IP路由能力。我們最終訪問的是域名,而域名最終會解析到實際的計算節點。

在微服務架構中,還通過微服務網關對所有註冊的接口服務進行完整的管理和監控,包括服務的註冊發佈,服務的路由,服務日誌,服務安全和限流等,這些通過微服務網關實現。

DevOps過程和API網關協同

對於API網關,我在前面文章都單獨談到過。在一個大型項目的多團隊協同下,如果都採用微服務架構,我們實際建議的是每個團隊都是自己獨立的微服務註冊中心,負責團隊內部多個微服務模塊之間的API接口調用,這些API接口調用走註冊中心即可,但是涉及到跨團隊協同的API接口服務時,就需要註冊到API網關進行統一管理。

簡單來說就是, 對外發布API或者跨團隊API接口調用都涉及將API註冊接入到網關統一管理

對於一個微服務模塊和API網關的協同,包括了提供API接口服務註冊和接入到網關,也包括了從網關調用API接口服務消費。因此需要從API註冊接入和API消費調用兩個方面來談協同。

API接口服務自動註冊接入網關

對於整個DevOps過程可以看到,底層是Docker容器+K8s資源調度,在我們編排流水線的時候涉及到編譯構建和打包,部署等各個動作。實際上可以看到在完成自動部署後接口服務會暴露一個k8s提供出來的動態ip訪問地址。而我們需要做的是將這個ip地址提供出來的接口註冊和接入到網關。

在整個過程搞清楚後,實際上可以有兩種方式來處理API註冊接入:

  • 在部署節點,增加自定義腳本,通過運行腳本來完成API接口註冊。
  • 增加接口註冊流水線編排節點,編排註冊節點,在該節點完成接口註冊內容。

由於整個DevOps流水線設計和執行偏開發人員使用,可以看到,採用第一種方式往往更加靈活。唯一需注意的就是在定義某一個流水線的時候,需要預先規劃好需要接入和註冊的接口內容。

而在DevOps支撐平臺雖然不需要完整的API網關管控功能,但是最好還是能夠在DevOps支撐平臺查詢到當前已經註冊和接入了哪些接口服務,註冊接入後提供的代理服務地址是什麼,是哪個微服務模塊註冊接入的該服務等基本服務目錄信息。

API接口消費調用

在採用了API網關後帶來的一個好處就是,API網關本身提供出來的API訪問地址的IP是固定的,不會隨着每次微服務模塊的自動構建和部署動態變化。對於API網關我們會提前先部署到測試環境和生產環境,在網關部署完成後再開始進行各個微服務模塊的持續集成和部署操作。

因此一個微服務模塊需要訪問其它微服務模塊哪些接口,一個方法是每次都到服務註冊中心去查詢具體的服務訪問地址,另一個方法就是微服務模塊本身要將訪問地址存在在本地配置文件中。而更好的方法是:

  • 首先調用先訪問服務註冊中心,獲取服務訪問地址,並存在到本地配置文件。
  • 在發現本地配置有服務訪問地址後,不再從服務註冊中心調用,除非有地址變更消息通知。

在這個確定後,微服務模塊本身的構建打包和部署,實際上和原來沒有和API網關協同是完全一樣的,只是配置文件訪問地址固定爲了API網關提供的地址而已。如何知道API網關提供了哪些地址,即我們談到的可以在API網關的管控平臺查詢,也可以在DevOps平臺提供的服務目錄查詢功能上進行查詢。

API接口本地調式

在前面我們談到過,在持續集成過程中的環境設置是否需要設置開發環境的問題。對於這個問題,個人的觀點是,在開發人員的本地開發項目只包括自己的開發包,其它交互和協同都需要調用其它模塊的API接口服務來完成的時候,必須設置開發環境,而且要確保開發環境和測試環境分離。

當開發人員在本地調試的時候,涉及到外部模塊的接口則直接配置爲開發環境接口地址進行本地調試,而外部模塊提供的接口也需要提前開發完成且已部署到開發環境上面。

API接口環境遷移

環境遷移是另外一個在協同過程中必須解決的關鍵問題。

即在環境遷移後,需要對服務訪問地址的配置文件中的ip地址進行自動化修改,這個過程可以在流水線編排的時候用腳本代碼來完成。我們可以準備多個備用的配置文件,在環境遷移的時候用當前環境的配置文件對缺省配置文件進行覆蓋即可。

相關文章