포스트

LLM 서빙 3대장(vLLM·TGI·Ollama) 2026년 3월 배포 레시피: 로컬 인프라 최적화까지 한 번에

LLM 서빙 3대장(vLLM·TGI·Ollama) 2026년 3월 배포 레시피: 로컬 인프라 최적화까지 한 번에

들어가며

2026년 현재 LLM을 “써보는” 단계는 끝났고, 어떻게 안정적으로 서빙할지가 실력 차이를 만듭니다. 특히 사내/온프렘/개인 워크스테이션 환경에서는 (1) GPU VRAM이 넉넉하지 않고, (2) 동시 요청이 조금만 늘어도 latency가 흔들리며, (3) 모델/버전이 자주 바뀌기 때문에 서빙 엔진 선택과 배포 방식이 곧 운영 난이도를 결정합니다.

이 글은 2026년 3월 기준으로 많이 쓰는 3가지 옵션을 한 축으로 묶어 비교/배포합니다.

  • vLLM: PagedAttention 기반의 KV cache 메모리 효율로 “고동시성/고처리량”에 강함. (arxiv.org)
  • TGI (Text Generation Inference): Hugging Face 생태계/운영 기능이 강하고, continuous batching 등 실서비스 기능이 탄탄함. (github.com)
  • Ollama: 로컬 배포/개발자 경험이 매우 좋고, 환경변수로 동시성/상주 정책을 쉽게 조절 가능. (docs.ollama.com)

🔧 핵심 개념

1) LLM 서빙 성능의 본질: KV cache + batching

LLM 추론에서 GPU 메모리를 가장 크게 잡아먹는 축은 대개 weights보다 KV cache(특히 긴 context, 동시 요청 증가)입니다. 요청이 많아지면 “prefill(프롬프트 처리)”과 “decode(토큰 생성)”가 섞이며, 이를 잘 묶어 처리하는 continuous batching이 처리량을 좌우합니다. TGI는 continuous batching을 핵심 기능으로 내세웁니다. (github.com)

2) vLLM의 PagedAttention: “KV cache를 페이지 단위로” 다루기

전통적인 방식은 KV cache를 연속 메모리로 크게 잡으면서 fragmentation과 낭비가 커지는데, PagedAttention은 이를 OS의 paging처럼 다뤄 낭비를 줄이고 더 큰 batch/동시성을 허용합니다. 이게 vLLM이 고동시성에서 강한 이유입니다. (arxiv.org)

3) TGI의 강점: 운영 친화성과 구성 파라미터의 “자동 최대화”

TGI는 max input/total tokens, batch 관련 파라미터를 하드웨어에 맞춰 자동으로 크게 잡는 전략을 제공하고(또는 env로 명시), /docs로 OpenAPI 문서까지 바로 노출됩니다. (huggingface.co)

4) Ollama의 포지션: 로컬 “멀티 모델/멀티 유저”를 환경변수로 제어

Ollama는 개발/로컬 배포에서 압도적으로 단순합니다. 대신 “대규모 GPU 효율”보다는 단일/소수 GPU에서 여러 모델을 로드/스케줄링하는 운영이 핵심이고, 이때 자주 만지는 것이:

  • OLLAMA_HOST(바인딩 주소)
  • OLLAMA_KEEP_ALIVE(모델 상주 시간)
  • OLLAMA_MAX_LOADED_MODELS(동시 로드 모델 수)
  • OLLAMA_NUM_PARALLEL(모델당 병렬 요청 수) (docs.ollama.com)

💻 실전 코드

아래 예제는 “로컬/온프렘에서 바로 실행”을 목표로 했습니다. (NVIDIA GPU 기준)

1) vLLM: OpenAI-Compatible Server로 띄우기

vLLM은 OpenAI 호환 API 서버 모드가 있어, 클라이언트 코드를 거의 그대로 재사용할 수 있습니다. (docs.vllm.ai)

1
2
3
4
5
6
7
8
9
10
# vLLM 설치 (예: 파이썬 venv 안에서)
pip install -U vllm

# OpenAI-compatible server 실행
# - model: Hugging Face 모델 ID 또는 로컬 경로
# - tensor-parallel-size: 멀티 GPU면 >1 (단일 GPU면 1)
vllm serve "meta-llama/Meta-Llama-3.1-8B-Instruct" \
  --host 0.0.0.0 \
  --port 8000 \
  --tensor-parallel-size 1

테스트(스트리밍은 클라이언트에서 구현 가능):

1
2
3
4
5
6
7
8
curl http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "meta-llama/Meta-Llama-3.1-8B-Instruct",
    "messages": [{"role":"user","content":"vLLM의 PagedAttention을 한 문단으로 설명해줘"}],
    "temperature": 0.2,
    "max_tokens": 200
  }'

2) TGI: Docker로 가장 표준적인 배포

TGI는 공식 repo 기준으로 docker run --gpus all ... ghcr.io/... 패턴이 가장 흔합니다. 또한 /docs로 API 문서가 바로 뜹니다. (github.com)

1
2
3
4
5
6
7
8
9
10
11
12
# (사전) NVIDIA Container Toolkit 설치 필요
# 모델 캐시 볼륨을 공유해 재시작 시 다운로드를 피한다.
export HF_TOKEN="YOUR_HF_TOKEN"
export MODEL="meta-llama/Meta-Llama-3.1-8B-Instruct"
export VOL="$PWD/tgi-data"

docker run --gpus all --shm-size 1g \
  -e HF_TOKEN=$HF_TOKEN \
  -p 8080:80 \
  -v $VOL:/data \
  ghcr.io/huggingface/text-generation-inference:latest \
  --model-id $MODEL

