前端⼤規模構建演進實踐

白玉蘭開源發表於2021-04-22

掌⻔教育⾃動化構建歷程

在業內前端構建,⼀般分為三種:

  1. ⼿動觸發構建:這個階段⾮常原始,需要我們⾃⼰在本地進⾏ git pull/npm install/npm run build 等 等操作,也容易出現問題;
  2. 虛擬機器 Jenkins 叢集分散式構建:通過 Master 將任務分配到對應的 Slave 機器上執⾏構建,能極⼤ 的均衡資源,利⽤效能,同時解放雙⼿;
  3. 容器叢集構建:容器構建,映象釋出,可以進⼀步的節約資源;

不過掌⻔教育在 2019 年之前,前端研發更多是在本地進⾏構建,再通過運維的指令碼來進⾏部署,也容易 導致出現⽣產故障。所以我們收集反饋,結合實際情況,開發出 v1.0 構建模式,也取得了很好的成果。但 並沒有以此就認為⾼枕⽆憂,也對很多痛點進⾏持續的優化,最後迭代出 v2.0 的⽅案。在這個過程中,前 端業務壯⼤,CI 構建經過 400+ 多應⽤,每週 2000+ 次構建,300+ 次的⽣產釋出的⼤考,持續的成 ⻓。

在這裡插入圖片描述

v1.0 ⾯對的挑戰

v1.0 前端構建狀況,⼀種是通過 webhook 來觸發流⽔線構建,第⼆種是通過在 cd 上新建構建單來觸構 建。如果構建任務⽐較多,按照單臺機器的是遠遠不夠,在這種情況下就需要藉助 Jenkins 的 Master/Slave 的主從模式,來解決伺服器的資源壓⼒。讓 Master 的伺服器來進⾏排程資源,指定空閒 Slave 機器進⾏構建。當 Slave 機器上構建任務滿了,構建任務繼續在 Master 排隊池中繼續等待,等 Slave 空閒後,再進⾏分配。
在這裡插入圖片描述

存在的挑戰

v1.0 不是最好的⽅案,同時暴露出第⼀次構建慢、錯誤⽇志反饋不明確等問題,另外⼀點就是 job 維護困 難。要解決這些問題,就需要重新開始,重新設計。
⾸先就是 JOB 維護困難,v1.0 的任務模式是多個應⽤對應 1 個 job,這就導致⼏個問題,如果 job 發版 導致掛了,影響到全部。假如需要復⽤該 job,進⾏定製化開發也⽐較困難。
其次第⼀次構建慢的問題,更多在資源排程上和⽆法復⽤ Workspace,根據之前的資源排程模式,當我們 把任務分配到 A 機器,該任務被執⾏成功,那麼下次的任務也會⾛⼊到這臺 A 機器。以此觀察,就會發 現⼤部分任務都會優先去搶佔 A 機器。這家就導致了⼏個問題:

  1. 資源排程不均衡;
  2. npm 快取越來越⼤;

最後是⽆法復⽤ Workspace 模式,在 v1.0 情況下,不復⽤ Workspace 模式是會帶來以下優勢:保證 node_module ⽆汙染問題,同時也避免了 npm run build 的各種因為 node_module 包汙染的問題,導 致的意外錯誤。所以在 v2.0 就需要應對汙染的問題。同時也要考慮在復⽤ Workspace 後,如何最⼤化 的利⽤其特點,⽐如,從 node_module 快取、npm install 跳過等。

v2.0 優化⽅案

資源排程
在這裡插入圖片描述
⾸先需要對資源排程進⾏優化,那就需要重新設計,把⼀組機器分為多個切⽚組,每個切⽚組排程順序不 同。當應⽤觸發構建時,分配對應的 key值:

 1 AppNodeKey = AppId%nodes 

再根據劃分的 key,尋找對應的機器組,如 [0,1,2,3,4],構建任務去尋找 0 號機,尋找對應的 AppId 的 Workspace ⽬錄地址去執⾏構建任務,假如任務被佔⽤(預設是 2 個任務,這樣可以優化資源不會被⼤ 量任務搶佔),會再尋找下⼀臺機器,這樣機器資源排程就會均衡化。

構建 job 流⽔線化

