반응형
1·2편은 Python 라이브러리로 개인이 직접 쓰는 방법이었습니다. 3편은 팀 전체가 쓰는 방법입니다. LiteLLM Proxy를 띄우면 팀원들은 각자 API 키 없이 http://our-gateway.com:4000으로 요청하면 됩니다. 비용은 중앙에서 집계되고, 팀별 예산도 설정됩니다.
[3편 핵심 요약]
→ LiteLLM Proxy: 팀 공용 OpenAI 호환 LLM 게이트웨이 — 셀프호스팅
→ 구성: config.yaml(모델·설정) + .env(API 키) + PostgreSQL(비용 추적) + Redis(고트래픽)
→ 포트: 4000 (API) / UI: http://localhost:4000/ui
→ Master Key: 관리자 키 (sk-로 시작) — 가상 키 발급에 사용
→ Virtual Key: 팀원·프로젝트별 발급 — 모델 접근·예산·RPM 제한 설정 가능
→ 주의: v1.82.7, v1.82.8 — 2026년 3월 공급망 보안 사고 — 즉시 v1.83.0+로 업그레이드
→ 프로덕션 최소 사양: 4 CPU 코어, 8GB RAM
→ Claude Code·Cursor도 proxy base_url로 연결 가능 — 팀 전체 비용 통합
실전 1 — 빠른 시작 (CLI)
# 설치
pip install 'litellm[proxy]'
# 또는
uv tool install 'litellm[proxy]'
# 단일 모델로 즉시 시작 (테스트용)
export ANTHROPIC_API_KEY="sk-ant-..."
litellm --model anthropic/claude-sonnet-4-6
# → http://0.0.0.0:4000 에서 실행
# → OpenAI 호환 API 즉시 사용 가능
# 클라이언트에서 proxy 사용
from openai import OpenAI
client = OpenAI(
api_key="anything", # proxy는 자체 인증 사용
base_url="http://localhost:4000"
)
response = client.chat.completions.create(
model="claude-sonnet-4-6", # config.yaml의 model_name
messages=[{"role": "user", "content": "안녕"}]
)
print(response.choices[0].message.content)
실전 2 — config.yaml 완전 가이드
# config.yaml — LiteLLM Proxy 핵심 설정 파일
# ── 모델 목록 ──────────────────────────────────────────
model_list:
# Claude (Anthropic 직접)
- model_name: claude-sonnet # 클라이언트에서 쓸 이름
litellm_params:
model: anthropic/claude-sonnet-4-6
api_key: os.environ/ANTHROPIC_API_KEY # env에서 읽음
model_info:
id: claude-sonnet-v1 # 고유 ID
# Claude (AWS Bedrock 경유 — 동일 그룹으로 로드밸런싱)
- model_name: claude-sonnet # 같은 이름 → 자동 분산
litellm_params:
model: bedrock/anthropic.claude-sonnet-4-6
aws_access_key_id: os.environ/AWS_ACCESS_KEY_ID
aws_secret_access_key: os.environ/AWS_SECRET_ACCESS_KEY
aws_region_name: us-east-1
model_info:
id: claude-sonnet-bedrock-v1
# GPT
- model_name: gpt
litellm_params:
model: openai/gpt-5.4
api_key: os.environ/OPENAI_API_KEY
# Gemini Flash (저렴한 기본 모델)
- model_name: gemini-flash
litellm_params:
model: gemini/gemini-3-flash
api_key: os.environ/GEMINI_API_KEY
# Ollama 로컬 모델 (비용 없음)
- model_name: llama
litellm_params:
model: ollama/llama3.3
api_base: http://ollama:11434 # Docker 네트워크
# 폴백 체인 설정
- model_name: default
litellm_params:
model: anthropic/claude-sonnet-4-6
api_key: os.environ/ANTHROPIC_API_KEY
# ── 라우터 설정 ────────────────────────────────────────
router_settings:
routing_strategy: latency-based-routing # 가장 빠른 프로바이더
num_retries: 3
timeout: 30
fallbacks:
- claude-sonnet: ["gpt", "gemini-flash"]
- gpt: ["claude-sonnet", "gemini-flash"]
context_window_fallbacks:
- claude-sonnet: ["gemini-flash"] # 200K → 1M 컨텍스트
# ── 일반 설정 ──────────────────────────────────────────
general_settings:
master_key: os.environ/LITELLM_MASTER_KEY # 관리자 키
database_url: os.environ/DATABASE_URL # PostgreSQL
# UI 로그인
ui_username: os.environ/UI_USERNAME
ui_password: os.environ/UI_PASSWORD
# CORS (웹 클라이언트 허용)
allow_requests_on_db_unavailable: true # DB 다운 시도 요청 허용
# ── LiteLLM 설정 ───────────────────────────────────────
litellm_settings:
# 캐시 (Redis)
cache: true
cache_params:
type: redis
host: os.environ/REDIS_HOST
port: 6379
password: os.environ/REDIS_PASSWORD
ttl: 3600
# 성공 콜백 (로깅)
success_callback: ["langfuse"]
# 비용 추적
callbacks: ["langfuse"]
실전 3 — Docker Compose 프로덕션 배포
# docker-compose.yml
version: "3.9"
services:
# LiteLLM Proxy
litellm:
image: ghcr.io/berriai/litellm:v1.83.2-stable # ⚠️ 버전 고정 필수
# v1.82.7, v1.82.8 사용 금지 (2026년 3월 공급망 보안 사고)
ports:
- "4000:4000"
volumes:
- ./config.yaml:/app/config.yaml:ro # 읽기 전용 마운트
env_file:
- .env
command: ["--config", "/app/config.yaml", "--port", "4000"]
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
# 프로덕션 최소 사양
deploy:
resources:
limits:
cpus: "4"
memory: 8G
# PostgreSQL — 비용 추적·가상 키 저장
db:
image: postgres:15-alpine
environment:
POSTGRES_DB: litellm
POSTGRES_USER: litellm
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U litellm"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
# Redis — 고트래픽 캐싱·레이트 리밋 (1000+ RPS 시 필수)
redis:
image: redis:7-alpine
command: redis-server --requirepass ${REDIS_PASSWORD}
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
volumes:
postgres_data:
redis_data:
# .env — API 키 및 설정
LITELLM_MASTER_KEY=sk-my-admin-key-2026 # 반드시 sk- 시작
DATABASE_URL=postgresql://litellm:${DB_PASSWORD}@db:5432/litellm
DB_PASSWORD=your-secure-db-password
REDIS_HOST=redis
REDIS_PASSWORD=your-redis-password
# LLM 프로바이더 키
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...
GEMINI_API_KEY=...
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
# Admin UI 로그인
UI_USERNAME=admin
UI_PASSWORD=your-ui-password
# Langfuse 연동 (선택)
LANGFUSE_PUBLIC_KEY=pk-lf-...
LANGFUSE_SECRET_KEY=sk-lf-...
# 실행
docker compose up -d
# 헬스체크
curl http://localhost:4000/health
# → {"status": "healthy", "litellm_version": "1.83.2", ...}
# 로그 확인
docker compose logs -f litellm
# Admin UI 접속
# http://localhost:4000/ui
실전 4 — 가상 키(Virtual Key) 관리
팀원에게 실제 API 키 대신 가상 키를 발급합니다. 각 키에 모델 접근·예산·RPM 제한을 설정합니다.
# ── 키 발급 ───────────────────────────────────────────
# 프론트엔드 팀용 키 (Claude만 접근, 월 $50 예산)
curl -X POST 'http://localhost:4000/key/generate' \
-H 'Authorization: Bearer sk-my-admin-key-2026' \
-H 'Content-Type: application/json' \
-d '{
"key_alias": "frontend-team",
"models": ["claude-sonnet", "gemini-flash"],
"max_budget": 50,
"budget_duration": "monthly",
"rpm_limit": 100,
"tpm_limit": 500000,
"metadata": {
"team": "frontend",
"created_by": "admin"
}
}'
# 응답: {"key": "sk-frontend-xxxx", "expires": null, ...}
# ── AI 코딩 툴 전용 키 (Opus 포함, 높은 예산) ─────────
curl -X POST 'http://localhost:4000/key/generate' \
-H 'Authorization: Bearer sk-my-admin-key-2026' \
-H 'Content-Type: application/json' \
-d '{
"key_alias": "claude-code-team",
"models": ["claude-sonnet", "claude-opus-4-7", "gpt"],
"max_budget": 200,
"budget_duration": "monthly",
"rpm_limit": 500,
"metadata": {"team": "engineering", "use_case": "coding"}
}'
# Python으로 키 관리
import httpx
PROXY_URL = "http://localhost:4000"
MASTER_KEY = "sk-my-admin-key-2026"
def create_virtual_key(
alias: str,
models: list[str],
monthly_budget: float,
rpm_limit: int = 100,
team_id: str = None,
) -> str:
"""가상 키 발급"""
payload = {
"key_alias": alias,
"models": models,
"max_budget": monthly_budget,
"budget_duration": "monthly",
"rpm_limit": rpm_limit,
}
if team_id:
payload["team_id"] = team_id
response = httpx.post(
f"{PROXY_URL}/key/generate",
headers={"Authorization": f"Bearer {MASTER_KEY}"},
json=payload
)
return response.json()["key"]
def list_keys() -> list[dict]:
"""전체 가상 키 목록"""
response = httpx.get(
f"{PROXY_URL}/key/list",
headers={"Authorization": f"Bearer {MASTER_KEY}"}
)
return response.json().get("keys", [])
def get_key_spend(key: str) -> dict:
"""특정 키 사용량 조회"""
response = httpx.get(
f"{PROXY_URL}/key/info",
headers={"Authorization": f"Bearer {MASTER_KEY}"},
params={"key": key}
)
return response.json()
def delete_key(key: str) -> bool:
"""가상 키 삭제"""
response = httpx.delete(
f"{PROXY_URL}/key/delete",
headers={"Authorization": f"Bearer {MASTER_KEY}"},
json={"keys": [key]}
)
return response.status_code == 200
# 팀별 키 자동 발급 예시
teams = [
{"name": "backend-team", "models": ["claude-sonnet", "gpt"], "budget": 100},
{"name": "frontend-team", "models": ["claude-sonnet", "gemini-flash"], "budget": 50},
{"name": "data-team", "models": ["gpt", "gemini-flash"], "budget": 30},
{"name": "devops-team", "models": ["llama", "gemini-flash"], "budget": 10},
]
for team in teams:
key = create_virtual_key(
alias=team["name"],
models=team["models"],
monthly_budget=team["budget"],
)
print(f"✅ {team['name']}: {key}")
실전 5 — 클라이언트 연동 (OpenAI SDK, Anthropic SDK, Claude Code)
# ── OpenAI SDK ───────────────────────────────────────
from openai import OpenAI
client = OpenAI(
api_key="sk-frontend-xxxx", # 팀원 가상 키
base_url="http://our-proxy.company.com:4000"
)
response = client.chat.completions.create(
model="claude-sonnet", # config.yaml의 model_name
messages=[{"role": "user", "content": "안녕"}]
)
# ── Anthropic SDK (Proxy 경유) ─────────────────────────
from anthropic import Anthropic
client = Anthropic(
api_key="sk-engineering-xxxx", # 가상 키
base_url="http://our-proxy.company.com:4000"
)
message = client.messages.create(
model="claude-sonnet-4-6", # 원본 모델명 또는 별칭
max_tokens=1024,
messages=[{"role": "user", "content": "안녕"}]
)
# ── Claude Code에서 Proxy 연결 ─────────────────────────
# Claude Code → 팀 proxy 경유 → 비용 통합 추적
# 환경변수 설정
export ANTHROPIC_BASE_URL="http://our-proxy.company.com:4000"
export ANTHROPIC_API_KEY="sk-claude-code-team-xxxx" # 가상 키
# 이후 Claude Code 실행 시 proxy 경유
claude
# 또는 CLAUDE.md에 설정 가능
# ── Cursor에서 Proxy 연결 ─────────────────────────────
# Cursor Settings → Models → OpenAI API Key
# Base URL: http://our-proxy.company.com:4000
# API Key: sk-frontend-team-xxxx (가상 키)
# Model: claude-sonnet (config.yaml의 model_name)
실전 6 — 팀 관리 및 비용 모니터링
# ── 팀 생성 ───────────────────────────────────────────
curl -X POST 'http://localhost:4000/team/new' \
-H 'Authorization: Bearer sk-my-admin-key-2026' \
-H 'Content-Type: application/json' \
-d '{
"team_alias": "engineering",
"max_budget": 300,
"budget_duration": "monthly",
"models": ["claude-sonnet", "gpt", "claude-opus-4-7"],
"rpm_limit": 1000
}'
# ── 팀 멤버 추가 ──────────────────────────────────────
curl -X POST 'http://localhost:4000/team/member_add' \
-H 'Authorization: Bearer sk-my-admin-key-2026' \
-H 'Content-Type: application/json' \
-d '{
"team_id": "team-engineering-xxxx",
"member": [
{"role": "user", "user_id": "alice@company.com"},
{"role": "admin", "user_id": "bob@company.com"}
]
}'
# 팀별 비용 리포트 자동화
import httpx
from datetime import datetime, timedelta
def get_team_spend_report() -> list[dict]:
"""팀별 이번 달 비용 리포트"""
response = httpx.get(
"http://localhost:4000/spend/teams",
headers={"Authorization": f"Bearer {MASTER_KEY}"},
)
teams = response.json().get("teams", [])
report = []
for team in teams:
report.append({
"team": team.get("team_alias", "unknown"),
"spend": team.get("spend", 0),
"budget": team.get("max_budget", 0),
"usage_pct": team.get("spend", 0) / max(team.get("max_budget", 1), 1) * 100
})
return sorted(report, key=lambda x: x["spend"], reverse=True)
def get_model_spend_report() -> list[dict]:
"""모델별 비용 리포트"""
response = httpx.get(
"http://localhost:4000/spend/models",
headers={"Authorization": f"Bearer {MASTER_KEY}"},
)
return response.json().get("models", [])
# 실행 및 출력
teams = get_team_spend_report()
print(f"\n[팀별 비용 리포트 — {datetime.now().strftime('%Y-%m')}]")
for t in teams:
bar = "█" * int(t["usage_pct"] / 5)
print(f" {t['team'][:20]:<20} ${t['spend']:.2f}/${t['budget']:.0f} [{bar:<20}] {t['usage_pct']:.1f}%")
실전 7 — Langfuse 로깅 연동
# config.yaml에 추가
litellm_settings:
success_callback: ["langfuse"]
failure_callback: ["langfuse"]
# .env에 추가
LANGFUSE_PUBLIC_KEY=pk-lf-...
LANGFUSE_SECRET_KEY=sk-lf-...
LANGFUSE_HOST=https://cloud.langfuse.com # 또는 셀프호스팅 URL
# 클라이언트에서 메타데이터 추가 (Langfuse 추적용)
from openai import OpenAI
client = OpenAI(
api_key="sk-team-xxxx",
base_url="http://localhost:4000"
)
response = client.chat.completions.create(
model="claude-sonnet",
messages=[{"role": "user", "content": "코드 리뷰해줘"}],
extra_body={
"metadata": {
# Langfuse 추적 메타데이터
"trace_name": "code-review",
"trace_user_id": "user-alice",
"trace_session_id": "session-123",
"tags": ["code-review", "claude", "production"],
"generation_name": "code_review_v2",
}
}
)
[Langfuse 대시보드에서 확인 가능한 것들]
→ 요청별 프롬프트·응답 전체 로그
→ 모델별·사용자별·세션별 비용
→ 레이턴시 P50/P95/P99
→ 에러율 트래킹
→ 프롬프트 버전 관리
→ 평가(Evaluation) 점수 추적
마무리
✅ 3편에서 한 것
→ config.yaml 전체 구조 (모델·라우터·일반 설정)
→ Docker Compose 프로덕션 배포 (Proxy + PostgreSQL + Redis)
→ 보안 주의: v1.82.7/1.82.8 사용 금지, 버전 고정 필수
→ 가상 키 발급·관리 (API + Python)
→ OpenAI SDK / Anthropic SDK / Claude Code / Cursor 연동
→ 팀 생성·멤버 관리·예산 설정
→ 팀별·모델별 비용 리포트 자동화
→ Langfuse 로깅 연동
❌ 4편에서 다룰 것
→ LangChain·LangGraph에서 Proxy 통합
→ 가드레일(콘텐츠 필터, PII 마스킹)
→ Prometheus + Grafana 메트릭
→ Kubernetes/Helm 배포
→ 고가용성 설정 (multi-instance + Redis)
→ 프로덕션 보안 (HTTPS, 방화벽)
관련 글
- LiteLLM 완전 가이드 1편 — 100개+ LLM 단일 인터페이스
- LiteLLM 완전 가이드 2편 — Router, 폴백, 비용 추적, 캐싱
- OpenRouter 완전 가이드 4편 — 모니터링, 팀 운영, ZDR
반응형
'AI 개발' 카테고리의 다른 글
| Wan2.2-T2V-A14B 완전 가이드 — 오픈소스 영상 생성 모델 로컬 서빙과 실전 영상 만들기 (0) | 2026.05.20 |
|---|---|
| LiteLLM 완전 가이드 4편 — LangChain·LangGraph 통합, 가드레일, Prometheus 모니터링, 프로덕션 운영 (0) | 2026.05.19 |
| LiteLLM 완전 가이드 2편 — 폴백·재시도, Router 로드밸런싱, 비용 추적, 예산·캐싱 실전 (0) | 2026.05.19 |
| LiteLLM 완전 가이드 1편 — 100개+ LLM을 코드 한 줄로 갈아타는 오픈소스 AI 게이트웨이 (0) | 2026.05.19 |
| OpenRouter 완전 가이드 4편 — 모니터링, 레이트 리밋 관리, OAuth PKCE, 팀 운영, ZDR (0) | 2026.05.19 |