llama.cpp 라우터 모델 재시작 없이 전체 언로드

llama-server를 종료하지 않고도 VRAM을 확보하는 방법

Page content

llama.cpp 라우터 모드는 수년 동안 llama-server에 도입된 변화 중 가장 유용한 변화 중 하나입니다. 이는 로컬 LLM 운영자에게 Ollama에서 기대하는 모델 관리 경험에 가까운 기능을 제공하면서도, llama.cpp를 처음부터 사용하게 만드는 원시 성능과 저레벨 제어를 그대로 유지합니다.

하지만 한 가지 날카로운 부분이 있습니다. 모든 것을 언로드하는 것은 HTTP API에서 하나의 마법 버튼으로 제공되지 않습니다.

라우터는 모델을 목록으로 표시할 수 있습니다. 모델을 로드할 수 있습니다. 모델을 언로드할 수 있습니다. --models-max에 도달했을 때 가장 최근에 사용되지 않은 모델을 추방할 수 있습니다. 현재 1급 엔드포인트로 문서화되어 있지 않은 것은 ‘지금 모든 모델을 언로드’하는 범용 호출입니다.

laptop and desktop with llms

이는 실제적인 방해 요인이 아닙니다. 올바른 패턴은 단순하고 명시적이며 스크립팅 가능합니다:

  1. 라우터에 어떤 모델이 존재하는지 요청합니다.
  2. 상태가 loaded(로드됨)인 모델을 필터링합니다.
  3. 로드된 모델 각각에 대해 /models/unload를 한 번 호출합니다.

이것이 제가 진지한 로컬 LLM 워크플로우를 위해 추천하는 접근 방식입니다. 지루하지만 가시성이 높으며 디버깅하기 쉽습니다. 이는 추론 서비스를 완전히 재시작하지 않고 VRAM을 확보하는 것이 목표일 때 정확히 원하는 것입니다.

llama.cpp 라우터 모드가 실제로 수행하는 작업

고전적인 llama-server 사용법에서는 하나의 서버를 하나의 모델과 함께 시작합니다:

llama-server \
  --model ./models/qwen3-8b.gguf \
  --port 8080

라우터 모드는 이러한 모델을 변경합니다. 서버를 하나의 GGUF 파일에 바인딩하는 대신, 라우터는 여러 모델의 조정자 역할을 합니다. 캐시 또는 모델 디렉토리에서 모델을 발견하고, 필요에 따라 로드하며, 요청을 올바른 모델로 라우팅하고, 필요할 때 모델을 언로드할 수 있습니다.

일반적인 라우터 모드 시작 명령은 다음과 같습니다:

llama-server \
  --models-dir ./models \
  --models-max 4 \
  --port 8080

여기서 중요한 옵션은 --models-max입니다. 이는 동시에 로드할 수 있는 모델의 수를 제어합니다. 제한에 도달하면 llama.cpp는 가장 최근에 사용되지 않은 모델을 추방할 수 있습니다. 이는 유용하지만, 의도적인 언로드 작업을 대체하는 것은 아닙니다. LRU 추방은 반응적입니다. 언로드 스크립트는 운영 제어입니다.

저의 편향된 견해: 실제 작업을 위해 로컬 모델을 실행한다면, 라우터 모드를 장난감 채팅 서버가 아니라 추론 프로세스 관리자처럼 취급해야 합니다. 명시적인 라이프사이클 작업이 중요합니다.

필요한 모델 관리 엔드포인트

탐색을 위한 주요 엔드포인트는 다음과 같습니다:

curl -s http://localhost:8080/models | jq

이 엔드포인트는 라우터가 알고 있는 모델과 그들의 현재 라이프사이클 상태를 반환합니다. JSON 형식은 빌드마다 약간 다를 수 있으므로, 자동화를 작성하기 전에 자신의 응답을 검사하십시오.

일반적인 응답 형식은 다음과 같습니다:

{
  "data": [
    {
      "id": "qwen3-8b",
      "status": "loaded"
    },
    {
      "id": "llama-3.2-3b",
      "status": "unloaded"
    }
  ]
}

하나의 모델을 언로드하려면 다음을 호출합니다:

curl -s -X POST http://localhost:8080/models/unload \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen3-8b"}' \
  | jq

이것이 기본 작업입니다. 이 기사의 모든 것이 이를 기반으로 합니다.

