哈囉大家好,歡迎來到 CI/CD 自動化的第三天!
在昨天的 Day 19,我們成功地在 Kubernetes 叢集中,部署並註冊了一位隨時待命的 GitLab Runner。它現在正等待著,準備接收來自 GitLab 的任何指令。
今天,我們的任務就是為它提供一份清晰的工作指示。這份指示,就是我們專案中最重要的自動化藍圖 —— .gitlab-ci.yml 檔案。
我們將學習這個設定檔的語法,並專注於實現 CI (持續整合) 的部分:
.gitlab-ci.yml 的基本結構.gitlab-ci.yml 是一個放置在專案根目錄下的 YAML 檔案。GitLab 會在每一次 git push 時,自動讀取這個檔案,並根據裡面的內容來執行 CI/CD 流程。
我們先來看看一份生產等級設定檔的整體結構與一些重要的「全域設定」。
# .gitlab-ci.yml
# --- 全域變數定義 ---
variables:
# Harbor Registry 設定
HARBOR_REGISTRY: "harbor.example.com"
HARBOR_PROJECT: "my-project"
# Docker Image 名稱模板
BACKEND_IMAGE: "${HARBOR_REGISTRY}/${HARBOR_PROJECT}/ota-backend"
FRONTEND_IMAGE: "${HARBOR_REGISTRY}/${HARBOR_PROJECT}/ota-frontend"
# 使用 GitLab 預定義變數,讓每個 commit 都有唯一的 Image Tag
IMAGE_TAG: "${CI_COMMIT_SHORT_SHA}"
# --- Pipeline 階段定義 ---
stages:
- test
- build
- deploy:dev
- deploy:staging
- deploy:prod
# ... 後續是各個 Job 的定義 ...
variables:定義了可以在所有 Job 中共用的全域變數。這讓我們可以集中管理 Image 名稱、倉庫位址等資訊,非常方便。$CI_COMMIT_SHORT_SHA 是 GitLab 提供的預定義變數,代表當次 commit 的前 8 位 hash 值。stages:我們定義了 test, build, deploy:dev 等多個階段,並規定了它們的執行順序。今天,我們將專注於前兩個階段。一個好的 CI 流程,第一步永遠是確保程式碼的品質。現在,我們在 stages 定義的下方,加入我們的 test 任務。
# .gitlab-ci.yml (接續)
# --- 測試階段 ---
test:backend:
stage: test
image: golang:1.21 # 為 Go 專案選擇合適的執行環境
tags:
- docker # 選擇我們在 K8s 中設定的 Runner
before_script: # 在主要 script 執行前的前置作業
- cd backend
script:
- go mod download
- go test -v ./...
rules: # 定義此 Job 何時被觸發
- if: $CI_COMMIT_REF_NAME == "main" || $CI_COMMIT_REF_NAME == "develop"
test:frontend:
stage: test
image: node:18-alpine
tags:
- docker
before_script:
- cd frontend
script:
- npm ci
- npm run lint
- npm run type-check
rules:
- if: $CI_COMMIT_REF_NAME == "main" || $CI_COMMIT_REF_NAME == "develop"
這段設定很直觀:
image: 我們為不同任務選擇了最合適的 Docker Image 作為執行環境 (golang vs. node)。script: 定義了該任務需要執行的核心指令,例如跑測試或語法檢查。rules: 讓我們可以精確控制 Job 的觸發時機。這裡我們設定為:只有當程式碼被推送到 main 或 develop 分支時,才執行這些任務。如果測試失敗,Pipeline 就會中止,確保有問題的程式碼不會被建置。當所有測試都通過後,下一步就是將我們的程式碼,建置成可部署的 Docker Image。
# .gitlab-ci.yml (接續)
# --- 建置階段 ---
build:backend:
stage: build
image:
name: gcr.io/kaniko-project/executor:v1.19.0-debug # 使用 Kaniko 來建置 Image
entrypoint: [""] # 覆寫預設的 entrypoint
tags:
- docker
script:
# 準備 Kaniko 需要的 Docker 認證檔,指向我們的 Harbor
- mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"${HARBOR_REGISTRY}\":{\"auth\":\"$(echo -n ${HARBOR_USERNAME}:${HARBOR_PASSWORD} | base64)\"}}}" > /kaniko/.docker/config.json
# 執行 Kaniko executor
- /kaniko/executor
--context ${CI_PROJECT_DIR}/backend
--dockerfile ${CI_PROJECT_DIR}/backend/Dockerfile
--destination ${BACKEND_IMAGE}:${IMAGE_TAG} # 推送到 Harbor
--cache=true
rules:
- if: $CI_COMMIT_REF_NAME == "main" || $CI_COMMIT_REF_NAME == "develop"
build:frontend 的任務與此類似,只是 --context 和 --destination 不同。
你可能會問,為什麼不直接用 docker build 指令?
docker build,會涉及到複雜的 Docker-in-Docker (dind) 設定,既不安全也不高效。script:我們不再執行 docker build,而是執行 Kaniko 的 /kaniko/executor。我們透過參數告訴它 Dockerfile 的位置 (-dockerfile)、建置上下文 (-context),以及最重要的——建置完成後要將 Image 推送到哪裡 (-destination)。echo "..." > /kaniko/.docker/config.json 這一長串指令,其實只是在動態地產生一個讓 Kaniko 可以登入我們私有 Harbor 倉庫的認證檔案。$HARBOR_USERNAME 和 $HARBOR_PASSWORD 是我們需要在 GitLab 專案中設定的受保護的 CI/CD 變數。設定檔寫好了,現在就是見證奇蹟的時刻!
Settings > CI/CD > Variables。新增兩個變數:HARBOR_USERNAME 和 HARBOR_PASSWORD,並填入登入 Harbor 的帳號密碼。建議將它們設定為 Protected 和 Masked,增加安全性。.gitlab-ci.yml 檔案 commit 並 push 到 develop 或 main 分支。Build > Pipelines,會看到一條新的 Pipeline 已經被觸發!我們的自動化生產線,現在已經成功地將我們的程式碼,轉化成了存放在 Harbor 倉庫中的、可隨時部署的 Docker Images。
我們已經完成了 CI 的部分。在接下來的文章中,我們就要來完成 CD 的最後一哩路:學習如何撰寫 deploy 階段的 Job,讓 GitLab Runner 自動地使用 Helm,將這些熱騰騰的新版 Image,部署到我們的 Kubernetes 叢集中! 我們明天見!