16장. vLLM 서빙 벤치마킹과 RAG 최적화
vLLM 띄우고 재기
새 주방 설비는 매장이 아니라 설비를 받쳐 줄 전기와 배관이 있는 곳에 놓아야 합니다. vLLM 도 Linux 와 NVIDIA GPU 를 바탕으로 만들어져, Windows 에서는 바로 설치되지 않습니다. Linux 서버나 Windows 의 WSL2(윈도 안의 리눅스)에서 띄웁니다.
1번은 Linux(또는 WSL2)의 파이썬 환경에 vLLM 을 설치합니다. 2번은 vllm serve 로 모델을 올려 OpenAI 호환 서버를 띄웁니다. 3번은 지금까지의 openai 코드에서 주소만 바꿔 부릅니다. 4번은 vLLM 에 들어 있는 벤치마크 명령으로 TTFT, TPOT, 처리량을 잽니다. 전기와 배관이 Linux 와 GPU 드라이버, 새 설비가 vLLM 입니다.
띄울 때 쓰는 옵션
| 옵션 | 뜻 | 4GB GPU 에서 |
|---|---|---|
--max-model-len | 요청 하나의 최대 토큰 수 | 줄여야 KV 캐시 자리가 남는다(예: 4096) |
--gpu-memory-utilization | vLLM 이 잡을 GPU 메모리의 몫 | 0.85 안팎 |
--dtype | 계산 정밀도 | GTX 1650 은 bf16 이 없어 half(fp16) |
--max-num-seqs | 연속 배치에 함께 넣을 최대 요청 수 | 메모리에 맞춰 줄인다 |
--enable-prefix-caching | 앞부분이 같은 프롬프트의 KV 캐시를 다시 쓴다 | 켜 둔다 |
서버 띄우기
아래는 이 PC 에서 실행하지 않은 안내 코드입니다. vLLM 은 GPU 의 계산 능력(compute capability)이 7.0 이상이어야 하는데 GTX 1650 은 7.5 라서 조건은 맞지만, 4GB 에서는 0.5B 급 모델만 넉넉히 올라갑니다.
# Linux 나 WSL2 의 우분투 터미널에서
python -m venv vllm-env && source vllm-env/bin/activate
pip install vllm
vllm serve Qwen/Qwen2.5-0.5B-Instruct --dtype half --max-model-len 4096 \
--gpu-memory-utilization 0.85 --enable-prefix-caching --port 8000예상 출력(실행하지 않고 유추한 예시)입니다. 판과 GPU 에 따라 글귀와 숫자가 다릅니다.
INFO ... Loading model weights took 약 1GB
INFO ... GPU KV cache size: 약 수만 tokens
INFO ... Maximum concurrency for 4,096 tokens per request: 약 10배 안팎
INFO ... Starting vLLM API server on http://0.0.0.0:8000벤치마크 명령
같은 서버를 vLLM 의 벤치마크 명령으로 잽니다. 이것도 이 PC 에서 실행하지 않은 안내 코드입니다.
vllm bench serve --model Qwen/Qwen2.5-0.5B-Instruct --base-url http://127.0.0.1:8000 \
--dataset-name random --random-input-len 256 --random-output-len 128 \
--num-prompts 200 --max-concurrency 16예상 출력(실행하지 않고 유추한 예시)입니다. 실제 숫자는 GPU 에 따라 몇 배씩 달라집니다.
============ Serving Benchmark Result ============
Successful requests: 200
Request throughput (req/s): 약 수 개
Output token throughput (tok/s): 약 수백~1천
Mean TTFT (ms): 약 수십~수백
Mean TPOT (ms): 약 10~30
P99 TTFT (ms): 평균보다 몇 배 크다
==================================================부르는 코드 꼴
서버가 뜨면 Ollama 를 부르던 코드에서 주소와 모델 이름만 바꿉니다. 아래 코드는 요청 본문을 만들어 보기만 하고 서버에 보내지는 않습니다.
import json
base_url = "http://127.0.0.1:8000/v1" # vLLM 서버 주소(Ollama 는 11434 포트)
request = {
"model": "Qwen/Qwen2.5-0.5B-Instruct",
"messages": [{"role": "system", "content": "규정 도우미입니다."},
{"role": "user", "content": "재택 근무는 며칠까지 되나요?"}],
"max_tokens": 80, "temperature": 0, "stream": True, # stream 이면 TTFT 를 잴 수 있다
}
print("POST", base_url + "/chat/completions")
print(json.dumps(request, ensure_ascii=False)[:120], "...")코드에 나오는 함수
| 코드 | 쉬운 뜻 |
|---|---|
"role": "system" | system(시스템)은 모델이 지킬 역할과 규칙을 주는 말이다. "user" 는 사용자의 질문이다 |
"stream": True | stream(시냇물, 흐름)처럼 답을 다 만들기 전에 토큰이 생기는 대로 조금씩 보내게 한다. 첫 조각이 오는 때로 TTFT 를 잰다 |
json.dumps(request, ...) | JSON(JavaScript Object Notation) 글자로 바꾼다. dumps 는 dump string(글자로 쏟아 내기)의 줄임말이다 |
ensure_ascii=False | ASCII(American Standard Code for Information Interchange, 영문 위주의 기본 글자표) 밖의 글자도 그대로 둔다. 한글이 연 같은 꼴로 바뀌지 않는다 |
[:120] | 글자가 길어 앞의 120자만 보여 준다 |
이 코드가 뜻하는 것
우체국에 가기 전에 봉투에 받는 곳 주소를 쓰고 편지를 넣어 보기만 하는 일과 같습니다. 아직 부치지는 않습니다. 여기서 봉투의 주소가 base_url + "/chat/completions", 편지가 request 본문입니다.
| 줄 | 하는 일 | 쉽게 말하면 |
|---|---|---|
base_url = ... | vLLM 서버 주소를 적는다 | 받는 곳 주소 |
request = {...} | 모델 이름, 대화, 길이, 온도, 스트리밍을 담는다 | 편지 내용 |
print("POST", ...) | 보낼 주소를 찍는다. POST 는 "부치다"라는 뜻의 HTTP 방식이다 | 봉투의 주소를 확인한다 |
print(json.dumps(...)[:120], "...") | 본문을 JSON 글자로 바꿔 앞부분만 찍는다 | 편지의 첫 줄을 훑어본다 |
출력은 주소 한 줄과, 한글이 그대로 보이는 JSON 앞부분 한 줄입니다. 서버에 보내지 않으므로 vLLM 이 없어도 돌아갑니다.
작게 줄여서 눈으로 보기
ensure_ascii 를 주고 안 주고의 차이를 봅니다.
import json
body = {"질문": "연차"}
print(json.dumps(body)) # 기본값: 한글을 \u 꼴로 바꾼다
print(json.dumps(body, ensure_ascii=False)) # 한글을 그대로 둔다
print(json.dumps(body, ensure_ascii=False)[:5], "...") # 앞 다섯 글자만{"\uc9c8\ubb38": "\uc5f0\ucc28"}
{"질문": "연차"}
{"질문" ...| 결과 | 읽는 법 |
|---|---|
{"질문": "연차"} | 기본값에서는 한글을 \u 뒤 번호로 바꿔 적는다. 서버는 읽을 수 있지만 사람은 읽기 어렵다 |
{"질문": "연차"} | ensure_ascii=False 를 주면 한글이 그대로 나온다 |
{"질문" ... | 글자 자르기 [:5] 로 앞 다섯 글자만 남겼다 |
주의할 점
- vLLM 은 시작할 때 GPU 메모리를 크게 잡아 두므로, 같은 GPU 에서 Ollama 나 다른 모델을 함께 돌리기 어렵습니다.
- 공개 벤치마크의 숫자는 큰 GPU 에서 잰 것이 많습니다. 우리 GPU 와 우리 질문 길이로 다시 재야 계획을 세울 수 있습니다.