第 08 章

Runner 進階應用與虛擬機實戰

Runner 是 CI/CD 的核心引擎——每一個 Workflow Job 都在 Runner 上執行。本章深入探討 GitHub-hosted 與 Self-hosted Runner 的差異、Docker 容器化執行、快取與 Artifact 策略、除錯技巧,以及多種真實應用場景,幫助你從「能用」提升到「善用」。

8.1 Runner 在 CI/CD 中的角色

CI/CD 的核心需求之一是隔離且可重現的執行環境。Runner 就是為此而生的——它提供一台全新的虛擬機器,讓你的程式碼在乾淨、獨立的環境中編譯、測試與部署。

🔄 用完即丟(Ephemeral)

每個 Job 都會啟動一台全新的 Runner VM。Job 結束後,VM 被銷毀,不留任何痕跡。

🔒 安全性

無殘留狀態代表沒有憑證外洩風險——前一個 Job 的 Token、密碼不會留給下一個 Job。

🎯 可重現性

同樣的程式碼 + 同樣的 Runner Image = 同樣的結果。消除「在我的電腦可以跑」的問題。

Runner 生命週期

從開發者推送程式碼到 Job 結束,Runner 經歷以下完整生命週期:

flowchart LR A["👨‍💻 開發者\npush 程式碼"] --> B["☁️ GitHub\n接收事件"] B --> C["🖥️ 佈建 Runner\n全新 VM"] C --> D["⚙️ 執行 Job\n跑測試/部署"] D --> E["🗑️ 銷毀 VM\n清除所有資料"] style A fill:#dbeafe,stroke:#3b82f6,color:#000 style B fill:#fef3c7,stroke:#f59e0b,color:#000 style C fill:#dcfce7,stroke:#22c55e,color:#000 style D fill:#fce7f3,stroke:#ec4899,color:#000 style E fill:#fee2e2,stroke:#ef4444,color:#000
關鍵設計哲學:Runner 的「用完即丟」設計確保每次執行都是全新環境。這不只是技術選擇,更是安全策略——即使 Workflow 中有惡意程式碼,也無法影響後續的執行。

8.2 GitHub-hosted Runner 深入

GitHub 提供的 hosted runner 預裝了大量開發工具,幾乎可以滿足所有常見的 CI/CD 需求。了解它們的能力與限制,有助於寫出更高效的 Workflow。

預裝軟體一覽

分類預裝內容
程式語言Node.js、Python、Java、Go、Ruby、.NET、Rust、PHP、Swift
套件管理npm、yarn、pip、Maven、Gradle、Bundler、Cargo
建構工具CMake、Make、Autoconf、GCC、Clang
容器工具Docker、Docker Compose、Podman(Linux)
雲端 CLIAWS CLI、Azure CLI、Google Cloud SDK
瀏覽器Chrome、ChromeDriver、Firefox、Geckodriver
資料庫SQLite、PostgreSQL client、MySQL client
版本控制Git、Git LFS、SVN、Mercurial

Runner Image 更新週期

GitHub 每週更新 Runner Image,確保工具保持最新版本。ubuntu-latest 目前對應到 Ubuntu 22.04,未來會漸進切換到 24.04。如果需要固定版本,可使用 ubuntu-22.04 明確指定。

硬體規格比較

規格Standard RunnerLarger Runner(付費)
vCPU2 核心2 / 4 / 8 / 16 / 32 / 64 核心
記憶體7 GB8 – 256 GB
磁碟空間14 GB SSD最高 2,040 GB SSD
作業系統Ubuntu、Windows、macOSUbuntu、Windows(macOS 另計)
GPU❌ 不支援✅ 可選 GPU runner
固定 IP❌ 動態 IP✅ 支援靜態 IP
費用Public Repo 免費依核心數按分鐘計費
何時該用 Larger Runner?當你的 Workflow 出現以下情況時值得考慮:建置時間超過 10 分鐘、大型測試套件需要更多記憶體、需要 GPU 加速(如 ML 模型訓練)、需要固定 IP 存取防火牆後的服務。

