LiteLLM Proxy: วาง Gateway ครอบ LLM ให้ทีมใช้งานแบบมีระบบ
สารบัญ
- intro
- TL;DR
- LiteLLM คืออะไร
- Features ที่ได้
- Architecture
- Installation
- Prerequisites
- File Structure
- 1. สร้างไฟล์ .env
- 2. สร้างไฟล์ config.yaml
- 3. สร้างไฟล์ docker-compose.yaml
- Startup
- Admin UI
- Best Practices ที่ควรรู้
- Worker Sizing
- Granian Server (Beta)
- Worker Recycling
- Database Tuning
- Redis
- Alerting
- Security
- API Key Encryption
- Docker Image Signing
- Secret Manager Integration
- Features ที่น่าสนใจเพิ่มเติม
- Fallback Models
- Traffic Mirroring
- Multi-Model Routing
- Limitations ที่เจอ
- Conclusion
- References
intro
ผมรัน LLM เองบน homelab มาสักพัก
เริ่มจาก llama.cpp บนเครื่องสเปกต่ำ ถามอะไรก็ตอบได้
แล้วขยับมา vLLM ที่ throughput สูงกว่า รับ request พร้อมกันได้มากกว่า
ทุกอย่างดูดี จนกระทั่งวันหนึ่งเพื่อนร่วมงานถามว่า
"ขอใช้ด้วยได้ไหม?"
ผมก็เลยเปิด port ให้เพื่อนยิง request ตรงเข้ามา
ผ่านไปสักพัก ก็เริ่มเจอปัญหา
ไม่รู้ว่าใครใช้ไปเท่าไหร่ — ไม่มี log ไม่มี metric รู้แค่ GPU ทำงานหนักขึ้น
ไม่มี API key แยกคน — ทุกคนใช้ key เดียวกัน ถ้า key หลุดก็จบเลย
ไม่มี rate limit — มีคนส่ง request ต่อเนื่อง ทำให้คนอื่นรอคิวนาน
ไม่มี cache — คำถามซ้ำๆ ถูกส่งไป LLM ทุกครั้ง เสียทรัพยากรโดยไม่จำเป็น
backend ล่มที ทุกอย่างก็หยุดทำงาน — ไม่มี fallback ไม่มี retry
ถ้าจะเขียนระบบจัดการเองก็ทำได้ แต่ต้องมานั่งทำ auth, logging, rate limit, cache, dashboard...
ไม่ใช่งานที่ผมอยากทำ
แค่อยากให้ทีมใช้ LLM ได้สะดวก โดยที่ยังคุมทุกอย่างได้
TL;DR
LiteLLM Proxy เป็น Open Source API Gateway สำหรับ LLM ที่ช่วยให้รัน LLM เองแล้วแชร์ให้ทีมใช้งานได้อย่างมีระบบ รองรับ Virtual Keys, Budget Control, Redis Cache, Fallback Models และมี Admin UI จัดการทุกอย่างผ่านเว็บ ไม่ต้องเขียนโค้ดเพิ่ม แค่ตั้งค่า YAML ไม่กี่บรรทัด
LiteLLM คืออะไร
LiteLLM เป็น Open Source API Gateway สำหรับ LLM
ทำหน้าที่เป็น proxy ระหว่าง client กับ LLM backend
ไม่ว่าฝั่งหลังจะเป็น vLLM, OpenAI, Azure, Anthropic, หรือ Ollama
ทุกอย่างจะถูกเรียกผ่าน API format เดียวกัน คือ OpenAI compatible
client ที่ใช้อยู่แล้ว เช่น OpenAI SDK, LangChain, LlamaIndex
ไม่ต้องแก้โค้ดเลย แค่เปลี่ยน base_url มาชี้ที่ LiteLLM
Features ที่ได้
Authentication & Access
- Virtual Keys — สร้าง API key แยกตามบุคคลหรือทีม แต่ละ key มีสิทธิ์ต่างกัน
- Model Access Control — กำหนดว่าแต่ละ key เข้าถึง model ไหนได้บ้าง
- Master Key — admin key สำหรับจัดการทุกอย่าง
Budget & Rate Limiting
- Budget per Key — กำหนดงบประมาณรายวัน/รายเดือนให้แต่ละ key
- Rate Limit (RPM/TPM) — จำกัดจำนวน request และ token ต่อนาที
- Spend Tracking — ติดตามค่าใช้จ่ายแบบ real-time
Observability
- Usage Logging — log ลง PostgreSQL
- Success/Failure Callback — แยก log success กับ failure
- Dashboard UI — view usage, spend, health ผ่าน web UI
Performance
- Redis Caching — cache ผลลัพธ์ที่ถามซ้ำ ลดภาระของ backend ได้ 60-80%
- Batch Write — batch write logs ทุก N วินาที ไม่ยิง DB ทุก request
- Connection Pooling — จัดการ DB connections อย่างมีประสิทธิภาพ
- Multiple Workers — กระจาย load บน multi-core
Reliability
- Fallback Models — ถ้า primary model ล่ม จะ auto-failover ไป backup model
- Auto Retry — auto-retry ถ้า backend overloaded
- Health Checks — backend health check อย่างต่อเนื่อง
- DB Unavailability Handling — proxy ยัง serve ได้แม้ DB ล่ม
Security
- API Key Encryption — encrypt keys ใน database ด้วย NaCl SecretBox
- Docker Image Signing — sign images ด้วย cosign และ verify ได้
- Secret Manager Support — อ่าน secrets จาก Vault, AWS, Azure, GCP ได้
Routing
- Load Balancing — กระจาย request ไปหลาย backend
- Routing Strategy — simple-shuffle, least-busy, latency-based
- Traffic Mirroring — mirror traffic ไป backup model เพื่อ A/B testing
Architecture
แนะนำให้แยก service ออกจากกัน
เหตุผลคือ แต่ละ service มี lifecycle ต่างกัน
อัปเดต proxy ไม่ต้องกระทบ database รีสตาร์ต proxy ไม่ทำให้ข้อมูลหาย
LiteLLM Proxy — รับ request จาก client, จัดการ auth, logging, caching
PostgreSQL — เก็บ virtual keys, spend logs, team data, config
Redis — แคช response, shared rate limit state, health check cache
ทั้ง 3 ตัวรันในคนละ container และ mount volume ออกมาที่ host
ตอน recreate ข้อมูลไม่หาย
Installation
Prerequisites
- Docker และ Docker Compose
- เครื่อง server (VM หรือ physical ก็ได้ RAM ขั้นต่ำ 4GB)
- LLM backend ที่รันอยู่แล้ว (เช่น vLLM บนเครื่องอื่น)
File Structure
litellm-gateway/
├── .env # environment variables (อย่า commit ขึ้น git)
├── config.yaml # กำหนด model, router, cache
└── docker-compose.yaml # 3 services
1. สร้างไฟล์ .env
cat > .env << EOF
LITELLM_MASTER_KEY=sk-master-$(openssl rand -hex 12)
LITELLM_SALT_KEY=$(openssl rand -base64 42)
POSTGRES_PASSWORD=$(openssl rand -base64 24 | tr -d '/+=' | head -c 32)
REDIS_PASSWORD=$(openssl rand -base64 24 | tr -d '/+=' | head -c 32)
EOF
Note: LITELLM_SALT_KEY
ค่านี้ใช้ encrypt API keys ที่เก็บใน database ด้วย NaCl SecretBox
ตั้งแค่ครั้งแรกตอน deploy เท่านั้น ห้ามเปลี่ยนภายหลัง
ถ้าเปลี่ยน key เก่าจะ decrypt ไม่ได้
ถ้าไม่กำหนด ระบบจะ fallback ไปใช้ LITELLM_MASTER_KEY แทน
แต่แนะนำให้กำหนดแยก เพราะ master key อาจต้อง rotate ได้ในอนาคต
2. สร้างไฟล์ config.yaml
model_list:
- model_name: my-model
litellm_params:
model: openai/my-model
api_base: "http://<IP_VLLM>:8000/v1"
api_key: "none"
rpm: 0
tpm: 0
router_settings:
routing_strategy: simple-shuffle
num_retries: 3
timeout: 30
general_settings:
database_connection_pool_limit: 10
proxy_batch_write_at: 60
disable_error_logs: true
allow_requests_on_db_unavailable: true
litellm_settings:
cache: true
cache_params:
type: redis
host: os.environ/REDIS_HOST
port: os.environ/REDIS_PORT
password: os.environ/REDIS_PASSWORD
success_callback: ["database"]
failure_callback: ["database"]
num_retries: 3
telemetry: false
drop_params: true
Note: rpm: 0 / tpm: 0
ปิด rate limit ที่ตัว proxy ปล่อยให้ backend จัดการคิวเอง
ลด overhead ของ proxy เหมาะกับกรณีที่มี backend เดียว
Note: proxy_batch_write_at: 60
batch write spend log ทุก 60 วินาที แทนที่จะ write ทุก request
ช่วยลด database round trips ได้มาก โดยเฉพาะตอนที่มี request เยอะ ๆ
Note: drop_params: true
ถ้า client ส่ง parameter ที่ backend ไม่รองรับ
LiteLLM จะลบ parameter นั้นทิ้ง ไม่ error กลับ
ช่วยให้ client ไม่ต้องกังวลเรื่อง compatibility ของแต่ละ backend
3. สร้างไฟล์ docker-compose.yaml
services:
litellm:
image: ghcr.io/berriai/litellm-database:main-latest
container_name: litellm-proxy
ports:
- "4000:4000"
volumes:
- ./config.yaml:/app/config.yaml:ro
environment:
- LITELLM_MASTER_KEY=${LITELLM_MASTER_KEY}
- LITELLM_SALT_KEY=${LITELLM_SALT_KEY}
- DATABASE_URL=postgresql://litellm:${POSTGRES_PASSWORD}@postgres:5432/litellm
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD}
command: ["--config", "/app/config.yaml", "--port", "4000", "--num_workers", "1"]
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
postgres:
image: postgres:16-alpine
container_name: litellm-postgres
environment:
POSTGRES_DB: litellm
POSTGRES_USER: litellm
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- ./data/postgres:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U litellm -d litellm"]
interval: 5s
timeout: 3s
retries: 5
redis:
image: redis:7-alpine
container_name: litellm-redis
command: redis-server --requirepass ${REDIS_PASSWORD} --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
- ./data/redis:/data
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
interval: 5s
timeout: 3s
retries: 5
Note: litellm-database image
image นี้มี Prisma migration ในตัว
ตอน startup จะรัน migration อัตโนมัติ ไม่ต้อง setup DB schema เอง
ถ้าใช้ image ธรรมดา (
litellm:main-latest) จะต้องรัน migration แยก
Note: --num_workers
จำนวน worker process ที่ proxy จะ spawn
ถ้า RAM จำกัด (4GB) ตั้งแค่ 1 พอ
ถ้า RAM เยอะ (16GB+) ตั้งตามจำนวน CPU cores ได้
แต่ละ worker กิน RAM เพิ่ม ต้อง balance ดีๆ
Note: Redis maxmemory 256mb
จำกัด RAM ที่ Redis ใช้ ไม่ให้กินเกิน
allkeys-lruหมายถึงถ้าเต็ม จะลบ key ที่ไม่ได้ใช้นานที่สุดอัตโนมัติ256 MB เพียงพอสำหรับทีมขนาดเล็ก ถ้าใช้เยอะค่อยเพิ่ม
Startup
cd litellm-gateway
docker compose up -d
สิ่งที่เกิดขึ้นตอน startup:
- PostgreSQL start รอ healthcheck ผ่าน
- Redis start รอ healthcheck ผ่าน
- LiteLLM Proxy start → รัน Prisma migration อัตโนมัติ
- Migration เสร็จ → Uvicorn worker เริ่มรับ request
ขั้นตอนที่ 3 ใช้เวลา 1-2 นาที ต้องรอสักครู่
Note: Prisma Migration
LiteLLM ใช้ Prisma เป็น ORM สำหรับจัดการ database schema
ตอน startup จะรัน migration อัตโนมัติ
ถ้าเห็น container restart หลายรอบในช่วงแรก ไม่ต้องตกใจ
รอให้ migration เสร็จ แล้ว proxy จะเริ่มทำงานปกติ
จากนั้น test:
# check health
curl http://localhost:4000/health
# list models (ใช้ master key จาก .env)
curl http://localhost:4000/v1/models \
-H "Authorization: Bearer <MASTER_KEY>"
# test เรียก LLM
curl http://localhost:4000/v1/chat/completions \
-H "Authorization: Bearer <MASTER_KEY>" \
-H "Content-Type: application/json" \
-d '{
"model": "my-model",
"messages": [{"role": "user", "content": "สวัสดี"}]
}'
Admin UI
เปิดเบราว์เซอร์ไปที่ http://<IP>:4000/ui
ใช้ master key จากไฟล์ .env เพื่อ log in
สิ่งที่ทำได้ใน UI:
- Virtual Keys — สร้าง API key แยกตามบุคคลหรือทีม แต่ละ key มี budget, rate limit, model access แยกกัน
- Budget Control — ตั้งงบประมาณรายวัน/รายเดือน ต่อ key และต่อทีม
- Usage Logs — ดู log ย้อนหลังของทุก request พร้อม token count, cost, latency
- Spend Dashboard — ดูค่าใช้จ่ายรวมและแยกตาม key/team
- Model Management — เพิ่ม/ลบ/แก้ไข model ผ่าน UI ได้เลย
- Team Management — จัดกลุ่ม users และตั้งงบประมาณทีม
Best Practices ที่ควรรู้
จากการศึกษา docs ของ LiteLLM อย่างจริงจัง
มีหลายจุดที่ควรรู้ถ้าจะใช้งานจริงจัง:
Worker Sizing
LiteLLM Proxy เป็น Python application ที่รันบน Uvicorn
Python มี GIL ดังนั้น single process ใช้ multi-core ไม่ได้เต็มที่
วิธีแก้คือใช้ --num_workers กระจาย load ไปหลาย process
สูตร connection pool:
total_connections = pool_limit x workers x instances
เช่น pool_limit=10, workers=1, instances=1 = 10 connections ไป PostgreSQL
ต้องไม่เกิน max_connections ของ PostgreSQL (default 100)
Granian Server (Beta)
ถ้าต้องการ throughput สูงกว่า Uvicorn ลองใช้ Granian:
--run_granian --num_workers 4
ตามที่ LiteLLM ระบุใน docs Granian ให้ throughput สูงกว่า Uvicorn ในบางกรณี 10-20 RPS
Worker Recycling
ป้องกัน memory leak จากการสะสม state:
--max_requests_before_restart 10000 --run_gunicorn
worker จะ restart ตัวเองอัตโนมัติหลังรับ request ครบ 10,000 ครั้ง
Database Tuning
proxy_batch_write_at: 60- batch write spend logs ทุก 60 วินาที ลด DB round trips อย่างมากdatabase_connection_pool_limit: 10- ควบคุมจำนวน connection ต่อ workerdisable_error_logs: true- stop writing LLM exceptions ลง DBallow_requests_on_db_unavailable: true- proxy ยัง serve ได้แม้ DB ล่ม
Redis
Redis ไม่ใช่แค่ cache แต่เป็น infrastructure ที่จำเป็นสำหรับ production:
- Shared Rate Limiting — ซิงก์ state ของ rate limit ข้ามหลาย instance
- Response Caching — ลดภาระของ backend ได้มาก ตามที่ LiteLLM ประเมินไว้ว่า 60-80% ในกรณีที่มี prompt ซ้ำ
- Health Check Cache — cache backend status ไม่ต้อง check ทุก request
ถ้ามีแค่ instance เดียว Redis ก็ยังมีประโยชน์เรื่อง caching
Alerting
เปิด alerting ผ่าน Slack เพื่อรับ alert:
litellm_settings:
alerting: ["slack"]
จะส่ง alert เมื่อ LLM exception, budget ใกล้หมด, response ช้า
Security
API Key Encryption
LiteLLM เข้ารหัส API keys ที่เก็บใน database ด้วย NaCl SecretBox
LITELLM_SALT_KEY ถูก hash ด้วย SHA-256 เพื่อ derive 256-bit key
ข้อมูลที่ถูกเข้ารหัส:
- LLM API keys ใน
LiteLLM_ProxyModelTable - Credentials ใน
LiteLLM_CredentialsTable - Config secrets ใน
LiteLLM_Config
Docker Image Signing
Docker images ของ LiteLLM ถูก sign ด้วย cosign ตั้งแต่ v1.83.0+
สามารถ verify ได้:
cosign verify \
--key https://raw.githubusercontent.com/BerriAI/litellm/v1.83.0-stable/cosign.pub \
ghcr.io/berriai/litellm-database:v1.83.0-stable
เปลี่ยน
v1.83.0-stableเป็น tag ที่ตรงกับเวอร์ชันที่ deploy อยู่
Secret Manager Integration
ถ้าใช้ enterprise secret manager อยู่แล้ว LiteLLM อ่าน secrets จาก:
- Azure Key Vault
- Google Secret Manager
- HashiCorp Vault
- AWS Secrets Manager / KMS
ได้เลยไม่ต้อง hardcode ใน config
Features ที่น่าสนใจเพิ่มเติม
Fallback Models
ถ้า model หลักล่ม ไปใช้ model สำรองอัตโนมัติ:
model_list:
- model_name: my-model
litellm_params:
model: openai/qwen-122b
api_base: "http://local-vllm:8000/v1"
litellm_settings:
fallbacks: [{"my-model": ["openai/gpt-4o"]}]
ถ้า vLLM บนเครื่อง local ไม่ตอบ จะ fallback ไป OpenAI API อัตโนมัติ
Traffic Mirroring
mirror production traffic ไป model สำรองเพื่อ A/B testing โดยไม่กระทบ user:
litellm_settings:
shadow_testing: {"my-model": ["test-model"]}
request ถูกส่งไปทั้ง 2 model แต่ user ได้ response จาก model หลักเท่านั้น
Multi-Model Routing
ถ้ามีหลาย backend สามารถ load balance ได้:
model_list:
- model_name: my-model
litellm_params:
model: openai/qwen-122b
api_base: "http://vllm-1:8000/v1"
- model_name: my-model
litellm_params:
model: openai/qwen-122b
api_base: "http://vllm-2:8000/v1"
router_settings:
routing_strategy: least-busy
request จะถูกส่งไป backend ที่ busy น้อยที่สุด
Limitations ที่เจอ
จากการ setup จริง มีหลายจุดที่ต้องระวัง:
RAM Usage — LiteLLM image ใหญ่ (~1GB) และ Prisma migration ตอน startup กิน RAM พอสมควร ถ้าเครื่องมี 4GB ควรตั้ง --num_workers 1 ไม่งั้น child process จะถูก OOM kill
Startup Time — Prisma migration ตอน startup ใช้เวลา 1-2 นาที ต้องรอให้เสร็จ ไม่ต้องตกใจถ้า container restart หลายรอบในช่วงแรก
Database — ใช้ได้แค่ PostgreSQL เท่านั้น ไม่รองรับ MySQL ทีม LiteLLM ลอง MySQL แล้วเจอ bug เยอะ ตัดสินใจ support แค่ PostgreSQL
Docker Compose YAML — ระวังอย่าใช้ heredoc shell สร้างไฟล์ docker-compose.yaml เพราะตัวแปร $ จะถูก expand ก่อนเขียน แนะนำให้ใช้ text editor หรือ script แทน
Worker Process Crash — ถ้าตั้ง --num_workers สูงเกิน RAM ที่มี child process จะถูก OOM kill โดยไม่มี error log ให้ดู ใช้ docker stats ประกอบการพิจารณา
Conclusion
LiteLLM Proxy เป็นเครื่องมือที่ช่วยเติมช่องว่างระหว่าง "รัน LLM ได้" กับ "ให้คนอื่นใช้งานได้อย่างมีระบบ"
ไม่ต้องเขียนโค้ดเพิ่ม ไม่ต้อง setup auth เอง
แค่ config YAML ไม่กี่บรรทัด ก็ได้ API Gateway ที่มีทั้ง:
- Authentication ด้วย Virtual Keys
- Budget Control แยกตามบุคคลหรือทีม
- Usage Logging ลง PostgreSQL
- Response Caching ผ่าน Redis
- Fallback เมื่อ backend ล่ม
- Admin UI จัดการทุกอย่างผ่านเว็บ
- Docker Image Signing ด้วย cosign
- API Key Encryption ด้วย NaCl SecretBox
สำหรับคนที่รัน LLM เองบน homelab หรือในองค์กร
LiteLLM เป็นตัวเลือกที่คุ้มค่ามากที่จะเอามาวางหน้า
โดยเฉพาะถ้าใช้กับ vLLM ที่รันอยู่บนเครื่องที่มี GPU เยอะ ๆ
LiteLLM จะช่วยให้ทรัพยากร GPU ถูกใช้อย่างมีประสิทธิภาพสูงสุด
โดยที่ผู้ใช้งานไม่ต้องรู้เลยว่า backend อยู่ที่ไหน
References
- LiteLLM Official Documentation
- LiteLLM Proxy - Getting Started Tutorial
- LiteLLM Proxy - Docker, Helm, Terraform
- LiteLLM Proxy - Config YAML Overview
- LiteLLM Proxy - Best Practices for Production
- LiteLLM Proxy - CLI Arguments
- LiteLLM GitHub Repository
- LiteLLM Docker Compose Example
- vLLM Documentation
เนื้อหานี้มีประโยชน์ไหม? ช่วยสนับสนุนค่ากาแฟให้ผู้เขียนสักแก้ว
Buy Me a Coffee