iT邦幫忙

2025 iThome 鐵人賽

DAY 9
0
自我挑戰組

30天 Git 版本控制實戰筆記系列 第 26

Day 26:CI/CD 整合 - 自動化你的工作流程

  • 分享至 

  • xImage
  •  

今日目標
• 理解 CI/CD 基本概念
• 學會使用 GitHub Actions
• 建立自動化測試和部署
• 整合 Git 工作流程


一、什麼是 CI/CD?
CI (Continuous Integration) - 持續整合

  • 自動測試
  • 自動建置
  • 及早發現問題

CD (Continuous Deployment) - 持續部署

  • 自動部署到測試環境
  • 自動部署到正式環境
  • 快速交付功能

簡單說:
Push → 自動測試 → 自動建置 → 自動部署
為什麼需要 CI/CD?
❌ 沒有 CI/CD:

  • 手動測試(容易漏掉)
  • 手動部署(容易出錯)
  • 問題晚發現(修復成本高)
  • 部署緊張(怕出事)

✅ 有 CI/CD:

  • 自動測試(每次都跑)
  • 自動部署(標準化流程)
  • 問題早發現(便宜 10 倍)
  • 隨時可部署(信心十足)

二、GitHub Actions 基礎
核心概念
Workflow(工作流程)
├─ Job 1(工作 1)
│ ├─ Step 1(步驟 1)
│ ├─ Step 2
│ └─ Step 3
└─ Job 2(工作 2)
├─ Step 1
└─ Step 2

觸發時機:

  • push(推送時)
  • pull_request(PR 時)
  • schedule(定時)
  • workflow_dispatch(手動)
    第一個 Workflow

.github/workflows/ci.yml

name: CI

什麼時候執行

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

要做什麼

jobs:
test:
runs-on: ubuntu-latest

steps:
  # 1. 下載程式碼
  - uses: actions/checkout@v3
  
  # 2. 設定 Node.js
  - uses: actions/setup-node@v3
    with:
      node-version: '18'
  
  # 3. 安裝依賴
  - run: npm ci
  
  # 4. 執行測試
  - run: npm test
  
  # 5. 執行 Lint
  - run: npm run lint

設定步驟

1. 在專案根目錄建立資料夾

mkdir -p .github/workflows

2. 建立 workflow 檔案

vim .github/workflows/ci.yml

貼上上面的內容

3. Commit 並 push

git add .github/workflows/ci.yml
git commit -m "ci: 新增 CI workflow"
git push

4. 查看結果

GitHub → Actions 標籤頁


三、常見 CI/CD 範例
範例 1:前端專案(React/Vue)
name: Frontend CI/CD

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

jobs:
build-and-test:
runs-on: ubuntu-latest

steps:
  - uses: actions/checkout@v3
  
  - uses: actions/setup-node@v3
    with:
      node-version: '18'
      cache: 'npm'  # 快取加速
  
  - name: 安裝依賴
    run: npm ci
  
  - name: 執行測試
    run: npm test
  
  - name: 建置
    run: npm run build
  
  - name: 部署到 GitHub Pages
    if: github.ref == 'refs/heads/main'
    uses: peaceiris/actions-gh-pages@v3
    with:
      github_token: ${{ secrets.GITHUB_TOKEN }}
      publish_dir: ./dist

範例 2:後端專案(Node.js/Python)
name: Backend CI/CD

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

jobs:
test:
runs-on: ubuntu-latest

# 測試多個版本
strategy:
  matrix:
    node-version: [16, 18, 20]

steps:
  - uses: actions/checkout@v3
  
  - uses: actions/setup-node@v3
    with:
      node-version: ${{ matrix.node-version }}
  
  - run: npm ci
  - run: npm test

deploy:
needs: test # 測試通過才部署
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest

steps:
  - uses: actions/checkout@v3
  
  - name: 部署到伺服器
    env:
      SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
    run: |
      echo "$SSH_PRIVATE_KEY" > key.pem
      chmod 600 key.pem
      ssh -i key.pem user@server "cd /app && git pull && npm install && pm2 restart all"

範例 3:Docker 建置和推送
name: Docker Build

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

jobs:
docker:
runs-on: ubuntu-latest

