CI/CD 完全指南

CI/CD(持續整合/持續交付)是現代軟體開發的核心實踐,自動化程式碼從提交到部署的整個流程。


目錄


什麼是 CI/CD?

CI - 持續整合(Continuous Integration)

持續整合是開發人員頻繁將程式碼變更合併到主分支的實踐,每次合併都會觸發自動化建構和測試。

開發者提交  →  自動建構  →  自動測試    →    回饋結果
    ↓           ↓           ↓              ↓
git push     compile     unit test       pass/fail

核心目標

  • 儘早發現整合問題
  • 減少手動測試時間
  • 確保程式碼品質

CD - 持續交付/持續部署

術語 英文 說明
持續交付 Continuous Delivery 自動化到「準備部署」階段,但需人工批准才部署到生產環境
持續部署 Continuous Deployment 完全自動化,通過所有測試後自動部署到生產環境
持續交付:
程式碼 → 建構 → 測試 → Staging → [人工批准] → Production

持續部署:
程式碼 → 建構 → 測試 → Staging → Production(全自動)

CI/CD 完整流程圖


核心概念

Pipeline(流水線)

Pipeline 是 CI/CD 的核心,定義了從程式碼到部署的自動化流程。

# Pipeline 基本結構
pipeline:
  stages:
    - build      # 階段 1:建構
    - test       # 階段 2:測試
    - deploy     # 階段 3:部署

  jobs:
    build-job:   # 工作 1
      stage: build
      steps: [...]

    test-job:    # 工作 2
      stage: test
      steps: [...]

Stage(階段)

階段是 Pipeline 的邏輯分組,同一階段的工作可並行執行。

Stage 1: Build     Stage 2: Test        Stage 3: Deploy
┌─────────────┐    ┌─────────────┐      ┌─────────────┐
│ compile     │    │ unit-test   │      │ staging     │
│ package     │ →  │ integration │  →   │ production  │
│ docker-build│    │ e2e-test    │      │             │
└─────────────┘    └─────────────┘      └─────────────┘
     串行              並行                  串行

Job(工作)

Job 是實際執行的任務單元,包含一系列步驟。

job-name:
  stage: build
  image: node:18           # 執行環境
  script:                  # 執行步驟
    - npm install
    - npm run build
  artifacts:               # 產出物
    paths:
      - dist/

Artifact(產出物)

Artifact 是 Job 產生的檔案,可傳遞給後續階段使用。

類型 說明 範例
建構產物 編譯後的程式 dist/build/.jar
測試報告 測試結果檔案 coverage/test-results.xml
Docker 映像 容器映像 myapp:latest
部署包 可部署的封裝 release.tar.gz

Runner/Agent

Runner 是執行 Pipeline 的實際機器或環境。

工具 Runner 名稱 類型
GitHub Actions GitHub-hosted / Self-hosted Runner
GitLab CI GitLab Runner Runner
Jenkins Agent/Node Agent
Azure DevOps Microsoft-hosted / Self-hosted Agent

CI/CD 流程階段

1. Source(原始碼階段)

# 觸發條件設定
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

常見觸發方式

  • Push 到特定分支
  • Pull Request 建立/更新
  • 定時排程(Cron)
  • 手動觸發
  • Tag 建立

2. Build(建構階段)

build:
  steps:
    # 程式碼檢查
    - name: Lint
      run: npm run lint

    # 編譯
    - name: Compile
      run: npm run build

    # 建構 Docker 映像
    - name: Docker Build
      run: docker build -t myapp:${{ github.sha }} .

建構階段任務

任務 說明 工具範例
Lint 程式碼風格檢查 ESLint、Pylint、golangci-lint
Compile 編譯程式碼 tsc、javac、go build
Package 打包應用 webpack、Maven、Gradle
Docker Build 建構容器映像 Docker、Buildah、Kaniko

3. Test(測試階段)

test:
  parallel:
    # 單元測試
    - name: Unit Tests
      run: npm run test:unit
      coverage: true

    # 整合測試
    - name: Integration Tests
      run: npm run test:integration
      services:
        - postgres:14
        - redis:7

    # E2E 測試
    - name: E2E Tests
      run: npm run test:e2e