문서화된 ‘모든 모델 언로드’ 엔드포인트는 없습니다

이 부분이 사람들을 헷갈리게 하는 부분입니다.

다음과 같은 것을 기대할 수 있습니다:

curl -X POST http://localhost:8080/models/unload-all

그 가정 주변에 구축하지 마십시오. 문서화된 작업은 모델별입니다. /models/unload에 모델 식별자를 전달하면, llama.cpp는 그 하나의 모델을 언로드합니다.

이것은 반드시 나쁜 API 설계는 아닙니다. 모델별 작업은 더 안전합니다. 호출자가 무엇을 언로드해야 할지 결정하게 합니다. 또한 다른 클라이언트가 사용 중인 모든 워밍업된 모델을 우연히 죽이는 놀라운 프로덕션 동작을 방지합니다.

워킹스테이션에서는 unload-all 단축키가 편리할 수 있습니다. 다중 사용자 추론 박스에서는 명시적인 루프가 더 좋습니다.

먼저 하나의 모델 언로드

무엇을 자동화하기 전에, 라우터가 기대하는 정확한 모델 식별자를 테스트하십시오.

먼저 모델을 나열합니다:

curl -s http://localhost:8080/models | jq

출력에서 로드된 모델 하나를 선택한 다음, 이를 언로드합니다:

curl -s -X POST http://localhost:8080/models/unload \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen3-8b"}' \
  | jq

모델 목록을 다시 확인합니다:

curl -s http://localhost:8080/models | jq

모델 상태가 unloaded(언로드됨)로 변경되면, 엔드포인트, 포트 및 모델 식별자가 올바릅니다.

작동하지 않으면 추측하지 마십시오. JSON을 검사하십시오. 라우터 별칭, GGUF 파일명 및 모델 ID는 종종 동일한 문자열이 아닙니다.

curl과 jq를 사용하여 모든 로드된 모델 언로드

단일 모델 언로드가 작동하면, 모든 모델 언로드 패턴은 단순한 셸 루프입니다.

/models 응답에 .data[].id.data[].status가 있을 때 다음을 사용하십시오:

curl -s http://localhost:8080/models \
| jq -r '.data[] | select(.status == "loaded") | .id' \
| while IFS= read -r model; do
    echo "Unloading: $model"
    curl -s -X POST http://localhost:8080/models/unload \
      -H "Content-Type: application/json" \
      -d "{\"model\":\"$model\"}" \
      | jq
  done

이것이 전체 요령입니다. 화려하지는 않지만, 관리 작업에 적합한 형태입니다:

  • 실제로 로드된 모델만 언로드합니다.
  • 무엇을 하는지 출력합니다.
  • 모든 것을 하나의 불투명한 작업 뒤에서 숨기는 대신 모델별로 실패합니다.
  • cron, systemd 훅, SSH 또는 CI 작업에서 작동합니다.

프로덕션 사용을 위한 재사용 가능한 스크립트

두 번 이상 실행하는任何事情에 대해서는 원라인 명령어를 붙여넣는 것을 멈추십시오. 스크립트를 저장하십시오.

llama-router-unload-all.sh를 생성합니다:

#!/usr/bin/env bash
set -euo pipefail

LLAMA_SERVER_URL="${LLAMA_SERVER_URL:-http://localhost:8080}"

models_json="$(curl -fsS "$LLAMA_SERVER_URL/models")"

loaded_models="$(printf '%s' "$models_json" \
  | jq -r '.data[] | select(.status == "loaded") | .id')"

if [ -z "$loaded_models" ]; then
  echo "No loaded models found."
  exit 0
fi

printf '%s\n' "$loaded_models" | while IFS= read -r model; do
  [ -z "$model" ] && continue

  echo "Unloading: $model"

  curl -fsS -X POST "$LLAMA_SERVER_URL/models/unload" \
    -H "Content-Type: application/json" \
    -d "{\"model\":\"$model\"}" \
    | jq

done

echo "Done. Current model state:"
curl -fsS "$LLAMA_SERVER_URL/models" | jq

실행 가능하게 만듭니다:

chmod +x llama-router-unload-all.sh

기본 로컬 서버에 대해 실행합니다:

./llama-router-unload-all.sh

다른 호스트에 대해 실행합니다:

LLAMA_SERVER_URL=http://192.168.1.50:8080 ./llama-router-unload-all.sh

