OpenAI 호환 로컬 LLM을 위한 llama.swap 모델 스위처 빠른 시작
클라이언트를 변경하지 않고 로컬 LLM을 핫스왑합니다.
곧 vLLM, llama.cpp 등 여러 스택을 각각 다른 포트에서 돌리게 될 것입니다. 하지만 하위 시스템은 여전히 **/v1**이라는 단일 기본 URL 을 원합니다. 그렇지 않으면 포트, 프로필, 일회용 스크립트를 계속 조정해야 합니다. llama-swap은 이러한 스택들 앞에 세워지는 /v1 프록시입니다.
llama-swap은 하나의 OpenAI 및 Anthropic 호환 진입구를 제공하며, 각 model 이름을 올바른 업스트림을 시작하는 명령어로 매핑하는 YAML 파일을 사용합니다. 모델을 요청하면 프록시가 해당 모델을 시작하거나 교체합니다. VRAM 이 부족하거나 여러 모델이 공존해야 하는 경우 TTL 과 그룹을 구성할 수 있습니다. 이 가이드에서는 설치 방법, 실용적인 config.yaml, HTTP 인터페이스, 그리고 스트리밍과 역 프록시가 도입되면서 나타나는 다양한 장애 모드를 다룹니다.
LLM 호스팅 옵션에 대한 광범위한 비교는 LLM Hosting in 2026: Local, Self-Hosted & Cloud Infrastructure Compared 를 참조하세요.
OpenAI 호환 로컬 LLM API 를 위한 llama-swap 모델 스위처 개요
llama-swap 은 경량 프록시 서버로, 단순한 운영 모델인 하나의 바이너리, 하나의 YAML 설정 파일, 종속성 없음을 중심으로 구축되었습니다. Go 로 작성되어 다른 스택과 함께 단일 정적 바이너리로 배포되므로 Python 런타임이나 데스크톱 앱이 필요 없습니다. 이는 OpenAI 및 Anthropic 호환 업스트림 앞에 모델 교체 레이어로 위치합니다.
개념적으로 이는 로컬 LLM 스택에서 자주 제기되는 매우 실용적인 질문에 답합니다:
OpenAI 호환 클라이언트를 사용하여 모델을 어떻게 전환하나요?
llama-swap 을 사용하면 평소처럼 /v1/... 요청을 계속 사용하지만 요청할 model 을 변경하면 됩니다. llama-swap 은 해당 model 값을 읽어서 일치하는 서버 구성을 로드하고, 만약 잘못된 업스트림이 실행 중이면 이를 올바른 것으로 교체합니다.
프로덕션급 설정을 위해서는 몇 가지 설계 세부 사항이 중요합니다:
llama-swap 은 MIT 라이선스를 따르며 모니터링 (telemetry) 기능이 없습니다. 실제 프롬프트가 노출되는 모든 호스트에서 이를 확인하는 것이 여전히 중요합니다.
이는 llama.cpp, vLLM, Whisper, stable-diffusion.cpp와 같은 백엔드를 온디맨드로 로드하도록 설계되었으며, 단일 추론 엔진에 고정되지 않습니다.
기본 구성 (특별한 그룹화 없음) 에서는 한 번에 하나의 모델만 실행됩니다: 다른 model 을 요청하면 현재 업스트림을 중단하고 올바른 것을 시작합니다. 하나 이상의 상주 모델이나 공존에 대한 더 세밀한 제어가 필요한 경우 groups 를 구성하세요.
대부분의 개발자가 유용하게 생각하는 사고 모델은 다음과 같습니다:
flowchart LR
C[Your app or SDK\nOpenAI-compatible client] -->|/v1/chat/completions\nmodel = qwen-coder| LS[llama-swap proxy\nsingle endpoint]
LS -->|starts or routes to| U1[Upstream server A\nllama-server]
LS -->|starts or routes to| U2[Upstream server B\nvLLM OpenAI server]
LS --> M[Management endpoints\nrunning, unload, events, metrics]
이것이 모델 스위처 프록시가 “단순히 모델을 실행하는 것"과 다른 이유입니다: 이는 하나 이상의 추론 서버 위에 오케스트레이션과 라우팅을 제공합니다.
llama-swap vs Ollama vs LM Studio vs llama.cpp 서버
네 옵션 모두 “로컬 LLM API"를 제공할 수 있지만 최적화되는 워크플로우가 다릅니다. 선택하는 가장 빠른 방법은 런타임 (모델 다운로드 + 실행) 을 원하는지, 라우터/프록시 (런타임 간 전환 + 오케스트레이션) 를 원하는지 결정하는 것입니다.
llama-swap
llama-swap 은 OpenAI 호환 엔드포인트 ( /v1/chat/completions, /v1/completions, /v1/embeddings, /v1/models 포함) 를 지원하고 요청된 모델에 따라 올바른 업스트림으로 요청을 라우팅하는 투명한 프록시에 중점을 둡니다. 또한 /running, /logs/stream 과 같은 비추론 운영 엔드포인트 및 /ui 에서 웹 UI 를 제공합니다.
Ollama
Ollama 는 자체 HTTP API(POST /api/chat, POST /api/generate, 그리고 일반적인 로컬 기본 포트 11434) 를 노출합니다.
keep_alive는 모델이 로드된 상태로 유지되는 시간을 제어하며, 0을 설정하면 즉시 언로드됩니다.
이는 모델을 불러와서 채팅을 하고자 하는 사용자에게 적합합니다. llama-swap은 모델별 명령, 혼합된 백엔드, 모든 클라이언트를 위한 단일 OpenAI 형식 URL에 적합합니다. vLLM 을 llama-server 옆에 배치하고 모델마다 다른 플래그를 오케스트레이션하는 것은 Ollama 의 목표 범위를 벗어납니다.
LM Studio
LM Studio 는 Developer 탭에서 로컬 API 서버 (localhost 또는 LAN) 를 제공하는 데스크톱 앱으로, OpenAI 호환 및 Anthropic 호환 모드와 터미널에서 실행하는 lms server start를 포함합니다.
이는 GUI 우선 워크플로우에 적합합니다: 모델을 탐색하고 클릭하여 테스트합니다. llama-swap은 서버 스타일 역할에 적합합니다: YAML, 프로세스 감독, 혼합된 업스트림, 데스크톱 세션 불필요.
llama.cpp 서버
llama-server는 /v1/completions, /v1/chat/completions, /v1/responses를 노출하며, 일반적인 패턴은 base_url을 통해 OpenAI 클라이언트를 이를 가리키는 것입니다.
llama.cpp 는 라우터 모드도 제공합니다: llama-server를 라우터로 실행하고 --models-dir를 사용하여 POST /models/load 및 POST /models/unload를 통해 별도의 프록시 없이 GGUF 모델을 관리합니다.
모든 모델이 하나의 llama.cpp 라우터 아래에 있다면 추가 프록시는 종종 필요하지 않습니다. 그러나 llama.cpp 가 vLLM 이나 다른 OpenAI 형식 서버와 함께 실행되어야 할 때, llama-swap 은 하나의 /v1 표면과 그 뒤에 여러 프로세스를 제공합니다.
비슷한 OpenAI 호환 호스팅 솔루션에 대해서는 LocalAI QuickStart: Run OpenAI-Compatible LLMs Locally 또는 SGLang QuickStart: Install, Configure, and Serve LLMs via OpenAI API 를 참조하세요.
Docker, Homebrew, WinGet 또는 바이너리로 llama-swap 모델 스위처 설치
Linux, macOS, Windows 는 모두 일류 지원입니다: Docker, Homebrew, WinGet, GitHub 바이너리, 또는 소스 빌드. 일반적인 선택: 헤드리스 서버에서는 Docker, 워크스테이션에서는 Homebrew 또는 WinGet, 설치 footprint 를 최소화해야 할 때는 독립 실행형 바이너리.
Docker 설치
하드웨어에 맞는 이미지를 풀 (pull) 하세요. 이미지는 업스트림을 밀접하게 추적하며 (나이트리 빌드) CUDA, Vulkan, Intel, MUSA, CPU 를 커버합니다. 습관적으로 “최신"을 선택하지 말고 실제로 가속화하는 방식에 맞는 것을 선택하세요.
# Example platform pulls
docker pull ghcr.io/mostlygeek/llama-swap:cuda
docker pull ghcr.io/mostlygeek/llama-swap:vulkan
docker pull ghcr.io/mostlygeek/llama-swap:intel
docker pull ghcr.io/mostlygeek/llama-swap:musa
docker pull ghcr.io/mostlygeek/llama-swap:cpu
가능하면 비루트 이미지 변형을 선호하세요: 컨테이너 경계가 잘못 설정된 경우 후회할 일이 적습니다.
Homebrew 설치
macOS 와 Linux 에서 tap 을 추가하고 설치하세요:
brew tap mostlygeek/llama-swap
brew install llama-swap
llama-swap --config path/to/config.yaml --listen localhost:8080
WinGet 설치
Windows 에서:
winget install llama-swap
winget upgrade llama-swap
사전 빌드 바이너리 및 릴리스
패키지 관리자가 원하지 않는 경우 GitHub Releases 에서 Linux, macOS, Windows, FreeBSD 바이너리를 제공합니다.
릴리스 번호는 빠르게 변경됩니다 (예를 들어 v198, v197 등 2026 년 초 기준). 자동화에서는 “어제 있었던 것"을 띄지 않고 버전을 고정하세요.
모델 교체, TTL, 그룹을 위한 config.yaml 로 llama-swap 구성
llama-swap 의 모든 것은 구성에 의해 주도됩니다. 최소 생존 구성은 단순히 models: 사전과 각 모델에 대한 cmd이며, 종종 ${PORT}가 모델별로 대치되는 llama-server를 시작합니다.
구성 시스템은 단순히 “프로세스 시작"보다 훨씬 더 나아가며, 몇 가지 옵션은 일반적인 FAQ 스타일의 문제 (자동 언로드, 보안, /v1/models에 의존하는 클라이언트) 를 직접 해결하므로 초기에 이해할 가치가 있습니다.
실제로 사용할 글로벌 설정
healthCheckTimeout은 llama-swap 이 모델이 시작 후 정상 상태가 될 때까지 기다리는 시간입니다 (기본값 120 초, 최소 15 초). 느린 디스크에서 수 GB 의 부하가 걸리는 경우 프록시를 비난하기 전에 이를 늘리세요.
globalTTL은 자동 언로드 전의 유휴 초수입니다. 기본값 **0**은 “언로드하지 않음"을 의미하므로 설정하지 않는 한 유효합니다. 장난감 설정을 넘어 VRAM 이 잊혀진 모델로 채워지지 않도록 TTL 을 명시적으로 선택하세요.
startPort는 ${PORT} 매크로의 시드입니다 (기본값 5800). 할당은 알파벳 순서 모델 ID에 의해 결정론적이며, 이는 “누가 어떤 포트를 잡았는지” 디버깅 시에는 기능이지만 모델을 부주의하게 이름 변경 시에는 실수가 될 수 있습니다.
includeAliasesInList는 별칭이 /v1/models에서 별도의 행으로 표시되는지 결정합니다. UI 가 열거된 모델만 제공하는 경우 켜세요.
apiKeys는 로컬호스트에서 도달 가능한 모든 것을 제어합니다: Basic, Bearer, 또는 x-api-key. llama-swap 은 전달 전에 이러한 헤더를 제거하므로 업스트림 로그가 클라이언트 비밀을 유지할 가능성이 줄어듭니다.
프로덕션 사용성을 위한 모델 수준 설정
모델별로 cmd는 유일한 필수 필드입니다.
proxy는 기본값이 http://localhost:${PORT}이며, 이는 해당 모델의 업스트림에 대한 전달 대상입니다.
checkEndpoint는 기본값이 /health이며, 백엔드에 헬스 라우트가 없거나 콜드 스타트가 기다릴 수 있는 시간을 초과하는 경우 "none"으로 설정합니다. 작동하지 않는 /health를 두고 ready가 되지 않는 이유를 궁금해하지 마세요.
ttl: -1은 globalTTL을 상속하고, 0은 절대 언로드되지 않으며, N > 0은 N 초 유휴 후 언로드됩니다. 한 모델은 오래 유지되고 다른 모델은 빠르게 사라져야 하는 경우 모델별 TTL 을 사용하세요.
aliases와 useModelName은 안정된 클라이언트 노출 이름을 유지하면서 특정 식별자를 요구하는 업스트림을 만족시킵니다.
cmdStop은 컨테이너에 필수적입니다: docker stop(또는 동등한 명령) 으로 매핑하세요. 없으면 llama-swap 이 시작한 PID 에 대해 POSIX SIGTERM / Windows taskkill 이 실행되며, 이는 순수 바이너리에는 적합하지만 컨테이너 이름에는 적합하지 않습니다.
concurrencyLimit은 초과 시 HTTP 429 로 모델별 병렬 요청을 제한합니다. 대기열에 영원히 쌓이는 것을 원하지 않고 부하를 줄이고자 할 때 설정하세요.
groups는 공존 (swap, exclusive) 과 항상 실행 모델 (persistent) 을 다룹니다. 후크는 시작 시 사전 로딩할 수 있습니다. 그룹 없이 여러 모델을 동시에 사전 로딩하면 충돌이 예상되므로, 모델이 GPU 를 공유하는 방식을 정의하기 위해 먼저 그룹을 정의하세요.
llama.cpp 와 vLLM 을 위한 최소 config.yaml 예제
이 예제는 모범 사례를 보여주는 “적당한” 구성을 목표로 합니다: 기본 TTL, 명시적인 헬스 체크, 안정적인 별칭, 그리고 큰 채팅 모델이 교체되는 동안 작은 “항상 실행” 모델을 실행 중인 그룹.
# config.yaml
healthCheckTimeout: 180
globalTTL: 900 # 15 분 유휴 후 언로드
includeAliasesInList: true
startPort: 5800
# 로컬 개발을 넘어선 모든 경우 권장
apiKeys:
- "${env.LLAMASWAP_API_KEY}"
models:
llama-chat:
cmd: |
llama-server --port ${PORT} --model /models/llama-chat.gguf
--ctx-size 8192
aliases:
- "llama-chat-latest"
# 기본값 사용:
# proxy: http://localhost:${PORT}
# checkEndpoint: /health
# ttl: -1 (globalTTL 상속)
qwen-coder:
cmd: |
llama-server --port ${PORT} --model /models/qwen-coder.gguf
--ctx-size 8192
aliases:
- "qwen-coder-latest"
vllm-coder:
# 예시 패턴: 컨테이너화된 OpenAI 호환 서버 관리
proxy: "http://127.0.0.1:${PORT}"
cmd: |
docker run --name ${MODEL_ID} --init --rm -p ${PORT}:8000 vllm/vllm-openai:latest
cmdStop: docker stop ${MODEL_ID}
checkEndpoint: "none"
ttl: 0 # 자동 언로드 안 함 (예: GPU 에 유지)
groups:
chat-models:
swap: true
exclusive: true
members: ["llama-chat", "qwen-coder"]
always-on:
persistent: true
swap: false
exclusive: false
members: ["vllm-coder"]
이 중 장식적인 것은 없습니다: cmd가 프로세스를 구동하고, proxy/checkEndpoint/ttl이 라우팅과 수명 주기를 제어하며, cmdStop은 Docker 기반 업스트림을 실제로 중지시키는 것이며, groups는 “한 번에 하나의 큰 모델"과 “두 개의 채팅 모델이 공존하면서 임베딩 서버가 고정됨"을 구분합니다.
OpenAI 호환 엔드포인트를 통해 모델 실행 및 교체
llama-swap 이 실행되면 다른 OpenAI 호환 엔드포인트와 마찬가지로 상호작용합니다. API 표면에는 /v1/chat/completions, /v1/completions, /v1/embeddings, /v1/models와 같은 핵심 엔드포인트가 포함되며, llama-swap 은 요청된 model을 사용하여 실행하고 라우팅할 업스트림을 결정합니다.
실용적인 빠른 시작 흐름:
# 1) llama-swap 시작
llama-swap --config ./config.yaml --listen localhost:8080
# 2) 사용 가능한 모델 확인
curl http://localhost:8080/v1/models
모델 목록은 일류 관리 기능이며 ID 로 정렬, unlisted 모델 제외, 선택적 별칭 포함 등의 행위를 포함합니다.
# 3) 특정 모델에 대한 채팅 완료 요청
curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${LLAMASWAP_API_KEY}" \
-d '{
"model": "qwen-coder",
"messages": [{"role":"user","content":"Write a TypeScript function that retries fetch with backoff."}]
}'
이제 "model": "llama-chat"로 호출을 반복하면 llama-swap 은 업스트림 프로세스를 교체합니다 (그룹 구성이 공존을 허용하지 않는 경우). 이는 요청에서 요청된 모델을 추출하고 적절한 서버 구성을 로드하기 때문입니다.
SDK 를 사용하는 경우 클라이언트를 http://localhost:8080/v1로 가리키세요—llama-server를 겨냥하는 OpenAI Python 라이브러리와 같은 트릭이지만, 안정적인 URL 은 이제 llama-swap 이며 model 필드가 업스트림을 선택합니다.
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8080/v1",
api_key="sk-your-llamaswap-key"
)
resp = client.chat.completions.create(
model="qwen-coder",
messages=[{"role": "user", "content": "Explain the difference between mutexes and semaphores."}],
)
print(resp.choices[0].message.content)
첫 번째 실제 요청 전에 모델을 예열하여 (콜드 스타트 대기 시간을 숨김) /upstream/<model>을 사용하세요—필요 시 자동 로드하고 바로 해당 업스트림으로 전달합니다. 벤치마크나 스크립트 테스트 전에 가중치가 상주하도록 보장하는 간단한 방법입니다.
관리 API 엔드포인트 및 SSE 이벤트를 통한 llama-swap 제어 및 모니터링
llama-swap 은 단순히 “프록시"가 아닙니다: 모델 수명 주기 및 관찰을 위한 도구를 구축할 수 있는 운영 제어 엔드포인트도 노출합니다.
실행 중인 항목 확인
GET /running은 로드된 모델의 런타임 상태를 반환하며 ready, starting, stopping, stopped, shutdown과 같은 상태 값을 포함합니다.
curl http://localhost:8080/running
VRAM 을 확보하기 위해 모델 언로드
모든 것을 즉시 언로드하려면 API 버전된 엔드포인트 POST /api/models/unload를 사용하세요. 단일 모델 (ID 또는 별칭) 을 언로드하려면 POST /api/models/unload/<model>을 사용하세요. 호환성을 위한 레거시 GET /unload도 존재합니다.
# 모두 언로드
curl -X POST http://localhost:8080/api/models/unload
# 하나의 모델 언로드
curl -X POST http://localhost:8080/api/models/unload/qwen-coder
TTL 을 기다리지 않고 지금 VRAM 이 필요한 경우 (벤치마크, 빠른 모델 전환, 또는 의도보다 훨씬 큰 체크포인트를 로드한 후) 이러한 엔드포인트를 사용하세요.
SSE 를 통해 실시간 이벤트 스트리밍
GET /api/events는 Server-Sent Events 스트림을 설정하며 모델 상태 변경, 로그, 메트릭, 진행 중인 요청 수를 포함하는 실시간 업데이트를 위해 설계되었습니다.
curl -N http://localhost:8080/api/events
SSE 와 토큰 스트리밍은 중간 상자가 버퍼링할 경우 끊어집니다—nginx(또는 동등한 것) 에서 /api/events 및 /v1/chat/completions에 대해 버퍼링을 비활성화하세요. llama-swap 은 SSE 에서 X-Accel-Buffering: no를 설정하지만, 헤더만으로는 올바른 프록시 구성을 대체할 수 없으므로 프록시에서도 버퍼링을 비활성화해야 합니다.
메트릭 및 요청 캡처
GET /api/metrics는 토큰 사용량 메트릭을 반환하며, metricsMaxInMemory(기본값 1000) 로 메모리 내 보관이 제어됩니다.
GET /api/captures/<id>는 captureBuffer > 0이 구성되었을 때만 전체 요청/응답 캡처를 검색할 수 있습니다.
로그 및 웹 UI
llama-swap 은 웹 인터페이스를 위한 /ui와 실시간 모니터링을 위한 /logs 및 /logs/stream과 같은 운영 로그 엔드포인트를 노출합니다.