steps:
  - uses: actions/checkout@v3
  
  - name: 登入 Docker Hub
    uses: docker/login-action@v2
    with:
      username: ${{ secrets.DOCKER_USERNAME }}
      password: ${{ secrets.DOCKER_PASSWORD }}
  
  - name: 建置並推送
    uses: docker/build-push-action@v4
    with:
      context: .
      push: true
      tags: |
        username/app:latest
        username/app:${{ github.sha }}

範例 4:自動發布到 npm
name: Publish to npm

on:
release:
types: [created]

jobs:
publish:
runs-on: ubuntu-latest

steps:
  - uses: actions/checkout@v3
  
  - uses: actions/setup-node@v3
    with:
      node-version: '18'
      registry-url: 'https://registry.npmjs.org'
  
  - run: npm ci
  - run: npm test
  - run: npm publish
    env:
      NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

四、進階功能

  1. 使用 Secrets(敏感資訊)

設定 Secret

GitHub → Settings → Secrets and variables → Actions → New secret

使用 Secret

steps:

  • name: 部署
    env:
    API_KEY: ${{ secrets.API_KEY }}
    DATABASE_URL: ${{ secrets.DATABASE_URL }}
    run: |
    echo "API_KEY=$API_KEY" >> .env
    npm run deploy
  1. 矩陣測試(多版本)
    jobs:
    test:
    strategy:
    matrix:
    os: [ubuntu-latest, windows-latest, macos-latest]
    node: [16, 18, 20]

    runs-on: ${{ matrix.os }}

    steps:

    • uses: actions/setup-node@v3
      with:
      node-version: ${{ matrix.node }}
    • run: npm test
  2. 條件執行
    steps:

只在 main 分支執行

  • name: 部署到 production
    if: github.ref == 'refs/heads/main'
    run: npm run deploy:prod

只在 PR 執行

  • name: 預覽部署
    if: github.event_name == 'pull_request'
    run: npm run deploy:preview

只在有 tag 時執行

  • name: 發布版本
    if: startsWith(github.ref, 'refs/tags/')
    run: npm run release
  1. 快取加速
    steps:
  • uses: actions/setup-node@v3
    with:
    node-version: '18'
    cache: 'npm' # 自動快取 node_modules

或手動快取

  • uses: actions/cache@v3
    with:
    path: ~/.npm
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
  1. 手動觸發
    on:
    workflow_dispatch: # 允許手動觸發
    inputs:
    environment:
    description: '部署環境'
    required: true
    type: choice
    options:
    - development
    - staging
    - production

jobs:
deploy:
steps:
- name: 部署到 ${{ inputs.environment }}
run: npm run deploy:${{ inputs.environment }}


五、完整的 CI/CD 流程範例
name: Complete CI/CD

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

jobs:

階段 1:程式碼品質檢查

lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run format:check

階段 2:測試

test:
needs: lint
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16, 18, 20]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm test
- run: npm run test:coverage

  # 上傳覆蓋率報告
  - uses: codecov/codecov-action@v3
    with:
      files: ./coverage/lcov.info

階段 3:建置

build:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- run: npm ci
- run: npm run build

  # 上傳建置結果
  - uses: actions/upload-artifact@v3
    with:
      name: build
      path: dist/

階段 4:部署到 staging

deploy-staging:
needs: build
if: github.ref == 'refs/heads/develop'
runs-on: ubuntu-latest
environment:
name: staging
url: https://staging.example.com
steps:
- uses: actions/download-artifact@v3
with:
name: build
- name: 部署
run: echo "Deploy to staging"

階段 5:部署到 production

deploy-production:
needs: build
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment:
name: production
url: https://example.com
steps:
- uses: actions/download-artifact@v3
with:
name: build
- name: 部署
run: echo "Deploy to production"


六、GitLab CI/CD
基本設定

.gitlab-ci.yml

stages:

  • test
  • build
  • deploy

測試階段

test:
stage: test
image: node:18
script:
- npm ci
- npm test
cache:
paths:
- node_modules/

建置階段

build:
stage: build
image: node:18
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
only:
- main

部署階段

deploy:
stage: deploy
script:
- echo "Deploy to production"
environment:
name: production
url: https://example.com
only:
- main


七、常見問題處理
問題 1:Workflow 沒有執行

檢查事項

□ workflow 檔案在 .github/workflows/ 目錄
□ 檔案是 .yml 或 .yaml 結尾
□ YAML 語法正確(縮排、格式)
□ 分支名稱正確
□ 有權限執行 Actions(Settings → Actions)

驗證 YAML 語法

