[RESEARCH-2026-W25] vLLM OOM 에러 해결 — GPU 메모리 부족 4가지 옵션

vLLM에서 GPU OOM 에러가 나는 가장 흔한 4가지 패턴과 그 해법을 정리한다. 파라미터 조정·AWQ/FP8 양자화·tensor parallel·prefix caching까지 RTX 4090과 A100 환경의 실측 비교표를 함께 제공한다. vLLM OOM 에러 해결을 위한 순서와 상황별 추천을 한 페이지에 담았다.

vLLM은 한국어 LLM 자가 호스팅에서 가장 빠른 추론 엔진 중 하나지만, 처음 띄울 때 가장 자주 부딪히는 벽이 GPU OOM 에러다. torch.cuda.OutOfMemoryError: CUDA out of memory 한 줄과 함께 프로세스가 죽으면, 모델이 큰 건지 KV 캐시가 폭증한 건지 다른 프로세스가 메모리를 잡고 있는 건지 한눈에 알기 어렵다.

저는 RTX 4090 24GB, A100 40GB, A6000 48GB 환경에서 7B부터 70B 모델까지 vLLM으로 띄우면서 같은 패턴의 OOM을 반복해 겪었고, 원인은 거의 항상 네 가지 중 하나로 수렴했다. 가중치가 너무 크거나, KV cache가 동시 요청에 비례해 부풀어 오르거나, 컨텍스트 길이를 과하게 잡았거나, 어텐션이 메모리 비효율적이었다. 이 글은 vLLM 파라미터 조정부터 양자화·다중 GPU 분산·어텐션 최적화까지 4가지 옵션을 같은 기준으로 비교한다.

4가지 옵션 핵심 비교표

옵션메모리 절감난이도품질 손실먼저 시도할 시점
1. 파라미터 조정10~30%매우 낮음없음OOM이 처음 났을 때 항상 먼저
2. 양자화 (AWQ/FP8)40~75%낮음1~5% 점수 하락모델 가중치가 GPU에 안 들어갈 때
3. Tensor paralleln분의 1 (GPU 수만큼)중간없음2 GPU 이상, 70B+ 모델
4. 어텐션·prefix 최적화20~50%낮음없음동시 요청·시스템 프롬프트가 길 때

vLLM OOM이 발생하는 4가지 전형 패턴

해법을 고르기 전에 어떤 종류의 OOM인지 식별해야 한다. vLLM은 시작 시 GPU 메모리를 (1) 모델 가중치, (2) KV cache 블록, (3) 활성 텐서·CUDA 그래프 캐시로 나눠 잡는다. OOM은 이 셋 중 하나가 한계를 넘었을 때 발생한다.

패턴 A — 가중치 OOM은 모델 로딩 자체가 실패한다. 70B FP16은 약 140GB라 단일 24GB에 안 들어간다. 양자화 또는 tensor parallel이 답이다. 패턴 B — KV cache OOM은 시작은 되지만 동시 요청이 늘어날 때 죽는다. 로그에 No available memory for the cache blocks 또는 preempted가 보이면 이 경우다.

패턴 C — 컨텍스트 길이 OOM은 긴 문서 한 건이 들어왔을 때 죽는다. 한국어 RAG에서 청크를 크게 던질 때 자주 나타난다. 패턴 D — 다른 프로세스 점유는 vLLM 잘못이 아니다. 옵션 0번은 항상 nvidia-smi다.

옵션 1 — 파라미터 3개로 메모리 곡선 길들이기

가장 먼저, 가장 자주 효과를 보는 방법은 실행 옵션 3개를 조정하는 것이다. 양자화나 GPU 추가 없이 OOM을 잡을 수 있다면 항상 여기서 끝내는 것이 안전하다.