apiKeys를 활성화했다면 심층 방어를 가정하세요: /health와 /ui의 일부는 키 없이도 접근 가능합니다—로컬 신뢰 경계에서는 괜찮지만, 공유 네트워크에 호스트가 있는 경우 안 좋습니다. llama-swap 을 실제 정책을 강제하는 것 뒤에 배치하세요. 내장 인증은 경량 클라이언트를 정직하게 유지하기 위한 것이며, 공개 다중 테넌트 API 를 위한 것이 아닙니다.
프로덕션에서 llama-swap 모델 전환 문제 해결
대부분의 llama-swap 문제는 몇 가지 운영 범주로 나뉩니다: 역 프록시 뒤의 스트리밍, 콜드 스타트 동안의 헬스 체크, 포트 및 프로세스 수명 주기, 인증.
nginx 또는 다른 역 프록시 뒤에 스트리밍이 끊어짐
nginx 는 SSE 와 스트림된 완료를 기꺼이 버퍼링합니다. /api/events 및 /v1/chat/completions에 대해 proxy_buffering(및 proxy_cache) 을 비활성화하세요. llama-swap 은 SSE 에서 X-Accel-Buffering: no를 내보내어 도움이 되지만—어쨌든 프록시를 수정하세요.
모델이 준비되지 않음
기본값으로 모델별 checkEndpoint는 /health이며 프로세스가 준비됨으로 간주되려면 HTTP 200 을 반환해야 합니다. checkEndpoint를 다른 경로로 설정하거나 헬스 체크를 완전히 비활성화하려면 "none"으로 설정하세요.
큰 모델이 로드하는 데 더 오래 걸린다면 healthCheckTimeout(기본값 120 초) 를 증가시키거나 특정 업스트림에 맞춤 헬스 체크를 사용하세요.
모델 전환 시 오래된 컨테이너가 실행 중임
업스트림이 Docker 또는 Podman 인 경우 cmdStop을 설정하세요. 그렇지 않으면 llama-swap 이 래퍼 프로세스를 중지하지만 컨테이너는 배경에서 VRAM 을 계속 사용합니다.
보안 활성화 후 401 응답 수신
apiKeys가 구성되면 llama-swap 은 유효한 키를 요구하며 세 가지 방법 (Basic 인증, Bearer 토큰, x-api-key) 을 수용합니다. 또한 업스트림으로 전달 전에 인증 헤더를 제거합니다.
429 Too Many Requests 수신
concurrencyLimit은 초과 시 429 를 반환합니다—설계상입니다. 프로비저닝이 부족했다면 상한을 높이거나, 스로틀링을 의도하지 않았다면 제한을 낮추세요.
포트 충돌 또는 이상한 라우팅 문제
cmd에서 하드코딩된 포트를 피하고 ${PORT}를 사용하며 5800+가 다른 것과 충돌하면 startPort를 이동하세요. 포트는 모델 ID 의 알파벳 순서로 할당된다는 것을 기억하세요—모델 이름을 변경하면 포트 매핑이 이동합니다.
운영 디버깅 체크리스트
진실을 위해 /running, 시작이 불투명할 때 /logs/stream, VRAM 이 지금 필요할 때 POST /api/models/unload를 사용하세요. 이 세 가지는 대부분의 “왜 GPU 가 꽉 찼나요” 세션을 커버합니다.