๋ฐฐ๊ฒฝ
์ฌ๋ด LLM ์๋น์ค ๊ฐ๋ฐ ์ค vLLM ์ด ๋ณ๋ ฌ์ฒ๋ฆฌ ๋์ง ์๋ ํ์์ด ๋ฐ์ํ์ต๋๋ค. vLLM ๋ก๊ทธ๋ฅผ ๋ณด๋ฉด vLLM ์๋ฒ์ ์์ฒญ์ด ํ๋์ฉ ์ ์ก๋์ด ์ฒ๋ฆฌ๋๊ณ ์๋๊ฒ์ ์ ์ ์์๋๋ฐ, ์ฒ์์ vLLM ๋ด๋ถ์์ multi GPU ์ธ์์ ํ์ง ๋ชปํด vram ์ ๊ณผ๋คํ๊ฒ ์ ์ ํ์ฌ ๋ณ๋ ฌ์ฒ๋ฆฌ๊ฐ ๋์ง ์๋ ๋ฌธ์ ๋ผ๊ณ ์๊ฐํ์ต๋๋ค.
ํ์ง๋ง, vLLM ์คํ์ multi gpu ์ต์ ์ ์คฌ๊ณ , ๋ก๊ทธ๋ฅผ ์ฐ์ด๋ณด์๋ 2๊ฐ์ gpu ๊ฐ ์ ์ธ์๋์ด ์๋๊ฒ์ ํ์ธํ๊ณ ๋ฌธ์ ๋ฅผ ์ฐพ๋ค FastAPI ์์ vLLM ์ ์์ฒญ์ ๋ณด๋ผ ๋ openai ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋๊ฒ์ด ๋ฌธ์ ์์ ์๊ฒ ๋์์ต๋๋ค. openai ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค OpenAI ๋ฅผ ์ฌ์ฉํ๋ฉด ๋๊ธฐ Request ๋ก ์๋ํ๊ณ AysncOpenAI ๋ฅผ ์ฌ์ฉํด์ผ ๋น๋๊ธฐ ์๋์ ํ๋ ๊ฒ์ ์๊ฒ ๋์์ต๋๋ค.
ํด๋น๋ด์ฉ์ ์ ๋ฆฌํ ๊ฒธ Request ๋ฅผ ์ฌ์ฉํ ๋ฐฉ์๊ฐ httpx ๋ฅผ ์ฌ์ฉํ ์์ฒญ๋ฐฉ์์ ์ฐจ์ด์ ๊ทธ๋ฆฌ๊ณ FastAPI ์ ๋๊ธฐ/๋น๋๊ธฐ, ๋ณ๋ ฌ๊ณผ ๋น๋๊ธฐ์ ์๋๋ฐฉ์์ ์ ๋ฆฌํ๋ ค๊ณ ํฉ๋๋ค.
1. FastAPI ๋๊ธฐ / ๋น๋๊ธฐ ์ฒ๋ฆฌ ๋ฐฉ์
FastAPI๋ ์๋ํฌ์ธํธ ํจ์๊ฐ def ์ธ์ง async def ์ธ์ง์ ๋ฐ๋ผ ์์ ํ ๋ค๋ฅธ ๋ฐฉ์์ผ๋ก ๋์ํฉ๋๋ค.
1.1 ๋๊ธฐ ์๋ํฌ์ธํธ (def)
from fastapi import FastAPI
import time
app = FastAPI()
@app.get("/sync")
def sync_endpoint():
time.sleep(5)
return {"msg": "done"}
๋๊ธฐ ์๋ํฌ์ธํธ์ ๊ฒฝ์ฐ FastAPI๋ ๋ด๋ถ์ ์ผ๋ก ThreadPoolExecutor๋ฅผ ์ฌ์ฉํด ์์ฒญ์ ์ฒ๋ฆฌํฉ๋๋ค.
์ฆ, ์์ฒญ ํ๋๋น ์ค๋ ๋ ํ๋๋ฅผ ์ ์ ํ๊ฒ ๋ฉ๋๋ค. ์ด ๋ฐฉ์์ ๋ฌธ์ ๋ ์ธ๋ถ API ํธ์ถ๊ณผ ๊ฐ์ด I/O ๋๊ธฐ ์๊ฐ์ด ๊ธด ์์ ์ด ์์ ๊ฒฝ์ฐ์ ๋๋ค. ์๋ต์ด ์ฌ ๋๊น์ง ์ค๋ ๋๊ฐ ์ ์ ๋๊ธฐ ๋๋ฌธ์, ๋์์ ์ฒ๋ฆฌํ ์ ์๋ ์์ฒญ ์๊ฐ ๊ธ๊ฒฉํ ์ค์ด๋ค๊ฒ ๋ฉ๋๋ค. ์ด ๊ฒฝ์ฐ vLLM ์ ์ฅ์์๋ ์์ฒญ์ด ํ๋์ฉ ์์ฐจ์ ์ผ๋ก ๋ค์ด์ค๋ ๊ฒ์ฒ๋ผ ๋ณด์ด๊ฒ ๋ฉ๋๋ค.
1.2 ๋น๋๊ธฐ ์๋ํฌ์ธํธ (async def)
from fastapi import FastAPI
import asyncio
app = FastAPI()
@app.get("/async")
async def async_endpoint():
await asyncio.sleep(5)
return {"msg": "done"}
๋น๋๊ธฐ ์๋ํฌ์ธํธ๋ ์ด๋ฒคํธ ๋ฃจํ ๊ธฐ๋ฐ์ผ๋ก ๋์ํฉ๋๋ค. I/O ์์ ์ ๊ธฐ๋ค๋ฆฌ๋ ๋์ ์ ์ด๊ถ์ ์ด๋ฒคํธ ๋ฃจํ์ ๋ฐํํ๊ณ , ๋ค๋ฅธ ์์ฒญ์ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค. ๋ค๋ง ์ฌ๊ธฐ์ ์ค์ํ ์ ์, async def๋ก ์ ์ธํ๋ค๊ณ ํด์ ์๋์ผ๋ก ๋น๋๊ธฐ๊ฐ ๋๋ ๊ฒ์ ์๋๋๋ค. ์๋ํฌ์ธํธ ๋ด๋ถ์์ ์ฌ์ฉํ๋ ๋ชจ๋ I/O ์์ ์ด ๋น๋๊ธฐ์ฌ์ผ๋ง ์๋ฏธ ์๋ ๋น๋๊ธฐ ์ฒ๋ฆฌ๊ฐ ๋ฉ๋๋ค.
์๋์์ ์ถ๊ฐ์ ์ผ๋ก ์ค๋ช ํ๊ฒ ์ง๋ง, ๋น๋๊ธฐ ์์ ์ ๋ณ๋ ฌ๊ณผ ๋ค๋ฆ ๋๋ค. ๋น๋๊ธฐ์์ ์ ๋์์ฑ ์์ ์ผ๋ก ๋์์ ์ฒ๋ฆฌ๋๋ ๊ฒ ์ฒ๋ผ ๋ณด์ด๊ฒ ๋ฉ๋๋ค.
2. FastAPI์์์ ๋์์ฑ๊ณผ ๋ณ๋ ฌ์ฑ
Concurrency and async / await - FastAPI
FastAPI framework, high performance, easy to learn, fast to code, ready for production
fastapi.tiangolo.com
์ด๋ฒ ์ด์๋ฅผ ์ดํดํ๊ธฐ ์ํด์๋ ๋์์ฑ๊ณผ ๋ณ๋ ฌ์ฑ์ ์ฐจ์ด๋ฅผ ๋ช ํํ ๊ตฌ๋ถํ ํ์๊ฐ ์์ต๋๋ค.
2.1 ๋์์ฑ (Concurrency)
๋์์ฑ์ ์ฌ๋ฌ ์์ ์ ๋ฒ๊ฐ์๊ฐ๋ฉฐ ์ฒ๋ฆฌํ๋ ๊ฐ๋ ์ ๋๋ค.
์ค์ ๋ก ๋์์ ์คํ๋๋ ๊ฒ์ ์๋์ง๋ง, ๋์์ ์ฒ๋ฆฌ๋๋ ๊ฒ์ฒ๋ผ ๋ณด์ด๊ฒ ๋ฉ๋๋ค.FastAPI์ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ ์ฌ๊ธฐ์ ํด๋นํฉ๋๋ค.
2.2 ๋ณ๋ ฌ์ฑ (Parallelism)
concurrent.futures — Launching parallel tasks
Source code: Lib/concurrent/futures/thread.py, Lib/concurrent/futures/process.py, and Lib/concurrent/futures/interpreter.py The concurrent.futures module provides a high-level interface for asynchr...
docs.python.org
๋ณ๋ ฌ์ฑ์ ์ฌ๋ฌ ์์ ์ ์ค์ ๋ก ๋์์ ์คํํ๋ ๊ฐ๋ ์ ๋๋ค.
FastAPI ๊ณต์๋ฌธ์์ ๊ท์ฌ์ด burger ์์๊ฐ ์๋๋ฐ์
1.๋์์ฑ


