CI/CD Pipeline X Gitlab Flow

用GitLab打造一個遵循GitLab Flow的Continuous Delivery Pipeline

CraftsmanHenry
19 min readDec 22, 2021

工作間從第一次觸CI工具到現在能從頭到尾打造一個完整的CI/CD pipeline,中間除了一大堆的trial and error外,還閱讀不少線上文獻和書籍,但一直都沒有機會能夠整理自己腦中關於CI/CD的知識。剛好最近工作上接觸了全新的GitLab CI/CD,也實作一套出來,因此就想說來分享一下,中間發現的有趣feature與自己建立pipeline的思維。

讀完這篇文章後,各位讀者會了解

  • CI/CD pipeline是什麼?還有這個概念想要解決什麼問題?
  • Branching Model的介紹與常見的幾種跟Git相關的branching model
  • 整合CI/CD pipeline和branching model的設計思維與實作

另外在開始前,先提醒各位實作CI/CD pipeline跟使用的工具有高度相關,而在這篇文章採用的Git 儲存庫(repository)是GitLab另外搭配使用的CI/CD Tools是GitLab內建的CI/CD Pipeline,因此若讀者們若使用的工具是別的,那建議可以略過實作的章節,幫自己省點時間 😄。

CI/CD管線、流水線(Pipeline)?

要用一句話來定義什麼是CI/CD Pipeline,那會是

一個應用程式從建置、部署、測試到發布整個流程的自動化實作

也可以說是

程式碼從版本控制系統到使用者手中,這中間的自動化表現形式

大家由上面兩個定義不難看出來這兩句話的重點其實就是「自動化」這三個字,所以接下來我要試著回答下面兩個問題

  • 為什麼要自動化?
  • 要自動化什麼?

為什麼要自動化?

經過工業化時代的我們,都不難理解自動化的好處。還記得我在剛開始工作的時候,當時公司在開發的軟體還沒有完整的自動化建置與部屬流程,因此每次要出版程式前,都要依序幾過以下步驟

  1. 自己小心的檢查設定,build完並試跑一下看能不能在自己電腦上運行
  2. 打開一個word寫下部屬時要對伺服器做哪些操作
  3. 規劃完後就要跟IT申請一台乾淨的Virtual Machine,試著按照自己的步驟操作看看會不會如預期般順利
  4. 最後找QA來照他們設計的測試案例操作看看部屬好的服務,看看有沒有哪些feature是不能運作的

以上這套做完如果一切順利沒卡關大概一周就過去了,更不用說如果中間出什麼問題或QA測試的結果不如預期,時間一長甚至有來來回回(back and forth)一個月以上的狀況。

其實問題還不一定是程式本身的問題,因為我們甚至沒辦法保證,每次部屬的步驟都是一樣的或是QA每次測試的方法都是一樣的,搞不好中間多做少做什麼!?想想就覺得恐怖。

而自動化能為我們解決什麼問題呢?以人的面向來看,一個極大的好處之一不用多說就是節省人力,上述的人工操作,由開發人員撰寫rundown、IT提供測試用的機器,由開發人員部署完後需要測試人員測試,以上工作重複性高又耗時且有可能因為操作人員的狀態不好而導致出錯;自動化後,所有人的時間就可以運用在更有價值的事上,把重複性高的工作交給機器

自動化的第二大好處就是行為重複、一致且不因人的因素而出錯,而要做到重覆、一致肯定就是要寫一份腳本(Script),每次就是跑那個腳本就好,而腳本本身就是一個部屬的操作文件,而且如果中間出錯,只要檢視腳本就好。

第三個好處則是腳本化,腳本(script)跟程式碼一樣可以透過版控工具進行管理,如此一來開發人員就可以檢視看看做過的修改並對部屬的流程進行優化,甚至還能夠退回過去的版本以修正錯誤的更動

自動化?什麼?

