Build and Deploy:CI/CD 建置與部署流程設計【2025】

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 包含的工作

  1. 自動建置(Build)
    - 編譯程式碼
    - 打包成可部署的格式
    - 產生 Docker Image

  2. 自動測試(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:使用現成 Action
  • run:執行 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 }}"

常見問題與解決方案

問題一:建置太慢

解決方案:

  1. 使用快取
  2. 平行執行測試
  3. 只在相關檔案變更時執行
on:
  push:
    paths:
      - 'src/**'
      - 'package.json'
      - '.github/workflows/**'

問題二:測試不穩定(Flaky Tests)

解決方案:

  1. 隔離測試環境
  2. 使用 retry 機制
  3. 修復或標記不穩定測試
- name: Run tests with retry
  uses: nick-fields/retry@v2
  with:
    timeout_minutes: 10
    max_attempts: 3
    command: npm test

問題三:Secrets 外洩風險

解決方案:

  1. 使用 GitHub Secrets / GitLab CI Variables
  2. 限制 Secrets 的使用範圍
  3. 定期輪換 Secrets
# 只在特定環境使用
deploy:
  environment: production
  steps:
    - run: ./deploy.sh
      env:
        API_KEY: ${{ secrets.PROD_API_KEY }}

問題四:部署失敗難以除錯

解決方案:

  1. 增加日誌輸出
  2. 保留 Artifact
  3. 設定通知
- 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 團隊可以幫你快速建立可靠的自動化部署流程,讓你專注在開發上。

幫我部署 →

分享文章:
V

VibeFix

專門解決 AI Vibe Coding 後的疑難雜症,讓你的專案順利上線。

這篇文章有幫到你嗎?

如果還有問題,讓我們直接幫你解決!

聯繫我們