測試類型金字塔

                 ╱╲
                ╱  ╲
               ╱ E2E╲         少量、慢、高成本
              ╱───────╲
             ╱         ╲
            ╱Integration╲     中等數量
           ╱─────────────╲
          ╱               ╲
         ╱   Unit Tests    ╲   大量、快、低成本
        ╱───────────────────╲
測試類型 範圍 速度 數量
Unit 單一函數/類別 快(毫秒) 多(70%)
Integration 多個元件互動 中(秒) 中(20%)
E2E 完整系統流程 慢(分鐘) 少(10%)

4. Security(安全掃描階段)

security:
  steps:
    # 依賴漏洞掃描
    - name: Dependency Scan
      run: npm audit --audit-level=high

    # 靜態程式碼分析
    - name: SAST
      uses: github/codeql-action/analyze@v2

    # 容器映像掃描
    - name: Container Scan
      run: trivy image myapp:latest

    # 密鑰掃描
    - name: Secret Scan
      uses: trufflesecurity/trufflehog@v3

安全掃描類型

類型 全名 說明 工具
SCA Software Composition Analysis 依賴漏洞掃描 npm audit、Snyk、OWASP
SAST Static Application Security Testing 靜態程式碼分析 CodeQL、SonarQube、Semgrep
DAST Dynamic Application Security Testing 動態安全測試 OWASP ZAP、Burp Suite
Container Container Image Scanning 容器映像掃描 Trivy、Clair、Anchore

5. Deploy(部署階段)

deploy:
  stages:
    # 部署到 Staging
    - name: Deploy to Staging
      environment: staging
      script:
        - kubectl apply -f k8s/staging/

    # 部署到 Production(需審核)
    - name: Deploy to Production
      environment: production
      when: manual  # 手動觸發
      script:
        - kubectl apply -f k8s/production/

6. Monitor(監控階段)

post-deploy:
  steps:
    # 健康檢查
    - name: Health Check
      run: |
        for i in {1..10}; do
          curl -f https://app.example.com/health && exit 0
          sleep 10
        done
        exit 1

    # 通知
    - name: Notify
      uses: slack-notify-action
      with:
        status: ${{ job.status }}

常見 CI/CD 工具

工具比較表

工具 類型 託管方式 特點 適用場景
GitHub Actions SaaS 雲端/自架 與 GitHub 深度整合 GitHub 專案
GitLab CI/CD SaaS/自架 雲端/自架 功能完整、一站式 GitLab 專案
Jenkins 自架 自架 高度可自訂、插件豐富 企業內部
CircleCI SaaS 雲端 快速、易用 中小型專案
Travis CI SaaS 雲端 開源專案友好 開源專案
Azure DevOps SaaS 雲端/自架 微軟生態整合 Azure 專案
Argo CD 自架 K8s GitOps、K8s 原生 K8s 部署
Tekton 自架 K8s 雲原生、可擴展 K8s 環境

選擇建議

你的程式碼在哪?
├─ GitHub → GitHub Actions(首選)
├─ GitLab → GitLab CI/CD(首選)
├─ 企業內部 Git
│   ├─ 需要高度自訂 → Jenkins
│   └─ 使用 K8s → Argo CD / Tekton
└─ 多平台/多雲 → Jenkins / CircleCI

Pipeline 設計

基本 Pipeline 結構

# 最小可行 Pipeline
stages:
  - build
  - test
  - deploy

build:
  stage: build
  script:
    - npm install
    - npm run build

test:
  stage: test
  script:
    - npm test

deploy:
  stage: deploy
  script:
    - deploy.sh
  only:
    - main

進階 Pipeline 模式

1. 平行執行

test:
  parallel:
    matrix:
      - NODE_VERSION: ['16', '18', '20']
        OS: ['ubuntu-latest', 'windows-latest']
  script:
    - npm test

2. 條件執行

deploy-production:
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
      when: manual
    - if: $CI_COMMIT_TAG
      when: on_success

3. 快取策略

cache:
  key: ${CI_COMMIT_REF_SLUG}
  paths:
    - node_modules/
    - .npm/

4. 多環境部署

