原創文章,轉載請務必將下面這段話置於文章開頭處。
本文轉發自技術世界,原文連接 http://www.jasongj.com/spark/ci_cd/git
本文所述內容基於某頂級互聯網公司數萬節點下 Spark 的 CI 與 CD & CD 實踐。爲了提升本文內容的可借鑑性,隱去了公司特有內容,只保留通用部分性能優化
持續集成是指,及時地將最新開發的且通過測試的代碼集成到主幹分支中。
工具
持續集成的優勢性能
目前主流的代碼管理工具備,Github、Gitlab等。本文所介紹的內容中,全部代碼均託管於私有的 Gitlab 中。單元測試
鑑於 Jenkins 幾乎是 CI 事實上的標準,本文介紹的 Spark CI CD & CD 實踐均基於 Jenkins 與 Gitlab。測試
Spark 源碼保存在 spark-src.git 庫中。優化
因爲已有部署系統支持 Git,所以可將集成後的 distribution 保存到 Gitlab 的發佈庫(spark-bin.git)中。ui
每次開發人員提交代碼後,均經過 Gitlab 發起一個 Merge Requet (至關於 Gitlab 的 Pull Request)spa
每當有 MR 被建立,或者被更新,Gitlab 經過 Webhook 通知 Jenkins 基於該 MR 最新代碼進行 build。該 build 過程包含了code
Jenkins 將 build 結果通知 Gitlab,只有 Jenkins 構建成功,Gitlab 的 MR 頁面才容許 Merge。不然 Gitlab 不容許 Merge
另外,還需人工進行 Code Review。只有兩個以上的 Reviewer 經過,才能進行最終 Merge
全部測試與 Reivew 經過後,經過 Gitlab Merge 功能自動將代碼 Fast forward Merge 到目標分支中
該流程保證了
持續交付是指,及時地將軟件的新版本,交付給質量保障團隊或者用戶,以供評審。持續交付可看做是持續集成的下一步。它強調的是,無論怎麼更新,軟件都是可隨時交付的。
這一階段的評審,通常是將上文集成後的軟件部署到儘量貼近生產環境的 Staging 環境中,並使用貼近真實場景的用法(或者流量)進行測試。
持續發佈的優勢
這裏有提供三種方案,供讀者參考。推薦方案三
以下圖所示,基於單分支的 Spark 持續交付方案以下
spark-src.git/dev
(即 spark-src.git 的 dev branch) 上進行spark-bin.git/dev
的 spark-${ build # }
(如圖中第 2 周的 spark-72)文件夾內spark-${ build # }
(如圖中的 spark-72)注:
在 Staging 環境中發現 spark-dev 的 bug 時,修復及集成和交付方案以下
spark-${ build \# }
(如圖中的 spark-73)spark-${ build \# }
(如圖中的 spark-73 )生產環境中發現 bug 時修復及交付方案以下
spark-${ build \# }
(如圖中的 spark-73)spark-${ build \# }
(如圖中的 spark-73 )以下圖所示,基於兩分支的 Spark 持續交付方案以下
spark-src.git
與 spark-bin.git
均包含兩個分支,即 dev branch 與 prod branchspark-src.git/dev
上進行spark-src.git/dev
打包出一個 release 放進 spark-bin.git/dev
的 spark-${ build \# }
文件夾內(如圖中第 2 週上方的的 spark-2 )。它包含了以前全部的提交(commit 一、二、三、4)spark-bin.git/dev
的 spark 做爲 symbolic 指向 spark-${ build \# }
文件夾內(如圖中第 2 週上方的的 spark-2)spark-src.git/prod
經過 fast-forward merge 將 spark-src.git/dev
一週前最後一個 commit 及以前的全部 commit 都 merge 過來(如圖中第 2 周需將 commit 1 merge 過來)spark-src.git/prod
打包出一個 release 放進 spark-bin.git/prod
的 spark-${ build \# }
文件夾內(如圖中第 2 周下方的的 spark-1 )spark-bin.git/prod
的 spark 做爲 symbolic 指向 spark-${ build \# }
在 Staging 環境中發現了 dev 版本的 bug 時,修復及集成和交付方案以下
spark-src.git/dev
上提交一個 commit (如圖中黑色的 commit 9),且 commit message 包含 bugfix 字樣spark-src.git/dev
打包生成一個 release 並放進 spark-bin.git/dev
的 spark-${ build \# }
文件夾內(如圖中第二週與第三週之間上方的的 spark-3 )spark-bin.git/dev
中的 spark 做爲 symbolic 指向 spark-${ build \# }
在生產環境中發現了 prod 版本的 bug 時,修復及集成和交付方案以下
spark-src.git/dev
上提交一個 commit(如圖中紅色的 commit 9),且 commit message 包含 hotfix 字樣spark-src.git/dev
打包生成 release 並 commit 到 spark-bin.git/dev
的 spark-${ build \# }
(如圖中上方的 spark-3 )文件夾內。 spark 做爲 symbolic 指向該 spark-${ build \# }
spark-src.git/prod
(如無衝突,則該流程全自動完成,無需人工參與。如發生衝突,經過告警系統通知開發人員手工解決衝突後提交)spark-src.git/prod
打包生成 release 並 commit 到 spark-bin.git/prod
的 spark-${ build \# }
(如圖中下方的 spark-3 )文件夾內。spark做爲 symbolic 指向該spark-${ build \# }
spark-src.git/dev
(包含 commit 一、二、三、四、5) 與 spark-src.git/prod
(包含 commit 1) 的 base 不同,有發生衝突的風險。一旦發生衝突,便需人工介入spark-src.git/dev
合併 commit 到 spark-src.git/prod
時須要使用 rebase 而不能直接 fast-forward merge。而該 rebase 可能再次發生衝突spark-bin.git/dev
的 bug,即圖中的 commit 一、二、三、4 後的 bug,而 bug fix commit 即 commit 9 的 base 是 commit 5,存在必定程度的不一致spark-bin.git/dev
包含了 bug fix,而最新的 spark-bin.git/prod
未包含該 bugfix (它只包含了 commit 二、三、4 而不包含 commit 五、9)。只有到第 4 周,spark-bin.git/prod
才包含該 bugfix。也即 Staging 環境中發現的 bug,須要在一週多(最多兩週)才能在 prod 環境中被修復。換言之,Staging 環境中檢測出的 bug,仍然會繼續出如今下一個生產環境的 release 中spark-src.git/dev
與 spark-src.git/prod
中包含的 commit 數一致(由於只容許 fast-forward merge),內容也最終一致。可是 commit 順序不一致,且各 commit 內容也可能不一致。若是維護不當,容易形成兩個分支差異愈來愈大,不易合併以下圖所示,基於多分支的 Spark 持續交付方案以下
spark-src.git/master
上進行spark-src.git/master
最新代碼合併到 spark-src.git/dev
。以下圖中,第 2 周將 commit 4 及以前全部 commit 合併到 spark-src.git/dev
spark-src.git/dev
打包生成 release 並提交到 spark-bin.git/dev
的 spark-${ build \# }
(以下圖中第 2 周的 spark-2) 文件夾內。spark 做爲 symbolic,指向該 spark-${ build \# }
spark-src.git/master
一週前最後一個 commit 合併到 spark-src.git/prod
。如第 3 周合併 commit 4 及以前的 commitspark-src.git/prod
中,由於它們是對 commit 4 進行的 bug fix。後文介紹的 bug fix 流程保證,若是對 commit 4 後發佈版本有多個 bug fix,那這多個 bug fix commit 緊密相連,中間不會被正常 commit 分開spark-src.git/prod
打包生成 release 並提交到 spark-bin.git/prod
的 spark-${ build \# }
(以下圖中第 2 周的 spark-2) 文件夾內。spark 做爲 symbolic,指向該 spark-${ build \# }
在 Staging 環境中發現了 dev 版本的 bug 時,修復及集成和交付方案以下
spark-src.git/dev
(包含 commit 一、二、三、4) 上提交一個 commit(如圖中黑色的 commit 9),且 commit message 中包含 bugfix 字樣spark-src.git/dev
打包生成 release 並提交到 spark-bin.git/dev
的 spark-${ build \# }
(如圖中的 spark-3) 文件夾內,spark 做爲 symbolic,指向該 spark-${ build \# }
git checkout master
切換到 spark-src.git/master
,再經過 git rebase dev
將 bugfix 的 commit rebase 到 spark-src.git/master
,若是 rebase 發生衝突,經過告警通知開發人員人工介入處理衝突spark-src.git/dev
上順序相連。所以它們被 rebase 到 spark-src.git/master
後仍然順序相連在生產環境中發現了 prod 版本的 bug 時,修復及集成和交付方案以下
spark-src.git/prod
中提交一個 commit,且其 commit message 中包含 hotfix 字樣spark-src.git/prod
打包生成 release 並提交到 spark-bin.git/prod
的 spark-${ build \# }
(如圖中的 spark-3) 文件夾內,spark 做爲 symbolic,指向該 spark-${ build \# }
git checkout master
切換到 spark-src.git/master
,再經過 git rebase prod
將 hotfix rebase 到 spark-src.git/master
本文介紹的實踐中,不考慮多個版本(經實踐檢驗,多個版本維護成本過高,且通常無必要),只考慮一個 prod 版本,一個 dev 版本
上文介紹的持續發佈中,可將 spark-bin.git/dev
部署至須要使用最新版的環境中(不必定是 Staging 環境,能夠是部分生產環境)從而實現 dev 版的部署。將 spark-bin.git/prod
部署至須要使用穩定版的 prod 環境中
本文介紹的方法中,全部 release 都放到 spark-${ build \# }
中,由 spark 這一 symbolic 選擇指向具體哪一個 release。所以回滾方式比較直觀
spark-src.git/master
上進行,Staging 環境的 bug fix 在 spark-src.git/dev
上進行,生產環境的 hot fix 在 spark-src.git/prod
上進行,清晰明瞭spark-src.git/master
時使用 rebase,從而保證了 spark-src.git/dev
與 spark-src.git/master
全部 commit 的順序與內容的一致性,進而保證了這兩個 branch 的一致性spark-src.git/master
時使用 rebase,從而保證了 spark-src.git/dev
與 spark-src.git/master
全部 commit 的順序性及內容的一致性,進而保證了這兩個 branch 的一致性spark-src.git/master
發生衝突時才需人工介入spark-bin.git/dev
與 spark-bin.git/prod
將開發版本與生產版本分開,方便獨立部署。而其路徑統一,方便版本切換與灰度發佈spark-src.git/master
提交時,須先 rebase 遠程分支,而不該直接使用 merge。在本方案中,這不只是最佳實踐,仍是硬性要求spark-src.git/master
。但發生衝突時,須要相應修改 spark-src.git/master
上後續 commit。如上圖中,提交紅色 commit 9 這一 hot fix 後,在 rebase 回 spark-src.git/master
時,若有衝突,可能須要修改 commit 2 或者 commit 三、四、5。該修改會形成本地解決完衝突後的版本與遠程版本衝突,須要強制 push 回遠程分支。該操做存在必定風險持續部署是指,軟件經過評審後,自動部署到生產環境中
上述 Spark 持續發佈實踐的介紹都只到 "將 *** 提交到 spark-bin.git
" 結束。可以使用基於 git 的部署(爲了性能和擴展性,通常不直接在待部署機器上使用 git pull --rebase,而是使用自研的上線方案,此處不展開)將該 release 上線到 Staging 環境或生產環境
該自動上線過程便是 Spark 持續部署的最後一環