使用線上工具:https://www.yamllint.com/

問題 2:測試失敗

顯示更多除錯資訊

jobs:
test:
steps:
- run: npm test
env:
DEBUG: '*' # 開啟 debug 模式

  # 或在 workflow 設定
  - run: npm test -- --verbose

問題 3:權限問題

給予特定權限

permissions:
contents: write # 寫入權限
pull-requests: write # PR 權限
issues: write # Issue 權限

jobs:
deploy:
steps:
- name: 部署
run: echo "deploy"
問題 4:Secret 抓不到

確認 Secret 名稱正確(區分大小寫)

steps:

  • name: 使用 Secret
    env:
    API_KEY: ${{ secrets.API_KEY }} # 必須完全一致
    run: |
    if [ -z "$API_KEY" ]; then
    echo "Secret 不存在"
    exit 1
    fi

八、最佳實踐

  1. Workflow 組織
    .github/workflows/
    ├── ci.yml # 測試和檢查
    ├── deploy-staging.yml # 部署到 staging
    ├── deploy-prod.yml # 部署到 production
    ├── release.yml # 發布流程
    └── cleanup.yml # 清理任務

  2. 快速失敗原則
    jobs:
    test:

    一個失敗就全部停止(節省時間)

    strategy:
    fail-fast: true
    matrix:
    node: [16, 18, 20]

    steps:

    Lint 先跑(最快)

    • run: npm run lint

    測試

    • run: npm test

    建置(最慢)

    • run: npm run build
  3. 分離關注點

✅ 好的做法:分離 jobs

jobs:
lint:
steps:
- run: npm run lint

test:
needs: lint
steps:
- run: npm test

build:
needs: test
steps:
- run: npm run build

❌ 不好:全部塞在一起

jobs:
all:
steps:
- run: npm run lint && npm test && npm run build
4. 使用可重用的 Workflows

.github/workflows/reusable-deploy.yml

on:
workflow_call:
inputs:
environment:
required: true
type: string

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- run: echo "Deploy to ${{ inputs.environment }}"

在其他 workflow 中使用

jobs:
call-deploy:
uses: ./.github/workflows/reusable-deploy.yml
with:
environment: production


九、實戰範例:個人部落格
name: Blog Deploy

on:
push:
branches: [main]

jobs:
deploy:
runs-on: ubuntu-latest

steps:
  # 1. 下載程式碼
  - uses: actions/checkout@v3
  
  # 2. 設定 Node.js
  - uses: actions/setup-node@v3
    with:
      node-version: '18'
      cache: 'npm'
  
  # 3. 安裝依賴
  - run: npm ci
  
  # 4. 建置網站
  - run: npm run build
  
  # 5. 部署到 GitHub Pages
  - uses: peaceiris/actions-gh-pages@v3
    with:
      github_token: ${{ secrets.GITHUB_TOKEN }}
      publish_dir: ./public
      cname: blog.example.com  # 自訂網域

十、監控和通知
Slack 通知
jobs:
notify:
runs-on: ubuntu-latest
if: always() # 無論成功失敗都執行
needs: [test, build, deploy]

steps:
  - name: Slack 通知
    uses: 8398a7/action-slack@v3
    with:
      status: ${{ job.status }}
      text: '部署結果:${{ job.status }}'
      webhook_url: ${{ secrets.SLACK_WEBHOOK }}

Email 通知
jobs:
notify:
steps:
- name: Email 通知
if: failure()
uses: dawidd6/action-send-mail@v3
with:
server_address: smtp.gmail.com
server_port: 465
username: ${{ secrets.EMAIL_USERNAME }}
password: ${{ secrets.EMAIL_PASSWORD }}
subject: 'Build Failed: ${{ github.repository }}'
to: developer@example.com
from: CI/CD Bot
body: 'Build failed. Check: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'


十一、效能優化

  1. 使用快取

快取 npm

  • uses: actions/setup-node@v3
    with:
    cache: 'npm'

快取 Docker layers

  • uses: docker/build-push-action@v4
    with:
    cache-from: type=gha
    cache-to: type=gha,mode=max