.deploy-template: &deploy
  script:
    - kubectl apply -f k8s/${ENVIRONMENT}/

deploy-staging:
  <<: *deploy
  variables:
    ENVIRONMENT: staging
  environment:
    name: staging

deploy-production:
  <<: *deploy
  variables:
    ENVIRONMENT: production
  environment:
    name: production
  when: manual

GitHub Actions 實戰

基本配置

# .github/workflows/ci.yml
name: CI Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

env:
  NODE_VERSION: '18'

jobs:
  build:
    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: Build
        run: npm run build

      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: build
          path: dist/

  test:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - run: npm ci

      - name: Run tests
        run: npm test -- --coverage

      - name: Upload coverage
        uses: codecov/codecov-action@v3

  deploy:
    needs: [build, test]
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    environment: production
    steps:
      - name: Download artifact
        uses: actions/download-artifact@v4
        with:
          name: build

      - name: Deploy to production
        run: |
          echo "Deploying to production..."
          # 部署腳本

Docker 建構與推送

# .github/workflows/docker.yml
name: Docker Build

on:
  push:
    branches: [main]
    tags: ['v*']

jobs:
  docker:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_TOKEN }}

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: myorg/myapp
          tags: |
            type=ref,event=branch
            type=semver,pattern={{version}}
            type=sha

      - 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

Kubernetes 部署

# .github/workflows/deploy-k8s.yml
name: Deploy to Kubernetes

on:
  workflow_run:
    workflows: ["Docker Build"]
    types: [completed]
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    if: ${{ github.event.workflow_run.conclusion == 'success' }}
    steps:
      - uses: actions/checkout@v4

      - name: Configure kubectl
        uses: azure/k8s-set-context@v3
        with:
          kubeconfig: ${{ secrets.KUBE_CONFIG }}

      - name: Deploy
        run: |
          kubectl set image deployment/myapp \
            myapp=myorg/myapp:sha-${{ github.sha }}
          kubectl rollout status deployment/myapp

GitLab CI/CD 實戰

基本配置

# .gitlab-ci.yml
stages:
  - build
  - test
  - security
  - deploy

variables:
  DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

# 建構階段
build:
  stage: build
  image: node:18
  script:
    - npm ci
    - npm run build
  artifacts:
    paths:
      - dist/
    expire_in: 1 hour
  cache:
    key: ${CI_COMMIT_REF_SLUG}
    paths:
      - node_modules/

# 測試階段
test:unit:
  stage: test
  image: node:18
  script:
    - npm ci
    - npm run test:unit
  coverage: '/Statements\s+:\s+(\d+\.?\d*)%/'
  artifacts:
    reports:
      junit: junit.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml

test:integration:
  stage: test
  image: node:18
  services:
    - postgres:14
    - redis:7
  variables:
    POSTGRES_DB: test
    POSTGRES_USER: test
    POSTGRES_PASSWORD: test
  script:
    - npm ci
    - npm run test:integration

# 安全掃描
security:sast:
  stage: security
  image: returntocorp/semgrep
  script:
    - semgrep --config=auto --json -o semgrep.json .
  artifacts:
    reports:
      sast: semgrep.json

security:dependency:
  stage: security
  image: node:18
  script:
    - npm audit --audit-level=high
  allow_failure: true

# Docker 建構
docker:build:
  stage: build
  image: docker:24
  services:
    - docker:24-dind
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build -t $DOCKER_IMAGE .
    - docker push $DOCKER_IMAGE

# 部署階段
deploy:staging:
  stage: deploy
  image: bitnami/kubectl:latest
  environment:
    name: staging
    url: https://staging.example.com
  script:
    - kubectl config set-cluster k8s --server=$KUBE_SERVER
    - kubectl set image deployment/myapp myapp=$DOCKER_IMAGE
  only:
    - develop

deploy:production:
  stage: deploy
  image: bitnami/kubectl:latest
  environment:
    name: production
    url: https://example.com
  script:
    - kubectl set image deployment/myapp myapp=$DOCKER_IMAGE
  when: manual
  only:
    - main

GitLab CI 特殊功能