現在知道自動化有這麼多好處,那實際上到底該自動化些什麼,可以由上述的問題來得知

  • 試試看程式碼能不能運行
  • 利用測試案例測看看部署好的服務有無如預期般運作
  • IT供應測試用伺服器的過程
  • 運用腳本自動將程式碼部署到伺服器

以上自動化的部分就是屬於Continuous Integration(CI)的範圍,其中還有一些實作時的mindset,例如:要時常提交程式碼、提交的更動不該範圍太大且多於一個目的…等,就稍微帶過就好;除了上述四項外,大多的服務可能還有不同的環境,以因應不同的測試與整合需求,這部分就是屬於Continuous Delivery(CD)的範疇,因此還需要自動化

  • 環境本身的設置與管理
  • 跨環境間的部署
  • 各環境使用的設定管理

Continuous Delivery是一個相當龐大且完整的系統,其中的自動化不止上述講到的這些,另外還有一些在設計Pipeline時需要的mindset與知識由於篇幅的問題,請各位允許我先略過,如果各位讀者對背後的概念、心法和進一步還能做哪些事情有興趣的話,推薦 Continuous Delivery by Jez Humble, David Farley

Branching Models 和 GitLab Flow

版控工具git在目前的業界已經是獨占鰲頭,就算沒用過也聽過,而git最有名的功能就是能夠對同一份source code建立出不同的branch,使developer能夠在不影響其他branch的情況下進行開發,這樣做的諸多好處不在這次文章的討論範圍內,請容許我這邊先行跳過。

知道branch的好,難道沒有壞嗎?當然有,尤其是在眾多developer協同開發同一個code base的情況下;人多雜亂就會應運而生

為了克服branch的混亂,過去的開發人員將處理這些雜亂的思維與實作的方法整理成一套可以遵循的規則

這就是這章節要討論的branching models。

branching models中與這篇文章比較相關的有三個 Git flow、GitHub flow和GitLab flow,在這章節中只打算深入探討GitLab flow而其他兩個這裡只提供參考文獻跟其不適用於CI/CD Pipeline這樣的Continuous Delivery概念的原因

Git Flow

  • 過於繁瑣的分支管理:過去缺少持續整合的概念(CI),無法確保branch隨時都在可以部署的狀態,因此需要feature、release和hot-fix branch來確保修改不會破壞主要的branch;另外要學習與習慣這樣的分支管理策略,需要時間的累積,起碼以我自身待過的團隊來說,還沒看過完全習慣Git Flow管理策略的人(包括我自己 😓)
  • 與不少支援git的工具概念有衝突:master(main) branch在Git Flow中,被用來表示能夠隨時部署的程式碼狀態,developers平時面對的是develop branch,在Git Flow底下反而develop才比較像是main branch,而據説(自己用過的工具真的很少 😫)有不少工具的設計還是將master作為main branch,因此對Git Flow的支援相當有限

GitHub Flow

  • 太過單純的分支管理:只留下master(main)和feature branch,個人的想法是因為,過去的GitHub還未存在有Actions特性,因此確實不需要其他的branch,來因應環境管理、部署和整合…等需求

GitLab Flow

GitLab Flow則是避開過於複雜和簡單的問題,而且與時俱進的考慮了Continuous Delivery所該包含的範疇,不過以下介紹的策略適用對象還是以SaaS(Software as a Service)為主的Environment Branches,若是裝機型的套裝軟體還請參考原文的Releases Branches策略

  • 主要 branch(master/main):眾多developers整合彼此程式碼的branch,若沒有特別要求可以將經過CI測試過的main branch程式碼直接往dev環境部署
  • featuer branch:如同其他的branching models,feature branch由main branch分支出來的;原則是使feature branch的生命週期盡量短,因為其他開發人員會持續將程式碼合併至main branch,存在的時間一拉長會導致與main branch的差異逐漸增大,甚至產生不知道怎麼處理的衝突
