持續集成將死

在思考「雲時代的研發環境長什麼樣」這個問題的時候,我逐漸意識到一件很重要的事。2000年首次被提出、在過去十幾年中我們習以為常的敏捷核心實踐持續集成,很可能正在走到它生命周期的尾聲。

讓我們來回顧一下Martin Fowler在他那篇著名的文章里如何描述持續集成這個過程:

一旦完成了修改,我就會在自己的計算機上啟動一個自動化build。……當我build成功後,我就可以考慮將改動提交到源碼倉庫。……然而,提交完代碼不表示就完事大吉了。我們還要做一遍集成build,這次在集成計算機上並要基於mainline的代碼。只有這次build成功了,我的修改才算告一段落。……在持續集成環境里,你必須儘可能快地修復每一個集成 build。好的團隊應該每天都有多個成功的 build。錯誤的 build 可以出現,但必須儘快得到修復。

從上面加粗的文字就能看出,過去的十多年裡,在談及持續集成這個實踐時,我們已經預設了這個場景:有一個集中式的持續集成伺服器在監聽代碼庫的變化,每當有人提交代碼時,持續集成伺服器會自動取出最新的代碼,執行整個構建和測試流程。圍繞著這個場景,我們發展出了一整套的紀律來保障持續集成少失敗、失敗的時候能儘快修復。圍繞著這個場景,我們發明了CruiseControl、GoCD、SnapCI等一代又一代的持續集成伺服器,並在我們所有的項目中使用它們。這個場景在我們的腦海中如此根深蒂固以至於我們不再去詢問:為什麼需要這樣做?

實際上,我們需要一個集中式的持續集成伺服器,這是有歷史原因的。2000年代初期的技術時代背景,尤其是以下兩個非常具體的約束條件,造成了今天我們看到的持續集成的形態:

  1. 計算資源短缺。這個約束條件決定了完整的、與生產環境相似的、能執行端到端驗證的環境必定是稀缺品。典型的交付團隊沒有能力給每個成員配備整套環境,只能在他們各自的計算機上模擬一套儘可能接近於生產環境的開發環境。於是開發環境的驗證結果不足為信,必須在一個標準的、更接近於生產環境的集成環境上通過驗證,才能說軟體達到了質量要求。
  2. 版本控制工具的局限性。Subversion(以及其他更早的版本控制工具)在pre-commit階段通過伺服器端回調鉤子很難——如果不是完全不可能的話——得到完整的「提交後版本」,因此svn的pre-commit鉤子基本只能用於檢查提交信息是否符合規範,完整的驗證則必須在代碼已經合入代碼庫之後才能——在一台獨立的「持續集成伺服器」上——進行。

雲計算徹底改變了第一個約束條件。計算資源仍然不能說極大豐富,但企業應用開發所需的x86架構計算資源在雲環境下已經不再短缺,結合各種基礎設施自動化和配置自動化的技術,隨時、按需提供整套環境已經不是難題。而且使用docker等容器技術開通出來的環境是拋棄型的、不可變更的,因此也就不存在環境不一致、驗證結果不可信的問題:每個開發人員都可以從雲上拿出一套環境,執行build,其過程與效果都與持續集成伺服器的build完全一致。

在過去的十多年裡,持續集成之所以必須是一種「技能」、一門「手藝」,而不僅僅是一套工具的定製與實施,很大程度上正是由於計算資源短缺這個約束條件造成的。因為計算資源幾乎總是短缺,所以每個團隊、每個項目擁有的計算資源幾乎總是有些這裡那裡的不同——這個項目可能有兩套完整的測試環境,那個項目可能只有一套。這種資源的局限,逼迫每個項目的技術領導者們不得不根據手上能得到的環境,來微調持續集成的流水線乃至軟體交付的流程。簡言之,流程是依據環境來調整的。

當計算資源短缺的約束條件不再存在,在考慮構建流水線時就可以有一個根本的觀念轉變:可以制訂一套標準的構建流水線,並要求計算環境向這套流水線對齊。這時,持續集成就可以不必是每個團隊的技術領導者都掌握的「技能」和「手藝」,它完全可以在一個組織範圍內定製和大規模實施。因為環境可以彈性地適配流程,我們就能夠為相同類型的項目定義統一的最佳流程。

