Mac mini での AI ワークロード・スケジューリング:Ollama + Claude Code + GitHub Runner による Swap を防ぐ

L2-Q03 · メモリ Scheduling 層

2026.06.04  ·  約 14 分  ·  運用 Runbook(インストール手順なし)

Mac mini ユニファイドメモリと AI workload scheduling:Ollama・Claude Code・GitHub Actions 並行時に排班で Swap を回避

Mac mini / Cloud Mac 上で Ollama、Claude Code、GitHub Actions を同時に回すと、よくある問題は「性能不足」ではなく Swap の発生、CLI の遅延、CI ビルドの低下です。

本稿はツールのインストール手順ではありません。3 種類の AI workload が同時に存在するときにメモリが揺れる理由と、scheduling でそれを避ける方法を説明します。反例、メモリ予算、閾値 scheduling、30 秒 Runbook、完全スクリプトを示し、16GB vs 24GB benchmarkは繰り返しません。

TL;DR

  • Ollama 常駐 + CI burst → M4 で Swap は非常に一般的(反例参照)
  • 解は多くの場合 Mac のグレードアップではなく、burst / interactive / background に workload 排班を定義すること
  • CI 前の ollama stop が最重要かつ最も手軽(30 秒版

読み進めると AI workload scheduling モデルが見えてきます。観測した Cloud Mac では、多くの Swap/OOM は絶対メモリ不足より scheduling 問題に近いです。

AI
Workload Scheduling
30s
最小 Runbook
0
本稿の brew install

本当の問題:多くはメモリ不足ではなく優先度がない

表面は「ローカルモデル空転 + 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 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 秒版(多くの人はこの 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 cron night-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 の第一層設計です。

よくある質問

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 全系列
AI workload Cloud Mac 料金