P.S. 有人可能會想問怎樣才叫生命週期短的branch,以我之前看過的文獻有各式各樣的建議,甚至有人建議branch過後不要超過3個小時(code這麼好寫? 😓)。但依照我個人的經驗,由於本人待過的團隊不少跑scrum的,所以我自己是最長不讓branch存在超過一個sprint,短的話可能一個工作天結束就merge
  • 環境 branch:當main branch(master/main)- dev打算deploy到staging時,便將main branch的code merge至staging branch,同理要部屬到production時,則將staging的code merge至production branch;在dev環境經過CI/CD的驗證能提供給開發人員足夠的信心說將main branch merge至staging和production branch是沒問題的,因此staging和production的branch可以用來反映當前部屬在該環境的最新版程式碼。

實作

了解完CI/CD Pipeline跟GitLab後,就來實現本文標題所述的整合GitLab Flow與CI/CD Pipeline,在開始講述前,先提醒一下各位「實作與使用的工具公司的文化有高度的正相關」,因此如果公司不是採用GitLab並搭配GitLab CI/CD來管理版控程式碼並執行自動化部屬的話,那本章節可能對讀者的用處非常小,可以建議跳過此部分,那就不多說廢話開始吧!

不過在真正開始說明pipeline之前,先說明一下接下來打算部署的專案與部署的環境。

專案的部分使用Python的FastAPI快速打造一個包含簡易CRUD與health check的RESTFul API,其中不包括任何儲存的solution(RDBMS, NoSQL, etc),會直接將值寫到記憶體以簡化專案複雜度,相關的教學與實際的專案結構請參考以下的文件與我實作完的GitLab Repository

環境的部分,則是採用Heroku的Free Plan;雖然我在公司是使用K8S(EKS by Amazon),不過因為費用和文章篇幅關係,採用有免費方案的PaaS供應商Heroku,Heroku申請好帳號那刻就已經提供給使用者一個easy to deploy的環境,大大的簡化在此篇文章中infrastucture上的複雜度,但依然不會花過多的篇幅介紹Heroku的概念,如果想知道細節的讀者請參考

詳細說明部署自建的docker image到Heroku上需要知道的所有細節

說明Heroku的基本概念application和Dyno

不了解以上概念,不太影響對CI/CD的理解,但還是把以上資源提供出來給喜歡深入研究的讀者。

由於以GitLab Flow為起點出發,會利用environment branch做為CI/CD的觸發點,並執行幾個階段性(stages)的工作(jobs)來形成pipeline,因此我們就先說明一下在本文的Pipeline中打算實作的階段(stages)吧!

stage

  • build:將程式碼build成可以執行的形式,由於現在非常流containerization因此也不免俗會以image的形式來輸出artifact,並push到gitlab本身的image registry中
  • static-test:靜態程式碼測試會執行一些還不需要部署執行,就可以針對程式碼本身完成的測試,像是單元測試、資安弱點掃描…等,但這裡就簡單跑個echo示意
  • deploy:將build過的image部署到Heroku上(雖然在公司是用Amazon的EKS,要用EKS做demo似乎需要點口袋 😅,徵求sponsor 😆)
  • release(production限定):本篇文章僅僅會對git repository打一個tag並將tag推到GitLab上,並發一個GitLab的release

以上4個階段是在本文中,以方便講解和理解為出發點設計的,也許跟企業中面面俱到的實際Pipeline間有段距離,但上面的概念是個非常好的出發點,接下來就以各環境的實際部署script講解給各位吧

Dev(main)

  • build image:跑docker指令build出與所處branch對應的image,並將結果推至GitLab內建的Container Registry;若所處的branch是main branch則還會再多推一個tag為latest的image到container registry內
  • static test:這裡將靜態程式碼測試放在dev的pipeline中;靜態程式碼測試包括單元測試、安全弱點掃描…等。靜態程式碼測試給予開發人員信心說當前版本的程式碼是沒有問題,允許部署的。不過此專案僅僅做展示用,因此只做了一個標記的job,並沒有提供實際的script
  • deploy:deploy會將於build image階段部署好並放置在container registry當中的image pull下來,並推至heroku的image registry後,並要求heroku release該image

