版本控制只是起點。讓每次提交都能自動建置、測試、並安全地送到使用者面前,才是現代交付的核心。本章涵蓋 CI 原則、部署管線各階段、.gitlab-ci.yml 的漸進式組態,以及發布策略。


一、持續整合的核心原則#

持續整合(Continuous Integration, CI)不是一套工具,而是一組紀律:

  1. 頻繁提交:每位開發者每天至少把變更整合進共用主線一次,讓衝突小而易解。
  2. 保持綠色建置:主線的建置必須隨時可通過。提交前先在本地確認測試通過。
  3. 快速回饋:建置與測試要快(理想數分鐘內),開發者才會願意頻繁等待結果。
  4. 壞了優先修:一旦主線建置變紅,修復它是團隊的最高優先事項,停止往破損的建置上堆疊新變更。

CI 的價值來自「持續」。每週整合一次的團隊得到的不是 CI,只是「偶爾跑一下測試」。整合的頻率決定了回饋的價值。

flowchart LR
    A[開發者提交] --> B[CI 自動觸發]
    B --> C{建置 + 測試通過?}
    C -->|是| D[綠色建置<br/>可繼續開發]
    C -->|否| E[紅色建置<br/>全員優先修復]
    E --> A
    D --> A

「建置壞了但先讓它紅著、晚點再修」是 CI 紀律崩壞的開始。紅燈累積會讓團隊對 CI 結果麻木,最終失去快速回饋的能力。


二、部署管線各階段#

部署管線(deployment pipeline)把一次提交從程式碼推進到生產環境的全過程自動化、可視化。越往後的階段越接近真實環境、執行越慢、但信心越高。

flowchart LR
    C[Commit<br/>編譯/單元測試] --> B[Build<br/>打包產物]
    B --> T[Test<br/>整合/驗收測試]
    T --> S[Stage<br/>類正式環境驗證]
    S --> P[Prod<br/>正式部署]
階段做什麼失敗代價執行速度
Commit編譯、靜態檢查、單元測試秒~分
Build打包成可部署產物(artifact)
Test整合測試、自動化驗收測試分~十分
Stage部署到類正式環境,做煙霧/非功能驗證中~高十分
Prod部署到正式環境視策略

管線的設計原則是快速失敗:把最便宜、最快的檢查(編譯、單元測試)放最前面,讓多數錯誤在數秒內就被擋下,昂貴的測試只對已通過前段的產物執行。

同一份產物貫穿全程:在 Build 階段產生的 artifact 應一路用到 Prod,而非每個環境重新編譯。重新編譯會引入「在我環境能跑」的不確定性。


三、.gitlab-ci.yml:由簡到繁#

GitLab CI 以倉庫根目錄的 .gitlab-ci.yml 描述管線。以下範例逐步加上常用能力。

最小可行#

stages:
  - test

unit-test:
  stage: test
  script:
    - echo "Running tests"
    - ./gradlew test

加入多階段與建置產物#

stages:
  - build
  - test

build:
  stage: build
  script:
    - ./gradlew assemble
  artifacts:
    paths:
      - build/libs/
    expire_in: 1 hour

unit-test:
  stage: test
  script:
    - ./gradlew test
  artifacts:
    when: always
    reports:
      junit: build/test-results/test/*.xml

artifacts 讓後續 job 取用前一階段的產物(如 build 編譯出的 jar)。reports:junit 則讓 GitLab 在 MR 介面直接呈現測試結果。

加入條件規則與多環境部署#

stages:
  - build
  - test
  - deploy

variables:
  GRADLE_OPTS: "-Dorg.gradle.daemon=false"

build:
  stage: build
  script:
    - ./gradlew assemble
  artifacts:
    paths:
      - build/libs/

unit-test:
  stage: test
  script:
    - ./gradlew test

deploy-staging:
  stage: deploy
  script:
    - ./deploy.sh staging
  environment:
    name: staging
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'

deploy-production:
  stage: deploy
  script:
    - ./deploy.sh production
  environment:
    name: production
  rules:
    - if: "$CI_COMMIT_TAG" # 只有打 tag 時才部署正式環境
  when: manual # 需人工點擊才執行

rules 取代舊的 only/exceptrules 支援更精細的條件組合(分支、tag、變數、檔案變更),是目前官方推薦的寫法。when: manual 為高風險的正式部署加上人工閘門。

機密(部署金鑰、token)絕不寫進 .gitlab-ci.yml。改用 GitLab 專案的 CI/CD Variables,並標記為 Protected(僅 protected 分支可用)與 Masked(日誌中遮蔽)。

Runner Executor 對比#

job 實際在 GitLab Runner 上執行,Runner 可用不同 executor:

Executor隔離性環境一致性成本/維運適用場景
Shell差(依賴主機)簡單腳本、自建單機
Docker好(映像固定)多數專案的預設選擇
Kubernetes大規模、需彈性擴縮的 job 平台

四、發布策略#

部署到正式環境的方式直接決定上線風險。

flowchart TB
    subgraph BlueGreen[Blue-Green]
        direction LR
        LB1[流量] --> Blue[Blue 舊版]
        LB1 -.切換.-> Green[Green 新版]
    end
    subgraph Canary[Canary]
        direction LR
        LB2[流量] --> Old[舊版 95%]
        LB2 --> New[新版 5%]
    end
    subgraph Rolling[Rolling]
        direction LR
        I1[實例1 新] --- I2[實例2 新] --- I3[實例3 舊]
    end
策略做法回滾速度資源成本風險暴露
Blue-Green兩套環境並存,瞬間切換流量極快高(雙倍環境)全量切換
Canary先導入少量流量,觀察後逐步放大漸進、可早期止損
Rolling逐批替換實例,新舊並存一段時間漸進

Feature Flags#

Feature Flag 讓「部署程式碼」與「啟用功能」解耦:新功能可以先部署到正式環境,但以開關隱藏,按需對特定使用者開啟。

flowchart LR
    A[功能程式碼已部署] --> B{Feature Flag 開?}
    B -->|關| C[使用者看不到]
    B -->|開, 限部分用戶| D[灰度驗證]
    D --> E[全量開啟]

發布(release)≠ 部署(deploy)

  • 部署 是把程式碼放到正式環境的技術動作。
  • 發布 是讓使用者實際看到新功能的商業決策。

透過 Feature Flag,團隊可以頻繁、低風險地部署,而把「何時對誰發布」變成獨立、可控、可隨時回收的決策。

Feature Flag 是 Trunk-Based Development 的關鍵支柱:未完成的功能用開關隱藏,便能安全地頻繁整合進主幹,而不必依賴長命分支。記得定期清理過期的 flag,否則會累積成新的技術債。