cover

當你管五個 repo 的時候,每個 repo 各寫一份 .gitlab-ci.yml 還行。當你管三十個的時候?有人忘了加安全掃描、有人的 deploy job 寫法跟其他人不一樣、合規規則更新了要逐一修改——這就是「沒有模板化」的代價。

先講結論

GitLab CI/CD 模板化的核心就是:把共通流程抽出來,集中管理,版本化。專案端只需要 include 加上少量覆寫就能跑。改一個模板,三十個 repo 同步更新。但模板化也有代價——改版不慎會瞬間炸掉所有專案,所以版本管理跟灰度釋出一樣重要。


什麼時候該開始模板化?

問自己兩個問題:

  1. 你是不是在不同 repo 裡 copy-paste 同一段 CI 配置?
  2. 某個規則要改的時候,你需要去幾個 repo 改?

如果答案讓你不舒服,就該開始了。具體來說,專案超過五個、有多語言多框架、需要統一安全掃描或發版規範的時候,模板化的 ROI 就很高。


怎麼用 include 引入模板

GitLab 提供四種 include 方式,最常用的是 include:project——從另一個 repo 引入模板:

# .gitlab-ci.yml
include:
  - project: 'infra/ci-templates'
    ref: 'v1.4.2'
    file: '/templates/base.yml'
  - project: 'infra/ci-templates'
    ref: 'v1.4.2'
    file: '/templates/security.yml'
 
stages:
  - lint
  - test
  - build
  - deploy

注意 ref: 'v1.4.2'——用 tag 而不是 main。用 main 的話,某天模板 repo 有人 push 了一個 breaking change,你所有專案的 pipeline 同時爆炸。問我怎麼知道的?別問。


專案端怎麼用模板

模板提供 job 定義,專案用 extends 繼承,只覆寫必要的變數:

lint:
  extends: .node_lint
  variables:
    NODE_VERSION: '20'
 
build:
  extends: .docker_build
  variables:
    IMAGE_NAME: 'api-service'

原則是:覆寫越少越好。如果你在專案端把整個 job 重寫了一遍,那模板就白做了。


模板的 rules 要寫好

不要讓 pipeline 在不必要的時候跑。每個 job 都應該有明確的觸發條件:

# templates/base.yml
.node_lint:
  stage: lint
  image: node:20
  script:
    - npm ci
    - npm run lint
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == "main"

沒有 rules 的 job 會在所有 pipeline 都跑,浪費時間也浪費 runner。


常見踩雷跟怎麼避

模板改版破壞性 —— 你改了一個變數名稱,所有引用的專案瞬間 fail。對策:用 semantic versioning,重大變更先在幾個專案灰度測試。

變數缺乏預設值 —— 模板裡用了 $IMAGE_NAME 但沒給預設值,專案沒傳就爆。永遠給合理的 default。

模板太多太大 —— 每個 pipeline 都跑一堆不相干的 job,所有專案都變慢。按需引入,不要 include 一個萬用模板。

沒有文件 —— 團隊不知道模板怎麼用、哪些變數可以覆寫。你的模板 repo 至少要有 README 跟 example。


延伸閱讀


CI/CD 模板化最大的成就感不是寫出一個漂亮的模板,而是某天有人問你「我的 pipeline 為什麼壞了」,你只要跟他說「你把 ref 從 v1.4.2 升到 v1.5.0 就好」。