요청 예시(기본 completions 스타일):

1
2
3
4
5
6
curl http://localhost:8080/generate \
  -H 'Content-Type: application/json' \
  -d '{
    "inputs":"continuous batching이 왜 throughput에 유리해?",
    "parameters":{"max_new_tokens":128, "temperature":0.2}
  }'

3) Ollama: Docker Compose로 “로컬 멀티유저/멀티모델” 세팅

Ollama는 OLLAMA_NUM_PARALLEL, OLLAMA_MAX_LOADED_MODELS, OLLAMA_KEEP_ALIVE 같은 knobs로 “내 GPU에서 어디까지 동시성을 줄지”를 빠르게 튜닝합니다. (docs.ollama.com)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# compose.yaml
services:
  ollama:
    image: ollama/ollama:latest
    container_name: ollama
    ports:
      - "11434:11434"
    volumes:
      - ollama:/root/.ollama
    environment:
      # 외부 접근 필요 시 0.0.0.0 바인딩
      - OLLAMA_HOST=0.0.0.0:11434
      # 모델 상주 시간(예: 10분 동안 미사용이면 언로드)
      - OLLAMA_KEEP_ALIVE=10m
      # 동시에 메모리에 올려둘 모델 수
      - OLLAMA_MAX_LOADED_MODELS=2
      # 모델당 동시 처리 요청 수(늘리면 VRAM/latency 압박 증가)
      - OLLAMA_NUM_PARALLEL=2
    deploy:
      resources:
        reservations:
          devices:
            - capabilities: [gpu]
volumes:
  ollama:

실행/모델 pull/테스트:

1
2
3
4
5
6
7
8
9
10
docker compose up -d
curl -s http://localhost:11434/api/tags | jq .

# 예: 모델 다운로드
docker exec -it ollama ollama pull llama3.1:8b

# 간단 호출
curl http://localhost:11434/api/generate \
  -H "Content-Type: application/json" \
  -d '{"model":"llama3.1:8b","prompt":"로컬 LLM 서빙에서 KV cache가 중요한 이유는?","stream":false}'

⚡ 실전 팁

1) 서빙 엔진 선택 기준을 ‘추상’이 아니라 ‘워크로드’로 잡기

  • 동시 요청이 많고 batch가 잘 모이는 서비스(예: 챗봇/에이전트 다중 사용자) → vLLM의 KV cache 효율이 큰 이득이 되는 경우가 많습니다(PagedAttention). (arxiv.org)
  • “운영 기능/문서화/허깅페이스 연동/배포 표준화”가 중요 → TGI가 편합니다(/docs, continuous batching, 다양한 런처 옵션). (github.com)
  • 개발자 PC/작은 서버에서 여러 모델을 빠르게 바꿔가며 로컬 제공 → Ollama가 압도적으로 생산적입니다(환경변수로 동시성/상주 제어). (docs.ollama.com)

2) 토큰 한도(max input/total tokens)는 “성능 슬라이더”다 (특히 TGI) TGI는 하드웨어 기반 자동 최대 설정을 하기도 하지만, 컨텍스트를 크게 열어두면 요청당 메모리 사용량이 커져 batching 효율이 떨어질 수 있습니다. “우리 서비스의 평균 prompt 길이/응답 길이”를 측정한 뒤 상한을 재설정하세요. (huggingface.co)

3) Ollama 동시성 튜닝의 함정: NUM_PARALLEL을 올리면 무조건 좋은가? OLLAMA_NUM_PARALLEL을 올리면 처리량이 늘 수 있지만, VRAM 여유가 적으면 오히려 swap/언로드/로드가 잦아져 tail latency가 튈 수 있습니다. 보통은:

  • OLLAMA_MAX_LOADED_MODELS는 1~2로 보수적으로
  • OLLAMA_NUM_PARALLEL을 2부터 올리며 관찰
  • OLLAMA_KEEP_ALIVE로 “재로딩 비용”과 “VRAM 상주” 사이 균형
    을 추천합니다. (docs.ollama.com)

4) 벤치마크는 TTFT vs TPOT vs Throughput을 분리해서 보라 서빙은 “첫 토큰까지 시간(TTFT)”과 “토큰 생성 속도(TPOT)”, 그리고 “동시성 하 throughput”이 서로 다른 병목을 가집니다. 단일 수치로 비교하면 판단을 그르치기 쉽습니다. (벤치 템플릿/지표 분리의 중요성은 BentoML 가이드도 강조합니다.) (bentoml.com)


🚀 마무리

  • vLLM은 PagedAttention으로 KV cache 메모리 효율을 끌어올려 고동시성에서 강한 선택지가 되기 쉽습니다. (arxiv.org)
  • TGI는 continuous batching, 풍부한 런처 옵션, /docs 기반 문서화로 운영 표준화에 강합니다. (github.com)
  • Ollama는 로컬 배포에서 환경변수 기반 튜닝(동시 처리/동시 로드/상주 정책)으로 개발·팀 내부 배포의 속도를 극대화합니다. (docs.ollama.com)

다음 학습으로는 (1) 실제 트래픽 로그 기반으로 prompt 길이/응답 길이 분포를 만들고, (2) TTFT/TPOT/throughput을 분리 측정하며, (3) KV cache가 VRAM을 얼마나 먹는지 관측(특히 긴 context)하는 과정을 추천합니다. 그러면 “엔진 선택”이 취향이 아니라 데이터 기반 결정이 됩니다. (bentoml.com)

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.