Mac mini 上的 AI 工作負載排班:如何避免 Ollama + Claude Code + GitHub Runner 觸發 Swap

L2-Q03 · 記憶體排班層

2026.06.04  ·  約 14 分鐘  ·  維運 Runbook,非安裝教學

Mac mini 統一記憶體與 AI 工作負載排班示意:Ollama、Claude Code、GitHub Actions 並行時透過排班避免 Swap

在 Mac mini / Cloud Mac 上同時執行 Ollama、Claude Code 和 GitHub Actions 時,最常見的問題不是「效能不夠」,而是系統開始 Swap、CLI 卡頓、CI 建置變慢

本篇不討論工具安裝,而是說明:為什麼三種 AI 工作負載同時存在時會出現記憶體抖動,以及如何透過排班避免它。下文提供反例、記憶體預算、閾值排班、30 秒 Runbook 與完整腳本;也不重複16GB vs 24GB 實測

TL;DR

  • Ollama 常駐 + CI burst → 在 M4 上 Swap 非常常見(見 反例
  • 解法通常不是升級 Mac,而是為 burst / interactive / background 定義工作負載排班
  • CI 前 ollama stop 是最關鍵、也最容易落地的一步(30 秒版

深入閱讀時會看到 AI workload scheduling 模型:在我們觀測的 Cloud Mac 場景中,大多數 Swap/OOM 更多來自排班問題,而非絕對記憶體不足

AI
Workload Scheduling
30s
最小 Runbook
0
條 brew install

真正的問題:多數情況不是記憶體不夠,是沒有優先順序

表面症狀是「本機模型空轉 + CI 尖峰 + 編碼 Agent 變卡」——根因常被誤讀為「該升級了」。

在我們觀測的 Cloud Mac 場景中,大多數 Swap/OOM 更多來自工作負載排班問題,而非絕對記憶體不足。 同一台 Mac mini 上同時存在三類負載(詳見下節):

  • Burst——GitHub Runner / xcodebuild,push 時瞬間 +4–8 GB
  • Interactive——Claude Code,IDE + 終端機 + 大儲存庫索引
  • Background——Ollama 等本機推理,8B loaded 後靜默佔 5–7 GB

三者預設平等搶統一記憶體,沒有人「必須讓路」。修正方向:定義工作負載優先順序與觸發條件,把背景推理從「裝完就常駐」改成可卸載、可被 CI 事件搶占的可排班資源。

錯誤排班反例(M4 上非常常見,不是偶發)

下面是我們在小團隊 Cloud Mac 上真實踩過的組合——可作為 onboarding 時的「禁止模式」:

Bad pattern · all three workloads default to "always on"

  qwen3:8b  always loaded          →  5–7 GB (background, zero calls)
  push triggers xcodebuild           →  +4–8 GB peak (burst)
  Claude Code indexing large repo    →  +2–3 GB (interactive)

Observed on 24GB M4 Mac mini
  Swap  0 → 2.1 GB
  xcodebuild link phase latency  +~40%
  Claude Code terminal noticeably slower

在我們觀測的 M4 統一記憶體場景下,8B 常駐 + CI burst + 互動式編碼同時發生,Swap 非常常見。反例的價值在於:讓讀者相信 workload scheduling 不是錦上添花,而是多負載並存的前置條件

三種負載形態:burst / interactive / background

排班不能只看時鐘。Cloud Mac 上三類工作負載的記憶體曲線完全不同:

形態 代表 記憶體特徵 排班優先順序
Burst xcodebuild、連結、模擬器 L1 Runner 尖峰、不可預測 最高——Fact 不能失敗
Interactive Claude Code、IDE、SSH 工作階段 L3 中等、人在場 高——但推理走 API,不佔大模型
Background Ollama embedding、日誌摘要 L2 可延遲、可卸載 最低——必須讓路給 burst

一句話:L2 是 Stack 裡唯一「可以被踢下線」的一層——不是因為它不重要,而是因為它的任務大多可非同步、可重試。

記憶體預算:三種工作負載各佔多少

下面以 Ollama / Claude Code / GitHub Runner 為範例元件,數字來自M4 Mac mini 實測(穩態,非編譯尖峰):

元件 典型佔用 尖峰說明
macOS + 系統快取 L0 3–4 GB 相對穩定
Claude Code 工作區 L3 1–3 GB 推理走 API,不佔模型權重
GitHub Runner job L1 2–6 GB(穩態) 連結階段瞬間 +4–8 GB
Ollama · qwen3:8b L2 5–7 GB ollama stop 可釋放
Ollama · qwen3:14b L2 9–13 GB 與 burst 並存易 Swap 2GB+
Ollama · nomic-embed-text L2 0.3–0.8 GB 白天可常駐的輕量 background

粗算 24GB 機白天「編碼 + CI + 8B 常駐」≈ 17 GB——尚餘餘量;疊 14B 常駐則輕鬆超 22 GB。預算表解決「能不能」;排班解決「誰該在什麼時候佔」。

排班模型:從時段表到 memory_pressure 閾值

入門可以用「日/夜時段表」(見下文模式 ②),但更可維運的升級是按記憶體壓力閾值排班——不依賴人記得「22 點該跑批處理」:

AI Workload Scheduler · L2-Q03 recommended rules

When memory_pressure enters warn / critical (or equivalent > ~70%):
  → auto ollama stop qwen3:8b / qwen3:14b

When memory_pressure normal (< ~50%) and idle > 10 min:
  → auto preload nomic-embed-text (keepalive 10m)

When CI event trigger (Runner job start):
  → force CI mode: stop all large Ollama models, priority L1 > L3 > L2

When CI job succeeds and memory recovers:
  → async trigger L2 batch (log summary / embedding rebuild)

時段排班是 baseline;閾值排班是 upgrade。 小團隊可以先抄 Runbook + CI 前 stop;穩定後再上 memory_pressure guard 腳本(見 Runbook 一節)。

三種基礎排班模式

與閾值排班配合使用的基礎策略:

模式 做法 適合
① 輕量並存 白天只常駐 nomic-embed-text;8B/14B 按需 load 16GB Mac mini、以編碼為主
② 時段拆分 09–18 編碼+CI / 22–06 夜間批處理 24GB、有定時 embedding / 日誌任務
③ CI 觸發讓路 job 前 ollama stop,結束後非同步 L2 push 頻繁、xcodebuild 尖峰高

推薦組合:① + ③ 全員預設;② 作為夜間批處理補充;閾值 guard 兜底。

Pipeline 分流:什麼走 L2、什麼走 L3

排班之前先釘死 pipeline,否則 Ollama 仍會空轉(見 L2-Q01 · 典型誤判):

任務 排班備註
改儲存庫、產生 patch L3 Claude Code(API) 不佔本機大模型
建置、測試、歸檔 L1 Runner burst 最高優先;尖峰前停 L2
CI 失敗日誌摘要 L2 qwen3:8b job 結束後非同步,或夜間批
CodeGraph / RAG embedding L2 nomic-embed-text 可白天常駐(<1GB)

Runner 尖峰與 CI 錯峰

L1 RunnerFact——Inference 不能取代建置結果。CI 側最小改動:

# .github/workflows/ios.yml · self-hosted macOS runner
- name: Enter CI mode — free memory for xcodebuild
  run: |
    ollama ps
    ollama stop qwen3:8b 2>/dev/null || true
    ollama stop qwen3:14b 2>/dev/null || true
    sleep 30   # wait for memory reclaim; do not start xcodebuild same second

- name: Build
  run: xcodebuild ...

實測:24GB M4 上 ollama stop qwen3:8b 後約 5–15 秒內釋放 5–7 GB;Swap 已產生時完全回收需數分鐘——故 CI 前置 stop 必須提前至少 30 秒

Runbook:30 秒版與完整腳本

30 秒版(大多數人只需要這三段)

不想讀完整腳本?複製下面三段即可覆蓋 80% 場景

① CI 前(最關鍵)——放進 GitHub Actions 或 Runner hook:

ollama stop qwen3:8b
ollama stop qwen3:14b
sleep 30

② 白天——只留輕量 embedding,不佔大模型權重:

ollama run nomic-embed-text --keepalive 30m

③ 夜間——批處理時段再 load 8B:

ollama run qwen3:8b
# then run your log-summary / embedding-rebuild scripts

完整版(生產 Runbook)

需要 LaunchAgent / cron / 多環境複用時,儲存為 ~/bin/cloud-mac-stack-runbook.sh

完整 Runbook · 子命令

day-start · ci-pre · ci-post · night-batch

#!/usr/bin/env bash
# cloud-mac-stack-runbook.sh — L2-Q03 standard Runbook
set -euo pipefail

OLLAMA_HOST="${OLLAMA_HOST:-127.0.0.1:11434}"
export OLLAMA_MAX_LOADED_MODELS=1

ensure_ollama() {
  curl -sf "http://${OLLAMA_HOST}/api/tags" >/dev/null || ollama serve &
  sleep 2
}

ci_pre() {
  # Before CI: force CI mode, L1 priority
  ollama ps || true
  ollama stop qwen3:8b 2>/dev/null || true
  ollama stop qwen3:14b 2>/dev/null || true
  sleep 30
}

ci_post() {
  # After CI: restore light embedding (lowest background tier)
  ensure_ollama
  ollama run nomic-embed-text --keepalive 10m
}

day_start() {
  # Day login / boot: embedding only or stop all
  ensure_ollama
  ollama stop qwen3:8b 2>/dev/null || true
  ollama stop qwen3:14b 2>/dev/null || true
  ollama run nomic-embed-text --keepalive 30m
}

night_batch() {
  # Night batch (cron: 0 22 * * *)
  ensure_ollama
  ollama run qwen3:8b --keepalive 6h
  # ./your-log-summary-or-embed-rebuild.sh
}

case "${1:-}" in
  day-start)   day_start ;;
  ci-pre)      ci_pre ;;
  ci-post)     ci_post ;;
  night-batch) night_batch ;;
  *) echo "Usage: $0 {day-start|ci-pre|ci-post|night-batch}"; exit 1 ;;
