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 빌드 저하입니다.

이 글은 도구 설치 튜토리얼이 아닙니다. 세 종류 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종 workload가 공존합니다(다음 절):

  • 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에서 유일하게 「kick 가능」한 레이어——덜 중요해서가 아니라 작업이 대부분 비동기·재시도 가능하기 때문입니다.

메모리 예산: 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, 코딩 중심
② 시간대 분리 09–18 코딩+CI / 22–06 야간 배치 24GB, 정기 embedding/로그
③ CI 트리거 양보 job 전 ollama stop, 종료 후 비동기 L2 push 잦음, xcodebuild 피크 높음

권장: ①+③ 전원 기본; ②는 야간 배치 보조; 임계 guard로 안전망.

Pipeline 분류: L2 vs 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 첫 step 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가 일주일 제로 호출 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 1층 설계입니다.

자주 묻는 질문

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 또는 시간 분리.

Cloud Mac OOM은 메모리 부족인가요?
관측상 대부분 OOM은 scheduling 문제. 16GB 한계는 L2-Q02 실측.

시간대 스케줄과 memory_pressure 임계값은?
먼저 30초 Runbook; 안정 후 memory-guard.sh(전체 Runbook).

이 글과 L2-Q01 차이는?
Q01은 Inference Service 위치; Q03(Memory 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 요금