Build and Deploy:CI/CD 建置與部署流程設計【2025】
每次部署都要手動打指令?改一行程式碼就要等半小時才能上線?
CI/CD(持續整合/持續部署)是現代軟體開發的標準實踐,讓你的程式碼從 commit 到上線完全自動化。這篇文章將帶你從概念到實作,建立一套完整的 CI/CD Pipeline。
不管你用的是 GitHub Actions、GitLab CI 還是 Jenkins,這篇指南都能幫你設計出高效的自動化部署流程。
CI/CD 基本概念
在開始實作之前,先搞清楚 CI/CD 到底是什麼。
CI:持續整合(Continuous Integration)
CI 的核心概念是「頻繁地將程式碼整合到主分支」。
傳統做法的問題
開發者 A:寫了 2 週的功能
開發者 B:同時也寫了 2 週的功能
合併時:衝突一堆,花 3 天解決
CI 的做法
開發者 A:每天(甚至每小時)push 小改動
開發者 B:同樣頻繁 push
自動化:每次 push 都自動建置、測試
結果:衝突早發現、早解決
CI 包含的工作
-
自動建置(Build)
- 編譯程式碼
- 打包成可部署的格式
- 產生 Docker Image -
自動測試(Test)
- 單元測試(Unit Test)
- 整合測試(Integration Test)
- 程式碼品質檢查(Lint)
CD:持續交付/持續部署
CD 有兩種解釋,略有不同:
Continuous Delivery(持續交付)
程式碼隨時可以部署,但部署到 Production 需要人工核准。
Code → Build → Test → Deploy to Staging → 人工核准 → Deploy to Production
Continuous Deployment(持續部署)
程式碼通過測試後自動部署到 Production,完全不需人工介入。
Code → Build → Test → Deploy to Staging → Auto Test → Deploy to Production
選擇哪一種?
| 情境 | 推薦 |
|---|---|
| 金融、醫療等高風險系統 | Continuous Delivery |
| 快速迭代的網站/App | Continuous Deployment |
| 團隊剛開始導入 CI/CD | Continuous Delivery |
想了解更多部署基礎知識,請參考我們的 程式部署完整指南。
CI/CD Pipeline 階段
一個完整的 CI/CD Pipeline 通常包含這些階段:
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Code │ → │ Build │ → │ Test │ → │ Staging │ → │Production│
│ Commit │ │ │ │ │ │ Deploy │ │ Deploy │
└─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘
│ │ │ │ │
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
觸發 編譯打包 自動測試 測試環境 正式環境
Pipeline 驗證
每個階段的任務
| 階段 | 任務 | 失敗時 |
|---|---|---|
| Build | 編譯、打包、產生 Artifact | 通知開發者修復 |
| Test | 單元測試、整合測試、Lint | 阻擋合併 |
| Staging Deploy | 部署到測試環境 | 檢查設定問題 |
| E2E Test | 端對端測試 | 不進入 Production |
| Production Deploy | 部署到正式環境 | 自動回滾 |
CI/CD 工具比較
市面上有很多 CI/CD 工具,各有優缺點。
GitHub Actions
GitHub 內建的 CI/CD 服務,與 GitHub 深度整合。
優點:
- 與 GitHub 無縫整合
- 免費額度充足(Public Repo 無限、Private 2000 分鐘/月)
- Marketplace 有大量現成 Action
- 設定簡單,YAML 語法直觀
缺點:
- 只能用於 GitHub
- 複雜流程設定較繁瑣
- Self-hosted Runner 需要自己維護
適合:
- 已使用 GitHub 的團隊
- 中小型專案
- 開源專案
GitLab CI/CD
GitLab 內建的 CI/CD,功能最完整。
優點:
- 與 GitLab 完整整合
- 功能豐富(環境管理、Review Apps)
- 支援 Self-hosted
- 內建 Container Registry
缺點:
- 學習曲線較陡
- GitLab 本身較重
- 免費版功能有限
適合:
- 已使用 GitLab 的團隊
- 需要完整 DevOps 平台
- 企業環境
Jenkins
老牌開源 CI/CD 工具,歷史最悠久。
優點:
- 完全開源免費
- 高度可客製化
- 插件生態豐富(1800+ 插件)
- 支援任何程式語言和平台
缺點:
- 需要自己架設和維護
- UI 較老舊
- 設定複雜
- 安全性需要自己管理
適合:
- 有專職 DevOps 團隊
- 需要高度客製化
- 已有 Jenkins 使用經驗
CircleCI
雲端 CI/CD 服務,效能優異。
優點:
- 建置速度快
- 支援 Docker Layer Caching
- 並行執行能力強
- 設定語法清晰
缺點:
- 免費額度較少
- 進階功能需付費
- 主要針對雲端使用
適合:
- 需要快速建置
- 願意付費換取效能
- 雲端優先團隊
💡 CI/CD Pipeline 設計需要協助?讓 VibeFix 幫你規劃
工具選擇建議
| 情境 | 推薦工具 |
|---|---|
| 已用 GitHub | GitHub Actions |
| 已用 GitLab | GitLab CI/CD |
| 需要完全控制 | Jenkins |
| 追求建置速度 | CircleCI |
| 預算有限 | GitHub Actions / Jenkins |
GitHub Actions 完整教學
GitHub Actions 是目前最流行的 CI/CD 工具,讓我們深入學習。
基本概念
Workflow
一個自動化流程,定義在 .github/workflows/*.yml 檔案中。
Event
觸發 Workflow 的事件,例如 push、pull_request、schedule。
Job
Workflow 中的一個執行單元,包含多個 Step。
Step
Job 中的一個步驟,可以執行指令或使用 Action。
Action
可重複使用的程式碼單元,可以來自 Marketplace 或自己定義。
第一個 Workflow
建立 .github/workflows/ci.yml:
name: CI Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build
run: npm run build
解析:
name:Workflow 名稱on:觸發條件(push 到 main/develop、或 PR 到 main)jobs:定義工作runs-on:執行環境(ubuntu-latest)steps:執行步驟uses:使用現成 Actionrun:執行 shell 指令
完整 CI/CD Pipeline
一個包含建置、測試、部署的完整 Pipeline:
name: CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
NODE_VERSION: '20'
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
# ===== 建置與測試 =====
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Type check
run: npm run type-check
- name: Unit tests
run: npm run test:unit
- name: Build
run: npm run build
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: build
path: dist/
# ===== 整合測試 =====
integration-test:
needs: build-and-test
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: testpass
POSTGRES_DB: testdb
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run integration tests
run: npm run test:integration
env:
DATABASE_URL: postgresql://postgres:testpass@localhost:5432/testdb
# ===== 建置 Docker Image =====
build-image:
needs: [build-and-test, integration-test]
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
permissions:
contents: read
packages: write
outputs:
image-tag: ${{ steps.meta.outputs.tags }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=sha,prefix=
type=raw,value=latest
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
# ===== 部署到 Staging =====
deploy-staging:
needs: build-image
runs-on: ubuntu-latest
environment: staging
steps:
- name: Deploy to Staging
run: |
echo "Deploying to staging..."
# 使用 kubectl、ssh 或其他部署工具
# kubectl set image deployment/app app=${{ needs.build-image.outputs.image-tag }}
# ===== 部署到 Production =====
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment: production
steps:
- name: Deploy to Production
run: |
echo "Deploying to production..."
# 實際部署指令
關鍵設定解析
Jobs 相依性
jobs:
build:
# 沒有 needs,第一個執行
test:
needs: build # 等 build 完成才執行
deploy:
needs: [build, test] # 等 build 和 test 都完成
條件執行
jobs:
deploy:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
# 只在 push 到 main 時執行
Environment Protection
在 GitHub Repository Settings → Environments 設定:
- Required reviewers:需要人工核准
- Wait timer:等待時間
- Deployment branches:限制可部署的分支
Secrets 管理
steps:
- name: Deploy
env:
API_KEY: ${{ secrets.API_KEY }}
run: ./deploy.sh
在 Settings → Secrets and variables → Actions 設定。
💡 不確定該選哪個 CI/CD 工具?讓 VibeFix 專家幫你評估
GitLab CI/CD 快速入門
如果你使用 GitLab,這裡是快速入門指南。
基本設定
建立 .gitlab-ci.yml:
stages:
- build
- test
- deploy
variables:
NODE_VERSION: "20"
# 建置階段
build:
stage: build
image: node:${NODE_VERSION}
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 hour
# 測試階段
test:
stage: test
image: node:${NODE_VERSION}
script:
- npm ci
- npm run test
coverage: '/Coverage: \d+\.\d+%/'
# 部署到 Staging
deploy-staging:
stage: deploy
image: alpine:latest
script:
- echo "Deploying to staging..."
environment:
name: staging
url: https://staging.example.com
only:
- main
# 部署到 Production
deploy-production:
stage: deploy
image: alpine:latest
script:
- echo "Deploying to production..."
environment:
name: production
url: https://example.com
when: manual # 需要手動觸發
only:
- main
GitLab CI 特色功能
Review Apps
為每個 MR 自動建立預覽環境:
review:
stage: deploy
script:
- deploy-review-app.sh
environment:
name: review/$CI_COMMIT_REF_SLUG
url: https://$CI_COMMIT_REF_SLUG.review.example.com
on_stop: stop-review
only:
- merge_requests
多環境部署
.deploy-template:
script:
- deploy.sh $ENVIRONMENT
deploy-staging:
extends: .deploy-template
variables:
ENVIRONMENT: staging
deploy-production:
extends: .deploy-template
variables:
ENVIRONMENT: production
when: manual
Jenkins Pipeline 設定
Jenkins 使用 Jenkinsfile 定義 Pipeline。
Declarative Pipeline
pipeline {
agent any
environment {
NODE_VERSION = '20'
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Install') {
steps {
sh 'npm ci'
}
}
stage('Test') {
steps {
sh 'npm run test'
}
}
stage('Build') {
steps {
sh 'npm run build'
}
}
stage('Deploy to Staging') {
when {
branch 'main'
}
steps {
sh './deploy.sh staging'
}
}
stage('Deploy to Production') {
when {
branch 'main'
}
input {
message "Deploy to production?"
ok "Deploy"
}
steps {
sh './deploy.sh production'
}
}
}
post {
success {
slackSend channel: '#deploys', message: "Deploy succeeded: ${env.JOB_NAME}"
}
failure {
slackSend channel: '#deploys', message: "Deploy failed: ${env.JOB_NAME}"
}
}
}
CI/CD 最佳實踐
遵循這些實踐可以建立更可靠的 CI/CD Pipeline。
快速失敗(Fail Fast)
讓問題盡早被發現:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm run lint # 最快的檢查放最前面
test:
needs: lint # lint 過了才跑測試
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm test
平行執行
不相依的工作同時執行:
jobs:
lint:
runs-on: ubuntu-latest
# ...
unit-test:
runs-on: ubuntu-latest
# 與 lint 同時執行
integration-test:
runs-on: ubuntu-latest
# 與 lint、unit-test 同時執行
build:
needs: [lint, unit-test, integration-test]
# 等全部完成才執行
依賴快取
加速重複執行:
- name: Cache node modules
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
版本標記
每次部署都有明確的版本:
- name: Create version tag
run: |
VERSION=$(git describe --tags --always)
echo "VERSION=$VERSION" >> $GITHUB_ENV
- name: Build with version
run: |
docker build -t myapp:${{ env.VERSION }} .
部署回滾機制
準備好回滾策略:
- name: Deploy with rollback
run: |
# 記錄當前版本
CURRENT_VERSION=$(kubectl get deployment myapp -o jsonpath='{.spec.template.spec.containers[0].image}')
# 嘗試部署
kubectl set image deployment/myapp app=$NEW_IMAGE
# 等待部署完成
if ! kubectl rollout status deployment/myapp --timeout=5m; then
echo "Deployment failed, rolling back..."
kubectl rollout undo deployment/myapp
exit 1
fi
監控與告警
部署後持續監控:
- name: Health check
run: |
for i in {1..10}; do
if curl -f https://myapp.com/health; then
echo "Health check passed"
exit 0
fi
sleep 10
done
echo "Health check failed"
exit 1
- name: Notify on failure
if: failure()
uses: slackapi/slack-github-action@v1
with:
channel-id: 'deploys'
slack-message: "Deployment failed! ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
常見問題與解決方案
問題一:建置太慢
解決方案:
- 使用快取
- 平行執行測試
- 只在相關檔案變更時執行
on:
push:
paths:
- 'src/**'
- 'package.json'
- '.github/workflows/**'
問題二:測試不穩定(Flaky Tests)
解決方案:
- 隔離測試環境
- 使用 retry 機制
- 修復或標記不穩定測試
- name: Run tests with retry
uses: nick-fields/retry@v2
with:
timeout_minutes: 10
max_attempts: 3
command: npm test
問題三:Secrets 外洩風險
解決方案:
- 使用 GitHub Secrets / GitLab CI Variables
- 限制 Secrets 的使用範圍
- 定期輪換 Secrets
# 只在特定環境使用
deploy:
environment: production
steps:
- run: ./deploy.sh
env:
API_KEY: ${{ secrets.PROD_API_KEY }}
問題四:部署失敗難以除錯
解決方案:
- 增加日誌輸出
- 保留 Artifact
- 設定通知
- name: Upload logs on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: deploy-logs
path: /var/log/deploy/
VibeFix CI/CD 服務
建立一套好的 CI/CD Pipeline 需要考慮很多細節:
- Pipeline 架構設計
- 測試策略規劃
- 部署流程自動化
- 安全性設定
- 監控與告警整合
VibeFix 團隊提供完整的 CI/CD 規劃與實作服務。
服務範圍:
- CI/CD 工具選型與評估
- Pipeline 架構設計
- GitHub Actions / GitLab CI / Jenkins 設定
- Docker 化與容器部署
- 監控與告警整合
想了解更詳細的服務?查看 VibeFix 服務內容。
結語:自動化是品質的保證
CI/CD 不只是自動化工具,更是軟體品質的保證。一個好的 CI/CD Pipeline 可以:
- 及早發現問題,降低修復成本
- 加速交付速度,提高競爭力
- 減少人為錯誤,提升穩定性
- 建立標準流程,方便團隊協作
透過這篇教學,你已經學會:
- CI/CD 的核心概念與區別
- 主流工具(GitHub Actions、GitLab CI、Jenkins)的比較
- GitHub Actions 完整設定
- CI/CD 最佳實踐
- 常見問題的解決方案
接下來,你可以學習 部署伺服器架設,建立集中化的部署管理。或者參考 軟體部署策略,了解藍綠部署、金絲雀部署等進階策略。
部署失敗?別擔心
CI/CD 設定複雜,踩坑是正常的。VibeFix 團隊可以幫你快速建立可靠的自動化部署流程,讓你專注在開發上。