# 動態環境
deploy:review:
  stage: deploy
  environment:
    name: review/$CI_COMMIT_REF_NAME
    url: https://$CI_COMMIT_REF_SLUG.example.com
    on_stop: stop:review
  script:
    - deploy-review.sh
  only:
    - merge_requests

stop:review:
  stage: deploy
  environment:
    name: review/$CI_COMMIT_REF_NAME
    action: stop
  script:
    - cleanup-review.sh
  when: manual

# 父子 Pipeline
trigger:child:
  stage: deploy
  trigger:
    include: child-pipeline.yml
    strategy: depend

Jenkins 實戰

Declarative Pipeline

// Jenkinsfile
pipeline {
    agent any

    environment {
        DOCKER_IMAGE = "myorg/myapp:${BUILD_NUMBER}"
        DOCKER_CREDENTIALS = credentials('docker-hub')
    }

    options {
        buildDiscarder(logRotator(numToKeepStr: '10'))
        timeout(time: 30, unit: 'MINUTES')
        timestamps()
    }

    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }

        stage('Build') {
            steps {
                sh 'npm ci'
                sh 'npm run build'
            }
        }

        stage('Test') {
            parallel {
                stage('Unit Tests') {
                    steps {
                        sh 'npm run test:unit'
                    }
                    post {
                        always {
                            junit 'junit.xml'
                        }
                    }
                }
                stage('Integration Tests') {
                    steps {
                        sh 'npm run test:integration'
                    }
                }
            }
        }

        stage('Security Scan') {
            steps {
                sh 'npm audit --audit-level=high'
            }
        }

        stage('Docker Build') {
            steps {
                script {
                    docker.build(DOCKER_IMAGE)
                }
            }
        }

        stage('Docker Push') {
            steps {
                script {
                    docker.withRegistry('https://registry.hub.docker.com', 'docker-hub') {
                        docker.image(DOCKER_IMAGE).push()
                        docker.image(DOCKER_IMAGE).push('latest')
                    }
                }
            }
        }

        stage('Deploy to Staging') {
            when {
                branch 'develop'
            }
            steps {
                sh 'kubectl apply -f k8s/staging/'
            }
        }

        stage('Deploy to Production') {
            when {
                branch 'main'
            }
            input {
                message "Deploy to production?"
                ok "Deploy"
            }
            steps {
                sh 'kubectl apply -f k8s/production/'
            }
        }
    }

    post {
        success {
            slackSend(color: 'good', message: "Build succeeded: ${env.JOB_NAME} #${env.BUILD_NUMBER}")
        }
        failure {
            slackSend(color: 'danger', message: "Build failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}")
        }
        always {
            cleanWs()
        }
    }
}

Shared Library

// vars/standardPipeline.groovy
def call(Map config) {
    pipeline {
        agent any

        stages {
            stage('Build') {
                steps {
                    sh "${config.buildCommand ?: 'npm run build'}"
                }
            }

            stage('Test') {
                steps {
                    sh "${config.testCommand ?: 'npm test'}"
                }
            }

            stage('Deploy') {
                when {
                    branch 'main'
                }
                steps {
                    sh "${config.deployCommand ?: './deploy.sh'}"
                }
            }
        }
    }
}

// 使用方式(Jenkinsfile)
@Library('my-shared-library') _

standardPipeline(
    buildCommand: 'gradle build',
    testCommand: 'gradle test',
    deployCommand: 'kubectl apply -f k8s/'
)

部署策略

部署策略比較

策略 風險 回滾速度 資源需求 複雜度
Rolling Update
Blue-Green 高(2x)
Canary
A/B Testing
Shadow 最低 N/A 最高

1. Rolling Update(滾動更新)

逐步替換舊版本實例。

# Kubernetes Rolling Update
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 4
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1        # 最多多 1 個 Pod
      maxUnavailable: 1  # 最多少 1 個 Pod
時間軸:
T1: [v1] [v1] [v1] [v1]     # 初始狀態
T2: [v1] [v1] [v1] [v2]     # 新增 1 個 v2
T3: [v1] [v1] [v2] [v2]     # 替換 1 個 v1
T4: [v1] [v2] [v2] [v2]     # 繼續替換
T5: [v2] [v2] [v2] [v2]     # 完成更新