在這裡插入圖片描述
我們對不同的⼯程項⽬進⾏了模板化,⽐如 PC項⽬、H5 項⽬、遊戲項⽬、hybrid 項⽬等等,在模板基 礎上,我們⼜封裝出來打包流⽔線模板,這樣的好處是,我們可以⾃⼰去針對各個型別的⼯程模板做⼀些 定向的配置優化,⽐如說我們的遊戲型別項⽬,我們去做⼀個構建、打包,我們就可以在對應的開發元件 庫依賴這⼀塊,做⼀些對應的快取、通知、報告等等。
流⽔線同時也帶來了⼀些好處:

  • 第⼀,我們把構建任務進⾏了⽣命週期化,git cone、npm install、npm build,把這些階段全部進⾏拆 開,讓整個任務流程顆粒化,這樣的好處是,我們可以在每⼀個顆粒之間找到優化空間,⽐如是不是可以 不進⾏npm install,⽐如上傳製品倉庫的邏輯優化等等。
  • 第⼆,可以指定DSL,我們可以實時監聽打包排隊的情況,在資源排程層⾯做⼀些優化。同時可以做⼀些 埋點進⾏採集資料,給後續進⾏深⼊分析。
  • 第三,我們甚⾄可以在構建、打包過程中,做⼀些互動的相關操作,⽐如,我們打包⼀個 h5 項⽬,需要測試同學來進⾏稽核,只有測試稽核通過完之後,才進⼊到下⼀流程,下⼀個流程可能是進⾏ UAT 打包、 ⽣產打包等等。
  • 第四,針對項⽬依賴拉取,最開始的時候,我們做的是全量的拉取,我們現在可以優化為增量拉取,這 樣,伺服器的壓⼒會減輕很多

錯誤治理
不管是在本地還是 cd 平臺上進⾏構建,也容易出現各種意想不到的錯誤。⽐如開發的疏忽,流⽔線的 git commit 未經過驗證進⾏提交程式碼,都可能在 npm run build、或者 npm install 這兩個階段報出不同的 錯誤,所以就需要對⽇志提示進⾏分級集,劃分為兩種型別:

  1. warning 型別:沒有 package-lock.json 提示,不影響到任務構建;
  2. error 型別:導致 job 任務退出,⽐如依賴包未找到等; 4

我們對 npm install、npm run build,及構建的各個階段的觀察,可以把失敗歸納為 4 個觸發 warning 或 error 型別:

  1. 語法錯誤:程式碼衝突...
  2. 程式異常:記憶體溢位…
  3. Install 失敗:未找到安裝包…
  4. 配置錯誤:構建未按照規範輸出…

遵守“觀察⽇志-沉澱規則-修正反饋準確率”規則,來沉澱⽇志規則。
在這裡插入圖片描述
npm install 跳過
在復⽤ Workspace 的情況下,已經 install 好的 node_modules,就沒必要進⾏⼆次重複 npm install, 就需要考慮只有依賴進⾏變更時,再重新 npm isntall。同時也容易帶來問題,node_modules 汙染,我 們採取了多種⽅式來避免 node_modules 被汙染掉。

  1. cache 定期檢查;
  2. 切換 node 版本時,執⾏ npm rebuild,重新進⾏編譯⼀些需要依賴 gc++ 環境構建的包,如 node- sass;
  3. CD ⻚⾯增加⼿動清理 Workspace 選項;

帶來的收益

⽬前,我們對⽐以前的 v1.0 ⽅案,整體有了 20%+ 構建速度的提升,這對我們團隊來說,也算是⼀個不 ⼩的正向激勵,說明我們之前努⼒的⽅向是正確的。

遷移過程

在 v1.0 遷移到 v2.0,需要考慮如何進⾏平滑遷移,我們基於以下來進⾏遷移:
1:每個應⽤建⽴獨⽴ v2.0 Job 任務,⽅便快速變更及排查問題;
2:⻚⾯上⽀持快速回滾到 v1.0;
3:選擇 git commit、node 版本等資訊保持不變,⽆感;

總結

好的架構不是設計出來⽽是演進出來。在未來,構建任務可能會越來越多,項⽬也越來越複雜化,我們就 會考慮容器化⽅案,根據實際情況去考慮,容器構建,映象釋出,儘可能的節約資源。

在這裡插入圖片描述

相關文章