GitLab CI/CD課程28

Configure Dynamic Versioning for Docker Image(花式變數轉移)

ZONGRU Li
9 min readAug 15, 2022

Dynamic Versioning:

在當前的基本Pipeline設計中,我們是hard code方式寫死版本號TAG:

如果沒有人去更改,最後打包的image就會不斷地覆寫上次打包的image

所以這邊就要避免hard code版本號TAG這部分

最後當然是每次新版本佈署都是使用可以區分的版本號

這樣哪一版有異常時

就能夠很快決定退版到哪一個TAG版本號的IMAGE重新佈署

而當然這部分版號隨打包佈署遞增最好是自動化

Application Versioning in General:

可能每家公司企業乃至團隊都有不同做法

常見的方式就是像Maven的三碼語意化版本(Semantic Version)

類似這樣
Semantic Version語意化版本

第一碼(Major):如不兼容的API變更(例如置換Framework,較大功能新增)

第二碼(Minor):較小功能新增或是嚴重Bug修正

第三碼(Patch):小的功能調整或小Bug修正

而這些版本號通常都是由打包管理工具設定檔來定義或提供:

npm← →package.json

Maven← →pom.xml

Gradle← →build.gradle

以目前例子來說就是:

所以版本變更就要來改這個版本號例如1.0.0 →1.0.1

Read version from package.json:

所以現在這邊要做的就是在打包image過程中

package.json中提取版號來用於打包imagetag使用

首先

然後在build_image這個Job內從package.json提取版號來用

#透過以下shell語法與jq工具來取提版號欄位:
cat app/package.json | jq -r .version

但是透過jq工具來截取json欄位,首先要在Runner上安裝該工具

#在Runner機台上安裝jq工具:
sudo apt-get install jq -y

簡單測試一下,直接vi一個package.json檔案貼入既有內容:

儲存
#執行以下指令看到版號:
cat package.json | jq -r .version

其他也可以:

Pipeline腳本內的build_image這個job內就要把拿到的版號提出為變數

Job變為如下:

此時Pipeline就不用在煩惱TAG要用的號碼了

Generate Unique Version Tag:

現在的困境在於,要有人去維護package.json上面的版本號

如果有人忘了更改package.json上面的版本號

那麼image包出來都是同一個TAG

所以要進一步讓imageTAG都是獨一無二的,簡單透過結合以下兩者:

package.json的版號 + GitLab預設的變數如下

LINK

在上面的"CI_PIPELINE_ID"則是GitLab instance層級唯一,應該不需要

所以在此,我們要設計的就是先簡化package.json的語意化版本

並且串接上"CI_PIPELINE_IID",所以:

Pipelinebuild_image這個Job改為:

即便有人忘了改package.json的版號,最後image組合的tag也會數字增加

Use artifacts in other jobs:

在上面完成的修改後,已在打包image階段,打好了可用的TAG

但是這邊會有一個問題

就是VERSION這個變數只會存在於build_image這個Job

但是在push_image這個Job的階段也會需要用到這個VERSION變數

這就是前面學到的,每個Job的執行都是在一個全新的環境下!

自然拿不到前一個Job定義的環境變數

當然這邊也可以讓push_image這個Job用一樣方式取得

但是這樣有點重工

此時就要引用"Job Artifacts",也就是前面做過的輸出測試報告

最後傳回給GitLab,並提供於Web介面上下載該報告

而這也是拿來在Jobs間傳遞變數的可用方式

也就是說要在build_image這個Job產變數檔

然後在push_image這個Job內讀取上述變數檔

這邊要特別提醒一個過程:

build_image這個Job完成時候

version-file.txt檔案會先上傳給Gitlab Server本身,接著才傳給後續Job使用

一般狀況下

所以在push_image這個Job新增以下

但是這邊會有個問題產生

預設情況下,GitLab Job產出的artifact的檔案

會被下一個"Stage"的Jobs下載使用

也就是說現在build_image這個Job產出的version-file.txt檔案

只會被"deploy stage"deploy_to_dev這個Job拿到!

也就是現在問題在於

build_image這個Jobpush_image這個Job都在同stage

push_image這個Job會拿不到檔案

所以當同一個stage內的後一個Job要拿到前一個Job產出的檔案時

需要 →"dependencies",裡面可以定義一個Jobs集合拿取其artifacts

再次調整push_image這個Job為:

如上,就看到needsdependencies兩兩成對

但是needs是為了stage內不同Jobs的順序性

前一個Job要結束才能執行該Job

另外還有一個重點要提及的是"dependencies"是相依於"needs"

例如:

這樣才可以:

另外可能是新版的關係,現在"needs",將自動包含"dependencies"功能

所以可以簡化:

接著還有效率上的疑慮是如果某個Job產出的artifact檔案超大或太多份

但是後續的Job可能根本不需要的情況!?

比如在deploy之後可能新增一個測試的Job

就可以用下面方式避免下載該artifact:

這樣也可以加速流程進行

當然這邊也可以拿取前面測試的報告:

不過上面只是演示,這段Code會移除不做

接著調整deploy_to_dev這個Job

如上面解釋,這個必然拿到剛剛版本的變數檔的artifact了!

所以現階段完整pipeline如下:

記得要改掉IP跟DNS位置才能用!

Commit上述Pipeline後看看結果:

來到以下位置看看image:

那個80對應到Pipeline的:

網站貓貓一樣還在:

另外在push_image這個Joblog可以看到下載artifact動作:

Pass Env Vars as Dotenv Artifact:

首先簡單解釋Dotenv檔案是輕量化的npm package

可以自動從.env檔案載入環境變數

但是每一行只能定義一個變數,並且格式是key=value形式

所以build_image這個Job可以調整為:

上面那個第二行echobuild.env只是示範打的,等等會拿掉

如上最後包成artifacts →reports →dotenv屬性

build_image這個Job進行上述調整後

會多一個功能是

後面的Job會自動引入那些變數為環境變數!

也就是:

這時候還要重提一個部分就是前面提到的

"needs"與"dependencies"這兩個屬性也可以拿來控制dotenv內的環境變數!

此時完整的Pipeline為:

記得改被佈署機的IP跟DNS位置變數才能用!

上述Pipeline Commit後等一陣子看到:

push_image這個Joblog內看到:

講師提到後面還是沿用通用方式,可能後面也比較容易理解:

參考課程(reference)

額外相關參考

--

--

ZONGRU Li
ZONGRU Li

Written by ZONGRU Li

2022/11/17 開源部分個人筆記給LINE "Java程式語言討論區"社群,希望能對社群的技術學習做一點點貢獻.(掩面....記得退訂閱!

No responses yet