由於continuous delivery的精髓之一就是要透過相同的自動化流程進行部署,因此會將deploy的script抽取出來為一個共用的hidden job,供其他job做繼承使用,步驟如下所示

Staging

GitLab無法在job數量三個以下時,產生如上的pipeline管線圖,以job相依圖替代
  • build image
  • deploy

由於staging上的步驟,完全跟dev環境上一模一樣,這裡就不再贅述;靜態程式碼測試(static test)與掃描的部分,因為在dev環境上已經做過,因此直接省略,避免需要花資源重複跑一次。

額外提一下,如果想做壓測(loading test)的話,建議放在staging環境的deploy之後,避免影響到production環境的客戶,不過由於本文實作的pipeline沒有包括loading test,因此只在最後小小提一下,對壓測有興趣可以參考

Production

想要觸發production pipeline除了與前面兩個stage相同的部分「需要將code merge到對應的branch」外,還需要在觸發pipeline時提供$RELEASE_VERSION這個變數,下面會在步驟中提到$RELEASE_VERSION的用途為何

  • build image:要build production的image,需要上面提及的條件因此build image job 需要多加排除的不能build production image的條件
大致與dev環境相同,除了line4 ~ line6的差異
  • deploy:同dev, staging不贅述
  • release:production部署完之後,在最後我們幫這份部署到production的code上一個tag並push回gitlab repository,並利用GitLab CI/CD的功能在repository內開一個release,文件可以參考
Release job的實際script

以上就是完整的GitLab CI/CD pipeline從develop到production,但有些關於heroku的API key與GitLab personal access key和repository的設定等細節,在本文先行略過。相信讀到這邊的讀者已經很辛苦,所以本文還是以說明整個pipeline為主,剩下的旁枝末節若有興趣大家可以留言告訴我,未來有時間再另外撰文告訴大家。最後就是把完整的repositiory提供給大家,希望能幫上大家的忙

在結束之前

到這邊算是說明完想要告訴大家的概念並完成實作展示的部分,本文一開頭說明CI/CD可以減少人力負擔和人為的錯誤,並能持續將不同開發人員的程式碼進行整合並以自動化的方式部屬到生產環境中

接著在下個章節中介紹版本控制工具git的branch管理策略branch model,並將常見的branch model的優缺點都略為帶過後,在最後強調本文的重點:GitLab Flow,並以GitLab Flow中以環境為主的branch管理策略為目標,打造一個簡易的pipeline

說明完兩個主要的核心概念後,接著利用GitLab內的CI/CD feature結合GitLab Flow打造一個完整的CI/CD pipeline。希望大家讀完這篇文章後,能夠獲得一些實作上的經驗。更重要的是對於CI/CD的思維方式,不管未來換了哪種CI/CD工具,都可以利用以上的概念打造出CI/CD pipeline。

而工具的選擇上為什麼使用GitLab?只是因為在近期的工作中,剛好從github切換到GitLab將自己的學習心得分享給大家,畢竟

沒有最好的工具,只有最適合你們團隊的工具

而Heroku的部分,則真的只是因為免費,如果未來有點$$的話,也許可以寫關於雲端供應商K8S(EKS, AKS or GKE)的分享,這邊也期待有看到最後的人可以把想看的東西留言告訴我

另外為了避免這篇文章變成一個CI/CD論文,因此還有諸多遺珠實在無法講和深入,比如說

  • 如何根據專案大小升級自己的pipeline
  • 價值流的概念
  • CI/CD的成熟度模型
  • DevOps…等

如果各位有興趣除了上述提到的Continuous Delivery這本書外,DevOps Handbook也相當值得一看

就在最後祝大家在未來的開發路上都能夠順利啦!Happy Coding!

--

--

CraftsmanHenry

一段尋求軟體工藝的旅程。 A journey by a software developer looking for software craftsmanship.