esac

Memory guard(建議 cron 每 5 分鐘,或 LaunchAgent):當系統記憶體壓力升高時自動卸載大模型——這是「閾值排班」的最小實作。

#!/usr/bin/env bash
# memory-guard.sh — memory_pressure threshold guard
PRESSURE=$(memory_pressure 2>/dev/null | head -1 || true)

if echo "$PRESSURE" | grep -qiE 'warn|critical|urgent'; then
  logger -t cloud-mac-stack "memory guard: stopping Ollama 8B/14B ($PRESSURE)"
  ollama stop qwen3:8b 2>/dev/null || true
  ollama stop qwen3:14b 2>/dev/null || true
fi

# Optional: restore embedding when pressure normal and no Runner job
# if echo "$PRESSURE" | grep -qi 'normal'; then ... fi

接入方式範例:

  • GitHub Actions——workflow 首 step 調 ci-pre,末 step 調 ci-post
  • LaunchAgent——登入時 day-start;22:00 cron night-batch
  • cron——*/5 * * * * /path/memory-guard.sh

16GB Mac mini 怎麼排

  • 禁止 14B 常駐;8B 僅 night-batch
  • 白天 Runbook 只用 day-start(embedding 或全停)
  • 每次 CI 必須 ci-pre,無例外
  • 預設「桌面 + 8B + Claude Code 同時在線」→ 直接選 24GB,排班救不了硬體邊界