自訂快取

  • uses: actions/cache@v3
    with:
    path: |
    ~/.npm
    ~/.cache
    key: ${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
  1. 並行執行
    jobs:

這些會同時執行

lint:
runs-on: ubuntu-latest
steps: [...]

test:
runs-on: ubuntu-latest
steps: [...]

security-scan:
runs-on: ubuntu-latest
steps: [...]

這個等上面都完成

deploy:
needs: [lint, test, security-scan]
steps: [...]
3. 只在需要時執行
on:
push:
paths:
- 'src/' # 只有 src 變更才執行
- 'package.json'
paths-ignore:
- 'docs/
' # 文件變更不執行
- '**.md'


十二、安全性整合
name: Security Checks

on: [push, pull_request]

jobs:
security:
runs-on: ubuntu-latest

steps:
  - uses: actions/checkout@v3
  
  # 1. 掃描敏感資訊
  - name: Gitleaks
    uses: gitleaks/gitleaks-action@v2
  
  # 2. 依賴套件漏洞掃描
  - run: npm audit --audit-level=moderate
  
  # 3. 程式碼安全掃描
  - uses: github/codeql-action/init@v2
  - uses: github/codeql-action/analyze@v2
  
  # 4. Docker 映像掃描
  - name: Trivy 掃描
    uses: aquasecurity/trivy-action@master
    with:
      image-ref: 'myapp:latest'

今日重點回顧
✅ 核心概念:

  1. CI/CD 基礎

    • 持續整合 = 自動測試
    • 持續部署 = 自動上線
    • 及早發現問題
  2. GitHub Actions

    • Workflow 語法
    • 常見觸發條件
    • Jobs 和 Steps
  3. 實用範例

    • 前端專案
    • 後端專案
    • Docker 建置
    • 自動發布
  4. 進階功能

    • Secrets 管理
    • 矩陣測試
    • 條件執行
    • 快取加速
  5. 最佳實踐

    • 快速失敗
    • 分離關注點
    • 並行執行
    • 安全整合

立即行動

今天就做(30 分鐘)

□ 為你的專案建立第一個 CI workflow
□ 加入自動測試
□ 測試 push 觸發

本週完成(2 小時)

□ 加入 Lint 檢查
□ 設定自動部署
□ 加入 Secrets
□ 建立多階段流程

基本 CI workflow 範本

mkdir -p .github/workflows
cat > .github/workflows/ci.yml << 'EOF'
name: CI

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

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- run: npm ci
- run: npm test
- run: npm run lint
EOF

git add .github/workflows/ci.yml
git commit -m "ci: add CI workflow"
git push


常見問題 FAQ
Q1: GitHub Actions 要付費嗎?
公開 repo:免費無限制
私有 repo:

  • 免費方案:2000 分鐘/月
  • Pro:3000 分鐘/月
  • 超過再付費

建議:

  • 優化 workflow 減少時間
  • 使用快取加速
  • 避免不必要的執行
    Q2: 如何在本地測試 workflow?

使用 act(模擬 GitHub Actions)

brew install act

執行 workflow

act

執行特定 job

act -j test

使用特定 event

act push
act pull_request
Q3: 如何除錯 workflow?

方法 1:增加 debug 訊息

  • run: |
    echo "Node version: $(node -v)"
    echo "NPM version: $(npm -v)"
    echo "Current dir: $(pwd)"
    ls -la

方法 2:啟用 debug logging

Settings → Secrets → 新增

ACTIONS_RUNNER_DEBUG = true

ACTIONS_STEP_DEBUG = true

方法 3:使用 tmate 遠端除錯

  • uses: mxschmitt/action-tmate@v3
    Q4: Workflow 太慢怎麼辦?

優化方法

  1. 使用快取
    cache: 'npm'

  2. 並行執行
    多個 jobs 同時跑

  3. 條件執行
    只在需要時執行

  4. 減少安裝
    npm ci 比 npm install 快

  5. 使用 artifacts
    在 jobs 之間傳遞檔案


實用資源
GitHub Actions:

  • 官方文件:https://docs.github.com/actions
  • Marketplace:https://github.com/marketplace?type=actions
  • 範例:https://github.com/actions/starter-workflows

GitLab CI/CD:

  • 官方文件:https://docs.gitlab.com/ee/ci/
  • 範例:https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/gitlab/ci/templates

學習資源:

  • GitHub Actions 教學
  • GitLab CI/CD 教學
  • DevOps 最佳實踐

上一篇
Day 25:Git 安全性 - 保護你的程式碼
下一篇
Day 27:疑難雜症解決 - Git 問題終結者
系列文
30天 Git 版本控制實戰筆記30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言