而git對svn的全面取代則帶來一個細微而深遠的影響:由於可以在pre-commit階段直接獲得完整的待提交快照、並在這個版本基礎上執行測試,不能通過build的代碼將直接被拒絕提交。換句話說,整個「持續集成紀律」嘗試解決的問題——有缺陷的代碼進入團隊的代碼倉庫從而妨礙其他人不斷提交沒有缺陷的代碼——將不復存在,有缺陷的代碼將根本無法進入團隊的代碼倉庫。

綜上所述,這兩個要素的結合:

  1. 每個開發人員(以及自動構建)都可以在PaaS雲上獲得完整的技術棧運行時環境;以及,
  2. pre-commit階段可以對待提交的代碼進行完整的構建

帶來的是一個非常重要的影響:持續集成伺服器這個東西,我們不再需要了。持續集成的「集成」這個動作,將在代碼進入團隊代碼庫之前發生。我們有辦法(git的pre-commit鉤子)確保這次集成發生,也有辦法(雲化、容器化的環境)確保這次集成是可信的。因此我們不再需要一個持續集成伺服器來扮演團隊的守門人。集中式的持續集成伺服器將退化為團隊研發行為的可視化儀錶盤:它不再負責管理環境和構建軟體,只負責採集所有構建中產生的數據、並以適當的形式展示,作為團隊研發過程的可視化呈現。

當持續集成伺服器消亡,一個開發者的典型工作流程可能會是這樣:

  • 從git倉庫clone出代碼,在自己的電腦上做修改;
  • 修改完成,從研發PaaS上獲得一個運行環境,把剛寫好的代碼運行起來,用瀏覽器查看一下效果;
  • 執行構建,構建腳本自動從研發PaaS上獲得一個運行環境,在其中執行編譯、打包、代碼檢查和測試;
  • 構建通過,提交代碼並push,git倉庫的pre-commit鉤子自動觸發一次構建,過程與效果都與剛才手動執行的完全一致;
  • 如果沒有手工執行構建就嘗試提交,自動構建會失敗,代碼無法push到團隊的代碼倉庫中,開發者自己去修復;
  • 如果自動構建成功,代碼提交完成,最新版本的代碼被構建成容器鏡像;
  • 測試人員從研發PaaS上獲得一個運行環境,把待測版本的容器鏡像裝載上去,執行測試,如果測試通過就將該版本標記為「發布候選」;
  • 運維人員從生產PaaS上獲得一個運行環境,把發布候選版本的容器鏡像裝載上去,即完成上線。

這個流程直接地實現了《持續交付》中描述的「兩道門」結構。雖然每個項目運行的環境不同,但這個持續交付的結構可以是完全一致的,因為環境可以彈性地適配研發流程。

與持續集成伺服器同時消亡的,還有持續集成這個概念本身。由於對響應力(responsiveness)的要求是如此之高,現代的IT團隊已經不能容忍有缺陷的代碼先進入代碼庫、阻塞整個團隊的工作、然後再來修復(甚至不修復、或者還需要說服某些團隊成員去及時修復)。持續集成是如此重要,以致於它會變成團隊的「空氣和水」。它會被嵌入到日常的研發工具當中,成為程序員感知不到、而又不可妥協的質量要求——正如IntelliJ之類現代IDE把「通過編譯」這項要求變成了程序員感知不到、而又不可妥協的質量要求。

持續集成對於軟體開發是如此重要,以至於不應該把它交給軟體開發者自己去做。

這就是為什麼我認為持續集成工具、以及這些工具背後的持續集成概念在雲計算深入研發之後將會消亡。取代持續集成的,將是更緊密地內嵌質量要求、更充分地利用雲計算優勢的雲原生(Cloud Native)開發方法及支撐工具。

(本文作者熊節是ThoughtWorks的總監諮詢師)

(作者保留本文一切權利,未經許可請勿轉載)


推薦閱讀:

承諾百分百可靠?你一定是遇到了假雲計算!
學習大數據要從哪些知識點開始著手?
雲計算行業中的影響是否被過分誇大了?
雲計算如果真的成熟了,還會有獨立的操作系統么?或者說,還會有人賣獨立的操作系統么?
Google Drive 支持增量同步嗎?國內使用速度如何?

TAG:持续集成CI | 敏捷 | 云计算 |