2. ๋ณ๋ ฌ์ฑ

์์ธํ ๋ด์ฉ์ ์ ๋งํฌ์์ ํ๋ฒ ํ์ธํด๋ณด์๊ธฐ ๋ฐ๋๋๋ค.
3. OpenAI ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋ณ๋ชฉ์ด ๋ ์ด์
3.1 OpenAI (๋๊ธฐ SDK) ์ฌ์ฉ ์
from openai import OpenAI
client = OpenAI(
base_url="<http://vllm:8000/v1>",
api_key="EMPTY"
)
@app.post("/chat")
def chat():
response = client.chat.completions.create(
model="qwen",
messages=[{"role": "user", "content": "hello"}]
)
return response.choices[0].message.content
OpenAI ํด๋์ค๋ ๋๊ธฐ ๋ฐฉ์์ผ๋ก ๋์ํฉ๋๋ค.
์ฆ, ์๋ต์ด ๋์์ฌ ๋๊น์ง FastAPI ์ค๋ ๋๋ฅผ ์์ ํ ์ ์ ํฉ๋๋ค.
์ด๋ก ์ธํด ๋ฐ์ํ ํ์์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- FastAPI ์์ฒญ์ด ์ง๋ ฌํ๋จ
- vLLM ์๋ฒ ๋ก๊ทธ์ ์์ฒญ์ด ํ๋์ฉ ์ฐํ
- GPU๊ฐ ์ถฉ๋ถํ ์์์๋ batching์ด ๋ฐ์ํ์ง ์์
์ฒ์์๋ vLLM ์ค์ ๋ฌธ์ ๋ก ์คํดํ๊ธฐ ์ฌ์ด ๋ถ๋ถ์ด์์ต๋๋ค.
3.2 AsyncOpenAI ์ฌ์ฉ ์ (ํด๊ฒฐ)
from openai import AsyncOpenAI
client = AsyncOpenAI(
base_url="<http://vllm:8000/v1>",
api_key="EMPTY"
)
@app.post("/chat")
async def chat():
response = await client.chat.completions.create(
model="qwen",
messages=[{"role": "user", "content": "hello"}]
)
return response.choices[0].message.content
AsyncOpenAI๋ฅผ ์ฌ์ฉํ๋ฉด์ ๋ฌธ์ ๊ฐ ํด๊ฒฐ๋์์ต๋๋ค.
- FastAPI ์ด๋ฒคํธ ๋ฃจํ๊ฐ block๋์ง ์์
- ์ฌ๋ฌ ์์ฒญ์ด ๋์์ vLLM์ผ๋ก ์ ๋ฌ๋จ
- vLLM batching ์ ์ ๋์
- multi GPU ์ฌ์ฉ ํ์ธ
๊ฒฐ๊ณผ์ ์ผ๋ก ๋ณ๋ ฌ์ฒ๋ฆฌ๊ฐ ๋์ง ์๋ ๊ฒ์ฒ๋ผ ๋ณด์๋ ๋ฌธ์ ์ ์์ธ์
FastAPI์ vLLM ์ฌ์ด์ ์์ฒญ ๋ฐฉ์์ด์์ต๋๋ค.
4. requests์ httpx ์ฐจ์ด
4.1 requests
import requests
def call_vllm():
r = requests.post(url, json=payload)
return r.json()
- ๋๊ธฐ ์ ์ฉ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- async def ๋ด๋ถ์์ ์ฌ์ฉ ์ ์ด๋ฒคํธ ๋ฃจํ๋ฅผ block
- FastAPI ๋น๋๊ธฐ ๊ตฌ์กฐ์ ๋ง์ง ์์
4.2 httpx (๋น๋๊ธฐ ๊ถ์ฅ)
import httpx
async def call_vllm():
async with httpx.AsyncClient(timeout=60) as client:
r = await client.post(url, json=payload)
return r.json()
- ๋น๋๊ธฐ I/O ์ง์
- connection pooling ์ ๊ณต
- FastAPI์ ๊ถํฉ์ด ๋งค์ฐ ์ข์
4.3 ์๋ชป๋ ์์ ์ฌ๋ฐ๋ฅธ ์
โ ์๋ชป๋ ์
@app.post("/bad")
async def bad():
r = requests.post(url, json=payload)
return r.json()
โญ ์ฌ๋ฐ๋ฅธ ์
@app.post("/good")
async def good():
async with httpx.AsyncClient() as client:
r = await client.post(url, json=payload)
return r.json()