이것이 도구 디렉토리에 실제로 보관할 버전입니다. HTTP 오류가 스크립트를 실패하게 하는 curl -f를 사용하며, 파일을 편집하지 않고 서버 URL을 재정의할 수 있게 합니다.

JSON 형식에 스크립트 적응

매우 llama.cpp 빌드가 영원히 완전히 동일한 필드를 반환한다고 맹목적으로 가정하지 마십시오. 라우터 모드는 아직 진화 중이며, 빌드는 약간 다른 JSON 형식을 노출할 수 있습니다.

응답을 검사하는 것으로 시작하십시오:

curl -s http://localhost:8080/models | jq

스크립트는 이 필터를 사용합니다:

jq -r '.data[] | select(.status == "loaded") | .id'

모델 식별자가 .name에 있으면 다음으로 변경합니다:

jq -r '.data[] | select(.status == "loaded") | .name'

상태 필드가 다른 값을 사용하는 경우, accordingly 필터를 조정하십시오. 중요한 것은 원칙입니다: 로드된 모델을 선택하고, /models/unload가 수락하는 식별자를 추출한 다음, 각 모델에 대해 언로드를 호출합니다.

언로드 후 모델이 다시 로드되는 이유

이것이 가장 흔한 혼란의 원인입니다.

라우터 모드는 온디맨드 로딩을 지원합니다. 현재 언로드된 모델에 대한 채팅 완성 요청을 클라이언트가 보내면, 라우터는 이를 자동으로 다시 로드할 수 있습니다.

즉, 다음과 같은 시퀀스가 가능합니다:

  1. 모든 모델을 언로드합니다.
  2. Open WebUI, 테스트 스크립트 또는 에이전트가 요청을 보냅니다.
  3. llama.cpp가 요청된 모델을 다시 로드합니다.
  4. 언로드가 실패했다고 생각하지만, 실패하지 않았습니다.

해결책은 기술적이 아니라 운영적입니다. VRAM을 확보하는 것이 목표라면 먼저 클라이언트 트래픽을 중지하십시오.

예를 들어:

  • 벤치마크 스크립트를 중지합니다.
  • 에이전트 및 cron 작업을 일시 중지합니다.
  • Open WebUI 세션을 닫거나 연결을 끊습니다.
  • 우연히 실제 모델 요청을 수행하는 헬스 체크를 비활성화합니다.

언로드는 방화벽이 아닙니다. 클라이언트가 계속해서 모델을 요청하면, 라우터 모드는 그들을 제공함으로써 자신의 역할을 수행하고 있습니다.

Open WebUI 및 Eject 버튼

Open WebUI는 llama.cpp 모델 언로드 지원과 통합될 수 있습니다. 제공자가 llama.cpp로 구성되면, Open WebUI는 로드된 모델 상태를 표시하고 관리자에게 Eject(이젝트) 작업을 노출할 수 있습니다.

내부적으로, 이 작업은 Open WebUI의 자체 언로드 API를 호출하며, 이는 구성된 연결에서 llama.cpp의 /models/unload 엔드포인트를 호출합니다.

이는 수동 작업에 좋지만, 저는 여전히 셸 스크립트를 유지할 것입니다. UI 버튼은 편리합니다. 스크립트는 감사 가능하고, 반복 가능하며, 새벽 2시 헤드리스 박스에서 사용 가능합니다.

모든 모델 언로드를 사용해야 할 때

모든 로드된 모델을 언로드하는 것은 다음과 같은 경우 유용합니다:

  • 더 큰 모델을 시작하기 전에 GPU 메모리를 확보할 때.
  • llama-server를 재시작하지 않고 개발 박스를 리셋할 때.
  • 깨끗한 메모리 상태로 벤치마크 실행을 준비할 때.
  • 유지보수 전에 로컬 추론 워크로드를 배출할 때.
  • 너무 많은 모델이 워밍업된 지저분한 세션에서 복구할 때.

활성 사용자가 워밍업된 모델에 의존하고 있을 때는 올바른 도구가 아닙니다. 그 경우, --models-max를 튜닝하고, 의도적인 라우팅을 사용하며, LRU 추방이 작업의 일부를 수행하도록 합니다. 모델별 라이프사이클 제어가 포함된 더 스마트한 타임아웃 기반 언로딩이 필요하다면, llama-swap은 정확히 이를 모든 llama-server 설정 위에 레이어하는 목적 구축 프록시입니다.

