🤖 GitLab CI/CD 最佳實踐:安全掃描、變數管理、Deploy Token 與模組化 Pipelines
GitLab CI/CD 幾乎是現代 DevOps 團隊的標配:從 Commit 開始自動進行建置、測試、部署與安全掃描,讓版本品質與發佈節奏都更穩定。 本篇整理實務上最常用、也最容易踩雷的部分:環境變數與密鑰管理、Deploy Token 使用方式、 內建安全掃描啟用、以及如何把 Pipeline 拆成模組化結構,讓專案越長越大也不會變成 YAML 地獄。
📌 一、GitLab CI/CD 核心架構概念
先快速用幾個關鍵字釐清 GitLab CI/CD 的核心組件:
- Runner:真正執行 Job 的實體/VM/Container,可以是 Shared Runner 或專案自建。
- Pipeline:由多個 Stage 與 Job 組成的一次完整流程,例如 build → test → deploy。
- .gitlab-ci.yml:放在 Repo 根目錄的 YAML 設定檔,描述 Pipeline 規則。
- Environment:部署目的地,例如 dev、test、stage、prod 等。
一個最簡單的 Pipeline 可能長這樣:
stages:
- build
- test
- deploy
build_job:
stage: build
script:
- echo "Building..."
test_job:
stage: test
script:
- echo "Running tests..."
deploy_job:
stage: deploy
script:
- echo "Deploying to production..."
只要把這個檔案命名為 .gitlab-ci.yml 並推上 GitLab,下一次 push 就會自動觸發 Pipeline。
📌 二、GitLab 內建安全掃描:SAST / DAST / Secret Detection
在企業與政府專案中,CI/CD 不只是「自動 build & deploy」,同時也扮演「安全守門員」: 每次 Commit 都順便跑一輪安全檢查,可以在正式上線前就抓出潛在問題。
| 掃描類型 | 主要用途 |
|---|---|
| SAST(Static Application Security Testing) | 直接分析原始碼,找出 SQL Injection、XSS 等常見漏洞模式。 |
| DAST(Dynamic Application Security Testing) | 在應用程式實際跑起來時,以黑箱方式模擬攻擊。 |
| Secret Detection | 檢查是否不小心把 API Key、Token、密碼 commit 到 Repo。 |
在 GitLab 中啟用其實非常簡單,只要在 .gitlab-ci.yml 中加入官方 Template:
include:
- template: Security/SAST.gitlab-ci.yml
- template: Security/DAST.gitlab-ci.yml
- template: Security/Secret-Detection.gitlab-ci.yml
加入後的效果:
- 每次 Pipeline 會自動跑對應掃描。
- 結果會顯示在 Merge Request 的「Security」或「Pipelines」頁籤。
- 可以產出報表,方便資安與稽核檢視。
📌 三、CI/CD 變數管理:密鑰、安全與分層配置
任何一條像樣的 Pipeline 一定會接觸到各式各樣的機密資訊:
- Docker Registry 的帳號密碼或 Token
- 各環境的 API Key、DB 密碼
- 第三方服務(Cloud、監控系統、Webhook)的憑證
這些絕對不能硬寫在 .gitlab-ci.yml 裡面,而是要放在 GitLab 的 CI/CD Variables 中。
1️⃣ Variables 層級與屬性
| 層級/屬性 | 說明 |
|---|---|
| Group-level Variable | 整個群組底下所有 Repo 都共用,例如公司統一的 Registry URL。 |
| Project-level Variable | 只在單一專案內使用,例如該系統專用的 API Key。 |
| Protected | 僅能在 Protected Branch / Tag(如 main、release)使用,避免在測試分支被濫用。 |
| Masked | 即使在 Job Log 中 echo 出來也不會顯示真正的值,可降低洩漏風險。 |
設定位置:Project → Settings → CI/CD → Variables。
例如定義一個供 Pipeline 使用的 Token:
MY_PRIVATE_TOKEN = xxxxxxx(同時勾選 Protected + Masked)
在 .gitlab-ci.yml 使用方式如下:
deploy_job:
stage: deploy
script:
- echo "Deploy with token (不會顯示真正內容)"
- curl -H "Authorization: Bearer $MY_PRIVATE_TOKEN" https://api.example.com/deploy
📌 四、Deploy Token 與 Deploy Key:差異與實務建議
很多團隊會疑惑:「我要讓外部系統拉 Repo 或拉 Container,要用 Deploy Key?還是 Deploy Token?」, 兩者都可以「給系統存取 Repo」,但設計目標與安全模型完全不同。
| 項目 | Deploy Token | Deploy Key |
|---|---|---|
| 型態 | 帳號+密碼形式,可存取 Repo / Registry | SSH 公鑰/私鑰配對,通常用於 Read-only Clone |
| 常見用途 | Kubernetes、外部服務拉 Docker Image | CI Job 或外部主機 clone Repo |
| 安全控制 | 可設定過期日與權限範圍 | 常被多專案共用,管理不當較容易留下後門 |
| 建議 | 正式環境部署強烈建議使用 | 適合單純 Read-only 使用情境 |
正式環境(prod)最佳實務:
- 使用 Deploy Token 搭配限定權限(僅 Pull Image / Read Repo)。
- 設定明確過期日,例如 30~90 天,定期汰換。
- Deploy Token 只給 CI / K8s / 部署系統使用,不給人類開發者日常使用。
📌 五、Pipeline 模組化設計:避免 YAML 地獄
專案一開始只有兩三個 Job,還可以靠一個 .gitlab-ci.yml 撐住;
但當你開始拆成多個微服務、支援多個部署環境、又加上一堆掃描與測試,
YAML 檔可能很快就破千行,維護成本爆炸。
建議做法是把 Pipeline 拆成「模組化檔案」,目錄結構可以像這樣:
.gitlab-ci.yml
ci-build.yml
ci-test.yml
ci-deploy.yml
ci-security.yml
主檔 .gitlab-ci.yml 負責把所有模組 include 進來:
include:
- local: "ci-build.yml"
- local: "ci-test.yml"
- local: "ci-deploy.yml"
- local: "ci-security.yml"
例如 ci-build.yml 可以專心描述 build 相關 Job:
stages:
- build
build_app:
stage: build
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
好處:
- 不同小組可以各自維護自己的 CI 模組檔。
- 同一個 build/test/deploy 模組可以被多個專案共用。
- 主檔
.gitlab-ci.yml變成「總目錄」,可讀性更高。
📌 六、多環境部署範例:dev / test / stage / prod
在實務中,最常見的環境分層大致如下:
| 環境 | 用途 |
|---|---|
| dev | 開發人員日常測試,允許較高頻率部署。 |
| test / qa | 測試團隊進行功能與回歸測試。 |
| stage / pre-prod | 幾乎等同正式環境,用來做最後驗證。 |
| prod | 正式對外服務,所有操作必須可追蹤、可稽核。 |
以下是一個簡化版的多環境部署 YAML 範例:
stages:
- build
- test
- deploy
build_app:
stage: build
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
test_app:
stage: test
script:
- npm test
deploy_dev:
stage: deploy
environment:
name: dev
url: https://dev.example.com
script:
- ./deploy.sh dev
only:
- branches
except:
- main
deploy_prod:
stage: deploy
environment:
name: production
url: https://www.example.com
script:
- ./deploy.sh prod
when: manual
only:
- main
這裡有幾個重點:
- dev 環境:對所有分支開放,自動部署,方便開發人員快速驗證。
- prod 環境:只有
main分支可以部署,且必須由人手動按下「Play」。 - Environment 區塊:可搭配 GitLab 的 Environment Dashboard 顯示每個環境目前跑的是哪一個 Commit。
📌 七、效能最佳化:Cache、Artifacts 與 Runner 策略
當專案變大、Pipeline 次數變多,CI/CD 速度就會成為開發效率的關鍵。以下是幾個常見的最佳化技巧:
1️⃣ 善用 Cache 減少重複下載依賴
cache:
key: "node-modules"
paths:
- node_modules/
搭配 npm ci 或 yarn install,可以大幅減少每次 Pipeline 的依賴下載時間。
2️⃣ 使用 Artifacts 傳遞建置結果
不要在每個 Stage 重新 build,而是把 build 出來的結果傳遞給後續 Job 使用:
build_app:
stage: build
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
deploy_prod:
stage: deploy
dependencies:
- build_app
script:
- ./deploy_dist.sh
3️⃣ Runner 規劃建議
- 針對不同任務類型使用不同 Tag(例如
docker、shell)。 - 怕影響正式環境的部署 Job,請使用專屬 Runner,不與一般 build/test 共用。
- 有大量 parallel job 時,可用 autoscaling Runner(例如搭配 K8s 或 Cloud)動態擴縮。
📌 八、實務安全建議總整理
最後整理一份「CI/CD 安全 Checklist」,方便在導入或稽核時快速檢查:
- 所有 Token、密碼都放在 CI/CD Variables,不寫死在 YAML 或程式碼。
- 重要變數一律設為 Protected + Masked。
- Production 部署 Job 一律設定
when: manual,並只允許 main / release 分支觸發。 - Deploy Token 設定過期日與必要最小權限(Principle of Least Privilege)。
- 啟用 SAST / DAST / Secret Detection 等安全掃描。
- 限制 Runner Tag,避免不可信的 Job 在敏感 Runner 上執行。
- 設定 Branch Protection 規則:要求 Merge Request、至少一位 reviewer 通過才能合併。
🔗 延伸閱讀
- 🤖 GitLab Runner 與 Pipeline 基礎教學:從零建立第一條 CI/CD
- 🐳 GitLab Docker Runner + Registry 實戰:Build、Push、Deploy 一條龍
- 🚀 高階 GitLab CI/CD:Artifacts 流程、Blue-Green 與 Canary 部署策略
— WWFandy・DevOps 自動化工程筆記
沒有留言: