로컬에서 LLM 실행하기: llama.cpp vs Ollama vs vLLM — 세 진영 thumbnail
비교2026년 7월 2일AI 인프라

로컬에서 LLM 실행하기: llama.cpp vs Ollama vs vLLM — 세 진영

오픈 모델을 자체 호스팅하는 도구는 어느 계층을 직접 운영하느냐에 따라 세 진영으로 나뉜다. llama.cpp는 원시 엔진이다. GGUF를 불러오고 가지고 있는 어떤 하드웨어로든 레이어를 오프로딩하는 독립 실행형 바이너리다. Ollama는 이를 감싸는 친근한 관리자다. vLLM은 이를 대체하는 처리량 서버다. 이 세 프로젝트의 소스를 모두 읽고 이식성, 편의성, 처리량을 비교했다.

ArcYou Editorial. 방법론 섹션에 고정된 저장소 상태를 기준으로 읽었습니다.

결정 요약

이 소스 리드가 분명하게 하는 것

실제로 무엇인가
로컬 LLM 런타임을 운영 계층으로 가르는 비교 분석
잘 맞는 경우
개발자 머신, 온프레미스 서버, GPU 서버 각각에 어떤 런타임이 맞는지 정하는 팀
피할 경우
클라우드 API만 쓰고 로컬 모델을 운영하지 않을 때
핵심 메커니즘
raw 엔진, 모델 매니저, 처리량 서버를 이식성과 처리량으로 비교한다.

읽은 기준: llama.cpp-20260701 @ a6647b1a32f2 / ollama-v0.31.1 @ v0.31.1 / vllm-v0.24.0rc2 @ v0.24.0rc2

자기 하드웨어에서 오픈 모델을 실행하고 싶다면 도구가 난립한 것처럼 보인다. 하지만 소스를 읽어보면 어느 계층을 직접 운영하고 싶은지에 따라 나뉘는 세 진영으로 정리된다. llama.cpp는 원시 엔진이다. 모델 파일을 불러와 가지고 있는 어떤 하드웨어에서든 실행하는 독립 실행형 바이너리다. Ollama는 이 엔진을 더 쓰기 좋은 패키지로 감싼 친근한 관리자다. vLLM은 엔진을 버리고 자체 엔진을 구축한 처리량 서버다. 나는 llama.cpp의 2026-07-01 master (a6647b1), Ollama의 v0.31.1, vLLM의 v0.24.0rc2 소스를 읽었다. 이 축을 알고 나면 비교가 명확해진다. 모든 주장은 근거가 되는 코드 줄을 가리킨다.

단 하나의 질문

어느 계층을 직접 운영하고 싶은가? 이 선택 하나가 사용할 수 있는 하드웨어 범위, 동시성, 도구가 대신 해주는 일의 범위를 결정한다.

  • 원시 엔진 — 추론 라이브러리를 직접 다룬다. 이식성과 제어력은 최대지만, 친절한 안내는 최소다. (llama.cpp)
  • 관리자 — 엔진을 감싸고 모델 레지스트리, API, 수명주기 관리를 더한다. 한 대의 머신을 편리하게 운영할 수 있다. (Ollama)
  • 처리량 서버 — 다수의 동시 요청을 처리하도록 최적화한 전용 엔진이다. 제대로 된 GPU가 필요한 대신 처리량을 얻는다. (vLLM)

진영 1 — 원시 엔진: llama.cpp

llama.cpp는 독립 실행형 C/C++ 추론 엔진이다. 작은 단일 목적 바이너리인 llama-clillama-server HTTP 서버(CMakeLists.txt:57)를 빌드하며, 이들은 GGUF 파일, 즉 ggml 자체의 양자화 모델 형식(llama-model-loader.cpp:550)을 불러와 실행한다. llama.cpp의 강점은 이식성이다. 연산 백엔드(CPU, CUDA, Metal, Vulkan, ROCm 외 십여 종)는 C vtable 뒤에 있고, 하나의 빌드가 런타임에 이를 동적으로 불러올 수 있다(ggml-backend-dl.cpp:35). Raspberry Pi에서도 H100에서도 실행된다.

