圖片出處: https://blog.infostretch.com/39-tools-for-building-your-cicd-stack/
這篇沒有要分享太多太硬的東西,或是太燒腦的架構文章,單純分享一下最近我協助團隊導入版控,以及 CI/CD 的想法而已。我一直覺得台灣 (還是世界各地都一樣?) 的軟體開發團隊存在這種 “工具控”、”框架控”、或是 “技術控” 的想法;選用跟得上潮流的技術,比什麼都重要。重要到這些技術或是框架本身到底是想解決什麼問題都不重要了,只要我用了最新的框架就是潮…
CI/CD, DevOps, SCRUM, Microservices, Containers 等等也是現在熱門的關鍵字,看過我前面文章的朋友大概都知道,我是很反對不知道這些技術是在幹嘛之前就一窩蜂的投入。最近在協助團隊從無到有建立起版控機制與 CI/CD 的過程有感,於是就寫了這一篇…
不過在開始進入正題之前,先讓我發發牢騷 XDD
抱怨
跟幾年前 DevOps 還很遙不可及的年代比起來,現在沒有弄個 DevOps 的流程,感覺起來你的團隊就遜掉了 XD, 這句話真的不誇張! 就拿面試工程師來說就好,我這幾年面試的工程師應該有好幾百個了吧 (有人想來讓我的面試人數 counter++ 嗎?), 每個大概都會問我:
- 你們有用版控嗎? 用哪一套版控系統?
- 你們有做 CI/CD 嗎? 用哪一套 CI Server?
- 你們有 Run SCRUM 嗎?
- 你們….
面試者會這樣問,我相信有很大一部分原因是 PTT 上的 Soft_Job 版都會說,沒有版控的公司就別去了… 有趣的是, 求職的人都對這些很有興趣, 但是當我反問這些東西你有哪些經驗, 場面就變得很乾了… 再來,當時面試時都對 CI/CD 很有興趣的人,找進公司後真的要開始做 CI/CD, 開始要求一些規範時, 通常就會莫名其妙地碰到一堆反彈… 有時就開始會聽到一些有趣的 feedback, 有各種奇妙的理由告訴你這樣 Run 會有問題.. XD
撇開工具不談,我不禁開始想: 這些 process 到底對工程師的意義是啥? 我想了想,有這幾種可能:
“我是來學習的,你應該要教我才對啊!”
先不管到底要不要教,至少有學習的動機是件好事。只是,動機應該是表現在自身的行為才對吧? 真的有興趣的話,自己應該會 找機會嘗試看看才對。以我自己來說,當年為了熟悉版控的機制,我開始把自己工作的東西都擺在 SVN, 強迫自己有機會練習使用。 為了熟悉 Git 的操作,還有版控的流程 Git Flow, 我強迫自己把 BLOG 都搬到 GitHub, 用 GitHub Pages 來取代 WordPress..
我只能說這樣的過程真的很值得,不但學到了技巧,也更清楚該如何應用這些流程來改善團隊的開發方式。也開始讓我體會在這個年代 xxx as code 的重要性。拿這個例子來說,我改用 GitHub 寫 BLOG 的心得可以參考這篇:
“我會 XXX + ZZZ 框架,還有 AAA + BBB 系統…”
當我聽到這種 “工具控”,如果接下來問幾句發現他完全搞不清楚這些東西是要幹嘛的時候,我心裡就會浮現出三條線 XDDD, 我不是很在意現在 “會” 幾種工具? 我比較在意是否能發揮它的潛力,拿來解決面臨的問題? 後者遠遠比前者重要的多,有了後者為目標,我自然會去學習他,把前者補起來。會哪些工具只是時間問題,拿它來幹嘛才是重點。學習 “工具” 需要花費的時間,通常是線性的,時間花下去就有。應用工具,拿來解決問題,就要靠一點腦袋了,有些沒想通的人可能花了時間一樣搞不定…。
在我近 20 年的工作經歷裡, 有十幾年的時間都在做系統開發與導入。對於在公司或是團隊內導入一套系統,我是很慎重地看待這件事的。在我的觀念裡面,導入一套系統花費的成本是很高的,即使系統是免費的也是。要換掉一套既有的系統也是。因此我一貫的原則是: 除非這套系統爛透了,有重大需求無法被滿足,或是你已經駕馭它 80% 以上的功能,想要替換更進階更深入的系統… 等等,否則我不大會輕易的替換任何一套系統。因為我一直相信 “找對的人來使用工具”,遠比 “找對的工具來用” 還重要,尤其是這些工具都需要一些經驗才能駕馭它的時候…。
我想要什麼樣的 CI / CD ?
回到主題,我這篇其實想講的是如何跨出 CI / CD 或是 DevOps 的第一步。網路上可以找到很多高手或是有經驗的人分享的文章,的確是已經有很多成功的導入經驗在那邊了,但是這些分享往往看在什麼都還沒有的團隊眼裡,卻是個遙不可及的理想。這些分享告訴你美好的目標在哪裡,但是沒經驗的團隊卻不知道第一步該怎麼做。
最重要的也許不是怎麼做,或是該選用什麼系統及工具…。這些都是你有明確想解決的問題之後再做的選擇而已。我先回顧一下當年我做類似事情的經過..
多年前 (2003) 土炮 Daily Build 流程的經驗
好,前面牢騷發完,其實我這篇主要要寫的在這邊啊,你 (or 你的團隊) 需要什麼樣的 CI / CD 流程? 我相信 google 可以找的到的文章 (尤其是中文的),一大部分都是著重在教你怎麼安裝,或是設定 XXX server 幫你把 CI build script 弄起來之類的文章。
我舉前面的例子,其實是要表達這些都是大家挑選系統或工具時常會犯的毛病 (尤其是台灣人 XD)。當你第一個考量的是 “工具”,那你已經被牽著鼻子走了.. 主要當然還是看需求而定。我分享一下我經歷的例子。
其實我很久很久以前,就自己土炮過 CI/CD 了。當時還沒有這名詞,當時只稱它做 “Daily Build”… 當時 (大約 2003 的年代)。當時我用的版控系統是 Microsoft Visual Source Safe, 自己裝了套 Virtual PC (當時 Connectix 好像還沒被 Microsoft 買下來), 寫了 script 每天半夜還原 vhd, VM 開機後就自動從 VSS get latest version, 然後跑一連串的 build script, 跟 deploy to local IIS 的 script… 跑完之後也緊接著跑簡單的測試 (當年我還不大會寫 unit test, 只是寫個 http client 把預先準備好的 page list 按順序點一次,看看有沒有 error..)。測完的 VM 就充當 dev server, 大家隔天可以在這台 VM 上做最新版的整合測試,若需要拿這天的 build 結果,也可以到共用目錄底下找的到…
這種土炮 CI/CD 其實很兩光,VSS 沒有主動觸發事件的能力 (VSS 只是個特定格式的資料庫,不是個 Service) ,只能排排程,固定一天跑一次,一次大概要跑一個多小時,一旦 build fail 要修復也很麻煩, 搞到最後還是只有我能維護這個 VPC .. 但是從那時候開始,我就可以放心了,因為每天早上我都可以知道昨天的 code 能不能成功編譯..
最精簡的 CI / CD 流程
時間跳回 2017, 到了現代, 你期望什麼樣的 CI / CD ? 整套完整的 CI / CD 建置起來很花時間,在一開始你最該注意的是哪個部分? 那些可以一步一步慢慢追加?
很標準的 CI/CD 流程圖範例之一
我知道整個 CI/CD 流程分很多步驟,如果你的團隊目前甚麼都還沒有,你想要一次到位的話,那這個導入流程的專案大概註定會失敗收場。但是反過來看,我能否找個兼顧完整、擴充性與未來性的 process, 但是只先只取其精華的部份優先導入? 順利導入之後,將來我的需求變複雜時,再逐步把被精減掉的部分慢慢加回來就好了。這樣的話,每一階段都能夠有導入流程的效益, 也不會變成短視近利,忽略了長期的成效。
以我的角度來看,開發團隊 “一定” 要顧好的流程,有這幾個部分:
-
版本控制:
Source code 是開發團隊最重要的資產, 好好的管理它是必要的。所謂的 “管理” 不只是記錄誰改了哪一行 code 而已,他橫跨了整個開發流程,包含開發中的版本,已發行或是以上線的版本,延伸到將來的 hotfix, 或是回報舊版本問題時,是否都能在版控系統內 精確 的定位到當時那份 code… 都在考量的範圍內。
其實這有點像在製造業的物料管理一樣,序號或是條碼掃描一下,就能知道所有這產品的來歷。在軟體來看,看到版號或是 commit sha, 就要能讓開發團隊追蹤到所有跟 source code 相關的細節。 -
自動建置與整合測試:
大致上就是現在 CI 在講的事情。包含建置 (build, compilation), 測試 (unit test, 其他 auto testing), 其他語法檢查, 源碼掃描等等對程式碼的品質管控機制都包括在內。目的在程式碼有異動時,能第一時間透過自動化的 process 讓 team member 第一時間能掌握這次異動後的系統品質是否仍然可靠? -
發行管理 (Release management):
包含將 CI 的成品 (artifacts) 經過一連串自動化的程序,部署到執行環境 (dev, qa, production 都算) 上的動作。簡單的說就是把 CI 的東西弄到可以上線測試或是使用的過程。
版控機制若有不同的分支 (branch), 則不同分支應該有不同的發行方式與規則,如 develop 發行的是 BETA 版,而 master 發行的是 RTM / RC 版本等等。
因此,如果你要改善的是個完全沒有流程制度的團隊,或是要創建一個新的開發團隊,我建議這三個部分都要挑選合適的系統或工具,把最基本最關鍵的環節弄出來。這時最重要的不是你用哪一套系統,最重要的是在 team member 能接受及掌握的情況下,讓這三大流程都能發揮實際的效益。
精簡既有的系統與工具組
舉例來說,我碰過團隊本身已經有用各種系統了,但是用的方式很不到位 (ex: gitlab + tfs + redmine + jenkins + ….), 這時我會把重點擺在如何善用既有的工具上,而不是很武斷的再導入另一個新系統… (除非原本的工具完全不適合)。因為在導入的過程中,團隊成員會面臨學習新工具的門檻,也會面臨學習新流程的門檻。沿用既有的系統,可以大幅降低學習新工具的門檻,也省去升級或是替換的成本,成員們的反對力道也會降低很多。
因此,原本就已經在使用的 GitLab, TFS, Jenkins 這三套有高度功能重疊的系統, 我就做了個決定,全部合併到一套 GitLab 了。人家說 “多個香爐多隻鬼”,沒錯,我要做的就是砍掉多餘的系統,而不是再加新系統進來替換既有系統;GitLab 同時涵蓋版控,開發管理,CI 整合等等功能,而且我判定足以應付團隊未來幾年的需求,即使可能還存在其他更好的系統,但是我現階段就決定繼續沿用 GitLab。
版控的流程 - Git Flow
版控的部分,重點在於 release management, 如何反推到你的團隊怎麼進行分支的管理。我這邊是參考 git flow 的規範。過去我花很多時間在研究這些,包含以前用 TFS 時,TFS 每改版一次,Microsoft 就會發行一份 branch & merge guidelines, 我從 VSS 年代一直看到 TFS 2013 … XD
隨便舉幾篇我看過的老文章:
- Team Development with Visual Studio .NET and Visual SourceSafe
光看架構圖就知道他的年紀了…
- Team Development with Visual Studio Team Foundation Server - CH1
Team Foundation Server Logical Workflow
- Guidelines: Source Control
- Branch strategically
後來看到 git flow, 的確 open source 的這套哲學就是比較精簡收斂, 沒有太多多餘的流程。git flow 很有系統的用 branch 來解決各種開發團隊會碰到的問題,例如新功能 (features) 的開發該如何隔離? 或是 develop -> release, 甚至是已經 release 後的 hotfix 該如何處理等等都包含在內。
git flow 分支管理的流程說明
不過,沒經驗的人看到這張圖大概就暈了,其實你的應用情境若沒那麼複雜,其實也不需要完全照做,多維護一個 branch 都是成本啊。這邊就要留意了,我強烈建議每個團隊的 team leader, 或是架構師等等這類的角色,務必先花一些時間研究搞清楚整套 git flow 的做法後 (即使你們自己不實際導入),再來決定哪些是要砍掉的。這件事情若是給沒經驗的人來決定,那後果大概就是把複雜的地方 (或是他搞不懂得地方) 都砍掉了,整個流程被改的四不像,那對團隊會是個災難…
我的經驗是這樣,git flow 規範的幾種不同的 branches (features, develop, release, master, hotfix), 其中只有 master 跟 develop 是永久存在的 branch, 其他都是需要時才建立,階段性任務結束 merge 後就會刪除的 branch… 如果你不知道你們團隊該從哪裡開始,那就先按照規範好好的使用 master / develop 吧! 其他的 branch, 可以等團隊上手後再逐步導入。
基本上,光是版控,你的團隊要是能好好的運用 master / develop, 就算後面還沒做 CI / CD, 我想在台灣你就已經贏過 80% 的團隊了吧 XDD
持續整合 - GitLab CI
接下來是 CI,CI 其實想要幫開發團隊解決很多問題,但是在導入初期,我建議只要顧好這幾項就夠了:
- success build
- pass unit test
- manage artifacts
簡單的說,只要有 programmer push 任何 code change, CI 要能告訴你最基本的 build 是否會失敗? 若成功的話再告訴你單元測試是否通過? 最終能把 build 好的東西統一管理, 讓往後的人或是接下來的 CD 可以直接接著 CI 的成果繼續,而不用再重頭來一次。
單元測試 (unit test) 大概也是很多團隊頭痛的部分。我一樣強烈建議,團隊的 team leader 或是 architect 一定要清楚知道 TDD,才能做出正確的決定。如果你的既有專案就是沒有寫 unit test, 你也不需要把所有的 unit test 一次補齊 (經驗告訴我,這種硬捕的 unit test 很多都是交差了事,測試都是為了測試而寫,不是為了真正的案例測試而寫)。unit test 除了測試本身之外,其實有很多附加的效益,我建議團隊可以從這幾點開始,新的專案開始用 TDD,舊的專案別急著補測試,先把流程建立好,再逐步累積測試的內容。Unit Test 除了測試之外,還可以拿來:
- 當作 library 的使用範例
- 碰到回報的 bugs, 可以寫 unit test 重現 (reproduce) 問題
- 拿來當作 API 規格, 用 unit test 來驗證 API 是否符合規範
最終,最重要的是要有地方看到 CI 執行的成果,這部分 GitLab 做得很不錯,到 pipeline 頁面看看, 都是綠色勾勾就 OK 了。 如果這是你關注的 project, 每次 CI 的結果也會 email 通知你, 你有裝官方的 APP 也會收到通知,最後你要是有多個 projects 同時進行中,也有貼紙可以讓你把燈號都擺在同一個網頁內..
gitlab pipeline 畫面
持續部署 - Package Management
CD 其實是我認為最傷神的地方。它的複雜度最高,跟你的執行環境的相依性最高。要從無到有建置完整,要嘛很花時間,要嘛你必須挑對系統整套導入 (例如整套都用 TFS + TFS agents)。這邊我抱持比較保守的態度,如果做不到全自動的 CD,其實也不需要那麼堅持… 沒有全自動,那就半自動也無訪,但是最重要的是,半自動最好要能避開複雜的重複人工操作,只留些不好自動化但是簡單不大會出錯的部分。否則這自動化是沒有解決問題的。
這邊我特別推薦善用各種的套件管理機制來簡化問題。其實只要用心觀察,你會發現,龐大的 open source community, 都能夠用這樣很簡單的機制,就做好版本跟相依性的管理。這麼龐大的規模都能運作了,我相信在一個團隊內應該完全沒問題。舉例來說,善用 apt-get, 則 ubuntu 的各種軟體安裝都不成問題了;善用 npm, 則 node js 的套件都能很輕易的管理好; 善用 nuget, .net 的 library 就不需要自己一個一個手動下載更新…。
不論是哪一套套件管理,大概都會幫你做好版本更新管理,與套件的相依性管理。版本號碼我建議參考 SemVer 2.0 的規範, 頂多最後再標記一下這是 BETA 或是 RC / RTM 就好了。因此,若 CD 還無法一步到位,能先把 CD 簡化成搭配套件管理,自動化的部分只做到 CI 後自動推送到套件管理,就已經能解決很多問題了。套件管理後面雖然部署仍然要手動,但是已經被大幅簡化了,即使人工也不容易犯錯,這部分團隊就可以量力而為,不需要在第一時間花費大量人力去搞定這一塊。
不過問題來了,如果我開發的就是 web sites, 沒有合適的 web sites 套件管理,那該怎麼辦?
這邊我強力推薦,盡快採用容器化的部署。containers 能夠把 web sites, services, jobs, cli tools 等等形式的 application 都包裝成 container image, 統一用 docker registry 的方式管理。看到這裡,我相信用過的朋友就知道我的意思了。docker registry 就是個典型的 container image 專用的套件管理啊! 因此,如果你的環境已經可以或是準備轉移到容器化的部署方式,那就盡快進行吧! 若短期內還無法容器化,那就先用 file server 運作一陣子。
實際把整個服務都容器化的作法,有興趣的朋友可以參考我這篇的介紹:
結論: 執行架構與方向
講了一大堆,其實我的目的是希望替有心導正流程的團隊,指引一條門檻低,成功率高,能夠循序漸進的方式,逐步導入 CI/CD 的方式。所有 google 能找到的參考資料,大都告訴你 CI / CD 應該要這樣做才對:
但是我想說的是,你的第一部可以先 focus 在這張圖就好了,完成後在逐步擴大到上一張圖的境界:
架構決定後,再來才是把你中意的系統或是工具填上去。以我自己碰到的案例為例:
需要人工介入的部分,主要就是 develop push code 到 source control, 剩下的就是等 CI / CD 完成,團隊可以直接透過 package manager 取得 build 後的成果。不論是開發團隊要拉 BETA 版回來測試,或是 OP 人員要拉正式版本到 production 部署,通通都是透過 package manager。
其實這邊的做法還有很重要的一個目的,就是資訊安全。舉個例子來說,如果沒有這些最基礎的 CI / CD 建置,那麼你要部署到 production environment 的東西是哪裡來的? 我想應該都是 developer 自己的 PC build 出來就丟上去了吧 XDDD
其實除了容易出錯等等問題之外,更重要的是這樣很不安全啊! 我舉個情境:
如果有個心懷不軌的 developer, 自己改了某幾段 code, 只要 login id == “andrew” 就給最大的權限… 然後把這樣的 code 編譯成 DLL 丟上 production server 了。由於這些 code 不需要 push to git, 所以事後也完全無法從版控追查問題。公司可能蒙受損失,而且還沒辦法知道是誰幹的!
這樣的行為,若沒有 CI / CD 及配套的管制的話,不但檔不住,而且連事後追查都有困難 (這有問題的 source code 沒有進版控)。試想,你的團隊能承受得起這樣的風險嗎? CI / CD 若有執行,那這問題就不存在了。因為這流程,間接地不需要讓 developer 親自進行 deploy 的動作了, 同時部署的檔案,早就透過 CI / CD 準備好,因此不需要 (也不能) 再透過 developer 當場用自己的 PC build 一份出來上版部署了。CI / CD 除了提高效率之外,同時也更為安全。有心想好好導入 CI / CD 的 team 可以仔細想想這個問題,通常大老闆 (尤其是不懂技術的那個層級) 可能搞不懂軟體工程這個領域,但是你要是跟他講資安領域他就知道了。如果你能進一步跟他講上面的案例,那我想你推廣 CI/CD 的政策應該會獲得更多支持!
這篇導入 CI / CD 的心得 + 碎碎念就先寫到這邊,我希望有心鑽研軟體開發與軟體工程的朋友們,都能有機會跳出工具,好好思考一下做這件事情的目的。知道要做什麼再來挑選或學習技術,你才不會被技術牽著鼻子跑啊! 這篇我就野人獻曝一下,貢獻一下我自己的想法。有目的的學習會事半功倍的,想通你想要的 CI / CD 到底是什麼之後,再針對你有興趣的工具好好學習吧!