前端大規(guī)模構(gòu)建演進實踐
掌⻔教育⾃動化構(gòu)建歷程
在業(yè)內(nèi)前端構(gòu)建,⼀般分為三種:
- ⼿動觸發(fā)構(gòu)建:這個階段⾮常原始,需要我們⾃⼰在本地進⾏ git pull/npm install/npm run build 等 等操作,也容易出現(xiàn)問題;
- 虛擬機 Jenkins 集群分布式構(gòu)建:通過 Master 將任務(wù)分配到對應(yīng)的 Slave 機器上執(zhí)⾏構(gòu)建,能極⼤ 的均衡資源,利⽤性能,同時解放雙⼿;
- 容器集群構(gòu)建:容器構(gòu)建,鏡像發(fā)布,可以進⼀步的節(jié)約資源;
不過掌⻔教育在 2019 年之前,前端研發(fā)更多是在本地進⾏構(gòu)建,再通過運維的腳本來進⾏部署,也容易 導(dǎo)致出現(xiàn)⽣產(chǎn)故障。所以我們收集反饋,結(jié)合實際情況,開發(fā)出 v1.0 構(gòu)建模式,也取得了很好的成果。但 并沒有以此就認為⾼枕⽆憂,也對很多痛點進⾏持續(xù)的優(yōu)化,最后迭代出 v2.0 的⽅案。在這個過程中,前 端業(yè)務(wù)壯⼤,CI 構(gòu)建經(jīng)過 400+ 多應(yīng)⽤,每周 2000+ 次構(gòu)建,300+ 次的⽣產(chǎn)發(fā)布的⼤考,持續(xù)的成 ⻓。
v1.0 ⾯對的挑戰(zhàn)
v1.0 前端構(gòu)建狀況,⼀種是通過 webhook 來觸發(fā)流⽔線構(gòu)建,第⼆種是通過在 cd 上新建構(gòu)建單來觸構(gòu) 建。如果構(gòu)建任務(wù)⽐較多,按照單臺機器的是遠遠不夠,在這種情況下就需要借助 Jenkins 的 Master/Slave 的主從模式,來解決服務(wù)器的資源壓⼒。讓 Master 的服務(wù)器來進⾏調(diào)度資源,指定空閑 Slave 機器進⾏構(gòu)建。當 Slave 機器上構(gòu)建任務(wù)滿了,構(gòu)建任務(wù)繼續(xù)在 Master 排隊池中繼續(xù)等待,等 Slave 空閑后,再進⾏分配。
存在的挑戰(zhàn)
v1.0 不是最好的⽅案,同時暴露出第⼀次構(gòu)建慢、錯誤⽇志反饋不明確等問題,另外⼀點就是 job 維護困 難。要解決這些問題,就需要重新開始,重新設(shè)計。
⾸先就是 JOB 維護困難,v1.0 的任務(wù)模式是多個應(yīng)⽤對應(yīng) 1 個 job,這就導(dǎo)致⼏個問題,如果 job 發(fā)版 導(dǎo)致掛了,影響到全部。假如需要復(fù)⽤該 job,進⾏定制化開發(fā)也⽐較困難。
其次第⼀次構(gòu)建慢的問題,更多在資源調(diào)度上和⽆法復(fù)⽤ Workspace,根據(jù)之前的資源調(diào)度模式,當我們 把任務(wù)分配到 A 機器,該任務(wù)被執(zhí)⾏成功,那么下次的任務(wù)也會⾛⼊到這臺 A 機器。以此觀察,就會發(fā) 現(xiàn)⼤部分任務(wù)都會優(yōu)先去搶占 A 機器。這家就導(dǎo)致了⼏個問題:
- 資源調(diào)度不均衡;
- npm 緩存越來越⼤;
最后是⽆法復(fù)⽤ Workspace 模式,在 v1.0 情況下,不復(fù)⽤ Workspace 模式是會帶來以下優(yōu)勢:保證 node_module ⽆污染問題,同時也避免了 npm run build 的各種因為 node_module 包污染的問題,導(dǎo) 致的意外錯誤。所以在 v2.0 就需要應(yīng)對污染的問題。同時也要考慮在復(fù)⽤ Workspace 后,如何最⼤化 的利⽤其特點,⽐如,從 node_module 緩存、npm install 跳過等。
v2.0 優(yōu)化⽅案
資源調(diào)度
⾸先需要對資源調(diào)度進⾏優(yōu)化,那就需要重新設(shè)計,把⼀組機器分為多個切⽚組,每個切⽚組調(diào)度順序不 同。當應(yīng)⽤觸發(fā)構(gòu)建時,分配對應(yīng)的 key值:
- 1 AppNodeKey = AppId%nodes
再根據(jù)劃分的 key,尋找對應(yīng)的機器組,如 [0,1,2,3,4],構(gòu)建任務(wù)去尋找 0 號機,尋找對應(yīng)的 AppId 的 Workspace ⽬錄地址去執(zhí)⾏構(gòu)建任務(wù),假如任務(wù)被占⽤(默認是 2 個任務(wù),這樣可以優(yōu)化資源不會被⼤ 量任務(wù)搶占),會再尋找下⼀臺機器,這樣機器資源調(diào)度就會均衡化。
構(gòu)建 job 流⽔線化
我們對不同的⼯程項⽬進⾏了模板化,⽐如 PC項⽬、H5 項⽬、游戲項⽬、hybrid 項⽬等等,在模板基 礎(chǔ)上,我們⼜封裝出來打包流⽔線模板,這樣的好處是,我們可以⾃⼰去針對各個類型的⼯程模板做⼀些 定向的配置優(yōu)化,⽐如說我們的游戲類型項⽬,我們?nèi)プ?#12032;個構(gòu)建、打包,我們就可以在對應(yīng)的開發(fā)組件 庫依賴這⼀塊,做⼀些對應(yīng)的緩存、通知、報告等等。
流⽔線同時也帶來了⼀些好處:
- 第⼀,我們把構(gòu)建任務(wù)進⾏了⽣命周期化,git cone、npm install、npm build,把這些階段全部進⾏拆 開,讓整個任務(wù)流程顆粒化,這樣的好處是,我們可以在每⼀個顆粒之間找到優(yōu)化空間,⽐如是不是可以 不進⾏npm install,⽐如上傳制品倉庫的邏輯優(yōu)化等等。
- 第⼆,可以指定DSL,我們可以實時監(jiān)聽打包排隊的情況,在資源調(diào)度層⾯做⼀些優(yōu)化。同時可以做⼀些 埋點進⾏采集數(shù)據(jù),給后續(xù)進⾏深⼊分析。
- 第三,我們甚⾄可以在構(gòu)建、打包過程中,做⼀些交互的相關(guān)操作,⽐如,我們打包⼀個 h5 項⽬,需要測試同學(xué)來進⾏審核,只有測試審核通過完之后,才進⼊到下⼀流程,下⼀個流程可能是進⾏ UAT 打包、 ⽣產(chǎn)打包等等。
- 第四,針對項⽬依賴拉取,最開始的時候,我們做的是全量的拉取,我們現(xiàn)在可以優(yōu)化為增量拉取,這 樣,服務(wù)器的壓⼒會減輕很多
錯誤治理
不管是在本地還是 cd 平臺上進⾏構(gòu)建,也容易出現(xiàn)各種意想不到的錯誤。⽐如開發(fā)的疏忽,流⽔線的 git commit 未經(jīng)過驗證進⾏提交代碼,都可能在 npm run build、或者 npm install 這兩個階段報出不同的 錯誤,所以就需要對⽇志提示進⾏分級集,劃分為兩種類型:
- warning 類型:沒有 package-lock.json 提示,不影響到任務(wù)構(gòu)建;
- error 類型:導(dǎo)致 job 任務(wù)退出,⽐如依賴包未找到等; 4
我們對 npm install、npm run build,及構(gòu)建的各個階段的觀察,可以把失敗歸納為 4 個觸發(fā) warning 或 error 類型:
- 語法錯誤:代碼沖突...
- 程序異常:內(nèi)存溢出…
- Install 失?。何凑业桨惭b包…
- 配置錯誤:構(gòu)建未按照規(guī)范輸出…
遵守“觀察⽇志-沉淀規(guī)則-修正反饋準確率”規(guī)則,來沉淀⽇志規(guī)則。
npm install 跳過
在復(fù)⽤ Workspace 的情況下,已經(jīng) install 好的 node_modules,就沒必要進⾏⼆次重復(fù) npm install, 就需要考慮只有依賴進⾏變更時,再重新 npm isntall。同時也容易帶來問題,node_modules 污染,我 們采取了多種⽅式來避免 node_modules 被污染掉。
- cache 定期檢查;
- 切換 node 版本時,執(zhí)⾏ npm rebuild,重新進⾏編譯⼀些需要依賴 gc++ 環(huán)境構(gòu)建的包,如 node- sass;
- CD ⻚⾯增加⼿動清理 Workspace 選項;
帶來的收益
⽬前,我們對⽐以前的 v1.0 ⽅案,整體有了 20%+ 構(gòu)建速度的提升,這對我們團隊來說,也算是⼀個不 ⼩的正向激勵,說明我們之前努⼒的⽅向是正確的。
遷移過程
在 v1.0 遷移到 v2.0,需要考慮如何進⾏平滑遷移,我們基于以下來進⾏遷移:
1:每個應(yīng)⽤建⽴獨⽴ v2.0 Job 任務(wù),⽅便快速變更及排查問題;
2:⻚⾯上⽀持快速回滾到 v1.0;
3:選擇 git commit、node 版本等信息保持不變,⽆感;
總結(jié)
好的架構(gòu)不是設(shè)計出來⽽是演進出來。在未來,構(gòu)建任務(wù)可能會越來越多,項⽬也越來越復(fù)雜化,我們就 會考慮容器化⽅案,根據(jù)實際情況去考慮,容器構(gòu)建,鏡像發(fā)布,盡可能的節(jié)約資源。