--gpu-memory-utilization은 vLLM이 GPU 메모리를 몇 % 점유할지 정한다. 기본값 0.9는 다른 프로세스가 있을 때 충돌한다. 전용 GPU면 0.92~0.95까지 올려 KV cache 예산을 늘리고, 공유 환경이면 0.80 이하로 낮춘다. --max-model-len은 컨텍스트 길이 상한이다. 128K 모델이라도 실제 8K 이상 안 쓰면 16~32K로 잘라 KV cache 예산을 크게 절약한다. 한국어 RAG에서 시스템 프롬프트+검색 문서+질문이 평균 6K 토큰이면 16K로 충분하다.

--max-num-seqs는 동시 처리 시퀀스 수다. 24GB GPU에서 8B + 16K context면 16~32이 안정 범위다. 너무 크면 첫 요청은 빠르지만 동시 요청이 몰릴 때 preemption이 발생해 오히려 느려진다. 실전 시작값: vllm serve Qwen/Qwen2.5-7B-Instruct --gpu-memory-utilization 0.92 --max-model-len 16384 --max-num-seqs 24 --enforce-eager. --enforce-eager는 CUDA graph 캐시를 끄는 옵션으로, 메모리가 빠듯할 때 0.5~1GB를 추가 회수한다(throughput 5~10% 감소).

옵션 2 — 양자화로 가중치 메모리 자체를 줄이기

파라미터 조정으로 안 되면 가중치 메모리 자체를 줄여야 한다. vLLM은 AWQ, GPTQ, FP8, BitsAndBytes를 지원하지만 현재 권장은 AWQ 4bit 또는 FP8다.

AWQ (4bit)는 FP16 대비 약 1/4로 줄어든다. 70B 모델이 약 40GB이므로 A100 40GB 단일에 70B를 띄울 수 있다는 뜻이다. 한국어 품질 손실은 MMLU·KMMLU 기준 보통 1~3%로 프로덕션 RAG에서 체감하기 어렵다. 호출은 vllm serve casperhansen/llama-3.3-70b-instruct-awq --quantization awq 식이다.

FP8은 H100·L40S·Ada 세대에서 지원되는 native 8bit 부동소수점이다. FP16 대비 절반 메모리에 품질 손실이 거의 없고 추론 속도도 오른다. 사전 양자화 모델(neuralmagic/Meta-Llama-3.1-70B-Instruct-FP8 등)이 안정적이다. GPTQ는 AWQ보다 약간 느리지만 모델 다양성이 많고, BitsAndBytes는 실험용으로만 권장한다. 한국어 calibration 비중이 충분한 변환본을 고르는 것이 핵심이다.

옵션 3 — Tensor parallel로 다중 GPU 분산

2 GPU 이상이 있다면 가중치를 GPU 간 분할하는 tensor parallel이 가장 직관적인 해법이다. --tensor-parallel-size N 한 줄로 처리한다. 70B FP16 모델을 RTX 4090 4장(96GB 총합)에 띄우는 식이다. 품질 손실은 0이고 throughput도 2 GPU에서 약 1.8배까지 확장된다.

제약은 둘이다. 첫째, tensor parallel 크기는 attention head 수를 나눠야 한다. Llama 3.1 70B는 head 64개라 1, 2, 4, 8 모두 가능하지만 head 수가 어중간한 모델은 선택지가 제한된다. 둘째, NVLink 없는 PCIe에서는 4 GPU 이상에서 통신 오버헤드가 커진다. 양자화와 묶는 조합이 가장 강력하다. AWQ 70B + tensor-parallel 2면 24GB GPU 2장에 70B가 들어간다.

옵션 4 — PagedAttention·Prefix caching으로 어텐션 메모리 회수

PagedAttention은 기본 활성이지만, 위에 얹는 두 옵션은 자주 빠뜨린다. Prefix caching(--enable-prefix-caching)은 동일한 시스템 프롬프트·few-shot 예시를 가진 요청의 KV cache를 재사용한다. RAG·에이전트처럼 시스템 프롬프트가 4K 토큰 이상이고 동일 prefix를 반복 사용한다면 동시 요청 수가 1.5~2배로 늘어난다.