저의 규칙은 간단합니다: 정상적인 압력에는 LRU를 사용하고, 운영자의 의도에는 명시적인 언로드를 사용합니다.

문제 해결

모델 엔드포인트가 404를 반환합니다

라우터 기능이 있는 빌드를 실행하지 않거나, 잘못된 포트를 호출하고 있을 수 있습니다.

서버 프로세스 및 사용 가능한 옵션을 확인합니다:

llama-server --help | grep -i models

그 다음 두 엔드포인트를 테스트합니다:

curl -s http://localhost:8080/models | jq
curl -s http://localhost:8080/v1/models | jq

/v1/models 엔드포인트는 OpenAI 호환 모델 목록입니다. /models 엔드포인트는 라우터 모델 관리 엔드포인트입니다. 이들은 관련이 있지만, 동일한 것이 아닙니다.

jq가 설치되지 않았습니다

JSON 파싱을 스크립팅하기 전에 설치하십시오.

Ubuntu 또는 Debian에서:

sudo apt-get update
sudo apt-get install jq

Homebrew가 있는 macOS에서:

brew install jq

언로드 호출이 오류를 반환합니다

대부분의 실패는 잘못된 모델 식별자를 전달하는 것에서 비롯됩니다. 파일명이 아닌 /models에서 반환된 정확한 식별자를 사용하십시오.

또한 모델 이름에 따옴표, 슬래시 또는 공백이 포함되어 있는지 확인하십시오. 위의 스크립트는 일반적인 문자열을 잘 처리하지만, 비정상적인 이름은 더 신중한 JSON 구성이 필요할 수 있습니다.

최대 안전을 위해 jq로 POST 본문을 구축할 수 있습니다:

jq -n --arg model "$model" '{model: $model}'

더 방어적인 언로드 루프는 손으로 이스케이프된 JSON 대신 그 본문을 사용할 것입니다.

VRAM이 즉시 해제되지 않습니다

먼저 모델 상태가 변경되었는지 확인합니다. 그런 다음 다른 요청이 이를 다시 로드했는지 확인합니다. 또한 GPU 메모리 도구가 지연되거나 즉시 애플리케이션 레벨 의도보다 할당자 동작을 보고할 수 있음을 기억하십시오.

실용적인 테스트는 간단합니다: 트래픽을 중지하고, 모델을 언로드하고, 모델 상태를 나열한 다음, GPU 메모리를 검사합니다. llama.cpp에서 모델 크기 및 컨텍스트 창에 걸쳐 측정된 VRAM 사용량을 위해, 16GB VRAM llama.cpp 벤치마크는 상식적인 검사를 위한 구체적인 수치를 제공합니다.

더 안전한 JSON 본문 버전

모델 식별자에 비정상적인 문자가 포함되어 있으면, jq를 사용하여 JSON 요청 본문을 생성하십시오:

curl -s http://localhost:8080/models \
| jq -r '.data[] | select(.status == "loaded") | .id' \
| while IFS= read -r model; do
    echo "Unloading: $model"
    body="$(jq -n --arg model "$model" '{model: $model}')"
    curl -s -X POST http://localhost:8080/models/unload \
      -H "Content-Type: application/json" \
      -d "$body" \
      | jq
  done

모델이 저장소 스타일 식별자, 사용자 정의 별칭 또는 경로로 명명된 경우 이 버전을 사용하십시오.

최종 결론

llama.cpp 라우터 모드는 로컬 LLM 운영을 위한 큰 진전입니다. 이는 llama-server의 직접성을 포기하지 않고 동적 로딩, 모델 전환 및 메모리 인식 추방을 제공합니다.

하지만 완벽한 모든 모델 언로드 엔드포인트를 기다리지 마십시오. 깨끗한 해결책은 이미 존재합니다: 로드된 모델을 나열하고 하나씩 언로드합니다.

이 패턴은 명시적입니다. 스크립팅 가능합니다. SSH를 통해 작동합니다. Open WebUI와 잘 호환됩니다. 그리고 가장 importantly, 라우터를 재시작하지 않고 VRAM을 해제합니다.

로컬 AI 인프라에 있어서, 이것은 당신이 원하는 지루한 제어 표면의 종류입니다.

구독하기

시스템, 인프라, AI 엔지니어링에 관한 새 글을 받아보세요.