로컬 LLM을 24시간 돌리는 사람이라면 한 번쯤 새벽에 Ollama가 조용히 죽어있는 걸 발견한 적 있을 것이다. 이 글은 M2 Pro Mac Mini 클러스터에서 프로세스 다운을 자동 감지하고 10초 안에 복구하는 시스템을 구축한 경험을 정리한 것이다. 셸 스크립트의 한계부터 n8n 워크플로우 기반 자가 치유 설계까지, 실패한 접근법을 포함해 모두 풀어본다.
문제 정의 — 로컬 AI 서버는 왜 이렇게 자주 죽나
Mac Mini에서 Ollama, Draw Things, llama.cpp 같은 로컬 LLM 서버를 상시 운용하면 프로세스 다운은 예외가 아니라 일상이다. 주요 원인만 꼽아도 이렇다.
| 원인 | 빈도 | 증상 |
|---|---|---|
| 메모리 초과 (OOM) | 매우 높음 | 프로세스 silently 종료 |
| 모델 파일 I/O 오류 | 중간 | 로딩 중 hang |
| GPU 컨텍스트 충돌 | 낮음 | SIGKILL |
| macOS 업데이트 후 경로 변경 | 드묾 | 실행 파일 찾기 실패 |
문제는 이걸 내가 직접 발견할 때까지 서비스가 통째로 멈춘다는 점이다. 새벽 2시에 n8n 자동화 파이프라인이 Ollama API를 호출하다 실패해서 전체 워크플로우가 멈춰있는 걸 아침에 발견한 적이 한두 번이 아니다.
첫 번째 시도는 당연히 셸 스크립트였다.
#!/bin/bash
# 나쁜 예시 — 절대 이렇게 하지 말 것
while true
do
/Applications/Ollama.app/Contents/MacOS/Ollama serve
echo "Ollama 프로세스 종료. 5초 후 재시작..."
sleep 5
done
이 코드의 문제는 단순하다. 종료 코드가 0이든 1이든 137(OOM kill)이든 무조건 재시작한다. 모델 파일이 손상된 경우라면 재시작해봤자 또 죽는다. 좀비 프로세스가 누적되거나 시스템 전체가 응답 불능에 빠지는 건 시간 문제다. "묻지마 재시작"은 해결책이 아니라 또 다른 문제다.
해결책 — 시스템에 면역 반응 심기
핵심 인사이트는 이거다. 재시작 자체가 목적이 아니라, 상태를 판단한 뒤 조건부로 개입하는 것이 목적이다. 의사가 환자를 보지도 않고 약을 처방하지 않듯, 시스템도 진단 없이 처방을 내리면 안 된다.
n8n을 이 "면역 체계"의 두뇌로 사용했다. 구조는 세 단계다.
1단계: 헬스체크 (진단)
// n8n HTTP Request Node 설정
{
"method": "GET",
"url": "http://localhost:11434",
"options": {
"continueOnFail": true,
"timeout": 5000
}
}
continueOnFail: true가 핵심이다. 이걸 켜야 실패했을 때 에러로 워크플로우가 멈추지 않고 "실패" 분기로 흘러간다.
2단계: 에러 분류 (판단)
모든 실패를 동일하게 취급하지 않는다. n8n의 IF 노드로 에러 종류를 분기한다.
// n8n Code Node — 에러 분류 로직
const statusCode = $input.first().json.statusCode;
const errorType = $input.first().json.error;
if (statusCode === 503) {
// 서버는 살아있지만 모델 로딩 중
return [{ json: { action: "wait", reason: "model_loading" } }];
}
if (!statusCode || errorType === "ECONNREFUSED") {
// 프로세스 자체가 죽은 경우
return [{ json: { action: "restart", reason: "process_down" } }];
}
// 그 외 알 수 없는 에러
return [{ json: { action: "alert", reason: "unknown_error" } }];
3단계: 조건부 재시작 (처방)
action === "restart" 일 때만 SSH 노드로 재시작 명령을 내린다.
# n8n SSH Node에서 실행하는 명령
pkill -f "Ollama" 2>/dev/null || true
sleep 2
/Applications/Ollama.app/Contents/MacOS/Ollama serve > /tmp/ollama.log 2>&1 &
# 재시작 후 10초 대기 후 재검증
sleep 10
curl -s http://localhost:11434 && echo "RESTART_OK" || echo "RESTART_FAILED"
재시작 후 반드시 재검증 단계를 넣는다. 재시작 명령을 내렸다고 끝이 아니다. 실제로 다시 살아났는지 확인하고, 그래도 실패하면 긴급 알림을 보낸다.
운영 팁 — 함정과 변형
재시작 쿨다운 필수
재시작이 반복 실패할 때 무한으로 시도하면 안 된다. n8n의 Static Data를 활용해 최근 재시작 시각을 기록하고, 5분 이내 재시작이 3회 이상이면 자동화를 멈추고 사람에게 알린다.
// n8n Code Node — 재시작 쿨다운 체크
const restartHistory = $getWorkflowStaticData('global');
const now = Date.now();
const recentRestarts = (restartHistory.timestamps || [])
.filter(t => now - t < 5 * 60 * 1000);
if (recentRestarts.length >= 3) {
// 사람에게 넘기고 자동화 중단
return [{ json: { action: "escalate" } }];
}
restartHistory.timestamps = [...recentRestarts, now];
환경별 차이
| 환경 | 주의사항 |
|---|---|
| macOS + launchd | launchctl kickstart 사용이 더 안전할 수 있음 |
| Docker | docker restart <container> 명령 사용 |
| Linux systemd | systemctl restart ollama |
macOS에서 직접 바이너리를 죽이고 재실행하는 방식은 launchd 관리 대상 프로세스라면 충돌이 생길 수 있다. 내 환경은 launchd 없이 직접 실행하는 방식이라 문제없었지만, launchd plist로 등록해둔 경우라면 launchctl을 통해 재시작해야 한다.
로그 적재
재시작 이력을 단순히 로그 파일에만 쌓으면 나중에 분석하기 어렵다. n8n HTTP Request로 간단한 webhook이나 Notion/Supabase에 구조화된 형태로 적재하면 나중에 "이 시간대에 왜 자주 죽었나"를 추적할 수 있다.
실측 결과
M2 Pro Mac Mini 두 대로 구성된 클러스터에서 2주간 운용한 결과다.
| 지표 | 수동 대응 | n8n 자가 치유 |
|---|---|---|
| 평균 장애 감지 시간 | 약 180분 (사람이 발견할 때까지) | 약 1분 (스케줄 주기) |
| 평균 복구 시간 | 3분 (발견 후 재시작) | 10초 |
| 총 장애 대응 시간 단축 | — | 99% 이상 |
숫자보다 중요한 건 "내가 자고 있는 동안에도 시스템이 스스로 돌아온다"는 사실이다. 이건 단순한 편의 기능이 아니라, 24시간 자동화 파이프라인의 신뢰도를 높이는 구조적 변화다.
스크립트를 짜는 것과 시스템을 설계하는 것은 다르다. 재시작 한 줄이 아니라, 진단 → 판단 → 처방 → 검증의 흐름이 자동화되어야 비로소 "자가 치유"라고 부를 수 있다.
다음 글에서는 이 헬스체크 시스템을 Ollama 단일 프로세스가 아니라 클러스터 전체로 확장하고, 각 노드의 GPU 메모리 사용량까지 모니터링하는 방법을 다룬다.
🐦 X에서 더 빠르게: @baegseungh7061
📚 이 시리즈 더 보기: Code 실전
💌 새 글 알림: X 팔로우 또는 블로그 RSS 구독
'Code 실전' 카테고리의 다른 글
| Claude Code Pre/Post 훅을 이벤트 버스로 연결해 비동기 파이프라인 구성하기 (0) | 2026.05.09 |
|---|---|
| Claude Code 실행 이력을 JSON으로 수집해 이상 탐지 자동화하기 (0) | 2026.05.08 |
| MCP 서버 내부 상태를 실시간으로 들여다보는 법 — 스냅샷 엔드포인트로 툴 호출 흐름 시각화하기 (0) | 2026.05.07 |
| Claude Code Hooks 완전 분석 — 8종 이벤트로 AI 실행을 통제하는 법 (0) | 2026.05.01 |
| Claude Code로 자율 검증 파이프라인 구축하기 — Plan-Execute-Verify 루프 실전 적용 (0) | 2026.04.29 |