-ngl로 GPU 분할을 제어할 수 있다(arg.cpp:2482). 기본값은 auto다. auto는 실제로 여유 VRAM을 측정하고 들어갈 수 있는 만큼의 레이어를 배치하며, 들어갈 수 있는 수까지 레이어 수를 줄인다(fit.cpp:375). 이 메모리 맞춤은 llama.cpp 자체에 들어 있는 진짜 엔지니어링이고, 다음 진영을 이해할 때 중요하다. 이 진영은 제어력과 도달 범위가 최대지만 친절한 안내는 가장 적다. 모델 레지스트리도, 데몬도 없어서 파일과 플래그를 직접 관리해야 한다.

진영 2 — 친근한 관리자: Ollama

Ollama는 추론을 다시 구현하지 않는다. llama.cpp의 llama-server를 서브프로세스로 감싸고(server.go:107) 그 주변의 모든 것을 더한다. ollama pull을 지원하는 모델 레지스트리, REST API, 유휴 모델을 다섯 분 뒤 언로드하는 keep-alive 스케줄러가 그 예다. Ollama 티어다운에서 확인했듯, Ollama는 GPU 레이어 분할까지 llama.cpp에 위임한다. 기본적으로 -ngl을 아예 전달하지 않고(llama_server.go:396) llama.cpp의 auto 맞춤이 결정하게 한 뒤, 서버 로그에서 최종 레이어 수를 다시 읽는다. 그래서 두 진영은 포개진다. Ollama는 편의 계층이고, 그 아래 엔진은 정확히 진영 1이다. 돈을 내는 대상은 패키징이다. 한 명이 쓰는 머신을 위한 pull-and-run, API, 수명주기 관리다.

진영 3 — 처리량 서버: vLLM

vLLM은 정반대의 선택을 한다. vLLM은 그 자체가 여러 요청을 동시에 처리하도록 전용으로 만든 엔진이다. llama.cpp가 어디서든 모델 하나를 실행하는 데 최적화한다면, vLLM은 GPU를 쉬지 않게 유지하는 데 최적화한다. vLLM 티어다운에서 확인했듯, KV 캐시를 고정된 16토큰 블록으로 페이징해 메모리를 촘촘하게 채우고 공유할 수 있게 한다(cache.py:46). 또한 스케줄러에는 prefill/decode 단계 구분이 없다. 토큰 예산을 기준으로 매 단계 배치를 다시 만든다(scheduler.py:391). 새 프롬프트와 진행 중인 생성을 섞어 아무것도 유휴 상태로 두지 않는다. 이것이 연속 배칭이며, 동시 부하에서 vLLM의 처리량이 다른 둘을 앞서는 이유다. 대신 모델 전체를 올려둘 수 있는 제대로 된 GPU를 요구한다.

비교

llama.cpp — 원시 엔진 Ollama — 관리자 vLLM — 처리량 서버
직접 운영하는 계층 추론 라이브러리 llama.cpp를 감싼 래퍼 전용 서빙 엔진
모델 형식 GGUF (양자화) GGUF (llama.cpp 경유) HF 가중치 (다양한 양자화)
하드웨어 범위 무엇이든: CPU → 어떤 GPU든 llama.cpp와 동일 제대로 된 GPU, 모델 전체 상주
동시성 기본적 단순한 모델별 스케줄링 연속 배칭 (높음)
VRAM 맞춤 -ngl auto가 VRAM에 맞춤 Ollama의 추정 + llama.cpp PagedAttention 블록
편의성 낮음 — 직접 다뤄야 함 높음 — pull과 REST API 서버 운영용, 노트북 도구가 아님
관계 엔진 엔진을 감쌈 엔진을 대체함

이 구분이 모든 것을 결정하는 이유

