在 GitLab CI 的流水線關卡設計中,除一個關卡接著一個關卡進行工作外,各個關卡中的工作也是可以與其他關卡的工作有互動的。像是取得前面關卡的工作所產出的工作成品等等。關於工作與工作間的關係,在 GitLab CI 與之相關的有 needs 及 dependencies 兩種參數,在這篇裡,主要講解 needs 參數。
以往流水線的安排通常是一個關卡完成後接著下一個關卡的工作才開始進行,但有時候關卡裡的工作其實只要前一個關卡中某個工作完成,就可以開始進行。
舉例來說,假設有一個產品需要針對 macOS 及 Linux 開發跨平台的產品,如在持續整合的流程中設計了 build、 test 及 deploy 三個關卡,那麼這三個關卡通常就會有各別針對 macOS 及 Linux 的工作。可以想像,當 macOS 的 build 工作完成後,其實不用等待 Linux 的 build 工作完成,就應該可以進到 test 關卡進行屬於 macOS 的測試工作 ,那麼,在 GitLab CI 裡可以怎麼達成這樣的流程呢?
needs 參數在 GitLab CI 裡,這種不用等待對應關卡的工作都完成了,就繼續開始下一關卡的需求工作的關係設定,可以使用 needs,以上面所提到的案例,其使用 needs 這個參數,則可以如下範例表達:
default:
image: ubuntu:20.04
stages:
- build
- test
- deploy
build:mac:
stage: build
script:
- echo 'build mac application'
build:windows:
stage: build
script:
- echo 'build windows application'
- sleep 10
test:mac:
stage: test
script:
- echo 'test mac application'
needs:
- build:mac
test:windows:
stage: test
script:
- echo 'test windows application'
- sleep 10
needs:
- build:windows
deploy:mac:
stage: deploy
script:
- echo 'deploy mac application'
needs:
- test:mac
deploy:windows:
stage: deploy
script:
- echo 'deploy windows application'
- sleep 10
needs:
- test:windows
PS. 在範例中,刻意的讓所有的 Windows 的工作都多執行了
sleep 10休息 10 秒的動作,主要是為了模擬,假設 Windows 這平台的工作都剛好比較慢的情境。
如上面的這個範例,在執行的過程中,就可以看到當 macOS 的 build 工作執行完後,即直接開始 test 關卡的對應工作,沒有再等 build 工作全數進行完畢後才開始。

同樣的,在 deploy 關卡的 deploy:mac: 工作也是,待 test:mac 工作完成後,即開始進行。

在上述範例的執行過程中,可以看到一個 DAG 的圖示,什麼是 DAG 呢?

所謂的 DAG 是 Directed Acyclic Graph 的縮寫,在圖論裡,其定義是:
圖由頂點和連接這些頂點的邊所構成。每條邊都帶有從一個頂點指向另一個頂點的方向的圖為有向圖。有向圖中的道路為一系列的邊,系列中每條邊的終點都是下一條邊的起點。如果一條路徑的起點是這條路徑的終點,那麼這條路徑就是一個環。有向無環圖即為沒有環出現的有向圖。
我的理解是,當一個圖上有各個點,每個點之間有方向性的線,任選一個起點出發,可以透過有方向性的連接到另外一個點,但出發後就再也回不了出發點,這就是「有向無環」。
而在流水線上,每個工作可當作是一個點,一個工作結束後,即往下一個連接的工作,以此類推直到最後一個工作完成,不會因為發生工作重複執行的狀況,這樣的工作關係,這就是一種「有向無環圖」。
延伸自上面的例子,在已有的關卡前後增加了 init 及 publish 兩關卡,並且把 build 的兩個工作均設定 needs start 這工作,在 publish 關卡中設定 needs deploy:mac 及 deploy:windows 工作。可以看到 DAG 的圖片變為如下:

在這樣的設定下,流水線的進行過程 build 關卡的兩個工作會等待 start 這工作完成後同時開始進行。

而最終的 publish 關卡工作 publish:all 因為設定了 needs deploy:mac 及 deploy:windows 兩工作,所以等到這兩個工作都完成了才會開始。

needs 的工作需要上一關卡的工作成品呢?如執行的工作需要某一個工作的「 artifacts」工作成品時,可以透過 artifacts: true 來宣告,以手冊上的例子:
build_job:
stage: build
artifacts:
paths:
- binaries/
rspec:
stage: test
needs:
- job: build_job
artifacts: true
rubocop:
stage: test
needs:
- job: build_job
artifacts: false
這邊 rspec 這工作,在啟動時,會「自動下載並解壓縮 build_job 這工作的工作成品」,但 rubocop 這個工作則因為設定 artifacts: false 則不會下載。
但,底下這個範例(完整內容),則因為語法的設計,會有一些現象:
deploy:all:
stage: deploy
script:
- echo 'deploy application'
- ls -al
needs:
- job: build:mac
artifacts: false
- job: build:windows
- build:linux
在上面這範例中,在 needs 中「其一」設定了 build:mac 之 artifacts 為 false。後續的 build:windows 及 build:linux,因 artifacts 本身的預設值即為 true,因此在 deploy:all 這個工作中,會只會下載到 build:windows 及 build:linux 這兩個的工作成品。如下圖,可以看到 windows.txt 及 linux.txt 兩個。

同樣的,修改範例,把 needs 中的 build:mac 的工作成品設定,改為 true,則會看到下載了三個工作成品。因另外兩個 artifacts 預設值為 true。

needs 使用限制與需求needs 參數可以設定關聯多個工作,但設定的上限預設為 50 個。needs 參考到使用 parallel 平行化處理的工作,則工作啟動時會一起啟動平行化的這些工作。GitLab CI 的 needs 在一般實務上可以用在同一關卡但某些工作完成的速度卻很不依,但又不想讓後續關卡的工作為了等其中一個,則整個關卡的工作都在等待的場景上。讓可以先開始做的工作先開始進行,也會提升整個流水線工作的完成速度,如專案中的流水線會發生如像這樣的場景,也許就可以導入使用。
接下來要談另外一種工作與工作之間的關係 dependencies 相依。我是墨嗓(陳佑竹),期待這系列的文章能夠讓人有些幫助。