Skip to main content

LiteLLM Proxy: วาง Gateway ครอบ LLM ให้ทีมใช้งานแบบมีระบบ

· 13 min read

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:

  1. PostgreSQL start รอ healthcheck ผ่าน
  2. Redis start รอ healthcheck ผ่าน
  3. LiteLLM Proxy start → รัน Prisma migration อัตโนมัติ
  4. 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 ต่อ worker
  • disable_error_logs: true - stop writing LLM exceptions ลง DB
  • allow_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

แชร์บทความ

เนื้อหานี้มีประโยชน์ไหม? ช่วยสนับสนุนค่ากาแฟให้ผู้เขียนสักแก้ว

Buy Me a Coffee
Loading...