這次的鐵人賽系列文章,大致上把 GitLab 13.3 開始到目前 18.3 期間,我覺得比較重要的變化及更新做了一些說明,接下來會進入「解題趣」系列,這系列主要是使用 GitLab CI/CD 至今遇到或聽到看到的一些難題。
這系列的每個題目,來源可能來自 stackoverflow 上的問題、工作中實際遇到的案例或朋友的詢問,題目有時候是 GitLab CI/CD 目前還不容易達成需求,或需要轉變換一下思維邏輯的題目。這些題目不一定會在一篇的篇幅內,就列出最終的解法,而可能會是一系列的實驗、驗證、測試、歸納的心得,以及特性說明。
首先第一個題目是來自 stackoverflow 上的題目「How to set a job to manual based on previous stage failure?」,稍微總結一次題目,其問題主要想了解的是:「當流水線目前階段的工作發生錯誤時,下一階段的工作是否可以改為手動啟動?」
這個問題合理嗎?可以運用在什麼樣的場景?現行的 GitLab CI 遇到什麼樣的問題?以及在現階段 GitLab CI 設計的結構下又可以如何達成這樣的需求?
這樣的需求,可能運用在什麼樣的場景?在原始題目的描述中,問題需求者提到了他的場景,他的流水線會控制一些主機的開機(Start)、使用、關閉(Teardown)及消滅(Destroy)程序,可以想像就是開機、使用、關閉、消滅四個階段(Stage)。
開機(Start) -> 使用 -> 關閉(Teardown) -> 消滅(Destroy)
當關閉程序執行成功時,下一個階段會進行主機的消滅(Destroy)程序,可是當執行關閉程序時,發生失敗,就希望後續主機自動消滅便改為手動進行。因為關閉失敗可能有些資源需要人工介入確認後,才能再繼續後續的消滅程序。主機停機程序以及消滅程序這樣的場景中,需要特定情況才改為手動執行,題目就蠻合理的。
稍微了解題目後,會發現題目的目的在於需要「依據特定階段工作執行狀態,來決定下一階段的工作為自動啟動或手動啟動」,於是根據這個特徵開始找尋解決方案。
rules:when
語法第一個想法可能會想使用 GitLab CI 提供的 rules:when
語法來解決,透過現行 rules:when
六個屬性:
when 屬性 | 說明 |
---|---|
on_success |
上一工作成功時 |
manual |
手動執行 |
always |
總是執行 |
on_failure |
上一工作失敗時 |
delayed |
延遲執行 |
never |
從不執行 |
目前題目的特徵,像是 when: on_failure && manual
,兩個屬性來做 AND 的條件操作,也就是:「當上一工作失敗時,且設定為手動執行模式時」執行。但截至 GitLab CI 現行版本 18.3 中,無論是 rules:when
或 when
,上面提到的六種屬性,均無法同時存在。因此這個題目並無法簡單的在一個 Job 裡頭直接透過 GitLab CI 的語法辦到。
CI_JOB_STATUS
在 Predefined variables 中,定義了 CI_JOB_STATUS
屬性,其特性是,可以在 after_script
的段落中,判斷到目前的 Job,在 script 的段落是否執行正確,當正確執行時為 success
當錯誤執行時為 failed
,當被關閉時為 canceled
,且無論 script 執行是否正確 after_script
都會執行。
因此可能可以透過這個屬性特性,當偵測到錯誤時,再透過 artifacts:reports:dotenv
可傳遞的變數設置,傳遞到往後的 stage,供後續 stage 的工作判斷。
stages:
- "teardown"
- "cleanup"
default:
image: ubuntu:24.04 #使用用 IMAGE 較小,image 載入速度較快
teardown job:
stage: teardown
variables:
IS_MANUAL_START: "false"
script:
- echo "run teardown job and exit 10"
# - exit 10
after_script:
- echo "in after script 01"
- if [[ $CI_JOB_STATUS == "failed" ]]; then IS_MANUAL_START="true"; fi;
- echo "IS_MANUAL_START=$IS_MANUAL_START" >> cleanup.env
- echo "in after script 02"
artifacts:
reports:
dotenv: cleanup.env
cleanup job:
stage: cleanup
needs:
- teardown job
rules:
- if: $IS_MANUAL_START == "true"
when: manual
- when: always
script:
- echo "IS_MANUAL_START = $IS_MANUAL_START"
- echo "run cleanup job"
當在下一個工作判斷變數時發現無論如何,工作並不會手動執行,但在 script 中又可以看到變數真的有如預期的設置。不如預期的執行,但這是 GitLab 的執行特徵,在 GitLab Server 要建立整個 Pipeline 前,就 已經 透過工作中的 rules
來判斷,該 Job 是否該建立、是否該做什麼,但由於透過 artifacts:reports:dotenv
所傳遞的變數,是在工作建立「後」才建立,因此 GitLab Server 無法判斷,導致永遠使用 rule:when: always
建立工作。
因此,這次的實驗假設不成立,無法解決原始問題。
今天做了兩個嘗試,第一是透過 GitLab CI/CD 的 rules:when
,但目前這個特性無法採用連集,而第二個嘗試,想透過 CI_JOB_STATUS
的屬性,並透過 artifacts:reports:dotenv
來傳遞變數到下一個階段,但由於 Pipeline 的 Job 是否執行是在 Pipeline 建立前就定義,因此無法滿足。透過這兩個嘗試,其實可以更深入的理解 GitLab 的細節特性特徵,接下來,會持續完成這個題目(其實最終我認為比較適用的方法已經在 stackoverflow 上可以看到)。我是墨嗓(陳佑竹),期待這次的內容能帶給你實用的啟發與幫助。