結論先講
每個微服務必須有自己獨立的 CI/CD Pipeline。 一個服務改了一行程式碼,不應該觸發其他 4 個服務的 build + test + deploy。Mono-repo 用路徑過濾(path filter)實現,multi-repo 天生就是獨立的。Shared library 用語意版號(semver)管理,不要用 latest——否則某天 shared lib 一更新,5 個服務同時爆炸。
單體 vs 微服務的 CI/CD 差異
單體:
git push → build 整個專案 → 跑全部測試 → deploy 一包
微服務:
git push(改了 order-service)
→ 只 build order-service
→ 只跑 order-service 的測試
→ 只 deploy order-service
→ 其他 4 個服務完全不動
聽起來很直覺,但實作起來有兩個大問題:
- 怎麼知道「只改了 order-service」? → 取決於 mono-repo 或 multi-repo
- 改了 shared library 怎麼辦? → 所有依賴它的服務都要重新 build
Mono-repo vs Multi-repo
Multi-repo:每個服務一個 Git repo
github.com/myteam/order-service → 自己的 CI/CD
github.com/myteam/payment-service → 自己的 CI/CD
github.com/myteam/user-service → 自己的 CI/CD
github.com/myteam/shared-lib → 發布到 npm / PyPI
優點:Pipeline 天生獨立,權限管理簡單(誰能改哪個 repo)。
缺點:跨服務改動要開多個 PR,shared library 更新要逐一升版。
Mono-repo:所有服務在同一個 Git repo
myproject/
├── services/
│ ├── order-service/
│ ├── payment-service/
│ └── user-service/
├── libs/
│ └── shared-utils/
└── .gitlab-ci.yml
優點:跨服務改動一個 PR 搞定,shared library 不用發佈到 registry。
缺點:CI/CD 需要路徑過濾,否則改一行就 build 全部。
怎麼選
| 團隊狀況 | 建議 |
|---|---|
| < 10 人,服務 < 5 個 | Mono-repo(溝通成本低) |
| 10-30 人,跨團隊 | Multi-repo(權限隔離) |
| > 30 人,各團隊獨立交付 | Multi-repo + 內部 package registry |
GitLab CI:Mono-repo 的路徑過濾
# .gitlab-ci.yml(根目錄)
stages:
- build
- test
- deploy
# Order Service Pipeline
order-build:
stage: build
rules:
- changes:
- services/order-service/**/*
- libs/shared-utils/**/* # shared lib 改了也要觸發
script:
- cd services/order-service
- docker build -t order-service:$CI_COMMIT_SHA .
order-test:
stage: test
rules:
- changes:
- services/order-service/**/*
- libs/shared-utils/**/*
script:
- cd services/order-service
- npm test
order-deploy:
stage: deploy
rules:
- changes:
- services/order-service/**/*
- libs/shared-utils/**/*
if: $CI_COMMIT_BRANCH == "main"
script:
- kubectl set image deployment/order-service order-service=order-service:$CI_COMMIT_SHAGitHub Actions 的等價寫法用 paths filter:
# .github/workflows/order-service.yml
on:
push:
paths:
- 'services/order-service/**'
- 'libs/shared-utils/**'Build → Test → Deploy 的標準流程
每個服務的 Pipeline:
1. Build
- docker build(產出 image)
- tag = commit SHA(不要用 latest)
2. Test
- Unit test(服務內部邏輯)
- Integration test(DB、Redis 用 docker compose 起)
- Contract test(API 合約有沒有破壞)← 這個超重要
3. Deploy
- Staging:自動部署,跑 smoke test
- Production:手動觸發或自動(看團隊信心)
Contract Test:避免改 A 壞 B
Order Service 依賴 Payment Service 的 POST /payments API。
Contract Test 做的事:
Payment Service 發布新版前,跑 consumer-driven contract test
→ 確認 Order Service 期望的 request/response 格式沒變
→ 如果變了 → Pipeline 失敗 → 不准 deploy
工具:Pact(最成熟)、Spring Cloud Contract
這是微服務 CI/CD 最關鍵的一環。沒有 contract test,你遲早會遇到「Payment Service 改了回傳格式,Order Service 噴 500」的慘劇。
Shared Library 的版本管理
❌ 錯誤做法:
shared-utils: "latest"
→ shared-utils 某天改了一個函式簽名
→ 5 個服務同時 build failure
✅ 正確做法:
shared-utils: "^2.3.0"
→ 每個服務明確鎖定版本
→ shared-utils 升版 → 各服務「主動」升級 + 跑測試
版本升級策略
| 升級類型 | 做法 | 自動化程度 |
|---|---|---|
| Patch(bug fix) | 自動升級(CI bot PR) | 全自動 |
| Minor(新功能) | 自動升級 + 人工 review | 半自動 |
| Major(breaking change) | 手動升級 + migration guide | 手動 |
工具推薦:Renovate Bot 或 Dependabot。它會自動開 PR 告訴你哪些 dependency 有新版本。
常見的坑
1. Pipeline 太慢
5 個服務各跑 10 分鐘 = 50 分鐘(如果串行)
解法:
- 平行化(GitLab CI 的 parallel、GitHub Actions 的 matrix)
- Docker layer cache(別每次從頭 build)
- 只跑被影響的測試(test impact analysis)
2. 環境不一致
「在我的電腦上跑得過啊」
→ CI 用 Docker,本機也用 Docker
→ 固定 base image 版本(node:20.11-alpine,不要 node:latest)
3. Deploy 順序有依賴
Payment Service v2 需要 Order Service v3 的新 API。
錯誤:先 deploy Payment v2 → 呼叫還不存在的 API → 500
正確:先 deploy Order v3 → 確認新 API 正常 → 再 deploy Payment v2
→ 用 API 版本化(/v2/orders)避免這個問題
→ 詳見 [[micro-service/49-zero-downtime-deploy|下一篇的部署策略]]
下一篇
Zero Downtime Deployment — Pipeline 跑完了,怎麼部署才不會中斷服務?Rolling Update、Blue-Green、Canary 三種策略的取捨。
本系列文章
完整 68 篇目錄見 系列首頁
← 上一篇:資料一致性(二):Eventual Consistency 用戶能不能接受 → 下一篇:Zero Downtime Deployment:Rolling Update vs Blue-Green vs Canary