포개지는 구조를 눈여겨봐야 한다. 이것이 진짜 이야기이기 때문이다. Ollama와 llama.cpp는 경쟁자가 아니다. Ollama는 llama.cpp에 리본을 묶은 것이다. 둘 중 하나를 고른다면 같은 엔진과 GGUF 모델을 두고 편의성과 제어력 중 무엇을 택할지 고르는 셈이다. vLLM이 진짜 갈림길이다. 엔진도, 모델 형식도, 최적화 목표도 다르다. 따라서 이 결정은 대칭적인 삼자 구도가 아니다. 먼저 “서빙 처리량이 필요한가?“를 묻고(vLLM과 llama.cpp 계열 중 선택), 그다음 “관리자를 원하는가, 원시 엔진을 원하는가?“를 묻는 구조다(Ollama와 llama.cpp 중 선택).

나는 어디에 해당할까?

  • 모델 하나, 머신 한 대에서 그냥 작동하기를 원한다 → Ollama. 아래에는 llama.cpp가 있고 그 위에 레지스트리, API, 유휴 시 언로드 기능이 있다. 엔진에서는 아무것도 포기하지 않고 모든 패키징을 얻는다.
  • 특이한 하드웨어, 세밀한 제어, 또는 자체 앱에 추론을 내장해야 한다 → llama.cpp를 직접 쓴다. 다른 어떤 것도 실행되지 않는 곳(CPU 전용, Metal, 엣지 기기)에서도 실행되고, 어떤 레이어를 GPU에 올릴지를 포함해 모든 조절 장치를 직접 쥔다.
  • 다수의 동시 사용자를 서빙하고 처리량이 기준이다 → vLLM. PagedAttention과 연속 배칭은 바로 그 부하를 위해 만들어졌고, 요청이 쌓이기 시작하면 단일 스트림 엔진을 멀찍이 따돌린다. 단, GPU는 준비해야 한다.

범위와 정직함

나는 master a6647b1의 llama.cpp (2026-07-01), Ollama v0.31.1 (710292f), vLLM v0.24.0rc2 (6d37570)를 읽었다. Ollama와 vLLM의 메커니즘은 링크한 각 티어다운에서 소스와 대조해 검증했다. llama.cpp의 엔진 구조(독립 실행형 바이너리, GGUF 로딩, 동적 ggml 백엔드)는 이 커밋에서 반증을 시도하는 claim-verifier를 거쳤고 확인됐다.

정직하게 밝혀둘 점이 두 가지 있다. 첫째, 읽는 도중 바로잡아야 했던 세부 사항이 있다. llama.cpp의 -ngl auto는 여유 VRAM을 실제로 측정하고 들어맞도록 레이어 수를 줄인다. 이 로직은 common/fit.cpp에 있고, 모델을 불러오기 전에 호출되며, auto에서만 작동한다(all이나 명시적인 수에서는 작동하지 않는다). 처음에는 코어 모델 로더만 검색해서 이 로직을 놓쳤고 “맞춤 기능이 없다”고 결론 내렸다. claim-verifier가 이 페이지에 실리기 전에 오류를 잡았다. 이 맞춤 기능이야말로 Ollama의 “그냥 llama.cpp에 위임한다”는 전략이 작동하게 해주는 요소다. 둘째, llama.cpp는 변화가 빠르다. 여기서는 다른 어디보다 날짜를 고정하는 것이 중요하고, Ollama는 자체 빌드를 번들로 제공하기 때문에 upstream master보다 뒤처지거나 앞설 수 있다.

방법론

이 글은 아래의 고정된 저장소 상태를 기준으로 읽었습니다. 디렉터리 이름은 편의용 키이고, 실제 고정 지점은 commit SHA입니다.

저장소 키버전커밋원본
llama.cpp-20260701태그 없음a6647b1a32f2github.com/ggml-org/llama.cpp
ollama-v0.31.1v0.31.1710292ff4f19github.com/ollama/ollama
vllm-v0.24.0rc2v0.24.0rc26d37570a1c6dgithub.com/vllm-project/vllm

관련 분석

결정 축은 운영 계층이다. 바이너리 엔진을 직접 통제하느냐, 모델 매니저를 쓰느냐, 서버 처리량을 위해 엔진을 갈아 끼우느냐로 갈린다.