《從頭搭建持續內建 DevOps 流水線》由資深靈活教練、極限程式設計學院進階講師、CODING 特邀靈活顧問李小波老師主講,将基于 CODING 展示如何編寫 Jenkinsfile 搭建 CI/CD 流水線,包括單元測試,端到端測試,代碼規範檢查,制品庫,Docker 化部署。
大家好,今天課程的主要内容為如何從頭搭建 DevOps 流水線以及其在研發工作中的意義,最後是 DevOps 流水線實踐與靈活開發的關系的總結。

最開始是在極限程式設計裡提出了持續內建,然後 ThoughtWorks 又提出了持續傳遞,之後又提出了DevOps 這個概念,為什麼要做這些呢?我認為原因是随着時間的增長,生産力會不斷的下降。團隊剛開始時效率很高,從 0 到 1,功能上線很快,但是到了後期速度就會越來越低,直到最後開發停滞。系統開發的後期往往會出現三個難點:第一,增加新特性難。随着系統功能的累積,會出現很多重複的代碼以及不合理的設計,導緻增加一個新特性時要改的地方非常多,改動成本非常高;第二,修複缺陷難。使用者可能會報一些缺陷或 Bug,但是因為代碼太亂、太複雜導緻很難定位到缺陷;第三,引出新的缺陷。做新 Feature 時,很容易出現打地鼠的現象,按下一個 Bug 結果又冒出來三個 Bug,給團隊帶來困境。
出現這些現象的原因首先是在需求演變的過程中,有的代碼會不斷地腐壞,方法會越來越長,類會變得越來越大,代碼出現大量的重複。雖然有些團隊會制定代碼規範,但在實際應用中,可能基本上都在 Word 或 PDF 裡“躺着”,檢查執行難以長期堅持。
其次是架構也會腐壞。項目開始時架構師通常會根據業務設計好架構,有多少個子產品、對象,分到幾層,哪層可以調,哪層不能調,怎麼依賴關系,這些都會很清楚,但在不斷的演變過程中,架構往往會變得亂七八糟。
戴明(William Edwards Deming)提出了品質内建的概念,即産品的品質在建設過程中就已經嵌入,并不是靠後期的檢測來發現的。後期的檢測并不能增進代碼品質,也不能提升産品品質,問題發現的越早修複的成本越低。是以我們希望每一次往代碼庫送出代碼時,都能夠馬上獲得回報:這次修改是好的還是不好的,是不是增加了重複的代碼,是不是降低了測試覆寫率,是不是破壞了某一些功能等等。有了這樣的評判标準,就能夠始終保證每個人每次送出代碼都是在産品上增加價值,而不是破壞它。在這個背景下就引入了流水線這個概念。
流水線是一個隐喻,意思是将軟體研發的各個環節銜接起來。我認為流水線在研發管理過程中扮演了三個角色:不辭辛勞的臨時工、鐵面無私的守護者以及快速精準的操作員。
流水線是不辭辛勞的臨時工。現在的建構流水線都可以按需建立。比如說 CODING,這麼多的企業在用它的持續內建功能,不可能給每一個使用者配置設定固定的計算、存儲等資源。如果要能線性的增長,政策應該是當使用者需要建構時會按需進行建立,并且用完之後進行銷毀。從這一點上看,流水線就很像一個臨時工。除此之外,流水線還可以不厭其煩地做重複的事情,尤其是持續內建的團隊每天都要送出很多次代碼,每一次送出都人工做一次檢查就很痛苦,但機器就能重複機械運動。
流水線是鐵面無私的守護者。首先是代碼規範。很多開發團隊的代碼規範都“活”在 Word 或 PDF 裡,即使有資深教練或者技術 Leader 偶爾會做一些代碼評審,這種執行力度也是遠遠不夠的。但是通過內建到流水線中的方式,比如指定一個方法不能超過多少行,一個類不能超過多少行,代碼重複率不能超過多少,代碼的寬度及命名等,進行自動化、标準化的檢測,就可以有效的保證代碼規範的落地;第二是測試覆寫率。在日常的開發工作中會有單元測試、元件測試、接口測試、內建測試、端到端測試等多種測試,每一次送出代碼都需要檢查測試覆寫率有沒有下降。但很多團隊這一塊是缺失的,他們在流水線上隻是做了建構、打包、部署等動作,并沒有跑測試覆寫率,而是靠大規模的手工測試來保證品質,導緻無法快速疊代;第三是架構限制。代碼一般有分層,每一層裡應該放什麼檔案,哪個檔案能夠調哪個檔案,這些都是有限制的,需要一套自動化的機制來保證落地;其次還有安全性檢查。比如做 Web 開發會引入一些第三方的開源代碼,這些開源代碼往往會有安全缺陷,需要在每次引入新内容時進行安全性檢查。
流水線是快速精準的操作員。越複雜的系統,環境就越多,包括開發聯調環境、測試環境、預釋出環境等,到正式的環境還會有多個執行個體。每個環境上通路資料庫的 URL 不一樣,通路其他服務的環境也會不一樣,如何保證在操作過程中都不出錯?可以依靠流水線來标準化、流程化、自動化地完成這些動作,每次代碼送出時都檢查規範,針對不同的環境打出不同的包,持續的部署到不同的環境上面。
那麼如何搭建一條流水線?有一種方式叫做流水線即代碼(Pipeline as Code),即把流水線放到代碼裡。我認為這樣做的好處是版本化,傳統的搭建方式問題在于操作沒有記錄,也無法強制 Review,當一台伺服器挂掉,換一台伺服器時需要把原來流水線的配置重新操作一遍。如果将流水線變成代碼,就可以跟蹤及重複建立,提高生産效率。另外補充一點,流水線在建構時主要有兩種方式,一個叫聲明式,一個叫指令式。聲明式就是規定好環節與步驟,需要怎樣的東西。而指定式需要寫很多的條件判斷,是邏輯式的,維護成本也會高一些,是以聲明式是目前普遍采用的一種方式。
一條典型流水線應該包含四個關鍵環節。第一是建構。前端、後端的代碼都需要編譯,前端比如 HTML、JS、CSS 等,可能還會用到一些模闆,需要做編譯工作将其轉成浏覽器能夠支援的格式;第二是檢查。編譯完成之後需要檢查代碼是不是符合規範,是不是有很多的重複代碼,重複率超過了多少等等;第三是測試。測試環節很重要,會影響團隊對于産品釋出的信心。這裡講一個測試金字塔理論:底層是大量的單元測試,中間是元件測試或者接口測試,頂部是端到端測試。因為大量的邏輯都是在各種 If else 分支裡,單元測試可以覆寫到這些分支,那麼上層的測試就不需要再覆寫下層已經覆寫過的邏輯了。而上層測試的價值在于把這些代碼內建起來,站在使用者的角度去使用它,看看能否正常工作。上層和下層的測試關注點不一樣,解決的問題也不一樣;第四是部署。最後需要建構鏡像,并推到制品庫裡面去,更新伺服器,做完這一系列的事情之後,流水線執行個體就會銷毀。
最後總結一下,為什麼會衍生出 DevOps 實踐及其跟靈活開發的關系:
第一,我們最重要的目标是通過持續不斷地及早傳遞有價值的軟體使客戶滿意。我認為這跟靈活是一脈相承的。靈活的原則裡說可工作的軟體高于詳盡的文檔,客戶更希望看到的是可用的軟體,而不僅僅是文檔說明,但是由于開發過程的不透明,造成了客戶喜歡做微觀管理的現象,同時也會讓開發團隊變得很被動。靈活裡有個價值觀叫尊重,這種尊重是需要團隊自己去赢得的。通過建立一套流程,提高開發過程的透明度,進而建立客戶與團隊之間的信任。另外跟靈活原則相契合的一點是響應變化高于遵循計劃。這不是一句口号,而是一種能力,它包含:項目管理能力、需求管理能力、配置管理能力以及品質保障能力。這四種能力建設起來之後,團隊才能擁有響應變化的能力。持續內建、自動化測試、自動部署等這些核心能力,搭配上代碼規範、CodeReview、TDD等這些實踐,才能真正提升開發團隊的實力,而不是僅僅把 Scrum 導入進來,開開計劃會、站立會就行了。
第二,可工作的軟體是進度的首要度量标準。不是每天站會或者每周寫個郵件告訴客戶這周完成了多少工作,而一定是部署完成後,變成了可以看到的、可以體驗的功能才算是真正的進度。
第三,堅持不懈的追求技術卓越和良好的設計,靈活能力由此增強。開發團隊擁有了代碼規範檢查、自動化測試等這些品質門禁以後,才有底氣去不斷的做優化,得到可持續的、快速疊代的速率。實踐都會随着技術的變化而變化,團隊能力也在持續的變化,但能不能持續地保持靈活,那就要看價值觀、原則是不是能夠持續地符合。
點選觀看完整錄播視訊