今天我們要繼續改善 CI/CD Pipeline。
首先,我們要回歸初心,詢問幾個問題。
第一個問題——為什麼我們需要做 CI/CD ?
第二個問題——如果工程師修改了一行程式碼,需要多久才能部署上線?
最後的問題——如何做到讓任何人可以任意的取得任何版本的程式?
(當然前提是這個人本來就是有取得程式的權限,也需要取得這個特定版本的程式。)
這三個都是團隊在建構 CI/CD 時非常重要的問題,務必要記在心中常常捫心自問,才不會在 CI/CD 的路上走偏了。(謎之音:是可以走到哪裡去~)
如果你比較早開始加入 gitlab.com,不知道你有沒有一種感覺,就是有一陣子在台灣普遍的下班時間 18:00 之後,gitlab.com 上的 GitLab CI 就很容易罷工遲遲都不執行 CI Job,甚至根本我明明幾分鐘前才 git push
了一個更新版的 .gitlab-ci.yml
到 gitlab.com,但 CI Pipeline 卻遲遲都不更新。那段日子我都戲稱,只要員工下班了,就連 CI Service 也一起下班了。
如果只是下班時間 CI service 掛掉,可能影響還不大,但你難保不會遇到「 CI Service 臨時掛掉,但偏偏這時又急著要部署某個版本的程式給重要人士觀看。」的狀況。對此,我們不禁要問「如果 CI Service 臨時掛掉時到底該怎麼辦?」。
答案:「那就手動啊,結案。」(揍飛)
其實這個答案一點問題都沒有,因為這本來就是現實狀況,當「自動化」故障時,當然你就只能手動處理了,只不過有沒有辦法可以讓「手動」這件事變得輕鬆一些呢?
這就和 CI/CD Pipeline,以及 Pipeline 裡面每一個 CI Job 是怎麼設計與規劃有關了,我們先看一個 CI Job 的範例。
build:
stage: build
tags:
- "build"
script:
- cd $deploy_dest
- touch $git_commit_sha
- composer install
- npm install
- npm run production
- tar -zc $deploy_dest -f /artifacts/"$project_name"-"$git_commit_sha".tar.gz
上面這個 Job 有六行 script,如果要改成人工手動執行,最慢的方式就是工程師看著這六行 script,然後在備用主機上一行又一行的輸入,在輸入的同時他還必須想辦法弄清楚那些變數 $deploy_dest
、$git_commit_sha
與 $deploy_dest
應該要填哪些值才對。
聰明一點的工程師,可能會先把變數釐清,接著臨時建立一個 shell script,把六行 script 貼進去,最後以一行 Command bash not-auto-build.sh
來完成它。但即便如此,還是免不了工程師需要人工進行腳本轉換的動作。
但假如是下面這種 CI Job 呢?
build:
stage: build
tags:
- "build"
script:
- ansible-playbook build-artifacts.yml -e env=$branch_name
同樣功能的 CI Job,但這次應該執行的 script 已經被事先撰寫成 Ansible 的 Playbook,變數的取得也同樣寫在 Playbook 內,只要透過 -e env=
輸入正確的 branch name 即可。如此一來工程師只要有辦法取得 build-artifacts.yml
,就可以直接在備用主機上執行它來完成原本 CI Service 代勞的工作。倘若我們將同樣的概念套用在所有的 CI Job 上,是否就能讓「手動」這件事變得輕鬆一些呢?
此外這種做法還有其他的好處,假如今天公司忽然決定不繼續採用 GitLab CI,要改換其他 CI Service 時,CI Job 需要執行的 script 並不需要改寫,因為 script 本來就是可以獨立執行的,因此要抽換到其他 CI Service 也不會太難。
以及,這種做法更容易做到 script 的重複利用與管理,例如以 deploy 為例。
deploy:
stage: deploy
tags:
- "ansible"
script:
- ansible-playbook deploy.yml -e env=$branch_name
只要正確的替換 $branch_name
,同一個 script 即可用來部署程式到不同的環境。因此不管是 CI Service 代勞或工程師自己手動執行,都能用同一個 script 搞定部署工作。並且這種方式也能善用 CI 來為 script 除錯,如果 script 的動作是一致的,只是變數不同,那麼假如 script 有問題,那 CI 在 dev 或 stg 環境執行時就會先出錯,而將問題揭露,而不會等到某天急著要執行 prod-deploy 時才浮現水面。
透過今天的說明,你有覺得將 CI/CD Pipeline 改成今天介紹的方式是比較好的嗎?這種做法會不會有哪些缺點呢?歡迎一起來思考與討論!
今天的內容其實跟 GitLab 沒太多直接關聯,反而是提供大家對於「工具」的一個反思,GitLab CI 提供了一條龍的服務,固然有方便之處,但也有被它綁住而受限於它的危機。
另外,今天的內容也試圖在點出 CI/CD Pipeline 的另一項關鍵——「相依性管理」。這所指的不僅是程式碼本身的相依性而已,也包含對於環境、CI Service、變數、Artifacts、Script⋯⋯等。
最後,再次邀請大家不妨重新調整一下思維,思考本文一開始的三個提問,努力做好 CI/CD 的各種「相依性管理」,嘗試達成「讓任何人可以任意的取得任何版本的程式」這項目標吧!
自 2021 年 12 月 12 日開始,我就一直想要將原發佈在 iT 邦幫忙的鐵人賽系列文章搬移至 https://gitlab-book.tw 並補充說明文章內容已有過期之處。
因為當初參加 iThome 鐵人賽時,GitLab 仍在 12 版,但如今 GitLab 已更新好幾版了,需要提醒大家注意一下。
本文已完成搬遷