2. Blue-Green Deployment(藍綠部署)

同時維護兩套環境,切換流量。

# 藍綠部署配置
# Blue 環境 (當前生產)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-blue
  labels:
    version: blue
---
# Green 環境 (新版本)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-green
  labels:
    version: green
---
# Service 切換
apiVersion: v1
kind: Service
metadata:
  name: myapp
spec:
  selector:
    version: green  # 切換到 green
部署流程:
┌─────────────────────────────────────────┐
│  Load Balancer                          │
│         │                               │
│    ┌────┴────┐                          │
│    ▼         ▼                          │
│  ┌────┐   ┌─────┐                       │
│  │Blue│   │Green│  ← 部署新版本           │
│  │ v1 │   │ v2  │                       │
│  │100%│   │ 0%  │                       │
│  └────┘   └─────┘                       │
│                                         │
│         切換後:                         │
│  ┌────┐   ┌─────┐                       │
│  │Blue│   │Green│                       │
│  │ v1 │   │ v2  │                       │
│  │ 0% │   │100% │  ← 流量切換            │
│  └────┘   └─────┘                       │
└─────────────────────────────────────────┘

3. Canary Deployment(金絲雀部署)

逐步將流量導向新版本。

# Kubernetes + Istio Canary
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: myapp
spec:
  hosts:
    - myapp
  http:
    - route:
        - destination:
            host: myapp
            subset: v1
          weight: 90      # 90% 到 v1
        - destination:
            host: myapp
            subset: v2
          weight: 10      # 10% 到 v2(金絲雀)
流量分配演進:
階段 1: v1=95%, v2=5%   # 小規模測試
階段 2: v1=80%, v2=20%  # 擴大測試
階段 3: v1=50%, v2=50%  # 半數流量
階段 4: v1=0%,  v2=100% # 完成遷移

4. A/B Testing

基於使用者特徵分流。

# Nginx A/B Testing
upstream backend_v1 {
    server v1.example.com;
}

upstream backend_v2 {
    server v2.example.com;
}

split_clients "${remote_addr}" $variant {
    50% backend_v1;
    50% backend_v2;
}

server {
    location / {
        proxy_pass http://$variant;
    }
}

回滾策略

# Kubernetes 回滾
# 查看歷史版本
kubectl rollout history deployment/myapp

# 回滾到上一版本
kubectl rollout undo deployment/myapp

# 回滾到特定版本
kubectl rollout undo deployment/myapp --to-revision=2

# 暫停/恢復部署
kubectl rollout pause deployment/myapp
kubectl rollout resume deployment/myapp

最佳實踐

1. Pipeline 設計原則

# ✅ 快速反饋
fast-feedback:
  stages:
    - lint          # 秒級
    - unit-test     # 秒-分鐘級
    - build         # 分鐘級
    - integration   # 分鐘級
    - e2e           # 分鐘-小時級

# ✅ 平行執行
parallel-jobs:
  test:
    parallel:
      - unit
      - lint
      - security-scan

# ✅ 快取依賴
cache:
  paths:
    - node_modules/
    - ~/.m2/
    - ~/.gradle/

2. 安全實踐

# ✅ 密鑰管理
secrets:
  # 使用環境變數,不硬編碼
  - name: Deploy
    env:
      API_KEY: ${{ secrets.API_KEY }}
      DB_PASSWORD: ${{ secrets.DB_PASSWORD }}

# ✅ 最小權限
permissions:
  contents: read
  packages: write

# ✅ 依賴鎖定
install:
  # 使用 lock file
  - npm ci          # 不是 npm install
  - pip install -r requirements.txt --require-hashes

3. 建構最佳實踐

# ✅ 多階段建構減少映像大小
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:18-slim AS production
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
USER node
CMD ["node", "dist/main.js"]

4. 測試策略

# ✅ 測試金字塔
test-strategy:
  unit:
    coverage: 80%      # 高覆蓋率
    timeout: 5m

  integration:
    coverage: 60%
    timeout: 15m
    services:
      - database

  e2e:
    coverage: critical-paths  # 關鍵路徑
    timeout: 30m