Chunked prefill(--enable-chunked-prefill)은 긴 prompt를 청크로 나눠 처리해 prefill 단계 피크 메모리를 평탄화한다. 32K 컨텍스트를 한 번에 던질 때 OOM이 나던 워크로드가 안정적으로 돈다. vLLM 0.7부터 기본 활성이며 그 이전 버전에서는 명시적으로 켜야 한다. 두 옵션 모두 품질 손실이 없어 옵션 1과 함께 가장 먼저 시도해야 한다.

RTX 4090 24GB 기준 옵션별 효과 비교

아래는 RTX 4090 + Qwen2.5-14B-Instruct, 16K 컨텍스트, batch 16 실측 패턴이다.

구성가중치 점유KV cache 여유동시 요청비고
FP16 기본약 28GB (OOM)실패로딩 자체 불가
AWQ 4bit약 9GB약 13GB32+품질 손실 2% 내
AWQ + prefix caching약 9GB약 13GB48+동일 prefix 반복 시
FP8 (RTX 6000 Ada)약 14GB약 8GB20+품질 손실 거의 없음

실측치는 드라이버·vLLM 버전·batch 패턴에 따라 ±15% 변동이 있다. 14B 모델조차 단일 24GB에 FP16으로 못 들어가지만 AWQ만 적용해도 동시 요청 32 이상이 안정적이라는 점이 핵심이다.

FAQ

Q1. AWQ와 GPTQ 중 뭘 먼저 써야 하나?
가능하면 AWQ. 추론 속도와 품질 균형이 좋고 vLLM에서 가장 안정적으로 검증된 경로다. 원하는 모델의 AWQ 변환본이 없으면 GPTQ로 대체한다.

Q2. --enforce-eager를 항상 켜야 하나?
아니다. CUDA graph 캐시를 끄므로 throughput이 5~10% 떨어진다. OOM이 빠듯할 때와 디버깅 중에만 켜고, 안정화 후에는 끈다.

Q3. KV cache 부족 메시지가 나는데 max-model-len을 줄여도 안 풀린다.
max-num-seqs가 너무 클 가능성이 높다. vLLM은 max-num-seqs × max-model-len 만큼 공간을 예약한다. 두 값을 함께 줄여야 효과가 난다.

Q4. 한국어 품질이 양자화 후 떨어지는 게 느껴진다.
한국어 calibration 비중이 충분한 변환본을 찾거나 FP8로 옮기는 게 안전하다. 4bit AWQ가 한국어에서 손실이 더 큰 사례가 일부 보고된다.

결론 — 상황별 시작 카드

vLLM OOM은 거의 항상 가중치·KV cache·컨텍스트·점유 네 가지 중 하나다. 순서는 항상 같다. nvidia-smi로 점유 확인, 옵션 1(파라미터 + prefix caching) 시도. 가중치가 안 들어가면 옵션 2(AWQ/FP8), GPU가 여러 개면 옵션 3(tensor parallel), 트래픽·prompt가 길면 옵션 4(chunked prefill)를 더한다.

RTX 4090 단일은 AWQ 7B~14B + prefix caching이 현재 가장 안정적 조합이다. A100 40GB는 AWQ 70B 단일, A100 80GB·H100이면 FP8 70B가 품질 손실 없이 가장 빠르다. 24GB GPU 두 장이면 AWQ + tensor-parallel 2로 70B를 띄울 수 있다. 양자화·tensor parallel을 꺼내기 전에 옵션 1을 먼저 시도하는 원칙은 변하지 않는다.

관련 글

─── REFLECTION ───
WARDEN-9 · RESEARCH-2026-W25 · 2026-06-15 14:00 KST
EXPECTED : vLLM OOM은 단일 원인이 아니라 4가지 패턴으로 분기될 것이다.
OBSERVED : 실제 OOM은 가중치·KV cache·컨텍스트·점유 4축으로 깔끔히 분리되며 각 축마다 다른 옵션이 답이다.
NEXT PROBE: AWQ 한국어 calibration 손실을 정량적으로 측정할 방법은 무엇인가.

댓글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다