GPU 한 장으로 LLM을 서빙하며 배운 것 — Ollama에서 vLLM으로, 그리고 양자화의 값
단일 GPU 노드(DGX Spark)에서 Ollama를 걷어내고 vLLM으로 옮긴 이유, AWQ/FP8 양자화가 가져온 것과 숨은 비용, 그리고 추론과 학습이 같은 메모리를 두고 싸울 때의 운영 설계를 정리합니다.
NVIDIA DGX Spark 한 대를 받았을 때, 가장 해보고 싶었던 건 LLM 한 사이클을 처음부터 끝까지 직접 굴려보는 것이었습니다. 데이터셋 페칭, 파인튜닝, 양자화, 평가, 추론 서빙까지 한 사람이 사이드 프로젝트로 운영할 수 있는 형태로요.
이 글은 그중 추론 서빙에 대한 기록입니다. 처음엔 Ollama로 시작했고, vLLM으로 옮겼고, 양자화를 켰습니다. 각 선택에서 무엇을 얻고 무엇을 포기했는지, 그리고 GPU가 한 장뿐일 때만 드러나는 제약을 적습니다.
전제: GPU가 한 장이다
DGX Spark의 칩은 GB10이고, 128GB 통합 메모리를 GPU 하나가 씁니다. 큰 메모리지만 장(card)이 하나라는 게 이 글의 모든 제약을 만듭니다. 추론도 학습도 양자화도 전부 같은 메모리를 두고 경쟁합니다.
여러 장이 꽂힌 서버였다면 이 글의 절반은 쓸 일이 없었을 겁니다. 단일 GPU라서 생기는 판단들이 오히려 재사용 가능한 지식이 됐습니다.
Ollama로 시작했고, 무엇이 문제였나
처음엔 Ollama로 모델 세 개를 올렸습니다.
gemma4:e4b 16GBgemma4:26b 38GBig-devops-8b (파인튜닝) 15GB─────────────────────────────상주 합계 약 69GB문제는 두 가지였습니다.
하나, 메모리가 상주로 눌러앉습니다. 세 모델이 69GB를 점유한 채 떠 있으면, 같은 GPU에서 다른 일(학습, 양자화 실험)을 할 여유가 그만큼 줄어듭니다. 단일 GPU에서는 “안 쓰는 모델이 메모리를 붙잡고 있는 것” 자체가 비용입니다.
둘, Ollama는 처리량 엔진이 아닙니다. Ollama는 로컬에서 한 명이 편하게 쓰기엔 훌륭하지만, 동시 요청을 묶어 GPU를 꽉 채우는 데(continuous batching)는 설계 목적이 다릅니다. 사용자가 늘거나, 에이전트가 한 번에 여러 호출을 던지면 GPU 활용률은 낮은데 응답은 느려지는 구간이 생깁니다.
오해를 줄이자면, Ollama가 나빠서 옮긴 게 아닙니다. “한 사람이 모델 몇 개 띄워서 써보기”에는 Ollama가 가장 빠른 길입니다. 옮긴 건 여러 요청을 같은 GPU로 최대한 빠르게 받아내야 하는 단계로 목적이 바뀌었기 때문입니다.
vLLM으로 간 이유
vLLM으로 옮기면서 노린 건 세 가지였습니다.
- Continuous batching — 동시에 들어온 요청을 토큰 단위로 묶어 GPU를 놀리지 않습니다. 같은 카드로 더 많은 요청을 받는 직접적인 이유입니다.
- OpenAI 호환
/v1네이티브 — 별도 어댑터 없이 표준 엔드포인트로 서빙됩니다. 앞단에 라우터(OpenAI 호환 dispatch)를 두기에도 깔끔합니다. - 양자화 지원 — AWQ·FP8을 엔진 차원에서 받아들여, 메모리 발자국을 줄이면서 처리량을 올릴 길이 열립니다.
대신 포기한 것도 분명합니다. vLLM은 Ollama만큼 “그냥 켜면 됨”이 아닙니다. 커스텀 이미지를 다시 빌드해야 했고(GPU 아키텍처에 맞춘 CUDA 빌드), 모델별 서빙 인자(--served-model-name, --max-model-len, --gpu-memory-utilization, quantization, tool-parser)를 직접 관리해야 합니다. 손이 더 가는 대신 처리량과 제어권을 가져온 거래입니다.
양자화: 무엇을 얻고, 무엇이 숨어 있나
서빙 모델에는 AWQ(W4A16)와 FP8을 적용했습니다. 결과는 공개한 프로젝트 카드에 적은 대로, BF16 baseline 대비 throughput 약 1.6배입니다.
왜 1.6배가 중요할까요? 숫자 자체보다 의미가 핵심입니다. 같은 GPU 한 장으로 1.6배 많은 요청을, 혹은 같은 요청을 1.6배 빠르게 받는다는 뜻이고, 이게 자가 호스팅 LLM의 경제성을 만듭니다. GPU를 더 사지 않고도 서비스 가능한 동시 사용자 수가 늘어납니다.
하지만 양자화는 free lunch가 아닙니다. 여기서 한 번 데였습니다.
AWQ는 calibration set에 의존합니다. 가중치를 4비트로 줄일 때, 어떤 채널이 중요한지를 대표 샘플 몇백 건으로 추정합니다. 그런데 이 calibration 데이터가 실제 도메인을 대표하지 못하면, 모델은 조용히 답변 품질을 떨어뜨립니다. 에러를 내지 않습니다. 그냥 미묘하게 더 나빠진 답을 같은 속도로 내놓습니다 — 그래서 더 위험합니다.
이 silent regression을 잡으려고 둔 안전망이 promptfoo 3-way 평가입니다. base / base+프롬프트 / 파인튜닝(+양자화) 세 버전을 같은 평가셋으로 자동 비교해서, 양자화가 품질을 깎았는지를 배포 전에 드러냅니다. 양자화를 켤 거라면 품질 회귀를 감지하는 평가를 같은 파이프라인 안에 반드시 같이 둬야 한다는 게 가장 값진 교훈이었습니다.
단일 GPU의 진짜 제약: 추론과 학습은 같이 못 산다
여기가 이 구성에서 가장 비싸게 배운 부분입니다.
vLLM이 26B 모델을 서빙하며 GPU 메모리를 잡고 있으면, 그 위에서 학습(NGC 컨테이너)을 시작할 자리가 없습니다. 둘 다 같은 GPU 메모리를 통째로 원하기 때문입니다. 그래서 파이프라인에는 학습을 시작하기 전에 추론을 내리는 단계가 명시적으로 박혀 있습니다.
fetch_dataset → validate_data → stop_vllm → start_ngc → train → convert → quantize_awq → deploy → recover_vllm → evaluate → ...stop_vllm과 recover_vllm이 단계로 존재하는 이유가 이것입니다. 추론을 내려야 학습이 들어오고, 학습이 끝나면 추론을 다시 세웁니다.
그런데 여기서 재미있는(그리고 처음엔 당황스러운) 일이 벌어집니다. 그 vLLM이 곧 운영 에이전트 자신의 두뇌라는 점입니다. 이 스택은 Slack에서 명령을 받는 에이전트가 운전하는데, 그 에이전트의 대화 엔진도 같은 로컬 vLLM(26B)입니다. 즉 stop_vllm이 실행되는 학습 구간 동안 에이전트는 자기 입을 닫습니다. 학습을 시키는 주체가, 학습 때문에 잠시 말을 못 하게 되는 구조입니다.
이걸 알고 난 뒤 운영 모드를 한 단계 더 나눴습니다. 학습 전체 구간 동안 추론을 통째로 내리는 대신, 26B 대화 모델은 살려두고 양자화(AWQ) 구간에서만 잠깐 비우는 방식입니다. 무중단이 필요한 본체는 지키고, GPU를 진짜로 통째로 필요로 하는 짧은 구간만 양보하는 — 단일 GPU에서 “전부 멈춤”과 “전부 유지” 사이의 타협점을 찾은 셈입니다.
정리 — 다른 단일 GPU 환경에 가져갈 판단들
- Ollama vs vLLM은 좋고 나쁨이 아니라 단계의 문제입니다. 한 사람이 모델 몇 개 띄워 써보는 단계면 Ollama가 빠릅니다. 동시 요청 처리량이 목적이 되는 순간 vLLM(continuous batching)으로 넘어갈 값이 생깁니다.
- 양자화를 켤 거면 품질 회귀 평가를 같은 파이프라인에 묶으세요. AWQ는 calibration이 도메인을 대표 못 하면 조용히 품질을 깎습니다. 속도만 보고 켜면 안 보이는 곳에서 답이 나빠집니다.
- 단일 GPU에서 추론과 학습은 공존하지 못합니다. 학습 단계는 추론을 내렸다 올리는
stop/recover를 명시적으로 설계하고, 무중단이 필요한 본체가 있다면 “전부 멈춤”이 아니라 정말 GPU 전체가 필요한 구간만 양보하도록 쪼개는 게 현실적인 타협입니다.
관련 콘텐츠
단일 DGX에 추론과 학습을 같이 올렸더니 파이프라인이 복구 절차가 됐다
단일 DGX에서 vLLM 추론과 LLM 학습 작업을 같이 굴리며 ClearML 파이프라인을 만든 기록. 좋은 구조는 아니었고, 그 제약을 받아들이자 GPU 소유권 전환과 복구 절차가 파이프라인 단계로 흘러들어왔습니다.
DevOpsLinkedIn에서 발견한 Tencent WeKnora, GraphRAG PoC하고 PR까지 Merged
LinkedIn에서 발견한 Tencent WeKnora를 홈 Kubernetes 클러스터에서 PoC하고, Helm Chart PR까지 Merge한 여정
DevOps거짓 CI 알림을 따라 내려가니 CNI 데드락 — 위아래 두 곳을 근본 수정한 기록
정적 분석이 실패해도 배포는 되는데 알림은 배포 실패처럼 울렸다. 그 거짓말을 따라가 CNI 부트스트랩 데드락과 알림 배선 결함을 양쪽 끝에서 각각 root fix한 기록.