- Published on
vLLM & Ollama 완벽 가이드: LLM 서빙 엔진의 구동, 파라미터, 환경변수 총정리
- Authors
- Name
- Part 1: vLLM
- Part 2: Ollama
- Part 3: 비교 및 실전
Part 1: vLLM
1. vLLM 소개
vLLM은 UC Berkeley에서 개발한 고성능 LLM 추론 및 서빙 엔진이다. 2023년 PagedAttention 논문과 함께 공개된 이후, 프로덕션 LLM 서빙의 사실상 표준(de facto standard)으로 자리잡았다. 2026년 3월 기준 최신 버전은 v0.16.x이며, V1 아키텍처로의 전환이 진행 중이다.
1.1 PagedAttention 핵심 원리
전통적인 LLM 추론에서 KV Cache는 시퀀스별로 연속된 GPU 메모리 블록에 할당된다. 이 방식은 최대 시퀀스 길이를 기준으로 메모리를 미리 예약하기 때문에, 실제로는 60~80%의 메모리가 낭비된다.
PagedAttention은 운영체제의 Virtual Memory Paging 개념을 KV Cache 관리에 도입했다.
┌─────────────────────────────────────────────────┐
│ Traditional KV Cache │
│ ┌──────────────────────────────────┐ │
│ │ Seq 1: [used][used][used][waste][waste][waste]│
│ │ Seq 2: [used][waste][waste][waste][waste] │
│ │ Seq 3: [used][used][waste][waste][waste] │
│ └──────────────────────────────────┘ │
│ → 60~80% Memory Waste │
├─────────────────────────────────────────────────┤
│ PagedAttention KV Cache │
│ Physical Blocks: [B0][B1][B2][B3][B4][B5]... │
│ Block Table: │
│ Seq 1 → [B0, B3, B5] (logical → physical) │
│ Seq 2 → [B1, B4] │
│ Seq 3 → [B2, B6] │
│ → < 4% Memory Waste │
└─────────────────────────────────────────────────┘
핵심 메커니즘은 다음과 같다.
- 고정 크기 블록: KV Cache를 고정 크기의 블록(기본 16 토큰)으로 분할
- Block Table: 시퀀스의 논리적 블록 번호를 물리적 블록 주소로 매핑하는 테이블 유지
- 동적 할당: 토큰 생성 시 필요한 만큼만 물리적 블록을 할당
- Copy-on-Write: Beam Search 등에서 시퀀스를 분기할 때, 동일한 물리적 블록을 공유하다가 수정이 필요할 때만 복사
1.2 Continuous Batching
기존의 Static Batching은 배치 내 모든 시퀀스가 완료될 때까지 기다렸다. Continuous Batching은 매 디코딩 스텝마다 완료된 시퀀스를 제거하고 새 요청을 삽입한다.
Static Batching:
Step 1: [Seq1, Seq2, Seq3, Seq4] ← Seq2 완료되어도 대기
Step 2: [Seq1, Seq2, Seq3, Seq4]
Step 3: [Seq1, ___, Seq3, Seq4] ← Seq2 종료 후에도 슬롯 낭비
...
Step N: 모든 시퀀스 완료 후 다음 배치 시작
Continuous Batching:
Step 1: [Seq1, Seq2, Seq3, Seq4]
Step 2: [Seq1, Seq5, Seq3, Seq4] ← Seq2 완료 즉시 Seq5 투입
Step 3: [Seq1, Seq5, Seq6, Seq4] ← Seq3 완료 즉시 Seq6 투입
→ GPU 유휴 시간 최소화, Throughput 극대화
1.3 지원 모델
vLLM은 Transformer 기반의 거의 모든 주요 LLM 아키텍처를 지원한다.
| 카테고리 | 지원 모델 |
|---|---|
| Meta Llama 계열 | Llama 2, Llama 3, Llama 3.1, Llama 3.2, Llama 3.3, Llama 4 |
| Mistral 계열 | Mistral 7B, Mixtral 8x7B, Mixtral 8x22B, Mistral Large, Mistral Small |
| Qwen 계열 | Qwen, Qwen 1.5, Qwen 2, Qwen 2.5, Qwen 3, QwQ |
| Google 계열 | Gemma, Gemma 2, Gemma 3 |
| DeepSeek 계열 | DeepSeek V2, DeepSeek V3, DeepSeek-R1 |
| 기타 | Phi-3/4, Yi, InternLM 2/3, Command R, DBRX, Falcon, StarCoder 2 |
| 멀티모달 | LLaVA, InternVL, Pixtral, Qwen-VL, MiniCPM-V |
| Embedding | E5-Mistral, GTE-Qwen, Jina Embeddings |
1.4 LLM 서빙 엔진 비교
| 항목 | vLLM | TGI | TensorRT-LLM | llama.cpp |
|---|---|---|---|---|
| 개발 | UC Berkeley / vLLM Project | Hugging Face | NVIDIA | Georgi Gerganov |
| 언어 | Python/C++/CUDA | Rust/Python | C++/CUDA | C/C++ |
| 핵심 기술 | PagedAttention | Continuous Batching | FP8/INT4 커널 최적화 | GGUF 양자화 |
| 멀티 GPU | TP + PP | TP | TP + PP | 제한적 |
| 양자화 | AWQ, GPTQ, FP8, BnB | AWQ, GPTQ, BnB | FP8, INT4, INT8 | GGUF (Q2~Q8) |
| API 호환 | OpenAI 호환 | OpenAI 호환 | Triton | 자체 API |
| 설치 난이도 | 중간 | 중간 | 높음 | 낮음 |
| 프로덕션 적합 | 매우 높음 | 높음 | 매우 높음 | 낮음~중간 |
| 커뮤니티 | 매우 활발 | 활발 | NVIDIA 주도 | 매우 활발 |
2. vLLM 설치 및 구동
2.1 pip 설치
# 기본 설치 (CUDA 12.x)
pip install vllm
# 특정 버전 설치
pip install vllm==0.16.0
# CUDA 11.8 환경
pip install vllm --extra-index-url https://download.pytorch.org/whl/cu118
2.2 conda 설치
conda create -n vllm python=3.11 -y
conda activate vllm
pip install vllm
2.3 Docker 설치
# 공식 Docker 이미지 (NVIDIA GPU)
docker run --runtime nvidia --gpus all \
-v ~/.cache/huggingface:/root/.cache/huggingface \
--env "HUGGING_FACE_HUB_TOKEN=<hf_token>" \
-p 8000:8000 \
--ipc=host \
vllm/vllm-openai:latest \
--model meta-llama/Llama-3.1-8B-Instruct
# ROCm (AMD GPU)
docker run --device /dev/kfd --device /dev/dri \
-v ~/.cache/huggingface:/root/.cache/huggingface \
-p 8000:8000 \
vllm/vllm-openai:latest-rocm \
--model meta-llama/Llama-3.1-8B-Instruct
2.4 기본 서버 시작
# vllm serve 명령 (권장)
vllm serve meta-llama/Llama-3.1-8B-Instruct \
--host 0.0.0.0 \
--port 8000
# Python 모듈 직접 실행 (레거시 방식)
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3.1-8B-Instruct \
--host 0.0.0.0 \
--port 8000
# YAML 설정 파일로 구동
vllm serve meta-llama/Llama-3.1-8B-Instruct \
--config config.yaml
config.yaml 예시:
# vLLM 서버 설정 파일
host: '0.0.0.0'
port: 8000
tensor_parallel_size: 2
gpu_memory_utilization: 0.90
max_model_len: 8192
dtype: 'auto'
enforce_eager: false
enable_prefix_caching: true
2.5 Offline Batch Inference
서버를 띄우지 않고 Python 코드에서 직접 배치 추론을 수행할 수 있다.
from vllm import LLM, SamplingParams
# 모델 로드
llm = LLM(
model="meta-llama/Llama-3.1-8B-Instruct",
tensor_parallel_size=1,
gpu_memory_utilization=0.90,
)
# 샘플링 파라미터 설정
sampling_params = SamplingParams(
temperature=0.7,
top_p=0.9,
max_tokens=512,
)
# 프롬프트 목록
prompts = [
"Explain PagedAttention in simple terms.",
"What is continuous batching?",
"Compare vLLM and TensorRT-LLM.",
]
# 배치 추론 실행
outputs = llm.generate(prompts, sampling_params)
for output in outputs:
prompt = output.prompt
generated = output.outputs[0].text
print(f"Prompt: {prompt!r}")
print(f"Output: {generated!r}\n")
2.6 OpenAI-Compatible API Server
vLLM 서버는 OpenAI API와 호환되는 엔드포인트를 제공한다.
# 서버 시작
vllm serve meta-llama/Llama-3.1-8B-Instruct \
--served-model-name llama-3.1-8b \
--api-key my-secret-key
# curl로 Chat Completion 호출
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer my-secret-key" \
-d '{
"model": "llama-3.1-8b",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "PagedAttention이란 무엇인가요?"}
],
"temperature": 0.7,
"max_tokens": 512
}'
# OpenAI SDK로 호출
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="my-secret-key",
)
response = client.chat.completions.create(
model="llama-3.1-8b",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "vLLM의 장점을 설명해주세요."},
],
temperature=0.7,
max_tokens=512,
)
print(response.choices[0].message.content)
3. vLLM CLI 인자 (Arguments) 완벽 정리
vllm serve 명령에 전달할 수 있는 주요 CLI 인자를 카테고리별로 정리한다. vllm serve --help로 전체 목록을 확인할 수 있으며, vllm serve --help=ModelConfig처럼 그룹 단위로 조회할 수도 있다.
3.1 Model 관련 인자
| 인자 | 타입 | 기본값 | 설명 |
|---|---|---|---|
--model | str | facebook/opt-125m | HuggingFace 모델 ID 또는 로컬 경로 |
--tokenizer | str | None (모델과 동일) | 별도의 토크나이저 지정 |
--revision | str | None | 모델의 특정 Git revision (branch, tag, commit hash) |
--tokenizer-revision | str | None | 토크나이저의 특정 revision |
--dtype | str | "auto" | 모델 가중치 데이터 타입 (auto, float16, bfloat16, float32) |
--max-model-len | int | None (모델 설정 따름) | 최대 시퀀스 길이 (입력 + 출력 토큰 합계) |
--trust-remote-code | flag | False | HuggingFace 원격 코드 실행 허용 |
--download-dir | str | None | 모델 다운로드 디렉토리 |
--load-format | str | "auto" | 모델 로드 형식 (auto, pt, safetensors, npcache, dummy, bitsandbytes) |
--config-format | str | "auto" | 모델 설정 형식 (auto, hf, mistral) |
--seed | int | 0 | 재현성을 위한 랜덤 시드 |
3.2 서버 관련 인자
| 인자 | 타입 | 기본값 | 설명 |
|---|---|---|---|
--host | str | "0.0.0.0" | 바인드할 호스트 주소 |
--port | int | 8000 | 서버 포트 번호 |
--uvicorn-log-level | str | "info" | Uvicorn 로그 레벨 |
--api-key | str | None | API 인증 키 (Bearer token) |
--served-model-name | str | None | API에서 사용할 모델 이름 (미지정 시 --model 값 사용) |
--chat-template | str | None | Jinja2 chat template 파일 경로 또는 문자열 |
--response-role | str | "assistant" | Chat completion 응답의 role |
--ssl-keyfile | str | None | SSL key 파일 경로 |
--ssl-certfile | str | None | SSL 인증서 파일 경로 |
--allowed-origins | list | ["*"] | CORS 허용 origin 목록 |
--middleware | list | None | FastAPI 미들웨어 클래스 |
--max-log-len | int | None | 로그에 출력할 최대 프롬프트/출력 길이 |
--disable-log-requests | flag | False | 요청 로깅 비활성화 |
3.3 병렬화 관련 인자
| 인자 | 타입 | 기본값 | 설명 |
|---|---|---|---|
--tensor-parallel-size (-tp) | int | 1 | Tensor Parallelism GPU 수 |
--pipeline-parallel-size (-pp) | int | 1 | Pipeline Parallelism 단계 수 |
--distributed-executor-backend | str | None | 분산 실행 백엔드 (ray, mp) |
--ray-workers-use-nsight | flag | False | Ray 워커에서 Nsight 프로파일러 사용 |
--data-parallel-size (-dp) | int | 1 | Data Parallelism 프로세스 수 |
사용 예시:
# 4-GPU Tensor Parallelism
vllm serve meta-llama/Llama-3.1-70B-Instruct \
--tensor-parallel-size 4
# 2-GPU Tensor + 2-way Pipeline (총 4 GPU)
vllm serve meta-llama/Llama-3.1-70B-Instruct \
--tensor-parallel-size 2 \
--pipeline-parallel-size 2
# Ray 분산 백엔드 사용 (멀티 노드)
vllm serve meta-llama/Llama-3.1-70B-Instruct \
--tensor-parallel-size 8 \
--distributed-executor-backend ray
3.4 메모리 및 성능 관련 인자
| 인자 | 타입 | 기본값 | 설명 |
|---|---|---|---|
--gpu-memory-utilization | float | 0.90 | GPU 메모리 사용률 (0.0~1.0) |
--max-num-seqs | int | 256 | 동시 처리 최대 시퀀스 수 |
--max-num-batched-tokens | int | None (자동) | 한 스텝에서 처리할 최대 토큰 수 |
--block-size | int | 16 | PagedAttention 블록 크기 (토큰 단위) |
--swap-space | float | 4 | CPU swap 공간 크기 (GiB) |
--enforce-eager | flag | False | CUDA Graph 비활성화, Eager 모드 강제 |
--max-seq-len-to-capture | int | 8192 | CUDA Graph 캡처 최대 시퀀스 길이 |
--disable-custom-all-reduce | flag | False | 커스텀 All-Reduce 비활성화 |
--enable-prefix-caching | flag | True (v1) | Automatic Prefix Caching 활성화 |
--enable-chunked-prefill | flag | True (v1) | Chunked Prefill 활성화 |
--num-scheduler-steps | int | 1 | 스케줄러당 디코딩 스텝 수 (Multi-Step Scheduling) |
--kv-cache-dtype | str | "auto" | KV Cache 데이터 타입 (auto, fp8, fp8_e5m2, fp8_e4m3) |
사용 예시:
# 메모리 최적화 설정
vllm serve meta-llama/Llama-3.1-8B-Instruct \
--gpu-memory-utilization 0.95 \
--max-num-seqs 128 \
--max-model-len 4096 \
--enable-prefix-caching \
--enable-chunked-prefill
# Eager 모드 (디버깅/호환성)
vllm serve meta-llama/Llama-3.1-8B-Instruct \
--enforce-eager \
--gpu-memory-utilization 0.85
3.5 양자화 관련 인자
| 인자 | 타입 | 기본값 | 설명 |
|---|---|---|---|
--quantization (-q) | str | None | 양자화 방식 선택 |
--load-format | str | "auto" | 모델 로드 형식 |
--quantization 지원 값:
| 값 | 설명 | 비고 |
|---|---|---|
awq | AWQ (Activation-aware Weight Quantization) | 4-bit, 빠른 추론 |
gptq | GPTQ (Post-Training Quantization) | 4-bit, ExLlamaV2 커널 |
gptq_marlin | GPTQ + Marlin 커널 | 4-bit, 더 빠른 커널 |
awq_marlin | AWQ + Marlin 커널 | 4-bit, 더 빠른 커널 |
squeezellm | SqueezeLLM | 희소 양자화 |
fp8 | FP8 (8-bit floating point) | H100/MI300x 전용 |
bitsandbytes | BitsAndBytes | 4-bit NF4 |
gguf | GGUF 형식 | llama.cpp 호환 |
compressed-tensors | Compressed Tensors | 범용 |
experts_int8 | MoE Expert INT8 | MoE 모델 전용 |
사용 예시:
# AWQ 양자화 모델
vllm serve TheBloke/Llama-2-7B-AWQ \
--quantization awq
# GPTQ 양자화 모델
vllm serve TheBloke/Llama-2-7B-GPTQ \
--quantization gptq
# FP8 양자화 (H100 이상)
vllm serve neuralmagic/Meta-Llama-3.1-8B-Instruct-FP8 \
--quantization fp8
# BitsAndBytes 4-bit (GPU 메모리 절약)
vllm serve meta-llama/Llama-3.1-8B-Instruct \
--quantization bitsandbytes \
--load-format bitsandbytes
3.6 LoRA 관련 인자
| 인자 | 타입 | 기본값 | 설명 |
|---|---|---|---|
--enable-lora | flag | False | LoRA 어댑터 서빙 활성화 |
--max-loras | int | 1 | 동시 로드 가능한 최대 LoRA 수 |
--max-lora-rank | int | 16 | 최대 LoRA rank |
--lora-extra-vocab-size | int | 256 | LoRA 어댑터의 추가 vocabulary 크기 |
--lora-modules | list | None | LoRA 어댑터 목록 (name=path 형식) |
--long-lora-scaling-factors | list | None | Long LoRA 스케일링 팩터 |
사용 예시:
# LoRA 어댑터 서빙
vllm serve meta-llama/Llama-3.1-8B-Instruct \
--enable-lora \
--max-loras 4 \
--max-lora-rank 64 \
--lora-modules \
adapter1=/path/to/lora1 \
adapter2=/path/to/lora2
3.7 Speculative Decoding 관련 인자
| 인자 | 타입 | 기본값 | 설명 |
|---|---|---|---|
--speculative-model | str | None | Draft 모델 (작은 모델 또는 [ngram]) |
--num-speculative-tokens | int | None | 투기적으로 생성할 토큰 수 |
--speculative-draft-tensor-parallel-size | int | None | Draft 모델의 TP 크기 |
--speculative-disable-by-batch-size | int | None | 배치 크기 초과 시 비활성화 |
--ngram-prompt-lookup-max | int | None | N-gram 기반 추측 시 최대 룩업 크기 |
--ngram-prompt-lookup-min | int | None | N-gram 기반 추측 시 최소 룩업 크기 |
사용 예시:
# 별도 draft 모델 사용
vllm serve meta-llama/Llama-3.1-70B-Instruct \
--speculative-model meta-llama/Llama-3.2-1B-Instruct \
--num-speculative-tokens 5 \
--tensor-parallel-size 4
# N-gram 기반 speculative decoding (추가 모델 불필요)
vllm serve meta-llama/Llama-3.1-8B-Instruct \
--speculative-model "[ngram]" \
--num-speculative-tokens 5 \
--ngram-prompt-lookup-max 4
4. vLLM 샘플링 파라미터 (Sampling Parameters)
vLLM은 OpenAI API 호환 파라미터와 추가적인 고급 파라미터를 지원한다.
4.1 파라미터 총정리
| 파라미터 | 타입 | 기본값 | 범위 | 설명 |
|---|---|---|---|---|
temperature | float | 1.0 | >= 0.0 | 낮을수록 결정적, 높을수록 창의적. 0이면 Greedy |
top_p | float | 1.0 | (0.0, 1.0] | Nucleus sampling. 누적 확률 기준 상위 토큰만 샘플링 |
top_k | int | -1 | -1 또는 >= 1 | 상위 k개 토큰만 고려. -1이면 비활성화 |
min_p | float | 0.0 | [0.0, 1.0] | 최소 확률 임계값. 최고 확률 대비 비율 기준 필터링 |
frequency_penalty | float | 0.0 | [-2.0, 2.0] | 빈도 기반 페널티. 양수면 반복 억제, 음수면 반복 촉진 |
presence_penalty | float | 0.0 | [-2.0, 2.0] | 존재 기반 페널티. 한 번이라도 등장한 토큰에 페널티 |
repetition_penalty | float | 1.0 | > 0.0 | 반복 페널티 (1.0이면 비활성화, >1.0이면 반복 억제) |
max_tokens | int | 16 | >= 1 | 생성할 최대 토큰 수 |
stop | list | None | - | 생성 중단 문자열 목록 |
seed | int | None | - | 랜덤 시드 (재현성 보장) |
n | int | 1 | >= 1 | 각 프롬프트당 생성할 응답 수 |
best_of | int | None | >= n | n개 중 best_of개를 생성하여 최적 응답 선택 |
use_beam_search | bool | False | - | Beam Search 활성화 |
logprobs | int | None | [0, 20] | 반환할 토큰별 로그 확률 수 |
prompt_logprobs | int | None | [0, 20] | 프롬프트 토큰의 로그 확률 반환 수 |
skip_special_tokens | bool | True | - | 특수 토큰 생략 여부 |
spaces_between_special_tokens | bool | True | - | 특수 토큰 사이 공백 삽입 |
guided_json | object | None | - | JSON Schema 기반 구조화된 출력 |
guided_regex | str | None | - | 정규식 기반 구조화된 출력 |
guided_choice | list | None | - | 선택지 기반 구조화된 출력 |
4.2 curl을 이용한 API 호출 예제
# 기본 Chat Completion
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "meta-llama/Llama-3.1-8B-Instruct",
"messages": [
{"role": "user", "content": "서울의 인구는?"}
],
"temperature": 0.3,
"top_p": 0.9,
"max_tokens": 256,
"frequency_penalty": 0.5,
"seed": 42
}'
# Structured Output (JSON mode)
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "meta-llama/Llama-3.1-8B-Instruct",
"messages": [
{"role": "user", "content": "서울, 부산, 대구의 인구를 JSON으로 알려줘"}
],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "city_population",
"schema": {
"type": "object",
"properties": {
"cities": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string"},
"population": {"type": "integer"}
},
"required": ["name", "population"]
}
}
},
"required": ["cities"]
}
}
},
"temperature": 0.1,
"max_tokens": 512
}'
# logprobs 반환
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "meta-llama/Llama-3.1-8B-Instruct",
"messages": [
{"role": "user", "content": "1+1=?"}
],
"logprobs": true,
"top_logprobs": 5,
"max_tokens": 10
}'
4.3 Python requests 예제
import requests
import json
url = "http://localhost:8000/v1/chat/completions"
headers = {"Content-Type": "application/json"}
payload = {
"model": "meta-llama/Llama-3.1-8B-Instruct",
"messages": [
{"role": "system", "content": "You are a helpful Korean assistant."},
{"role": "user", "content": "양자 컴퓨팅이란?"},
],
"temperature": 0.7,
"top_p": 0.9,
"top_k": 50,
"max_tokens": 1024,
"repetition_penalty": 1.1,
"stop": ["\n\n\n"],
}
response = requests.post(url, headers=headers, json=payload)
result = response.json()
print(result["choices"][0]["message"]["content"])
4.4 OpenAI SDK를 이용한 Streaming 예제
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="token")
# Streaming 응답
stream = client.chat.completions.create(
model="meta-llama/Llama-3.1-8B-Instruct",
messages=[
{"role": "user", "content": "Python으로 퀵소트를 구현해줘"},
],
temperature=0.2,
max_tokens=2048,
stream=True,
)
for chunk in stream:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
print()
5. vLLM 환경변수 총정리
vLLM은 다양한 환경변수를 통해 런타임 동작을 제어한다. 주요 환경변수를 카테고리별로 정리한다.
5.1 핵심 환경변수
| 환경변수 | 기본값 | 설명 |
|---|---|---|
VLLM_TARGET_DEVICE | "cuda" | 타겟 디바이스 (cuda, rocm, neuron, cpu, xpu) |
VLLM_USE_V1 | True | V1 코드 경로 사용 여부 |
VLLM_WORKER_MULTIPROC_METHOD | "fork" | 멀티프로세스 생성 방법 (spawn, fork) |
VLLM_ALLOW_LONG_MAX_MODEL_LEN | False | 모델 설정보다 긴 max_model_len 허용 |
CUDA_VISIBLE_DEVICES | None | 사용할 GPU 디바이스 번호 |
5.2 Attention 및 커널 관련
| 환경변수 | 기본값 | 설명 |
|---|---|---|
VLLM_ATTENTION_BACKEND | None | Attention 백엔드 (deprecated, v0.14부터 --attention-backend 사용) |
VLLM_USE_TRITON_FLASH_ATTN | True | Triton Flash Attention 사용 |
VLLM_FLASH_ATTN_VERSION | None | Flash Attention 버전 강제 (2 또는 3) |
VLLM_USE_FLASHINFER_SAMPLER | None | FlashInfer sampler 사용 |
VLLM_FLASHINFER_FORCE_TENSOR_CORES | False | FlashInfer 텐서 코어 강제 사용 |
VLLM_USE_TRITON_AWQ | False | Triton AWQ 커널 사용 |
VLLM_USE_DEEP_GEMM | False | DeepGemm 커널 사용 (MoE 연산) |
VLLM_MLA_DISABLE | False | MLA Attention 최적화 비활성화 |
5.3 로깅 관련
| 환경변수 | 기본값 | 설명 |
|---|---|---|
VLLM_CONFIGURE_LOGGING | 1 | vLLM 로깅 자동 설정 (0이면 비활성화) |
VLLM_LOGGING_LEVEL | "INFO" | 기본 로깅 레벨 |
VLLM_LOGGING_CONFIG_PATH | None | 커스텀 로깅 설정 파일 경로 |
VLLM_LOGGING_PREFIX | "" | 로그 메시지 앞에 추가할 접두사 |
VLLM_LOG_BATCHSIZE_INTERVAL | -1 | 배치 크기 로깅 간격 (초, -1이면 비활성화) |
VLLM_TRACE_FUNCTION | 0 | 함수 호출 추적 활성화 |
VLLM_DEBUG_LOG_API_SERVER_RESPONSE | False | API 응답 디버그 로깅 |
5.4 분산 처리 관련
| 환경변수 | 기본값 | 설명 |
|---|---|---|
VLLM_HOST_IP | "" | 분산 설정에서 노드 IP |
VLLM_PORT | 0 | 분산 통신 포트 |
VLLM_NCCL_SO_PATH | None | NCCL 라이브러리 파일 경로 |
NCCL_DEBUG | None | NCCL 디버그 레벨 (INFO, WARN, TRACE) |
NCCL_SOCKET_IFNAME | None | NCCL 통신용 네트워크 인터페이스 |
VLLM_PP_LAYER_PARTITION | None | Pipeline Parallelism 레이어 파티션 전략 |
VLLM_DP_RANK | 0 | Data Parallel 프로세스 랭크 |
VLLM_DP_SIZE | 1 | Data Parallel 월드 사이즈 |
VLLM_DP_MASTER_IP | "127.0.0.1" | Data Parallel 마스터 노드 IP |
VLLM_DP_MASTER_PORT | 0 | Data Parallel 마스터 노드 포트 |
VLLM_USE_RAY_SPMD_WORKER | False | Ray SPMD 워커 실행 |
VLLM_USE_RAY_COMPILED_DAG | False | Ray Compiled Graph API 사용 |
VLLM_SKIP_P2P_CHECK | False | GPU 간 P2P 능력 검사 건너뛰기 |
5.5 HuggingFace 및 외부 서비스
| 환경변수 | 기본값 | 설명 |
|---|---|---|
HF_TOKEN | None | HuggingFace API 토큰 |
HUGGING_FACE_HUB_TOKEN | None | HuggingFace Hub 토큰 (레거시) |
VLLM_USE_MODELSCOPE | False | ModelScope에서 모델 로드 |
VLLM_API_KEY | None | vLLM API 서버 인증 키 |
VLLM_NO_USAGE_STATS | False | 사용 통계 수집 비활성화 |
VLLM_DO_NOT_TRACK | False | 추적 옵트아웃 |
5.6 캐시 및 경로
| 환경변수 | 기본값 | 설명 |
|---|---|---|
VLLM_CONFIG_ROOT | ~/.config/vllm | 설정 파일 루트 디렉토리 |
VLLM_CACHE_ROOT | ~/.cache/vllm | 캐시 파일 루트 디렉토리 |
VLLM_ASSETS_CACHE | ~/.cache/vllm/assets | 다운로드 에셋 캐시 경로 |
VLLM_RPC_BASE_PATH | 시스템 temp | IPC 멀티프로세싱 경로 |
5.7 환경변수 사용 예시
# 멀티 GPU + 로깅 + HF 토큰 설정
export CUDA_VISIBLE_DEVICES=0,1,2,3
export HF_TOKEN="hf_xxxxxxxxxxxx"
export VLLM_LOGGING_LEVEL="DEBUG"
export VLLM_WORKER_MULTIPROC_METHOD="spawn"
vllm serve meta-llama/Llama-3.1-70B-Instruct \
--tensor-parallel-size 4 \
--gpu-memory-utilization 0.90
# Docker에서 환경변수 전달
docker run --runtime nvidia --gpus all \
-e CUDA_VISIBLE_DEVICES=0,1 \
-e HF_TOKEN="hf_xxxxxxxxxxxx" \
-e VLLM_LOGGING_LEVEL="INFO" \
-e VLLM_WORKER_MULTIPROC_METHOD="spawn" \
-p 8000:8000 \
--ipc=host \
vllm/vllm-openai:latest \
--model meta-llama/Llama-3.1-8B-Instruct \
--tensor-parallel-size 2
6. vLLM 고급 설정
6.1 멀티 GPU 설정
Tensor Parallelism (TP): 모델의 각 레이어를 여러 GPU에 분할한다. 단일 노드에서 가장 일반적으로 사용되는 방식이다.
# TP=4 (4 GPU에 모델 분산)
vllm serve meta-llama/Llama-3.1-70B-Instruct \
--tensor-parallel-size 4 \
--gpu-memory-utilization 0.90
Pipeline Parallelism (PP): 모델의 레이어를 순차적으로 여러 GPU에 배치한다. 느린 인터커넥트 환경에서 유리하다.
# PP=2, TP=2 (총 4 GPU, 2×2 구성)
vllm serve meta-llama/Llama-3.1-70B-Instruct \
--tensor-parallel-size 2 \
--pipeline-parallel-size 2
멀티 노드 설정 (Ray 사용):
# 마스터 노드
ray start --head --port=6379
# 워커 노드
ray start --address=<master-ip>:6379
# vLLM 실행 (마스터에서)
vllm serve meta-llama/Llama-3.1-405B-Instruct \
--tensor-parallel-size 8 \
--pipeline-parallel-size 2 \
--distributed-executor-backend ray
6.2 양자화 상세
AWQ (Activation-aware Weight Quantization):
# AWQ 사전 양자화 모델 사용
vllm serve TheBloke/Llama-2-13B-chat-AWQ \
--quantization awq \
--max-model-len 4096
# Marlin 커널로 더 빠르게 (SM 80+ GPU)
vllm serve TheBloke/Llama-2-13B-chat-AWQ \
--quantization awq_marlin
GPTQ (Post-Training Quantization):
# GPTQ 모델 (ExLlamaV2 커널 자동 사용)
vllm serve TheBloke/Llama-2-13B-chat-GPTQ \
--quantization gptq
# Marlin 커널 사용
vllm serve TheBloke/Llama-2-13B-chat-GPTQ \
--quantization gptq_marlin
FP8 (8-bit Floating Point): H100, MI300x 이상 GPU에서 하드웨어 가속 지원.
# 사전 양자화 FP8 모델
vllm serve neuralmagic/Meta-Llama-3.1-8B-Instruct-FP8 \
--quantization fp8
# 동적 FP8 양자화 (사전 양자화 불필요)
vllm serve meta-llama/Llama-3.1-8B-Instruct \
--quantization fp8 \
--kv-cache-dtype fp8
BitsAndBytes 4-bit NF4: 캘리브레이션 데이터 없이 즉시 양자화.
vllm serve meta-llama/Llama-3.1-8B-Instruct \
--quantization bitsandbytes \
--load-format bitsandbytes \
--enforce-eager # BnB는 Eager 모드 필요
6.3 LoRA 서빙
# LoRA 어댑터 활성화
vllm serve meta-llama/Llama-3.1-8B-Instruct \
--enable-lora \
--max-loras 4 \
--max-lora-rank 64 \
--lora-modules \
korean-chat=/path/to/korean-lora \
code-assist=/path/to/code-lora
API 호출 시 LoRA 모델 지정:
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="token")
# 특정 LoRA 어댑터 사용
response = client.chat.completions.create(
model="korean-chat", # LoRA 어댑터 이름
messages=[{"role": "user", "content": "안녕하세요!"}],
temperature=0.7,
max_tokens=256,
)
6.4 Prefix Caching & Chunked Prefill
Automatic Prefix Caching: 공통 프롬프트 접두사의 KV Cache를 재사용하여 TTFT를 단축한다. 동일한 시스템 프롬프트를 사용하는 다수의 요청이 있을 때 특히 효과적이다.
# v1에서는 기본 활성화, v0에서는 명시 필요
vllm serve meta-llama/Llama-3.1-8B-Instruct \
--enable-prefix-caching
Chunked Prefill: 긴 프롬프트를 청크로 분할하여 Prefill과 Decode를 인터리빙한다. 긴 프롬프트가 짧은 요청의 Decode를 차단하는 문제를 방지한다.
vllm serve meta-llama/Llama-3.1-8B-Instruct \
--enable-chunked-prefill \
--max-num-batched-tokens 2048
6.5 Structured Output (Guided Decoding)
# JSON Schema 기반 구조화 출력
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "meta-llama/Llama-3.1-8B-Instruct",
"messages": [
{"role": "user", "content": "서울의 날씨 정보를 JSON으로 제공해주세요"}
],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "weather_info",
"schema": {
"type": "object",
"properties": {
"city": {"type": "string"},
"temperature_celsius": {"type": "number"},
"condition": {"type": "string"},
"humidity_percent": {"type": "integer"}
},
"required": ["city", "temperature_celsius", "condition"]
}
}
}
}'
# Regex 기반 출력 (Completion API)
curl http://localhost:8000/v1/completions \
-H "Content-Type: application/json" \
-d '{
"model": "meta-llama/Llama-3.1-8B-Instruct",
"prompt": "Generate a valid email address:",
"extra_body": {
"guided_regex": "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}"
},
"max_tokens": 50
}'
6.6 Docker 배포
# docker-compose.yaml
version: '3.8'
services:
vllm:
image: vllm/vllm-openai:latest
runtime: nvidia
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
ports:
- '8000:8000'
volumes:
- ~/.cache/huggingface:/root/.cache/huggingface
environment:
- HF_TOKEN=${HF_TOKEN}
- VLLM_LOGGING_LEVEL=INFO
- VLLM_WORKER_MULTIPROC_METHOD=spawn
ipc: host
command: >
--model meta-llama/Llama-3.1-8B-Instruct
--host 0.0.0.0
--port 8000
--tensor-parallel-size 2
--gpu-memory-utilization 0.90
--max-model-len 8192
--enable-prefix-caching
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:8000/health']
interval: 30s
timeout: 10s
retries: 3
start_period: 120s
# Docker Compose로 실행
HF_TOKEN=hf_xxxx docker compose up -d
# 로그 확인
docker compose logs -f vllm
6.7 Kubernetes 배포
# vllm-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: vllm-llama3
namespace: ai-serving
labels:
app: vllm
spec:
replicas: 1
selector:
matchLabels:
app: vllm
template:
metadata:
labels:
app: vllm
spec:
containers:
- name: vllm
image: vllm/vllm-openai:latest
ports:
- containerPort: 8000
name: http
args:
- '--model'
- 'meta-llama/Llama-3.1-8B-Instruct'
- '--host'
- '0.0.0.0'
- '--port'
- '8000'
- '--tensor-parallel-size'
- '2'
- '--gpu-memory-utilization'
- '0.90'
- '--max-model-len'
- '8192'
env:
- name: HF_TOKEN
valueFrom:
secretKeyRef:
name: hf-secret
key: token
- name: VLLM_WORKER_MULTIPROC_METHOD
value: 'spawn'
resources:
limits:
nvidia.com/gpu: '2'
requests:
nvidia.com/gpu: '2'
memory: '32Gi'
cpu: '8'
volumeMounts:
- name: shm
mountPath: /dev/shm
- name: model-cache
mountPath: /root/.cache/huggingface
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 120
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 180
periodSeconds: 30
volumes:
- name: shm
emptyDir:
medium: Memory
sizeLimit: 2Gi
- name: model-cache
persistentVolumeClaim:
claimName: model-cache-pvc
nodeSelector:
nvidia.com/gpu.product: 'NVIDIA-A100-SXM4-80GB'
---
apiVersion: v1
kind: Service
metadata:
name: vllm-service
namespace: ai-serving
spec:
selector:
app: vllm
ports:
- port: 8000
targetPort: 8000
name: http
type: ClusterIP
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: vllm-hpa
namespace: ai-serving
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: vllm-llama3
minReplicas: 1
maxReplicas: 4
metrics:
- type: Pods
pods:
metric:
name: vllm_num_requests_running
target:
type: AverageValue
averageValue: '50'
Part 2: Ollama
7. Ollama 소개
Ollama는 로컬 환경에서 LLM을 쉽게 실행할 수 있게 해주는 오픈소스 도구다. Docker처럼 ollama run llama3.1이라는 한 줄 명령으로 모델을 다운로드하고 즉시 대화할 수 있다.
7.1 아키텍처 특징
- GGUF 기반: llama.cpp의 GGUF(GPT-Generated Unified Format) 양자화 모델을 사용
- llama.cpp 엔진: 내부적으로 llama.cpp를 추론 엔진으로 사용
- 단일 바이너리: Go로 작성된 서버 + llama.cpp C++ 엔진을 하나의 바이너리로 배포
- 자동 GPU 가속: NVIDIA CUDA, AMD ROCm, Apple Metal을 자동 감지하여 GPU 오프로딩
- 모델 레지스트리:
ollama.com/library에서 사전 양자화된 모델을 Docker Hub처럼 pull/push
7.2 지원 모델
| 카테고리 | 모델 | 크기 |
|---|---|---|
| Meta Llama | llama3.1, llama3.2, llama3.3 | 1B ~ 405B |
| Mistral | mistral, mixtral | 7B ~ 8x22B |
gemma, gemma2, gemma3 | 2B ~ 27B | |
| Microsoft | phi3, phi4 | 3.8B ~ 14B |
| DeepSeek | deepseek-r1, deepseek-v3, deepseek-coder-v2 | 1.5B ~ 671B |
| Qwen | qwen, qwen2, qwen2.5, qwen3 | 0.5B ~ 72B |
| 코드 특화 | codellama, starcoder2, qwen2.5-coder | 3B ~ 34B |
| Embedding | nomic-embed-text, mxbai-embed-large, all-minilm | - |
| 멀티모달 | llava, bakllava, llama3.2-vision | 7B ~ 90B |
8. Ollama 설치 및 구동
8.1 플랫폼별 설치
macOS:
# Homebrew
brew install ollama
# 또는 공식 설치 스크립트
curl -fsSL https://ollama.com/install.sh | sh
Linux:
# 공식 설치 스크립트 (권장)
curl -fsSL https://ollama.com/install.sh | sh
# 또는 수동 설치
curl -L https://ollama.com/download/ollama-linux-amd64 -o /usr/local/bin/ollama
chmod +x /usr/local/bin/ollama
Windows:
공식 웹사이트(ollama.com)에서 Windows 설치 프로그램을 다운로드하여 실행한다.
Docker:
# CPU only
docker run -d -v ollama:/root/.ollama -p 11434:11434 \
--name ollama ollama/ollama
# NVIDIA GPU
docker run -d --gpus=all -v ollama:/root/.ollama -p 11434:11434 \
--name ollama ollama/ollama
# AMD GPU (ROCm)
docker run -d --device /dev/kfd --device /dev/dri \
-v ollama:/root/.ollama -p 11434:11434 \
--name ollama ollama/ollama:rocm
8.2 기본 사용법
# 서버 시작 (백그라운드에서 자동 시작되지 않는 경우)
ollama serve
# 모델 다운로드 및 대화
ollama run llama3.1
# 특정 태그(크기/양자화) 지정
ollama run llama3.1:8b
ollama run llama3.1:70b-instruct-q4_K_M
ollama run qwen2.5:32b-instruct-q5_K_M
# 모델만 다운로드 (실행하지 않음)
ollama pull llama3.1:8b
# 한줄 프롬프트
ollama run llama3.1 "PagedAttention이란 무엇인가요?"
9. Ollama CLI 명령어 완벽 정리
9.1 명령어 총정리
| 명령어 | 설명 | 주요 옵션 |
|---|---|---|
ollama serve | Ollama 서버 시작 | --help로 환경변수 확인 |
ollama run <model> | 모델 실행 (없으면 자동 pull) | --verbose, --nowordwrap, --format json |
ollama pull <model> | 모델 다운로드 | --insecure |
ollama push <model> | 모델을 레지스트리에 업로드 | --insecure |
ollama create <model> | Modelfile로 커스텀 모델 생성 | -f <Modelfile>, --quantize |
ollama list / ollama ls | 설치된 모델 목록 | - |
ollama show <model> | 모델 상세 정보 | --modelfile, --parameters, --system, --template, --license |
ollama cp <src> <dst> | 모델 복제 | - |
ollama rm <model> | 모델 삭제 | - |
ollama ps | 실행 중인 모델 목록 | - |
ollama stop <model> | 실행 중인 모델 중지 | - |
ollama signin | ollama.com 로그인 | - |
ollama signout | ollama.com 로그아웃 | - |
9.2 각 명령어 상세 예시
ollama serve - 서버 시작:
# 기본 시작 (localhost:11434)
ollama serve
# 환경변수로 바인드 주소 변경
OLLAMA_HOST=0.0.0.0:11434 ollama serve
# 디버그 모드
OLLAMA_DEBUG=1 ollama serve
ollama run - 모델 실행:
# 대화형 모드
ollama run llama3.1
# 한줄 프롬프트
ollama run llama3.1 "Explain quantum computing"
# JSON 형식 출력
ollama run llama3.1 "List 3 Korean cities" --format json
# 멀티모달 (이미지 입력)
ollama run llama3.2-vision "What's in this image? /path/to/image.png"
# verbose 모드 (성능 통계 표시)
ollama run llama3.1 --verbose
# 시스템 프롬프트와 함께
ollama run llama3.1 --system "You are a Korean translator."
ollama create - 커스텀 모델 생성:
# Modelfile 기반 생성
ollama create my-model -f ./Modelfile
# GGUF 파일로부터 생성
ollama create my-model -f ./Modelfile-from-gguf
# 양자화 변환
ollama create my-model-q4 --quantize q4_K_M -f ./Modelfile
ollama show - 모델 정보 확인:
# 전체 정보
ollama show llama3.1
# Modelfile 출력
ollama show llama3.1 --modelfile
# 파라미터 확인
ollama show llama3.1 --parameters
# 시스템 프롬프트 확인
ollama show llama3.1 --system
# 템플릿 확인
ollama show llama3.1 --template
ollama ps - 실행 중인 모델:
$ ollama ps
NAME ID SIZE PROCESSOR UNTIL
llama3.1:8b a]f2e33d4e25 6.7 GB 100% GPU 4 minutes from now
qwen2.5:7b 845dbda0ea48 4.7 GB 100% GPU 3 minutes from now
10. Ollama API 엔드포인트
Ollama는 REST API와 OpenAI 호환 API를 모두 제공한다. 기본 주소는 http://localhost:11434이다.
10.1 네이티브 API 엔드포인트
| 엔드포인트 | 메서드 | 설명 |
|---|---|---|
/api/generate | POST | 텍스트 Completion 생성 |
/api/chat | POST | Chat Completion 생성 |
/api/embed | POST | 임베딩 벡터 생성 |
/api/tags | GET | 로컬 모델 목록 |
/api/show | POST | 모델 상세 정보 |
/api/pull | POST | 모델 다운로드 |
/api/push | POST | 모델 업로드 |
/api/create | POST | 커스텀 모델 생성 |
/api/copy | POST | 모델 복제 |
/api/delete | DELETE | 모델 삭제 |
/api/ps | GET | 실행 중인 모델 목록 |
/api/version | GET | Ollama 버전 정보 |
10.2 OpenAI 호환 엔드포인트
| 엔드포인트 | 메서드 | 설명 |
|---|---|---|
/v1/chat/completions | POST | OpenAI Chat Completion 호환 |
/v1/completions | POST | OpenAI Completion 호환 |
/v1/models | GET | 모델 목록 (OpenAI 형식) |
/v1/embeddings | POST | 임베딩 (OpenAI 형식) |
10.3 API 호출 예제
Generate (Completion):
# 기본 생성
curl http://localhost:11434/api/generate -d '{
"model": "llama3.1",
"prompt": "Why is the sky blue?",
"stream": false
}'
# 스트리밍 (기본값)
curl http://localhost:11434/api/generate -d '{
"model": "llama3.1",
"prompt": "Write a haiku about coding",
"options": {
"temperature": 0.7,
"num_predict": 100
}
}'
# JSON 형식 출력
curl http://localhost:11434/api/generate -d '{
"model": "llama3.1",
"prompt": "List 3 programming languages as JSON",
"format": "json",
"stream": false
}'
Chat (대화):
curl http://localhost:11434/api/chat -d '{
"model": "llama3.1",
"messages": [
{"role": "system", "content": "You are a helpful Korean assistant."},
{"role": "user", "content": "서울의 명소를 추천해주세요."}
],
"stream": false,
"options": {
"temperature": 0.8,
"top_p": 0.9,
"num_ctx": 4096,
"num_predict": 512
}
}'
Embed (임베딩):
# 단일 텍스트 임베딩
curl http://localhost:11434/api/embed -d '{
"model": "nomic-embed-text",
"input": "Hello, world!"
}'
# 복수 텍스트 임베딩
curl http://localhost:11434/api/embed -d '{
"model": "nomic-embed-text",
"input": ["Hello world", "Goodbye world"]
}'
OpenAI 호환 API:
# OpenAI 형식 Chat Completion
curl http://localhost:11434/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "llama3.1",
"messages": [
{"role": "user", "content": "Hello!"}
],
"temperature": 0.7,
"max_tokens": 256
}'
# 모델 목록
curl http://localhost:11434/v1/models
Python에서 호출:
import requests
# Generate API
response = requests.post("http://localhost:11434/api/generate", json={
"model": "llama3.1",
"prompt": "Explain Docker in Korean",
"stream": False,
"options": {
"temperature": 0.7,
"num_predict": 512,
},
})
print(response.json()["response"])
# Chat API
response = requests.post("http://localhost:11434/api/chat", json={
"model": "llama3.1",
"messages": [
{"role": "user", "content": "쿠버네티스란?"},
],
"stream": False,
})
print(response.json()["message"]["content"])
# OpenAI SDK로 Ollama 사용
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama", # Ollama는 API key 불필요, 임의 값 입력
)
response = client.chat.completions.create(
model="llama3.1",
messages=[
{"role": "user", "content": "Python의 GIL을 설명해주세요."},
],
temperature=0.7,
max_tokens=512,
)
print(response.choices[0].message.content)
11. Ollama 파라미터 (Modelfile & API)
11.1 Modelfile 구조
Modelfile은 Ollama 커스텀 모델을 정의하는 파일이다. Dockerfile과 유사한 구조를 가진다.
# 기본 모델 지정 (필수)
FROM llama3.1:8b
# 파라미터 설정
PARAMETER temperature 0.7
PARAMETER top_p 0.9
PARAMETER top_k 40
PARAMETER num_ctx 4096
PARAMETER num_predict 512
PARAMETER repeat_penalty 1.1
PARAMETER stop "<|eot_id|>"
PARAMETER stop "<|end_of_text|>"
# 시스템 프롬프트
SYSTEM """
당신은 친절한 한국어 AI 어시스턴트입니다.
정확하고 간결한 답변을 제공하며, 필요할 때 예시를 들어 설명합니다.
"""
# 대화 템플릿 (Jinja2 또는 Go template)
TEMPLATE """
{{- if .System }}<|start_header_id|>system<|end_header_id|>
{{ .System }}<|eot_id|>{{ end }}
{{- range .Messages }}<|start_header_id|>{{ .Role }}<|end_header_id|>
{{ .Content }}<|eot_id|>{{ end }}<|start_header_id|>assistant<|end_header_id|>
"""
# LoRA 어댑터 적용 (선택)
ADAPTER /path/to/lora-adapter.gguf
# 라이선스 정보 (선택)
LICENSE """
Apache 2.0
"""
| 지시어 | 설명 | 필수 여부 |
|---|---|---|
FROM | 기본 모델 (모델 이름 또는 GGUF 파일 경로) | 필수 |
PARAMETER | 모델 파라미터 설정 | 선택 |
TEMPLATE | 프롬프트 템플릿 | 선택 |
SYSTEM | 시스템 프롬프트 | 선택 |
ADAPTER | LoRA/QLora 어댑터 경로 | 선택 |
LICENSE | 라이선스 정보 | 선택 |
MESSAGE | 대화 히스토리 사전 설정 | 선택 |
11.2 PARAMETER 옵션 상세
| 파라미터 | 타입 | 기본값 | 범위/설명 |
|---|---|---|---|
temperature | float | 0.8 | 0.0~2.0. 높을수록 창의적, 낮을수록 결정적 |
top_p | float | 0.9 | 0.0~1.0. Nucleus sampling 확률 임계값 |
top_k | int | 40 | 1~100. 상위 k개 토큰만 고려 |
min_p | float | 0.0 | 0.0~1.0. 최소 확률 필터링 |
num_predict | int | -1 | 생성할 최대 토큰 수 (-1: 무제한, -2: 컨텍스트 채울 때까지) |
num_ctx | int | 2048 | 컨텍스트 윈도우 크기 (토큰 수) |
repeat_penalty | float | 1.1 | 반복 페널티 (1.0이면 비활성화) |
repeat_last_n | int | 64 | 반복 체크 범위 (0: 비활성화, -1: num_ctx) |
seed | int | 0 | 랜덤 시드 (0이면 매번 다른 결과) |
stop | string | - | 생성 중단 문자열 (여러 개 지정 가능) |
num_gpu | int | auto | GPU에 오프로드할 레이어 수 (0: CPU only) |
num_thread | int | auto | CPU 스레드 수 |
num_batch | int | 512 | 프롬프트 처리 배치 크기 |
mirostat | int | 0 | Mirostat 샘플링 (0: 비활성화, 1: Mirostat, 2: Mirostat 2.0) |
mirostat_eta | float | 0.1 | Mirostat 학습률 |
mirostat_tau | float | 5.0 | Mirostat 타겟 엔트로피 |
tfs_z | float | 1.0 | Tail-Free Sampling (1.0이면 비활성화) |
typical_p | float | 1.0 | Locally Typical Sampling (1.0이면 비활성화) |
use_mlock | bool | false | 모델을 메모리에 고정 (swap 방지) |
num_keep | int | 0 | 컨텍스트 재활용 시 유지할 토큰 수 |
penalize_newline | bool | true | 줄바꿈 토큰에 페널티 적용 |
11.3 API에서 파라미터 사용
API 호출 시 options 필드로 파라미터를 전달한다.
curl http://localhost:11434/api/chat -d '{
"model": "llama3.1",
"messages": [
{"role": "user", "content": "Hello"}
],
"options": {
"temperature": 0.3,
"top_p": 0.9,
"top_k": 50,
"num_ctx": 8192,
"num_predict": 1024,
"repeat_penalty": 1.2,
"seed": 42,
"stop": ["<|eot_id|>"]
}
}'
12. Ollama 환경변수 총정리
12.1 서버 및 네트워크
| 환경변수 | 기본값 | 설명 |
|---|---|---|
OLLAMA_HOST | 127.0.0.1:11434 | 서버 바인드 주소와 포트 |
OLLAMA_ORIGINS | 없음 | CORS 허용 origin (쉼표 구분) |
OLLAMA_KEEP_ALIVE | 5m | 모델 언로드까지 유휴 시간 (5m, 1h, -1=영구 로드) |
OLLAMA_MAX_QUEUE | 512 | 최대 대기열 크기 (초과 시 요청 거부) |
OLLAMA_NUM_PARALLEL | 1 | 모델당 동시 요청 처리 수 |
OLLAMA_MAX_LOADED_MODELS | 1 (CPU), GPU수*3 | 동시 로드 가능한 최대 모델 수 |
12.2 스토리지 및 경로
| 환경변수 | 기본값 | 설명 |
|---|---|---|
OLLAMA_MODELS | OS별 기본 경로 | 모델 저장 디렉토리 |
OLLAMA_TMPDIR | 시스템 temp | 임시 파일 디렉토리 |
OLLAMA_NOPRUNE | 없음 | 부팅 시 미사용 blob 정리 비활성화 |
플랫폼별 기본 모델 저장 경로:
| OS | 기본 경로 |
|---|---|
| macOS | ~/.ollama/models |
| Linux | /usr/share/ollama/.ollama/models |
| Windows | C:\Users\<user>\.ollama\models |
12.3 GPU 및 성능
| 환경변수 | 기본값 | 설명 |
|---|---|---|
OLLAMA_FLASH_ATTENTION | 0 | Flash Attention 활성화 (1로 설정) |
OLLAMA_KV_CACHE_TYPE | f16 | KV Cache 양자화 타입 (f16, q8_0, q4_0) |
OLLAMA_GPU_OVERHEAD | 0 | GPU당 예약할 VRAM (바이트) |
OLLAMA_LLM_LIBRARY | auto | 사용할 LLM 라이브러리 강제 지정 |
CUDA_VISIBLE_DEVICES | 전체 GPU | 사용할 NVIDIA GPU 디바이스 번호 |
ROCR_VISIBLE_DEVICES | 전체 GPU | 사용할 AMD GPU 디바이스 번호 |
GPU_DEVICE_ORDINAL | 전체 GPU | 사용할 GPU 순서 |
12.4 로깅 및 디버그
| 환경변수 | 기본값 | 설명 |
|---|---|---|
OLLAMA_DEBUG | 0 | 디버그 로깅 활성화 (1로 설정) |
OLLAMA_NOHISTORY | 0 | 대화형 모드에서 readline 히스토리 비활성화 |
12.5 컨텍스트 및 추론
| 환경변수 | 기본값 | 설명 |
|---|---|---|
OLLAMA_CONTEXT_LENGTH | 4096 | 기본 컨텍스트 윈도우 크기 |
OLLAMA_NO_CLOUD | 0 | 클라우드 기능 비활성화 (1로 설정) |
HTTPS_PROXY / HTTP_PROXY | 없음 | 프록시 서버 설정 |
NO_PROXY | 없음 | 프록시 우회 호스트 |
12.6 환경변수 설정 방법
macOS (launchctl):
# 환경변수 설정
launchctl setenv OLLAMA_HOST "0.0.0.0:11434"
launchctl setenv OLLAMA_MODELS "/Volumes/ExternalSSD/ollama/models"
launchctl setenv OLLAMA_FLASH_ATTENTION "1"
launchctl setenv OLLAMA_KV_CACHE_TYPE "q8_0"
launchctl setenv OLLAMA_NUM_PARALLEL "4"
launchctl setenv OLLAMA_KEEP_ALIVE "-1"
# Ollama 재시작
brew services restart ollama
Linux (systemd):
# systemd 서비스 오버라이드 생성
sudo systemctl edit ollama
# 에디터에서 다음 내용 추가:
[Service]
Environment="OLLAMA_HOST=0.0.0.0:11434"
Environment="OLLAMA_MODELS=/data/ollama/models"
Environment="OLLAMA_FLASH_ATTENTION=1"
Environment="OLLAMA_KV_CACHE_TYPE=q8_0"
Environment="OLLAMA_NUM_PARALLEL=4"
Environment="OLLAMA_KEEP_ALIVE=-1"
Environment="OLLAMA_MAX_LOADED_MODELS=3"
Environment="CUDA_VISIBLE_DEVICES=0,1"
# 서비스 재시작
sudo systemctl daemon-reload
sudo systemctl restart ollama
Docker:
docker run -d --gpus=all \
-e OLLAMA_HOST=0.0.0.0:11434 \
-e OLLAMA_FLASH_ATTENTION=1 \
-e OLLAMA_KV_CACHE_TYPE=q8_0 \
-e OLLAMA_NUM_PARALLEL=4 \
-e OLLAMA_KEEP_ALIVE=-1 \
-v /data/ollama:/root/.ollama \
-p 11434:11434 \
--name ollama \
ollama/ollama
13. Ollama 고급 활용
13.1 Modelfile 작성 가이드
한국어 어시스턴트 모델:
FROM llama3.1:8b
PARAMETER temperature 0.7
PARAMETER top_p 0.9
PARAMETER num_ctx 4096
PARAMETER num_predict 1024
PARAMETER repeat_penalty 1.15
PARAMETER stop "<|eot_id|>"
PARAMETER stop "<|end_of_text|>"
SYSTEM """
당신은 대한민국의 문화와 역사에 정통한 한국어 AI 어시스턴트입니다.
항상 정확하고 친절하게 한국어로 응답하며, 필요시 영어 기술 용어를 병기합니다.
답변은 구조적으로 정리하여 제공합니다.
"""
MESSAGE user 안녕하세요, 자기소개 해주세요.
MESSAGE assistant 안녕하세요! 저는 한국어에 특화된 AI 어시스턴트입니다. 한국의 문화, 역사, 기술 등 다양한 주제에 대해 도움을 드릴 수 있습니다. 무엇이든 물어보세요!
# 모델 생성
ollama create korean-assistant -f ./Modelfile-korean
# 실행
ollama run korean-assistant "서울 3대 궁궐에 대해 알려줘"
코드 리뷰 모델:
FROM qwen2.5-coder:7b
PARAMETER temperature 0.2
PARAMETER top_p 0.85
PARAMETER num_ctx 8192
PARAMETER num_predict 2048
SYSTEM """
You are an expert code reviewer. Analyze code for:
1. Bugs and potential issues
2. Performance improvements
3. Security vulnerabilities
4. Code style and best practices
Provide specific, actionable feedback with corrected code examples.
"""
양자화 레벨 선택 가이드:
| 양자화 | 크기 비율 | 품질 | 속도 | 추천 용도 |
|---|---|---|---|---|
Q2_K | ~30% | 낮음 | 매우 빠름 | 테스트용 |
Q3_K_M | ~37% | 보통 | 빠름 | 메모리 제한 환경 |
Q4_0 | ~42% | 양호 | 빠름 | 일반 사용 (기본) |
Q4_K_M | ~45% | 양호+ | 빠름 | 일반 사용 (권장) |
Q5_K_M | ~53% | 우수 | 보통 | 품질 중시 |
Q6_K | ~62% | 매우 우수 | 보통 | 높은 품질 요구 |
Q8_0 | ~80% | 최우수 | 느림 | 원본에 가까운 품질 |
F16 | 100% | 원본 | 느림 | 기준선/벤치마크 |
13.2 GPU 가속 설정
NVIDIA GPU:
# NVIDIA 드라이버 확인
nvidia-smi
# 특정 GPU만 사용
CUDA_VISIBLE_DEVICES=0 ollama serve
# 멀티 GPU
CUDA_VISIBLE_DEVICES=0,1 ollama serve
AMD GPU (ROCm):
# ROCm 드라이버 확인
rocm-smi
# 특정 GPU 지정
ROCR_VISIBLE_DEVICES=0 ollama serve
Apple Silicon (Metal):
macOS에서는 자동으로 Metal GPU 가속이 활성화된다. 별도 설정이 불필요하다.
# GPU 사용 확인 (ollama ps에서 Processor 컬럼)
ollama ps
# NAME ID SIZE PROCESSOR UNTIL
# llama3.1:8b a]f2e33d4e25 6.7 GB 100% GPU 4 minutes from now
13.3 Docker 배포
# docker-compose.yaml
version: '3.8'
services:
ollama:
image: ollama/ollama
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
ports:
- '11434:11434'
volumes:
- ollama_data:/root/.ollama
environment:
- OLLAMA_HOST=0.0.0.0:11434
- OLLAMA_FLASH_ATTENTION=1
- OLLAMA_KV_CACHE_TYPE=q8_0
- OLLAMA_NUM_PARALLEL=4
- OLLAMA_KEEP_ALIVE=24h
restart: unless-stopped
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:11434/api/version']
interval: 30s
timeout: 5s
retries: 3
# 모델 초기화 (선택)
ollama-init:
image: curlimages/curl:latest
depends_on:
ollama:
condition: service_healthy
entrypoint: >
sh -c "
curl -s http://ollama:11434/api/pull -d '{\"name\": \"llama3.1:8b\"}' &&
curl -s http://ollama:11434/api/pull -d '{\"name\": \"nomic-embed-text\"}'
"
volumes:
ollama_data:
13.4 멀티모달 모델 활용
# LLaVA 모델 실행
ollama run llava "What's in this image? /path/to/photo.jpg"
# Llama 3.2 Vision
ollama run llama3.2-vision "이 이미지를 한국어로 설명해주세요. /path/to/image.png"
import requests
import base64
# 이미지를 base64로 인코딩
with open("image.jpg", "rb") as f:
image_base64 = base64.b64encode(f.read()).decode("utf-8")
response = requests.post("http://localhost:11434/api/chat", json={
"model": "llava",
"messages": [
{
"role": "user",
"content": "이 이미지에 무엇이 있나요?",
"images": [image_base64],
}
],
"stream": False,
})
print(response.json()["message"]["content"])
13.5 Tool Calling / Function Calling
Ollama는 OpenAI 호환 Tool Calling을 지원한다.
from openai import OpenAI
import json
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a city",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name"},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
},
},
"required": ["city"],
},
},
}
]
response = client.chat.completions.create(
model="llama3.1",
messages=[
{"role": "user", "content": "서울의 현재 날씨는?"}
],
tools=tools,
tool_choice="auto",
)
message = response.choices[0].message
if message.tool_calls:
for tool_call in message.tool_calls:
print(f"Function: {tool_call.function.name}")
print(f"Arguments: {tool_call.function.arguments}")
Part 3: 비교 및 실전
14. vLLM vs Ollama 비교
14.1 종합 비교표
| 항목 | vLLM | Ollama |
|---|---|---|
| 주요 용도 | 프로덕션 API 서빙, 고처리량 추론 | 로컬 개발, 프로토타이핑, 개인 사용 |
| 엔진 | 자체 엔진 (PagedAttention) | llama.cpp |
| 모델 형식 | HF Safetensors, AWQ, GPTQ, FP8 | GGUF (양자화) |
| API | OpenAI 호환 | 네이티브 + OpenAI 호환 |
| 설치 난이도 | 중간 (Python/CUDA 환경 필요) | 매우 쉬움 (단일 바이너리) |
| GPU 요구 | 거의 필수 (NVIDIA/AMD) | 선택 (CPU에서도 동작) |
| 멀티 GPU | TP + PP (최대 수백 GPU) | 자동 분산 (제한적) |
| 동시 처리 | 수백~수천 요청 | 기본 1~4 병렬 |
| 양자화 | AWQ, GPTQ, FP8, BnB | GGUF Q2~Q8, F16 |
| Continuous Batching | 지원 | 미지원 (llama.cpp 제한) |
| PagedAttention | 핵심 기술 | 미지원 |
| Prefix Caching | 지원 (자동) | 미지원 |
| LoRA 서빙 | 멀티 LoRA 동시 서빙 | 단일 LoRA |
| Structured Output | JSON Schema, Regex, Grammar | JSON 모드 |
| Speculative Decoding | 지원 (Draft model, N-gram) | 미지원 |
| Streaming | 지원 | 지원 |
| Docker 배포 | 공식 이미지 (GPU) | 공식 이미지 (CPU/GPU) |
| Kubernetes | 공식 가이드 + Production Stack | 커뮤니티 Helm Chart |
| 메모리 효율 | 매우 높음 (< 4% 낭비) | 높음 (GGUF 양자화) |
| 라이선스 | Apache 2.0 | MIT |
14.2 처리량 비교 (Llama 3.1 8B, RTX 4090)
| 동시 사용자 | vLLM (tokens/s) | Ollama (tokens/s) | 배수 |
|---|---|---|---|
| 1 | ~140 | ~65 | 2.2x |
| 5 | ~500 | ~120 | 4.2x |
| 10 | ~800 | ~150 | 5.3x |
| 50 | ~1,200 | ~150 | 8.0x |
| 100 | ~1,500 | ~150 (큐 대기) | 10.0x |
Red Hat의 벤치마크에서는 동일 하드웨어에서 vLLM이 793 TPS vs Ollama 41 TPS로 19배 차이를 보인 사례도 있다. 이는 동시 요청 수, 배치 크기, 모델 크기에 따라 달라진다.
15. 성능 벤치마크
15.1 Throughput (처리량) 비교
| 메트릭 | vLLM | Ollama | 비고 |
|---|---|---|---|
| 단일 요청 TPS | 100~140 tok/s | 50~70 tok/s | RTX 4090, Llama 3.1 8B |
| 10 동시 요청 총 TPS | 700~900 tok/s | 120~200 tok/s | Continuous Batching 효과 |
| 50 동시 요청 총 TPS | 1,000~1,500 tok/s | ~150 tok/s | Ollama는 큐 대기 발생 |
| 배치 추론 (1K prompts) | 2,000~3,000 tok/s | 지원 안 함 | vLLM offline inference |
15.2 Latency (지연시간) 비교
| 메트릭 | vLLM | Ollama | 비고 |
|---|---|---|---|
| TTFT (Time To First Token) | 50~200 ms | 100~500 ms | 프롬프트 길이에 따라 변동 |
| TPOT (Time Per Output Token) | 7~15 ms | 15~25 ms | 단일 요청 기준 |
| P99 Latency | 80~150 ms | 500~700 ms | 10 동시 요청 기준 |
| 모델 로딩 시간 | 30~120 초 | 5~30 초 | GGUF가 더 빠름 |
15.3 메모리 사용량 비교 (Llama 3.1 8B)
| 설정 | vLLM GPU 메모리 | Ollama GPU 메모리 | 비고 |
|---|---|---|---|
| FP16 | ~16 GB | N/A | vLLM 기본 |
| FP8 | ~9 GB | N/A | H100 전용 |
| AWQ 4-bit | ~5 GB | N/A | vLLM 양자화 |
| GPTQ 4-bit | ~5 GB | N/A | vLLM 양자화 |
| Q4_K_M (GGUF) | N/A | ~5.5 GB | Ollama 기본 |
| Q5_K_M (GGUF) | N/A | ~6.2 GB | 더 높은 품질 |
| Q8_0 (GGUF) | N/A | ~9 GB | 최고 품질 양자화 |
| KV Cache 포함 (4K ctx) | +0.5~2 GB | +0.5~1.5 GB | 시퀀스 수에 비례 |
16. 실전 시나리오별 추천
16.1 개인 개발자 로컬 환경
추천: Ollama
# 설치 후 즉시 사용
ollama run llama3.1
# VS Code + Continue 확장 연동
# settings.json에 Ollama 엔드포인트 설정
이유: 설치가 간단하고, CPU에서도 동작하며, macOS/Windows/Linux 모두 지원. IDE 확장과의 연동이 쉬움.
16.2 프로덕션 API 서빙
추천: vLLM
vllm serve meta-llama/Llama-3.1-70B-Instruct \
--tensor-parallel-size 4 \
--gpu-memory-utilization 0.90 \
--max-num-seqs 256 \
--enable-prefix-caching \
--enable-chunked-prefill \
--api-key ${API_KEY}
이유: Continuous Batching으로 동시 요청 처리 능력이 압도적. PagedAttention으로 메모리 효율성이 높음. 멀티 GPU 지원, Kubernetes 배포, 모니터링 연동이 성숙함.
16.3 엣지/IoT 환경
추천: Ollama + 높은 양자화
# 작은 모델 + 고양자화
ollama run phi3:3.8b-mini-instruct-4k-q4_0
# 또는 Qwen 0.5B
ollama run qwen2.5:0.5b
이유: 단일 바이너리로 배포 간편. GGUF 양자화로 저사양에서도 동작. CPU 전용 추론 지원.
16.4 대규모 배치 추론
추천: vLLM Offline Inference
from vllm import LLM, SamplingParams
llm = LLM(
model="meta-llama/Llama-3.1-8B-Instruct",
tensor_parallel_size=2,
gpu_memory_utilization=0.95,
)
# 수천 개의 프롬프트를 한 번에 처리
prompts = load_prompts_from_file("prompts.jsonl") # 10,000+ prompts
sampling_params = SamplingParams(temperature=0.0, max_tokens=512)
outputs = llm.generate(prompts, sampling_params)
save_outputs(outputs, "results.jsonl")
이유: GPU 메모리를 최대한 활용하는 배치 스케줄링. 수천~수만 개 프롬프트를 효율적으로 처리.
16.5 RAG 파이프라인
둘 다 가능 -- 상황에 따라 선택:
# Ollama 기반 RAG (개발/소규모)
from langchain_ollama import OllamaLLM, OllamaEmbeddings
llm = OllamaLLM(model="llama3.1")
embeddings = OllamaEmbeddings(model="nomic-embed-text")
# vLLM 기반 RAG (프로덕션)
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
llm = ChatOpenAI(
base_url="http://vllm-server:8000/v1",
api_key="token",
model="meta-llama/Llama-3.1-8B-Instruct",
)
17. 요청 추적 (Request Tracing) 연동
프로덕션 환경에서 LLM 요청을 추적하는 것은 디버깅, 감사(audit), 성능 모니터링에 필수적이다.
17.1 vLLM의 Request ID 추적
vLLM은 OpenAI API 호환 서버에서 자동으로 request_id를 생성한다. 커스텀 ID를 전달하려면 extra_body를 사용한다.
from openai import OpenAI
import uuid
client = OpenAI(base_url="http://localhost:8000/v1", api_key="token")
# 커스텀 request_id 전달
xid = str(uuid.uuid4())
response = client.chat.completions.create(
model="meta-llama/Llama-3.1-8B-Instruct",
messages=[{"role": "user", "content": "Hello"}],
extra_headers={"X-Request-ID": xid},
)
print(f"XID: {xid}")
print(f"Response ID: {response.id}")
17.2 Ollama의 요청 추적
Ollama 네이티브 API는 별도의 request ID를 지원하지 않으므로, 리버스 프록시에서 처리한다.
import requests
import uuid
xid = str(uuid.uuid4())
response = requests.post(
"http://localhost:11434/api/chat",
headers={"X-Request-ID": xid},
json={
"model": "llama3.1",
"messages": [{"role": "user", "content": "Hello"}],
"stream": False,
},
)
# 로깅에 xid 포함
import logging
logger = logging.getLogger(__name__)
logger.info(f"[xid={xid}] Response: {response.status_code}")
17.3 API Gateway에서 X-Request-ID 전달
NGINX 설정:
upstream vllm_backend {
server vllm-server:8000;
}
server {
listen 80;
location /v1/ {
# X-Request-ID가 없으면 자동 생성
set $request_id $http_x_request_id;
if ($request_id = "") {
set $request_id $request_id;
}
proxy_pass http://vllm_backend;
proxy_set_header X-Request-ID $request_id;
proxy_set_header Host $host;
# 응답 헤더에 X-Request-ID 추가
add_header X-Request-ID $request_id always;
# 액세스 로그에 request_id 포함
access_log /var/log/nginx/vllm_access.log combined_with_xid;
}
}
# 로그 포맷 정의
log_format combined_with_xid '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'xid="$http_x_request_id"';
17.4 OpenTelemetry 연동
# vLLM + OpenTelemetry 분산 추적
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
# Tracer 초기화
provider = TracerProvider()
exporter = OTLPSpanExporter(endpoint="http://jaeger:4317")
provider.add_span_processor(BatchSpanProcessor(exporter))
trace.set_tracer_provider(provider)
tracer = trace.get_tracer(__name__)
# LLM 호출을 Span으로 래핑
def call_llm(prompt: str, xid: str) -> str:
with tracer.start_as_current_span("llm_inference") as span:
span.set_attribute("xid", xid)
span.set_attribute("model", "llama-3.1-8b")
span.set_attribute("prompt_length", len(prompt))
response = client.chat.completions.create(
model="meta-llama/Llama-3.1-8B-Instruct",
messages=[{"role": "user", "content": prompt}],
extra_headers={"X-Request-ID": xid},
)
result = response.choices[0].message.content
span.set_attribute("response_length", len(result))
span.set_attribute("tokens_used", response.usage.total_tokens)
return result
17.5 로깅에서 xid 활용 패턴
Python 예제:
import logging
import uuid
from contextvars import ContextVar
# Context Variable로 xid 관리
request_xid: ContextVar[str] = ContextVar("request_xid", default="")
class XIDFilter(logging.Filter):
def filter(self, record):
record.xid = request_xid.get("")
return True
# 로거 설정
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter(
"%(asctime)s [%(levelname)s] [xid=%(xid)s] %(message)s"
))
handler.addFilter(XIDFilter())
logger = logging.getLogger("llm_service")
logger.addHandler(handler)
logger.setLevel(logging.INFO)
# 사용
async def handle_request(prompt: str):
xid = str(uuid.uuid4())
request_xid.set(xid)
logger.info(f"Received prompt: {prompt[:50]}...")
response = await call_llm(prompt, xid)
logger.info(f"Generated {len(response)} chars")
return {"xid": xid, "response": response}
Go 예제:
package main
import (
"context"
"fmt"
"log/slog"
"net/http"
"github.com/google/uuid"
)
type contextKey string
const xidKey contextKey = "xid"
// XID 미들웨어
func xidMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
xid := r.Header.Get("X-Request-ID")
if xid == "" {
xid = uuid.New().String()
}
ctx := context.WithValue(r.Context(), xidKey, xid)
w.Header().Set("X-Request-ID", xid)
slog.Info("request received",
"xid", xid,
"method", r.Method,
"path", r.URL.Path,
)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
// Ollama 호출 함수
func callOllama(ctx context.Context, prompt string) (string, error) {
xid := ctx.Value(xidKey).(string)
slog.Info("calling ollama",
"xid", xid,
"prompt_len", len(prompt),
)
// ... Ollama API 호출 로직 ...
slog.Info("ollama response received",
"xid", xid,
"response_len", len(response),
)
return response, nil
}
18. 참고 자료 (References)
vLLM
- vLLM 공식 문서
- vLLM GitHub
- vLLM 서버 인자 (Server Arguments)
- vLLM 환경변수 (Environment Variables)
- vLLM Docker 배포 가이드
- vLLM Kubernetes 배포 가이드
- vLLM 양자화 (Quantization)
- vLLM Production Stack (GitHub)
Ollama
- Ollama 공식 문서
- Ollama GitHub
- Ollama API 문서
- Ollama Modelfile 레퍼런스
- Ollama CLI 레퍼런스
- Ollama FAQ (환경변수 포함)
- Ollama 모델 라이브러리
논문 및 기술 자료
- Efficient Memory Management for Large Language Model Serving with PagedAttention (2023)
- Inside vLLM: Anatomy of a High-Throughput LLM Inference System (vLLM Blog, 2025)
- Ollama vs. vLLM: Performance Benchmarking (Red Hat Developer, 2025)
- vLLM vs Ollama vs llama.cpp vs TGI vs TensorRT-LLM: 2025 Guide (ITECS)