16GB 口訣:白天無大模型,夜間單模型單任務,CI 前必 ci-pre24GB 口訣:白天可 embedding,8B 與 CI 錯峰,14B 僅夜間。

決策表:你該用哪種策略

你的情況 推薦
24GB · push 少 · mainly Claude Code day-start + embedding;無夜間批
24GB · 每日 CI · 要日誌摘要 ci-pre/post + night-batch + memory guard
16GB · 必須本機 8B night-batch;白天 Claude API
Ollama 仍一週零呼叫 先回 L2-Q01 定義 pipeline

系列定位 · Cloud Mac AI Stack

L2-Q03 · 記憶體排班層——對外回答「Mac mini 如何避免 Swap」;對內是 L2-Q01 私有推理層 的排班續篇:

  • L2-Q01 — Inference 是什麼(定位)
  • L2-Q03 · 本篇 — 記憶體排班層(同機排班)
  • 計畫中 — 模型 pin、11434 健康檢查、CI 側呼叫 Ollama
  • 下游 — L4 Context + MCP 排班(明日 L4-Q03)

它不是 Ollama 使用教學,也不是純 CI/CD 優化文——而是 Apple Silicon 上 AI Workload Scheduler 的第一層設計

常見問題

Mac mini 上 Ollama、Claude Code、GitHub Actions 同時跑會 Swap 嗎?
會,若 burst / interactive / background 三種工作負載沒有優先順序。見 真正的問題反例

Ollama 需要一直執行嗎?
不需要。 應把本機推理當成可排班資源:白天可只留 nomic-embed-text,大模型按需 load 或僅在夜間批處理。

CI 建置時要停 Ollama 嗎?
建議停。30 秒 Runbook · CI 前,或完整版 ci-pre

Claude Code 和 Ollama 能同時跑嗎?
能。 編碼主路徑走 API;本機爭用的是模型權重與 CI 尖峰。用 CI 前 stop 或時段拆分即可。

Cloud Mac 上 OOM 是記憶體不夠嗎?
在我們觀測的場景中,大多數 OOM 更多來自排班問題,而非絕對記憶體不足。16GB 硬邊界見 L2-Q02 實測

時段排班和 memory_pressure 閾值怎麼選?
先上 30 秒 Runbook;穩定後加 memory-guard.sh(見完整 Runbook)。

本篇和 L2-Q01 有什麼不同?
Q01 講 Inference Service 定位;Q03(記憶體排班層)講同機 AI 工作負載如何排班

Cloud Mac AI Stack · 明日預告

Claude Code + MCP:GitHub / 本機檔案 / API 怎麼連成一條鏈

L2 排班釘死後,下一層是 L4 Context:MCP 如何把 GitHub、儲存庫檔案與 API 接進 Claude Code 工作流。

返回部落格 · Cloud Mac AI Stack 全系列
AI 排班 Cloud Mac 定價