8.3 Self-hosted Runner 完整指南

Self-hosted Runner 讓你在自己的伺服器上執行 GitHub Actions Job。當 GitHub-hosted Runner 無法滿足需求時,這是最佳替代方案。

何時使用 Self-hosted Runner?

  • 特殊硬體需求——需要 GPU、FPGA 或其他客製硬體。
  • 內部網路存取——需要連線到公司內部的資料庫或 API。
  • 大量執行降低成本——每月 Actions 分鐘數很高時,自架更划算。
  • 合規性要求——程式碼不能離開特定環境執行。

⚠️ 安全注意事項

  • 公開 Repo 勿用——任何人都能 Fork 並觸發 Workflow,等於在你的機器上執行任意程式碼。
  • 保持更新——定期更新 Runner 軟體以取得安全修補。
  • 隔離執行——建議用 Docker 或 VM 隔離 Runner 環境。
  • 最小權限——Runner 帳號僅授予必要的檔案系統與網路權限。

安裝 Self-hosted Runner

三大平台安裝步驟

前往 Repo → Settings → Actions → Runners → New self-hosted runner,依平台選擇指令。

# 1. 建立目錄並下載
$ mkdir actions-runner && cd actions-runner
$ curl -o actions-runner-linux-x64.tar.gz -L \
  https://github.com/actions/runner/releases/download/v2.x.x/actions-runner-linux-x64-2.x.x.tar.gz
$ tar xzf ./actions-runner-linux-x64.tar.gz

# 2. 設定(Token 由 GitHub 頁面提供)
$ ./config.sh --url https://github.com/YOUR_ORG/YOUR_REPO \
              --token YOUR_TOKEN \
              --labels linux,gpu,self-hosted

# 3. 安裝為系統服務(開機自動啟動)
$ sudo ./svc.sh install
$ sudo ./svc.sh start
$ sudo ./svc.sh status
# 1. 建立目錄並下載
$ mkdir actions-runner && cd actions-runner
$ curl -o actions-runner-osx-x64.tar.gz -L \
  https://github.com/actions/runner/releases/download/v2.x.x/actions-runner-osx-x64-2.x.x.tar.gz
$ tar xzf ./actions-runner-osx-x64.tar.gz

# 2. 設定
$ ./config.sh --url https://github.com/YOUR_ORG/YOUR_REPO \
              --token YOUR_TOKEN \
              --labels macos,self-hosted

# 3. 安裝為 launchd 服務
$ ./svc.sh install
$ ./svc.sh start
# 1. 在 PowerShell 中下載並解壓縮
mkdir actions-runner; cd actions-runner
Invoke-WebRequest -Uri https://github.com/actions/runner/releases/download/v2.x.x/actions-runner-win-x64-2.x.x.zip -OutFile actions-runner-win-x64.zip
Add-Type -AssemblyName System.IO.Compression.FileSystem
[System.IO.Compression.ZipFile]::ExtractToDirectory("$PWD\actions-runner-win-x64.zip", "$PWD")

# 2. 設定
.\config.cmd --url https://github.com/YOUR_ORG/YOUR_REPO --token YOUR_TOKEN

# 3. 安裝為 Windows 服務
.\svc.cmd install
.\svc.cmd start

Runner Labels 與 Workflow 搭配

安裝時指定的 Label 就是 Workflow 中 runs-on 用來選擇 Runner 的依據:

# 在 Workflow 中指定 Self-hosted Runner
jobs:
  train-model:
    runs-on: [self-hosted, linux, gpu]   # 需要同時符合三個 label
    steps:
      - uses: actions/checkout@v4
      - run: python train.py

Runner Group 架構

在組織 (Organization) 層級,可以用 Runner Group 將多台 Runner 分組管理,並指定哪些 Repo 可以使用:

flowchart TD ORG["🏢 Organization"] --> G1["Runner Group: General"] ORG --> G2["Runner Group: GPU"] G1 --> R1["🖥️ Runner #1\nlinux, x64"] G1 --> R2["🖥️ Runner #2\nlinux, x64"] G2 --> R3["🖥️ Runner #3\nlinux, gpu, a100"] G2 --> R4["🖥️ Runner #4\nlinux, gpu, v100"] G1 -.->|"指派給"| REPO1["📦 web-app repo"] G1 -.->|"指派給"| REPO2["📦 api-server repo"] G2 -.->|"指派給"| REPO3["📦 ml-training repo"] style ORG fill:#dbeafe,stroke:#3b82f6,color:#000 style G1 fill:#dcfce7,stroke:#22c55e,color:#000 style G2 fill:#fef3c7,stroke:#f59e0b,color:#000

8.4 Runner 上的 Docker 使用

Docker 是 Runner 上最強大的工具之一。你可以將整個 Job 跑在容器裡、啟動資料庫服務容器,甚至使用自製的 Docker Image。以下介紹三種常見的 Docker 使用模式。

三種 Docker 使用模式

根據需求選擇合適的容器化策略。

使用 container: 語法,將整個 Job 跑在指定的 Docker 容器中。好處是環境精確可控,且小型 Image 啟動更快。

name: Container Job 範例
on: push

jobs:
  test:
    runs-on: ubuntu-latest
    container: node:20-slim       # 整個 Job 跑在此容器中

    steps:
      - uses: actions/checkout@v4
      - run: node --version       # 使用容器內的 Node.js
      - run: npm ci
      - run: npm test
提示:選擇 -slim-alpine 版本的 Image 可以大幅減少下載時間。例如 node:20-slim 約 200 MB,而完整版約 1 GB。

Service Container 可以在 Job 旁邊啟動資料庫、快取等服務。服務名稱就是 hostname,無需額外設定。

name: Service Container 範例
on: push

jobs:
  integration-test:
    runs-on: ubuntu-latest

    services:
      postgres:                    # hostname = "postgres"
        image: postgres:16
        env:
          POSTGRES_USER: testuser
          POSTGRES_PASSWORD: testpass
          POSTGRES_DB: testdb
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

      redis:                       # hostname = "redis"
        image: redis:7-alpine
        ports:
          - 6379:6379

    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run test:integration
        env:
          DATABASE_URL: postgres://testuser:testpass@localhost:5432/testdb
          REDIS_URL: redis://localhost:6379

將所有專案依賴預裝在自製 Image 中,大幅縮短每次 CI 的安裝時間。

步驟 1:撰寫 Dockerfile

# .docker/ci.Dockerfile
FROM node:20-slim