5. 監控與通知

# ✅ 部署後驗證
post-deploy:
  - health-check:
      endpoint: /health
      timeout: 5m

  - smoke-test:
      script: ./smoke-tests.sh

  - notify:
      channels:
        - slack: #deployments
        - email: team@example.com

6. 文件化

# ✅ Pipeline 文件
# pipeline.yml
#
# 此 Pipeline 執行以下任務:
# 1. 建構 - 編譯程式碼並建構 Docker 映像
# 2. 測試 - 執行單元測試和整合測試
# 3. 部署 - 部署到 staging/production
#
# 觸發條件:
# - push to main: 部署到 production
# - push to develop: 部署到 staging
# - PR: 僅執行建構和測試
#
# 必要密鑰:
# - DOCKER_TOKEN: Docker Hub 存取權杖
# - KUBE_CONFIG: Kubernetes 配置

常見問題

Pipeline 執行太慢?

解決方案

# 1. 平行執行
test:
  parallel:
    matrix:
      - shard: [1, 2, 3, 4]
  script: npm test -- --shard=$shard/4

# 2. 快取依賴
cache:
  key: deps-${{ hashFiles('package-lock.json') }}
  paths:
    - node_modules/

# 3. 增量建構
build:
  script:
    - npm run build -- --incremental

# 4. 選擇性執行
test:
  rules:
    - changes:
        - src/**/*
        - tests/**/*

如何處理 Flaky Tests?

# 1. 重試機制
test:
  retry:
    max: 2
    when:
      - runner_system_failure
      - stuck_or_timeout_failure

# 2. 隔離測試
test:
  parallel: 4  # 分散執行

# 3. 標記並追蹤
test:
  script: |
    npm test --reporter=junit
    # 將 flaky tests 報告到追蹤系統

如何管理多環境配置?

# 使用環境變數
.deploy:
  script:
    - envsubst < k8s/deployment.tmpl > k8s/deployment.yaml
    - kubectl apply -f k8s/deployment.yaml

deploy:staging:
  extends: .deploy
  variables:
    ENVIRONMENT: staging
    REPLICAS: 2

deploy:production:
  extends: .deploy
  variables:
    ENVIRONMENT: production
    REPLICAS: 5

如何實現零停機部署?

# 1. 健康檢查
readinessProbe:
  httpGet:
    path: /health
    port: 8080
  initialDelaySeconds: 10
  periodSeconds: 5

# 2. 優雅關閉
lifecycle:
  preStop:
    exec:
      command: ["/bin/sh", "-c", "sleep 10"]

# 3. 滾動更新策略
strategy:
  rollingUpdate:
    maxSurge: 25%
    maxUnavailable: 0  # 不允許服務中斷

總結

CI/CD 核心流程

┌──────────┐   ┌───────┐   ┌──────┐   ┌──────────┐   ┌─────────┐
│  Source  │ → │ Build │ → │ Test │ → │ Security │ → │ Deploy  │
└──────────┘   └───────┘   └──────┘   └──────────┘   └─────────┘
    Code        Compile      Unit        SAST         Staging
    Commit      Package    Integration   SCA          Production
    PR          Docker       E2E        Container     Monitor

工具選擇快速指南

需求 推薦工具
GitHub 專案 GitHub Actions
GitLab 專案 GitLab CI/CD
企業內部、高度自訂 Jenkins
Kubernetes 環境 Argo CD / Tekton
簡單快速上手 CircleCI

部署策略選擇

需求 推薦策略
簡單、低風險 Rolling Update
快速回滾需求 Blue-Green
漸進式驗證 Canary
功能實驗 A/B Testing

Pipeline 設計檢查清單

  • 建構階段:Lint、Compile、Package、Docker Build
  • 測試階段:Unit、Integration、E2E
  • 安全掃描:SAST、SCA、Container Scan
  • 部署階段:Staging → Production
  • 監控驗證:Health Check、Smoke Test
  • 通知機制:Success/Failure 通知
  • 快取策略:依賴快取、建構快取
  • 密鑰管理:使用 Secrets,不硬編碼

建立日期:2025-12-05 最後更新:2025-12-05

🔗相關文章