初探GitLab CI/CD - 如何撰寫.gitlab-ci.yml
使用 CI/CD 建構應用程式
開始
CI/CD 是軟體開發其中一個continuous method,你可以在這方法中持續的建構、測試、部署、監測迭代的程式碼異動。
這種方式有助於降低在有誤的前一個版本中開發新程式,GitLab CI/CD在開發早期階段就能抓錯,確保部署到正式環境的程式碼符合所建立的程式碼標準,而這個程序是更大的workflow之中的一個環節:
Plan | Create | Verify | Secure | Release | Monitor |
---|---|---|---|---|---|
Manage your organization | Learn Git | Use CI/CD to build your app | Secure your app | Deploy and release your app | Monitor app performance |
Organize work with projects | Manage your code | Manage infrastructure | Monitor GitLab Runner usage | ||
Plan and track work | Analyze GitLab usage |
1. 首先建立.gitlab-ci.yml
在project根目錄建立 .gitlab-ci.yml
,定義在CI/CD pipeline中所要執行的 stages, jobs, script。
另外也會定義variables,不同jobs之間的依賴關係,並且指定每個job什麼時候會被執行,應該如何被執行。
沒有硬性規定要怎麼命名,但.gitlab-ci.yml
是這個 CI/CD configuration file 最常見的名字。
2. 接著尋找或新建 runners
Runners 是替你執行 jobs 的 agents,可以在實體機或者虛擬instances上run jobs。在 yml 檔可以指定你run jobs的時候,要指定使用的container image。Runner會把image載下來,clone你的project,並且在本地或者container裡面 run the job
如果使用的是 GitLab.com,那就已經可以使用 runners on Linux, Windows and macOS,也可以在GitLab.com註冊你自己的runners。
如果不是使用 GitLab.com,那可以: (1) 在自己管理的instance,使用已經註冊的runner或者註冊一個runner (2) 在本機新建一個runner
3. 定義你的 pipelines
pipeline 就是定義在 yml 檔的東西,即檔案內容在 runner 上執行時會發生的事情,是由 jobs 跟 stages 組成:
- Stages 定義執行的順序,通常包含
build
,test
,deploy
- Jobs 指定在每個 stage 所要執行的 tasks,例如編譯或測試程式的 job
pipeline 可以藉由不同類型的事件觸發,例如 commits, merges 或者依排程執行
4. 使用CI/CD variable作為 jobs 其中一部分
GitLab CI/CD variable 鍵值對應,是用來儲存『配置設定以及機敏資訊』(例如pcode或者API keys),並且傳進 pipeline jobs
有兩種 variables: custom variable 跟 predefined variable
- Custom variable: 由 user 定義,在 yml 檔、GitLab UI或者API建立/管理
- Predefined variable: 由 GitLab 自動設定,提供了關於當前job、pipeline以及enviroment相關的資訊
5. 使用 CI/CD components
CI/CD component 可以重複使用的 pipeline 配置單位,一個 pipeline 可以由一個甚至多個CI/CD component組建而成。
可以用include:component
關鍵字來把 CI/CD component 加進 pipeline 設定檔。
優點: 可以減少重複,改善維護性,使整個project有更好的一致性
比較複雜的 pipeline
這一個TUTORIAL挺不錯的,跟著做會得到一個用Docusaurus產出的網站 連結在這裡 自己的 github 是用 hugo,但 gitlab + docusaurus 是前公司共筆的做法,很好奇,找天來做看看
image
: 告訴 runner 這個 job 要以哪一個 image 的 docker container 來 run,這裡 runner 負責:- 下載 container image 並且啟動它
- 把你的 GitLab project CLONE 進 running container
- 執行
script
裡面列的命令 (一次一行)
artifacts
: jobs各自獨立,不會跟其他 jobs 共享資源,如果要讓某個 job 產出的檔案能夠為後續的 jobs 所用,必須要把它另存成 artifacts,這樣後續的 jobs 就能接收到 artifacts 並使用產出的檔案build-job: image: node script: - npm install - npm run build artifacts: paths: - "build/"
allow_failure
: 會斷斷續續失敗,或者預期會失敗的 job 可能會降低生產力或者難以排除錯誤。使用這個關鍵字可以讓 job 失敗時不至於造成 pipeline 執行的停擺allow_failure: true
dependencies
: 藉由列出 artifacts 的取得來源 jobs,來管控個別的 jobs 要下載的 artifacts- don’t fetch any artifacts:
dependencis: []
- fetch artifacts from dependencies list:
1 2
dependencies: - build-job
- don’t fetch any artifacts:
rules
: 在每個jobs裡面加上rules,以配置這些jobs應該在那些 pipeline 執行,例如 merge request pipeline、scheduled pipelines。Rules是由高到低來評估,如果其中一個規則 rules 吻合,這 job 就會被加進 pipeline1 2 3 4 5 6 7 8 9 10 11 12 13 14
build-job: stage: build image: node script: - npm install - npm run build artifacts: paths: - "build/" rules: - if: $CI_PIPELINE_SOURCE == 'merge_request_event' # Run for all changes to a merge request's source branch - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Run for all changes to the default branch
規劃遷移到 GitLab CI/CD
from Bamboo, GitHub actions, Jenkins, a maven build from Jenkins, CircleCI …
CI/CD 範例
使用 Dpl部署, end-to-end, npm, php
CI/CD YAML 句法參考
關鍵字分成三種類型: Global keyword, Header keyword, Job keyword (另外有 Deprecated keyword) CI/CD configuration file syntax reference
GitLab Runner 這應用程式跟 GitLab CI/CD 協作在pipeline run jobs,有兩種 runners:
- GitLab-hosted runners
所有 GitLab.com 或 GitLab Dedicated 上面建立的 project 預設都有啟用這個 ,如果有owner角色的話,可以停用 runners
- Self-managed runners
如果要建自有的runner,首先要安裝GitLab Runner,再到GitLab.com註冊自有的runner
若是你的組織有一整組 runners,擴增縮減的監控調校方式可以參考這裡
GitLab Runner TAGS
註冊完 runner 之後,可以加上 tags。這個 tag 會映射到每個 jobs 設定tags:
底下的內容
Example: 以下的 job 執行時會使用掛ruby
tag的 runner
job:
tags:
- ruby
- 使用
config.toml
設定 runner,這檔案是在 runner installation 的時候就載下來的。可以用來調整個別指定runner或者所有runners的設定。可以調 logging, cache, concurrency, memory, CPU limits 等其它設定 - 使用 Prometheus 監控 runners,可以檢視當前運行中 jobs 的數量,以及這些 runners 用了多少 CPU
Runner execution flow
以下文章解釋 GitLab, GitLabRunner 還有 Executor 之間的關係很易懂,就不在此贅述了
Pipelines
Pipeline 是持續整合、持續交付、持續部署之中,最高層級的component
Pipeline 包括了:
- Jobs: 定義了要做什麼事情,例如編譯code的job、測試code的job
- Stages: 定義什麼時候要run這些jobs,例如在compile stage之後才到run tests stage
Job 是由 runners 執行,如果concurrent runner數量足夠的話,同一個stage的多個jobs會被平行處理。
如果一個stage的所有jobs都執行成功,pipeline才會執行到下一個stage。
如果一個stage有任何一個job fails,通常不會執行下個stage,pipeline會提前結束。
通常 pipeline會自動執行,一旦建立了就不需要其它的介入。但有時候你也可以手動跟pipeline做交互。
典型的pipeline會包含四個stages,依序以下列的順序執行:
build
stage: 包含一個compile
jobtest
stage: 通常會有一至多個test jobsstaging
stage: 包含一個名為deploy-to-stage
的job名稱production
stage: 包含一個名為deploy-to-prod
的job名稱
可以用多種不同方式來配置pipelines
-
basic pipelines: 在每個stage中,並行執行所有的jobs,後接下個stage
-
directed acyclic graph pipeline (DAG): 根據job之間關係,run的速度通常比basic pipelines還要快 (可以避免不必要的等待)
-
merge request pipelines: 只有 merge request 執行時才會用的 pipeline
-
merged result pipelines: 在實際合併分支之前測試合併之後的結果
-
merge trains: 利用 merged results pipelines,讓merges的動作一個接著一個執行
-
parent-child pipelines: 將複雜的pipeline拆解成一個parent pipeline,這個parent pipeline可以觸發多個 child sub-pipelines,parent & children 都run在相同一個project,使用一樣的SHA,這種類型的pipeline架構常常出現在 mono-repos
-
multi-project pipelines: 將負責不同projects的pipelines合併起來
pipeline配置設定
可以用 CI/CD yaml 配置 jobs 以及 stages,也可以用 GitLab UI 配置特定層面的 pipelines
如果是用VS Code編輯GitLab CI/CD配置文件,可以安裝GitLab Workflow VS code extension幫助你驗證configuration並檢視pipeline status
在manual pipeline預先填變數
可以使用description
與value
關鍵字來定義 pipeline-level(global)的變數,這些變數在手動run pipeline時可以預填內容。description 主要是用來解釋這個variable用來做什麼,允許的值是什麼。
job-level variable 無法填預設內容。
如果configuration檔案的pipeline-level variable有列description,但沒有定義value的話,就會是空白值(blank value),例如:
variables:
DEPLOY_CREDENTIALS:
description: "The deployment credentials."
DEPLOY_ENVIRONMENT:
description: "Select the deployment target. Valid options are: 'canary', 'staging', 'production', or a stable branch of your choice."
value: "canary"
DEPLOY_CREDENTIALS
沒有預先填value,所以每次手動run pipeline的時候都要定義值DEPLOY_ENVIRONMENT
預設值是 canary,在description區塊解釋了其它可用的選項
配置一個可擇的預填變數清單
在手動run pipeline時,也可以定義一個陣列讓執行pipeline的user做選擇,用options
關鍵字列清單,value
關鍵字列預設選項。注意放在 value 的字串也必須包含在 options 清單中,這個清單在Run pipeline UI頁會變成一個下拉選單。
variables:
DEPLOYMENT_ENVIRONMENT:
value: "staging"
option:
- "production"
- "staging"
- "canary"
description: "The deployment target, Set to 'staging' env by default."
run a pipeline by URL query string
可以使用一個查詢字串來預填 Run Pipeline頁,例如:
.../pipelines/new?ref=my_branch&var[foo]=bar&file_var[file_foo]=file_bar
- Run for field:
my_branch
- 指明要用哪一個branch來預填 Run for field
- Variables:
- variable:
- 指明
Variable
類型variable - Key:
foo
- Value:
bar
- 指明
- file:
- 指明
File
類型的variable - Key:
file_foo
- Value:
file_bar
- 指明
- variable:
- Run for field:
Add manual interaction
- Manual jobs:
- 定義: 除非user啟動之,否則這個job永遠不會run,通常用在 deploy to production 的情境中
- 設定: 在這個job之下加上關鍵字
when: manual
,這樣pipeline啟動時就會被skipped掉
Start all manual jobs in a stage
- 如果某一個stage裡面全部都是manual jobs,可以透過
Run all manual
來同時啟動所有jobs (但如果這stage有包含non-manual job就不會顯示此按鈕)
Skip a pipeline
- 如果要commit但不想要觸發pipeline,則在commit message加上
ci skip
或者skip ci
(大小寫不分) - 另外,在 Git 2.10 或更後期的版本,使用
ci.skip
作為 git push 的選項,通常並不會跳脫不執行merge request pipelinegit push -o ci.skip #
-o
: 這裡指的是 –push-option- 使最近的一筆push不會產生一個CI/CD pipeline (只會skip branch pipeline,merge request pipeline如果有的話還是會執行)
- 另外這個command也不會跳過CI/CD整合相關的 pipeline (例如Jenkins)
Delete a pipeline
擁有該 project 的 Owner 角色的 user 有權限刪除 pipeline,但刪除一個pipeline並不會自動刪掉它的child pipelines
刪除一個 pipeline 會使所有 pipeline caches 逾期,並且立刻刪除所有相關物件,例如jobs, logs, artifacts, triggers (刪除後就無法復原了)
Pipeline security on a protected branches
-
protected branch
- 可以設定哪些user可以merge, 哪些user可以push,那些能夠force push, 那些user能透過 Commits API 修改branch
- 如果pipeline在這類branch執行,會實行嚴格的 security model
-
如果某 user 有權限 merge 或 push 進這個 branch,就可以
- 使用 Web UI 或 pipelines API,run manual pipeline
- run 有排程的 pipeline
- 觸發 pipeline
- 跑動態應用程式安全測試(DAST, as dynamic application security test)
- 在既有的 pipeline 觸發手動的action
- 使用 Web UI 或 pipelines API,重試或取消既有的jobs
-
protected variable:
- 只有當user存取機敏資料 (例如 deployment credentials, deployment token) 的權限,才適合分配 merge code to protected branches 的權限給此 user
- protected branches的pipeline,其下的jobs可以存取protected variables
-
protected runner:
- 受保護的runner只能在受保護的分支run jobs,以避免未受信任的程式碼在受保護的runner上執行,也避免部署相關的機敏資料被意外取得
- 為了確保要在受保護的runner執行的jobs不在regular普通runners執行,這些jobs需要加上關鍵字
<job_name>: tags:
reference job tags
檢視pipeline
- 可以用來過濾pipeline list的條件有: trigger author, branch name, status, tag, source
使用 stage 或者 needs
配置來分組 jobs
如果用needs
關鍵字配置jobs,則會有兩種在pipeline明細頁分組jobs的選項:group jobs by stage
, group jobs by Job dependencies
needs: []
: 不依賴於任何其它jobs
設定依賴的方式:
- 在jobs yml設定檔裡面加
needs: ["<the_job_name_to_depend_on>"]
- jobs array 上限數量50個
- 設為 empty array,則這個 job 在此 pipeline 創建時就會開始作業了
- 其它 needs syntax
- 可以在UI檢視依賴關係: Group jobs by Job dependencies -> 打開 show dependencies
pipeline success and duration charts
點選要查看的 project -> 左側選單 -> Analyze -> CI/CD 就可以看到
Note: DORA 指標(DevOps Research and Assessment) Reference: 部署頻率,異動交付期間,服務恢復時間,更改失敗率
Jobs
Jobs是GitLab的CI/CD pipeline中,最基本的元素,在.gitlab-ci.yml
用 command清單來配置jobs,以達成建構、測試、部署程式碼的目的。
基本上是
export=
儲存變數,符合某條件時if[...]; then ...; fi
儲存變數,cd
移動到某資料夾,docker build
…,docker push
…,./gradlew
…,查看java --version
,或者aws ecr
指令操作
Jobs:
- 定義在符合哪些條件下,應該執行哪些作業
- 是 top-level 元素,任意命名(arbitary name),至少必須包含
script
子句 - 可以定義的jobs數量沒有上限
Job 由 runners 收集,在 runner environement 執行,每個 jobs 的運行都各自獨立於其它的 jobs。
在 pipeline 中檢視 jobs
可以在pipeline查看其中的jobs,點選個別的job可以查看這個job log,另外可以:
- 取消這個job
- 如果失敗,重試這個job
- 如果通過,再跑一次這個job
- 清除這個job log
查看 job failed 失敗原因
可以用滑鼠 hover 到 pipeline graph(在pipeline details檢視頁), pipeline widgets(在 merge requests&commit頁面) 或者 job views 查看失敗原因
pipeline 裡面的 job order
視pipeline graph的類型不同,有不同的job順序
full pipeline graph
(詳細大圖): 以name排序pipeline mini graph
(): 先以status排序。再以name排序
Job status order:
failed
red xwarning
yellow !pending
grey circlerunning
blue two-thirds sectormanual
black gearscheduled
?canceled
white \success
green vskipped
gray »created
?
job 命名限制
-
job名稱必須小於或等於255字元,如果一個檔案有多個相同名稱的jobs,只有一個會被加進pipeline,而且無法預判是哪一個被加進pipeline
-
如果被included的job同名,其參數會被merging
-
以下的關鍵字不能當作job名稱: image | services | stages | types before_script | after_script | variables cache | include | true | false | nil pages:deploy (configured for a
deploy
stage)
在 pipeline 裡面替 jobs 做分組
如果你有很多類似的 jobs,呈現的 pipeline graph 就會太長難以閱讀
如果 job names 是以特定格式命型,就可以自動將相似的 jobs 做分組,在 regular pipeline 就會被摺疊成單一的組別
在.gitlab-ci.yml
將多個jobs寫成一組,可以用數字以及以下符號擇一:
- Slash 正斜線:
slash-test 1/3
,slash-test 2/3
,slash-test 3/3
- Colon 冒號:
slash-test 1:3
,slash-test 2:3
,slash-test 3:3
- Space 空格:
slash-test 0 3
,slash-test 1 3
,slash-test 2 3
隱藏 hidden jobs
可以用 comment out (加#,CTRL+L) 或者job_name前面打句點
前面打句點的 job 可以用來作為範本給其它 jobs 做重複使用的配置,使用方式有二:
extends
關鍵字- YAML anchors,即
&
,<<
,*
控制 default keyword 以及 global variable 的繼承
可以用inherit:default
管控 default keywords
的繼承
可以用inherit:variables
管控 global variables
的繼承
default:
image:
before_script:
variables:
DOMAIN: ywc.wednes.com
WEBHOOK_URL: https:/wysiwyz.io
job_name_taht_inherits_all:
inherit:
default: true
variables: true
script: bundle exec inheriting all
job_that_doesnt_inherit_anything:
inherit:
default: false
variables: false
script: bundle exec inheriting nothing
job_that_doesnt_require_before_script_and_domain:
inherit:
default: [image]
variables: [WEBHOOK_URL]
script: echo this job does not inherit before script and domain
run manual jobs的時候指定variables
在 pipeline view 點擊要手動執行的 job 的名稱,不要點 Run ▶ 按鈕
可以看到 Variables 有 key 跟 value 的欄位可輸入內容
如果輸入的 key 已經定義在 CI/CD settings 或者 .gitlab-ci.yml
檔,那這個手動加入的變數會覆寫原有的值,新的值不會被隱碼,會被expanded(加上$參照到其它variable)
延後一個job的執行
當你不想要立刻執行job,可以使用when:delayed
關鍵字來使延遲 job execution 一段時間
roll out 新程式碼的時候,timed incremental rollout 尤其有利於逐步部署程式碼到生產環境
例如說,如果你要開始部署新程式碼:
- User 沒有遇到困難,GitLab 可以自動地從0~100% 完成部署
- User 在新程式碼遇到疑難的話,可以藉由取消pipeline並倒回到上一個穩定版本,來停止 timed incremental rollout
展開或折疊 job log sections
Job log 可區分為不同 sections,每個section可以被折疊或展開,每個section會顯示這期間的相關訊息
點擊 >
可以展開 section;點擊 v
可以折疊 section
客制化可折疊的section
可以用 manual output special codes (這是GitLab用來判斷哪一個section要摺疊的程式碼),在 job logs 裡面建立 collapsible section:
- Marker: Section起始處
\e[0Ksection_start:UNIX_TIMESTAMP:SECTION_NAME\r\e[0K
+TEXT_OF_SECTION_HEADER
- Marker: Section結束的地方
\e[0Ksection_end:UNIX_TIMESTAMP:SECTION_NAME\r\e[0K
這兩段marker都是加在 CI configuration 的腳本區塊,使用 echo -e
“starting_marker”、echo -e
“ending_marker”
如果使用的是 Zsh shell,會需要跳脫字元,例如\e
→\\e
以及\r
→ \\r
- UNIX_TIMESTAMP: 例如
data+%s
,是Unix timestamp - SECTION_NAME: 這個section的名稱,只能由英數字以及
_
,.
,-
這三種符號構成 \r\e[0K
: 用來避免將section markers顯示在渲染出來的job log裡面 (在raw job log仍然看的到),切換到 raw job log 可以點 job log 右上角,選擇show complete raw (文件icon)\r
: carriage return\e[0K
: 清除 line ANSI escape sequence (一定要加0,\e[K
無效)
另外也可以 用腳本改善可折疊的section display
deployment jobs 部署作業
任何使用 environment
關鍵字,以及action:start
的job,就稱之為Deployment jobs,用途是部屬程式碼到環境中
Deployment jobs 不必須在 deploy stage
deploy_job_this_is:
script:
- deploy-to-cats.sh
environment:
name: production
url: https://this-is-a-simple-url.com
action: start
CI/CD components
CI/CD component 是可以重複使用的單一的 pipeline 配置單位。component 可以建立成一個大型 pipeline 的其中一小部分,甚至可以構成完整的 pipeline configuration。
component可以用傳入參數的配置來動態調整行為。
CI/CD components 類似於用include
關鍵字加入其它的configuration yml file (參考官方Docs) ,但使用components有以下不少優勢:
- 可以將 components 列在 CI/CD catalog 之中 (問: 這應該是有Owner角色的使用者才能看到,因為我都看不到😅)
- 可以發行 component 並指明使用特定一個版本
- 多個 component 可以定義在同一個 project 並且一起定義版本編號
除了創建自有的 components,也可以在CI/CD catalog 搜尋現成且符合所需功能的已發布 components
- 可以參考以下的影片 & Beta版的
Component project
component project 是一個 GitLab project,包含 個 repository,hosting 一至多個 components。這個 project 之中的多個 components 都一同編版本號,一個 project 上限最多可以有 30 個 components。
如果其中某一個 component 要求跟其它 components 不一樣的版本編號,那這個 component 應該被搬到另外一個專用的 project。
建立一個 component project
- New project with README.md (如要發布到 CI/CD catalog 的話,顯示這個component摘要的時候,會用到 description & project avatar)
- 依照以下的 required directory structure,替每一個 component 加上 YAML 配置檔案
需求目錄架構
spec: inputs: stage: default: test --- component-job: script: echo job 1 stage: $[[ inputs.stage ]]
這樣就可以立刻開始使用component了,使用 include 關鍵字,component 參照的格式:
<fully-qualified-domain-name>/<project-path>/<component-name>@<specific-version>
:
include:
- component: gitlab.example.com/yws-org/security-components/[email protected]
inputs:
stage: build
secret-detection
指的可能是secret-detection.yml
檔名,也可能是指某一個目錄,目錄裡面的template.yml
component project 的目錄架構
-
如果只有單一一個component
├── templates/ │ └── my-component.yml ├── LICENSE.md ├── README.md └── .gitlab-ci.yml
- 這裡的
gitlab-ci.yml
作用是用來寫job測試 my-component,並且發布新版本用的 - component都放在最高層的
templates/
目錄底下 - 每一個components也可以添加自己的
README.md
文件,再連向上層的 top-levelREADME.md
文件 LICENSE.md
,選擇像是 The MIT License from open source initiative 或者 Apache License, ver.2.0這個LICENSE是開源許可證,用來定義使用、複製、修改程式碼的條款與條件,Apache License 2.0 適合涉及專利保護相館的考量,而 The MIT License 比較簡單靈活,沒有專利條款限制,只要求保留版權聲明和許可證
- 這裡的
-
如果有多個components
├── templates/ │ ├── my-simple-component.yml │ └── my-complex-component/ │ ├── template.yml │ ├── Dockerfile │ ├── test.sh │ └── README.md ├── LICENSE.md ├── README.md └── .gitlab-ci.yml
Component versions:
以最高優先權排序,component version可以是:
- commit SHA code,例如
439e77c88be865fa99afeed0ecd4ee3b17ae4af7
- tag
- 例如
1.0.0
- 如果一個name有tag跟commit SHA同時存在,則以commit SHA優先
- 發佈到 CI/CD 的 component 應該依照 semantic versioning 的標準加上 tag
- semantic version 是定義該異動為 major, minor, patch 或其他類型異動的標準
1.0.0
、2.3.4
、1.0.0-alpha
這些都是有效 semantic versions
- 例如
- branch name分支名稱,
main
,如果 tag 跟 branch 皆存在,以 tag 為優先 ~latest
,永遠指向CI/CD catalog上面最新發行的 semantic version
建議使用有發布至 CI/CD catalog 的版本,用 commit SHA 或者 branch 名稱參照的版本可能尚未發布到 catalog,是供測試用。
避免使用 global keywords 關鍵字
避免使用會影響到整個pipeline裡面所有jobs執行的所有components,包括定義在 main .gitlab-ci.yml
或者其他 included components 的 jobs
Global keywords:
- default: 如果job沒有定義這些keyword,就會用到default區的keyword,
- subkey 包含
image
,retry
,script
,services
,interruptible
,tags
,timeout
,cache
,hooks
,id_tokens
,artifiacts
,before_script
,after_script
etc.
- subkey 包含
- include: 用來定義外部引入的 yaml 檔,把主要的
.gitlab-ci.yml
拆成多個檔案增加可讀性或減少重複程式碼- subkey 包含
component
,local
,project
,remote
,template
,inputs
,rules
- subkey 包含
- stages: 定義在某個或某些stages所要執行的jobs
- subkey 包含
.pre
,build
,test
,deploy
,.post
- subkey 包含
- workflow: 用來控制 pipeline 行為
- subkey 包含
auto_cancel:on_new_commit
,auto_cancel:on_job_failure
,name
,rules
, etc.
- subkey 包含
避免使用global keyword的替代方案:
- Not_recommand:
1 2 3 4 5 6 7 8
default: image: ruby:3.0 rspec-1: script: bundle exec rspec dir2/ rspec-2: script: bundle exec rspec dir2/
- Solution_1: 直接在每個 jobs 裡面加 configuration
1 2 3 4 5 6 7
rspec-1: image: ruby:3.0 script: bundle exec rspec dir2/ rspec-2: image: ruby:3.0 script: bundle exec rspec dir2/
- 在 component 之中使用
extends
關鍵字,使用獨特字以減少 component 在 merged 進 configuration 的時候可能造成的重複命名風險.rspec-image
is hidden job, as template for reducing and extending duplicate configuration.1 2 3 4 5 6 7 8 9 10 11
.rspec-image: image: ruby:3.0 rspec-1: extends: - .rspec-image script: bundle exec rspec dir2/ rspec-2: image: ruby:3.0 script: bundle exec rspec dir2/
用 inputs
取代 hardcoded 值
避免在 CI/CD component 裡面寫死資料,造成後續使用者要審核component內部細節之後才能使用之
hard-coded寫死相關的問題常常發生在stage
關鍵字這區,如果一個component job的stage寫死,所有使用這個component的pipeline都必須定義完全相同的stge,或者覆寫configuration,用 merging method 重新定義變數的值。
比較好的方法是,使用input
關鍵字來做動態component配置,讓component使用者可以自行指定所需的值。
Example: 在 component 使用 inputs.stage
當參數,使用$[[
{要動態傳入的參數}]]
參照傳入參數名稱
-
以下是
component
配置文件:1 2 3 4 5 6 7 8 9 10 11
spec: inputs: stage: default: test --- unit-test: stage: $[[ inputs.stage ]] script: echo unit tests integration-test: stage: $[[ inputs.stage ]] script: echo integration tests
-
以下是使用上述component的project:
1 2 3 4 5 6
stages: [verify, deploy] include: - component: $CI_SERVER_FQDN/ywsorg/ruby/[email protected] inputs: stage: verify
用 inputs
取代客製的 CI/CD variables
如果要在component使用CI/CD variable,可以先評估可否改用inputs
關鍵字。如果inputs
是較佳解,應該避免要求user去定義custom variable來配置component。
通常inputs是明確定義在components的spec
區塊,比variable有更好的驗證性(檢查格式、可維護性、文檔化,有助於預期pipeline的可靠性和穩定性)
Example:
- component 宣告 inputs.scanner-output 要傳入變數 (默認是 json):
1 2 3 4 5 6 7
spec: inputs: scanner-output: default: json --- my-scanner: script: my-scan --output $[[ inputs.scanner-output ]]
- 參照到這個component的project (指定用 yaml)
1 2 3 4
include: - component: $CI_SERVER_FQDN/path/to/project/[email protected] inputs: scanner-output: yaml
但有些情況,使用CI/CD variable會比使用inputs更適合,例如:
- 使用預定義的變數,自動配置component以符合user’s project
- 要求user用遮碼或受保護的CI/CD variable儲存機敏資訊
Variables
CI/CD variable 可以用來:
- 控制 job 與 pipelines 的行為
- 儲存想要重複使用的數值
- 避免在
.gitlab-ci.yml
hardcode 數值
可以在run manual jobs的時候幫某個特定 pipeline 覆寫變數的值,或者在 manual pipeline 預填變數
如果要在 .gitlab-ci.yml
檔案裡面建立一個 CI/CD 變數,需要用 variables 關鍵字:
variables:
GLOBAL_JEN: "A global variable"
job1:
variables:
JOB_GEM: "A Job1 variable"
script:
- echo "Variables are '$GLOBAL_JEN' and '$JOB_GEM'"
job2:
script:
- echo "Variables are '$GLOBAL_JEN' and '$JOB_GEM'"
- 如果定義在檔案最上方,那這個檔案裡面所有jobs都能夠使用
- 如果定義在job裡面,那就只有這一個job才能使用
- 就以上例子來看:
job1
會印出'Variables are 'A global variable' and 'A Job1 variable'
job2
會印出'Variables are 'A global variable' and ''
如何在單一個single job裡面 skip掉不用全域變數
如果要在某一個 job 裡面 block 掉不用全域變數的話,給 variables 設定{}
variables:
NOSY_GLOBAL_VAR: "helloIamJennie"
job1:
variables: {}
script:
- echo This job does not need any global variables
機敏資料的環境變數不適合放在 yml 檔,要透過 UI 設定,也可以透過 API endpoint 新增這些變數,例如:
project-level | group-level | instance-level | |
---|---|---|---|
create | /project/:id/variables | /group/:id/variables | /admin/ci/variables |
預設而言,forked出來的專案無法存取父專案的 CI/CD variables。
CI/CD variable security
有任何異動到.gitlab-ci.yml
檔的 merge request,在做以下動作之前都要先仔細審核:
- before merging the changes
- 為了從forked project提交的 merge request,在parent project run pipeline
像是在 job script 裡面引用到 USER, PASSWORD, SECRET_VARIABLE 這類 variable 就會造成機敏資訊暴露
預防對策:
- job logs中,機敏訊息的variable設定都要在mask variable checkbox打勾,再update variable
- 限制某些受保護的branches或者受保護的tags才能使用機敏的variables (protect variable checkbox)
- 或者用 3rd party secret management provider 儲存取得機敏資訊
什麼是 variable type variable, 什麼是 file type variable
Variable type variable定義:
- 一個key-value對
- 可以作為job裡面的環境變數
通常project, group, instance的CI/CD variable預設為variable type
(variable_type of env_var
),但也可以設定成file type
(variable_type of file
)
File type variable定義:
- 包含一個key, value 以及 file
- 也可依當作job裡面的環境變數,如下作用
- key: 環境變數名稱
- value: 要存在某一個暫存檔的值,代表文件的內容
- path to the temporary file: 暫存文件的路徑當作環境變數的值
要注意,在 GitLab 15.6 或更早之前的版本中,文件的內容當作value,而不是檔案路徑當作value
不能將定義在 .gitlab-ci.yml
檔的 CI/CD variable 設置為檔案類型的變數,有繞過的方法:
- 先在 variables 設置 variable type variable
- 接著在 job script(CICD作業)中,把這個 variable 的值寫入一個檔案
echo "$VAR_TYPE_VAR_URL" > "transformed_file.txt"
- 這樣需要檔案路徑的指令列工具(mytool)就能使用了
在 job scripts 使用 CI/CD variables
- Bash, sh 或類似的 shells:
$
{KEY} - PowerShell:
$
{KEY} 或者$env:
{KEY}- 有些情況需要加quotes,如下
"-DsosposDailyUsr=$env:SOSPOS_DAILY_USR"
- 有些情況需要加quotes,如下
- Windows Batch*:
%
{KEY}%
- 如果是在 windows 環境中跑 CI/CD pipeline
- 這類型的腳本附檔名為
.bat
或.cmd
另外有其他以下功能
- 將job_A建立的variable傳遞給後續的job_B (存在副檔名為 .env 的檔案)
- 控制哪些jobs不需要使用
.env
(dotenv
)裡面的環境變數- 變數的value設定多個字串,以空格隔開,再用script的迴圈達到一個變數多個值(array)的效果
- 另外還有覆寫variable,限制哪些人才能複寫 … 其它參考網址👉 Doc
predefined CICD variables
每個 GitLab CI/CD pipeline 都可以使用預定義的變數。
在兩個不同pipeline執行階段,可以使用預定義的變數,有些是在 GitLab 創建 pipeline 時有效,可以在 job scripts 裡面使用,或者可以用來做 pipeline 配置。另外一些則是在 runners run job 的時候才有效,只能在 job scripts 使用。
在 runner 階段才有效的預定義變數,無法在 trigger-jobs 或者以下三個關鍵字內使用:
workflow
,include
,rules
- trigger-jobs
where variables can be used
可以放在兩個地方:
- GitLab 端,放在
.gitlab-ci.yml
檔裡 - GitLab Runner 端,
config.toml
GitLab 內部變數的 expansion 機制
要寫成 $variable
, ${variable}
, 或者%variable%
這樣的格式
Pipeline security
secret 定義:password, SSH keys, access token 或其他驗證身分用的資料
secrets storage
分成兩種
- Secrets management providers
存在 GitLab instance 以外的空間,例如 HashiCorp Vault、Azure Key Vault、Google Cloud Secret Manager 實作細節可參考:Using external secrets in CI
- CI/CD variables
要在 CICD pipeline 儲存重複使用 data 的話,CI/CD variables是很方便的選項,但相較於前者 secrets management providers 不安全
-
variables 存在 project, group, instances 的
settings
,有權限存取 settings 的 user 就能訪問 variables[路徑] Repository / Settings / CI/CD / Variables
-
variables 可以被覆寫(這樣就很難識別哪裡的變數被用到
-
如果 pipeline 不小心配置錯誤的話,可能會意外暴露 variable
適合存在 variables 的資訊應該是非機敏資料(不用擔心資料暴露),如果有機敏層級比較低的資料要存在 Variables,應該要遮蔽(mask variable checkbox 打勾)或保護之(protect variable checkbox 打勾,再update variable)