# 安裝系統依賴
RUN apt-get update && apt-get install -y \
    python3 make g++ \
    && rm -rf /var/lib/apt/lists/*

# 預裝全域工具
RUN npm install -g typescript eslint

WORKDIR /app

步驟 2:在 Workflow 中使用

name: 使用自製 Image
on: push

jobs:
  build:
    runs-on: ubuntu-latest
    container:
      image: ghcr.io/my-org/ci-image:latest   # 從 Container Registry 拉取
      credentials:
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}

    steps:
      - uses: actions/checkout@v4
      - run: npm ci                # 系統依賴已預裝,安裝極快
      - run: npm run build
      - run: npm test

8.5 Runner 的進階技巧

掌握快取、Artifact 與除錯技巧,能讓你的 Workflow 更快、更穩定、更容易排錯。

三大進階技巧

每一項都能顯著提升 CI/CD 體驗。

快取機制可以在不同 Workflow Run 之間保留依賴套件,避免每次都重新下載。核心概念是 Cache Key——用 lock 檔案的 hash 作為 key,當依賴沒變時直接恢復快取。

name: 使用快取
on: push

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

      # 快取 node_modules
      - uses: actions/cache@v4
        id: npm-cache
        with:
          path: node_modules
          key: node-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
          restore-keys: |
            node-${{ runner.os }}-

      # 只有 cache miss 時才安裝
      - if: steps.npm-cache.outputs.cache-hit != 'true'
        run: npm ci

      - run: npm test
情境行為效果
Cache Hit ✅直接恢復快取,跳過 npm ci節省 30-120 秒
Partial Hit 🔶使用 restore-keys 恢復舊快取,再安裝差異節省部分時間
Cache Miss ❌完整安裝,執行結束後自動儲存快取下次會命中

Artifact 用於在不同 Job 之間傳遞檔案,或保存建置產物供事後下載。

name: Artifact 跨 Job 傳遞
on: push

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm run build

      # 上傳建置產物
      - uses: actions/upload-artifact@v4
        with:
          name: build-output
          path: dist/
          retention-days: 7        # 保留 7 天

  deploy:
    needs: build                    # 等 build 完成才執行
    runs-on: ubuntu-latest
    steps:
      # 下載前一個 Job 的產物
      - uses: actions/download-artifact@v4
        with:
          name: build-output
          path: dist/

      - run: echo "部署 dist/ 內容到伺服器..."

Workflow 出問題時,以下技巧能幫你快速定位問題:

① 啟用 Step Debug Logging

# 在 Repo → Settings → Secrets → Actions 中新增:
#   Name:  ACTIONS_STEP_DEBUG
#   Value: true
# 重新執行 Workflow,每個 Step 會顯示詳細的除錯資訊。

② SSH 進入 Runner 互動除錯

# 在 Workflow 中加入此 Step,執行時會暫停並提供 SSH 連線
- name: 互動除錯(SSH)
  uses: mxschmitt/action-tmate@v2
  if: failure()                # 只在失敗時啟動
  with:
    limit-access-to-actor: true   # 僅允許觸發者連線

③ 寫入 Step Summary

# 用 $GITHUB_STEP_SUMMARY 在 Actions 頁面顯示自訂 Markdown 摘要
- name: 產生測試報告摘要
  run: |
    echo "## 🧪 測試結果" >> $GITHUB_STEP_SUMMARY
    echo "| 類別 | 通過 | 失敗 |" >> $GITHUB_STEP_SUMMARY
    echo "|------|------|------|" >> $GITHUB_STEP_SUMMARY
    echo "| 單元測試 | 42 | 0 |" >> $GITHUB_STEP_SUMMARY
    echo "| 整合測試 | 18 | 1 |" >> $GITHUB_STEP_SUMMARY

Matrix 策略優化

Matrix 可以平行測試多個環境組合,搭配進階參數讓策略更靈活:

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false            # 一個失敗不會取消其他 Job
      max-parallel: 4             # 最多同時跑 4 個 Job
      matrix:
        node: [18, 20, 22]
        os: [ubuntu-latest, windows-latest]
        exclude:
          - node: 18              # 排除 Node 18 + Windows 組合
            os: windows-latest
        include:
          - node: 22              # 額外加入 Node 22 + macOS 組合
            os: macos-latest

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
      - run: npm ci && npm test
參數說明預設值
fail-fast一個 Job 失敗時是否取消其他正在執行的 Jobtrue
max-parallel同時執行的 Job 最大數量無限制
include在 matrix 組合外額外加入特定組合-
exclude從 matrix 組合中排除特定組合-

8.6 實際應用場景

以下列出六種常見的 Runner 應用場景,涵蓋前端、後端、教學、文件、行動 App 與機器學習領域。

🌐 前端專案

自動跑 Lint + 單元測試,通過後自動部署到 GitHub Pages 或 Vercel。搭配快取加速 node_modules 安裝。

GitHub Pages Vercel Node.js

⚙️ 後端專案

使用 Service Container 啟動 PostgreSQL / Redis,執行整合測試,通過後建構 Docker Image 並推送到 AWS ECR / GCP Artifact Registry。

Docker AWS GCP

🎓 教學情境

配合 GitHub Classroom,學生 push 作業後自動執行測試腳本,即時回饋評分結果。老師可透過 Actions 記錄追蹤每位學生的提交。

GitHub Classroom Autograding

📄 文件生成

將 Markdown 原始碼自動轉換為精美網站。支援 MkDocs、VitePress、Jekyll 等靜態網站生成器,push 即自動部署。

MkDocs VitePress Jekyll

📱 行動 App

在 macOS runner 上建構 iOS App(使用 Xcode),在 Linux runner 上建構 Android App(使用 Gradle)。搭配 Fastlane 自動化發布到 App Store / Google Play。

iOS Android Fastlane

🤖 Machine Learning

在 Self-hosted GPU Runner 上訓練模型。將訓練結果作為 Artifact 上傳,並自動產生模型指標的 Step Summary 報告。

Self-hosted GPU PyTorch

🔧 動手練習

練習 1:使用 Container Job

  1. 建立一個包含簡單測試的 Node.js 專案。
  2. 撰寫 Workflow,使用 container: node:20-slim
  3. 在容器內執行 npm cinpm test
  4. Push 後到 Actions 頁面確認 Job 成功在容器中執行。
  5. 嘗試改用 node:20-alpine,比較啟動時間差異。

練習 2:Service Container

  1. 在專案中新增一個需要資料庫的整合測試腳本。
  2. 撰寫 Workflow,定義 services.postgres 區塊。
  3. 設定 POSTGRES_USERPOSTGRES_PASSWORDPOSTGRES_DB
  4. 在 Step 中透過 localhost:5432 連線到 PostgreSQL。
  5. 確認整合測試能讀寫資料庫並通過。

練習 3:快取優化

  1. 找一個已有 Workflow 的 Node.js 專案。
  2. 記錄目前 npm ci 步驟的執行時間。
  3. 加入 actions/cache@v4,使用 package-lock.json 的 hash 作為 key。
  4. Push 兩次:第一次建立快取,第二次命中快取。
  5. 比較兩次的執行時間,觀察加速效果。

練習 4:除錯 Workflow

  1. 故意在 Workflow 的某個 Step 中寫入錯誤指令。
  2. 在 Repo Secrets 中加入 ACTIONS_STEP_DEBUG = true
  3. 重新執行 Workflow,觀察詳細的除錯日誌。
  4. 加入一個 Step 使用 $GITHUB_STEP_SUMMARY 輸出自訂摘要。
  5. 修正錯誤後確認 Workflow 成功通過。

本章小結

知識重點

  • Runner 採用用完即丟(Ephemeral)設計,確保安全性與可重現性。
  • GitHub-hosted Runner 預裝豐富工具,Image 每週更新;Larger Runner 提供更高規格與 GPU。
  • Self-hosted Runner 適合特殊硬體、內部網路與合規需求,但須注意安全性。
  • Docker 在 Runner 上有三種模式:Container Job、Service Container、自製 Image。
  • 快取策略透過 lock 檔案 hash 作為 key,能大幅縮短安裝時間。
  • Artifact 可在 Job 之間傳遞建置產物,保留天數可自訂。
  • Matrix 策略支援 fail-fastincludeexcludemax-parallel 微調。

技能重點

  • 能根據需求選擇 GitHub-hosted 或 Self-hosted Runner。
  • 能在三大平台上安裝與設定 Self-hosted Runner。
  • 能使用 container: 語法將 Job 跑在 Docker 容器中。
  • 能設定 Service Container 啟動資料庫與快取服務。
  • 能實作 actions/cache 快取策略,顯著加速 Workflow。
  • 能使用 Artifact 在不同 Job 之間傳遞檔案。
  • 能使用 Debug Logging、tmate SSH、Step Summary 除錯失敗的 Workflow。
🚀 掌握 Runner 就掌握了 CI/CD 的基礎設施! 從理解 Runner 的生命週期、善用 Docker 容器化,到快取優化與除錯技巧——這些知識讓你能建構出高效、穩定且安全的自動化流水線。繼續在實際專案中探索與實踐吧!