Mac mini / Cloud Mac 上で Ollama、Claude Code、GitHub Actions を同時に回すと、よくある問題は「性能不足」ではなく Swap の発生、CLI の遅延、CI ビルドの低下です。
本稿はツールのインストール手順ではありません。3 種類の AI workload が同時に存在するときにメモリが揺れる理由と、scheduling でそれを避ける方法を説明します。反例、メモリ予算、閾値 scheduling、30 秒 Runbook、完全スクリプトを示し、16GB vs 24GB benchmarkは繰り返しません。
読み進めると AI workload scheduling モデルが見えてきます。観測した Cloud Mac では、多くの Swap/OOM は絶対メモリ不足より scheduling 問題に近いです。
本当の問題:多くはメモリ不足ではなく優先度がない
表面は「ローカルモデル空転 + CI ピーク + コーディング Agent の卡顿」——根因を「グレードアップ」と誤読しがち。
観測した Cloud Mac では、多くの Swap/OOM は絶対メモリ不足より workload scheduling 問題に近い。 同一 Mac mini 上に次の 3 クラスが共存します(次節):
- Burst——GitHub Runner /
xcodebuild、push 時 +4–8 GB - Interactive——Claude Code、IDE + ターミナル + 大規模リポジトリ index
- Background——Ollama などローカル Inference、8B loaded 後 5–7 GB を静かに占有
デフォルトではユニファイドメモリを平等に奪い合い、誰も「譲らない」。修正は workload 優先度とトリガー条件の定義——background Inference を「入れたら常駐」から、unload 可能で CI イベントに preempt される schedulable リソースへ。
誤った排班の反例(M4 で非常に一般的)
小規模チームの Cloud Mac で実際に踏んだ組み合わせ——onboarding の「禁止パターン」として:
Bad schedule · three workloads default "all on" qwen3:8b always loaded → 5–7 GB (background, idle) push triggers xcodebuild → +4–8 GB peak (burst) Claude Code indexing large repo → +2–3 GB (interactive) Observed (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 が飾りではなく、多 workload 共存の前提条件だと信じさせることです。
3 形態:burst / interactive / background
時計だけでは足りない。Cloud Mac 上で 3 workload のメモリ曲線はまったく異なります:
| 形態 | 代表 | 層 | メモリ特性 | scheduling 優先度 |
|---|---|---|---|---|
| Burst | xcodebuild、リンク、シミュレータ |
L1 Runner | 尖峰・予測困難 | 最高——Fact は失敗できない |
| Interactive | Claude Code、IDE、SSH | L3 | 中程度・人在 | 高——Inference は API、大モデルは使わない |
| Background | Ollama embedding、ログ要約 | L2 | 遅延可・unload 可 | 最低——burst に譲る |
一言で:L2 は Stack で唯一「キック可能」な層——重要でないからではなく、タスクが多く非同期・リトライ可能だからです。
メモリ予算:3 workload の占有
Ollama / Claude Code / GitHub Runner を例コンポーネントとし、数値はM4 Mac mini 実測(steady、コンパイル尖峰除く):
| コンポーネント | 層 | 典型占有 | ピーク説明 |
|---|---|---|---|
| macOS + システムキャッシュ | L0 | 3–4 GB | 比較的安定 |
| Claude Code ワークスペース | L3 | 1–3 GB | Inference は API、モデル重みなし |
| GitHub Runner job | L1 | 2–6 GB(steady) | リンク段階で +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 超えやすい。予算表は「できるか」;scheduling は「いつ誰が占めるか」。
scheduling モデル:時間帯から memory_pressure 閾値へ
入門は「昼/夜の時間帯表」(下のモード ②)。運用向けの upgrade はメモリ圧力閾値での scheduling——「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 + memory recovers: → async L2 batch (log summary / embedding rebuild)
時間帯排班は baseline;閾値 scheduling は upgrade。小チームは Runbook + CI 前 stop から;安定後 memory_pressure guard(§ Runbook)。
3 つの基本排班モード
閾値 scheduling と併用する基本戦略:
| モード | やり方 | 向く構成 |
|---|---|---|
| ① 軽量併存 | 昼は nomic-embed-text のみ常駐;8B/14B は on-demand |
16GB Mac mini、コーディング中心 |
| ② 時間帯 split | 09–18 コーディング+CI / 22–06 夜間バッチ | 24GB、定期 embedding / ログタスク |
| ③ CI トリガーで譲路 | job 前 ollama stop、終了後に非同期 L2 |
push 多め、xcodebuild ピーク高 |
推奨:① + ③ を全員デフォルト;② は夜間バッチ補助;閾値 guard で兜底。
Pipeline 分流:L2 と L3 の切り分け
排班の前に pipeline を固定。さもないと Ollama は空転(L2-Q01 · 典型の誤認):
| タスク | 層 | scheduling メモ |
|---|---|---|
| リポジトリ変更、patch 生成 | L3 Claude Code(API) | 本機大モデル不要 |
| ビルド・テスト・アーカイブ | L1 Runner | burst 最優先;ピーク前に L2 stop |
| CI 失敗ログ要約 | L2 qwen3:8b |
job 後に非同期、または夜間バッチ |
| CodeGraph / RAG embedding | L2 nomic-embed-text |
昼間常駐可(<1GB) |
Runner ピークと CI 错峰
L1 Runner は Fact を産出——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 秒版(多くの人はこの 3 段で十分)
完全スクリプトを読まない?下の 3 段をコピーすれば 80% をカバー:
① CI 前(最重要)——GitHub Actions または Runner hook へ:
ollama stop qwen3:8b ollama stop qwen3:14b sleep 30
② 昼間——軽量 embedding のみ、大モデル重みを使わない:
ollama run nomic-embed-text --keepalive 30m
③ 夜間——バッチ窓で 8B を load:
ollama run qwen3:8b # then run your log summary / embedding rebuild script
完全版(本番 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 full stop 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 推奨):メモリ圧力上昇時に大モデルを自動 unload——「閾値 scheduling」の最小実装。
#!/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: when pressure normal + no Runner job, restore embedding # if echo "$PRESSURE" | grep -qi 'normal'; then ... fi
接続例:
- GitHub Actions——workflow 先頭で
ci-pre、末尾でci-post - LaunchAgent——ログイン時
day-start;22:00 cronnight-batch - cron——
*/5 * * * * /path/memory-guard.sh
16GB Mac mini の排方
- 14B 常駐禁止;8B は
night-batchのみ - 昼の Runbook は
day-startのみ(embedding または全 stop) - CI のたびに
ci-pre、例外なし - 「デスクトップ + 8B + Claude Code 同時オン」がデフォルトなら 24GB を選ぶ——排班ではハード限界は救えない
16GB 口诀:昼は大モデルなし、夜は単一モデル単一タスク、CI 前は必ず ci-pre。 24GB 口诀:昼は embedding 可、8B は CI と错峰、14B は夜のみ。
決定表:どの戦略を選ぶか
| あなたの状況 | 推奨 |
|---|---|
| 24GB · push 少 · 主に Claude Code | day-start + embedding;夜間バッチなし |
| 24GB · 毎日 CI · ログ要約あり | ci-pre/post + night-batch + memory guard |
| 16GB · ローカル 8B 必須 | night-batch のみ;昼は Claude API |
| Ollama が 1 週間ゼロ呼び出し | L2-Q01 へ戻り pipeline を定義 |
系列 · Cloud Mac AI Stack
L2-Q03 · メモリ Scheduling 層——対外は「Mac mini で Swap を避ける」;対内は L2-Q01 プライベート Inference 層 の scheduling 続編:
- L2-Q01 — Inference とは(位置づけ)
- L2-Q03 · 本稿 — メモリ Scheduling 層(同機 scheduling)
- 予定 — モデル pin、11434 ヘルスチェック、CI からの Ollama 呼び出し
- 下流 — L4 Context + MCP scheduling(翌 L4-Q03)
Ollama チュートリアルでも純 CI 最適化文でもなく、Apple Silicon 上 AI Workload Scheduler の第一層設計です。
既存記事との関係
- L2-Q01 · プライベート Inference 層——L2 位置づけ;本稿はその scheduling 続編。
- L2-Q02 · 16GB vs 24GB——Swap 数値の出典;benchmark は繰り返さない。
- L1-Q01 · Runner——burst 最優先。
- L3 · Claude Code——interactive 主経路。
よくある質問
Mac mini で Ollama、Claude Code、GitHub Actions を同時に回すと Swap する?
する。burst / interactive / background に優先度がないと同時フル稼働になりやすい。§ 本当の問題と反例参照。
Ollama は常時起動が必要?
不要。 ローカル Inference は schedulable リソース:昼は nomic-embed-text のみ、大モデルは on-demand または夜間バッチ。
CI ビルド時に Ollama を止めるべき?
推奨。 30 秒 Runbook · CI 前 または ci-pre。
Claude Code と Ollama は同時実行できる?
できる。 コーディング主経路は API。本機で争うのはモデル重みと CI ピーク。CI 前 stop か時間帯 split。
Cloud Mac の OOM はメモリ不足?
観測では 多くの OOM は scheduling 問題。16GB 硬境界は L2-Q02 実測。
時間帯排班と memory_pressure 閾値は?
まず 30 秒 Runbook;安定後 memory-guard.sh(完全 Runbook)。
本稿と L2-Q01 の違いは?
Q01 は Inference Service の位置づけ;Q03(メモリ Scheduling 層)は同機 AI workload の scheduling。
30 秒で使えるコマンドはある?
ある。CI 前:ollama stop qwen3:8b && ollama stop qwen3:14b && sleep 30。昼:ollama run nomic-embed-text --keepalive 30m。夜:ollama run qwen3:8b。
Cloud Mac AI Stack · 明日予告
Claude Code + MCP:GitHub / ローカルファイル / API を一本のチェーンに
L2 の排班が固まったら次は L4 Context:MCP が GitHub・リポジトリ・API を Claude Code ワークフローへ接続する方法。
ブログへ · Cloud Mac AI Stack 全系列