1·2편은 Python 라이브러리로 개인이 직접 쓰는 방법이었습니다. 3편은 팀 전체가 쓰는 방법입니다. LiteLLM Proxy를 띄우면 팀원들은 각자 API 키 없이 http://our-gateway.com:4000으로 요청하면 됩니다. 비용은 중앙에서 집계되고, 팀별 예산도 설정됩니다.
[3편 핵심 요약]
LiteLLM Proxy는 팀 공용 OpenAI 호환 LLM 게이트웨이로 셀프호스팅 방식으로 운영합니다. 구성 요소는 config.yaml(모델·설정), .env(API 키), PostgreSQL(비용 추적), Redis(고트래픽 캐싱)입니다. 포트는 4000번을 쓰며 Admin 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)
테스트 목적이라면 Docker 없이 CLI만으로 바로 실행할 수 있습니다. pip 또는 uv로 설치한 뒤 모델명과 API 키만 지정하면 로컬 4000번 포트에서 OpenAI 호환 서버가 즉시 올라옵니다.
# 설치
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 즉시 사용 가능
서버가 올라가면 기존 OpenAI SDK 코드에서 base_url만 바꿔 바로 연결됩니다. api_key 값은 proxy 자체 인증을 쓰기 때문에 아무 값이나 넣어도 무방합니다.
# 클라이언트에서 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의 핵심 설정 파일입니다. 모델 목록, 라우팅 전략, 인증, 캐시, 로깅까지 모든 동작이 이 파일 하나에서 제어됩니다. 아래는 Claude, GPT, Gemini, 로컬 Ollama 모델을 동시에 등록하고 폴백 체인까지 구성한 실전 예시입니다.
# 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
model_info:
id: claude-sonnet-v1
# 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
# 폴백 체인용 기본 모델
- 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
ui_username: os.environ/UI_USERNAME
ui_password: os.environ/UI_PASSWORD
allow_requests_on_db_unavailable: true
# ── LiteLLM 설정 ───────────────────────────────────────
litellm_settings:
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"]
같은 model_name으로 여러 엔트리를 등록하면 자동 로드밸런싱이 적용됩니다. 위 예시에서 claude-sonnet은 Anthropic 직접 호출과 AWS Bedrock 경유 호출이 레이턴시 기반으로 분산됩니다.
실전 3 — Docker Compose 프로덕션 배포
프로덕션 배포는 LiteLLM Proxy, PostgreSQL, Redis를 Docker Compose로 묶어서 운영하는 것이 표준입니다. 특히 버전 고정이 중요한데, v1.82.7과 v1.82.8은 2026년 3월 공급망 보안 사고가 발생한 버전이라 절대 사용하면 안 됩니다.
# docker-compose.yml
version: "3.9"
services:
litellm:
image: ghcr.io/berriai/litellm:v1.83.2-stable # ⚠️ 버전 고정 필수
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
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:
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 키와 DB 비밀번호를 넣습니다. Master Key는 반드시 sk-로 시작해야 하며, 이 키로 Admin UI 접근과 가상 키 발급이 이루어집니다.
# .env
LITELLM_MASTER_KEY=sk-my-admin-key-2026
DATABASE_URL=postgresql://litellm:${DB_PASSWORD}@db:5432/litellm
DB_PASSWORD=your-secure-db-password
REDIS_HOST=redis
REDIS_PASSWORD=your-redis-password
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...
GEMINI_API_KEY=...
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
UI_USERNAME=admin
UI_PASSWORD=your-ui-password
LANGFUSE_PUBLIC_KEY=pk-lf-...
LANGFUSE_SECRET_KEY=sk-lf-...
컨테이너를 올린 뒤 헬스체크로 정상 작동 여부를 확인합니다. {"status": "healthy"} 응답이 오면 Admin UI에 접속할 수 있습니다.
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와 Gemini Flash만 접근 가능하고 월 $50 예산이 걸린 키를 발급하는 예시입니다. 예산 초과 시 자동으로 요청이 차단됩니다.
# 프론트엔드 팀용 키 (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, models, monthly_budget, rpm_limit=100, team_id=None):
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():
response = httpx.get(
f"{PROXY_URL}/key/list",
headers={"Authorization": f"Bearer {MASTER_KEY}"}
)
return response.json().get("keys", [])
def get_key_spend(key):
response = httpx.get(
f"{PROXY_URL}/key/info",
headers={"Authorization": f"Bearer {MASTER_KEY}"},
params={"key": key}
)
return response.json()
def delete_key(key):
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)
Proxy 서버가 올라가면 기존 코드에서 base_url과 api_key(가상 키)만 교체하면 연동이 끝납니다. OpenAI SDK, Anthropic SDK, Claude Code, Cursor 모두 동일한 방식으로 연결합니다.
# ── 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",
messages=[{"role": "user", "content": "안녕"}]
)
Anthropic SDK도 같은 방식입니다. base_url을 proxy 주소로 지정하고 가상 키를 api_key로 넣으면 됩니다.
# ── Anthropic SDK ─────────────────────────────────────
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와 Cursor는 환경변수 또는 설정 UI에서 base_url을 바꾸는 것으로 연동됩니다. 이 방식으로 연결하면 Claude Code나 Cursor에서 발생하는 모든 비용이 Proxy를 통해 중앙 집계됩니다.
# ── Claude Code ──────────────────────────────────────
export ANTHROPIC_BASE_URL="http://our-proxy.company.com:4000"
export ANTHROPIC_API_KEY="sk-claude-code-team-xxxx"
claude
# ── Cursor ──────────────────────────────────────────
# Settings → Models → OpenAI API Key
# Base URL: http://our-proxy.company.com:4000
# API Key: sk-frontend-team-xxxx
# Model: claude-sonnet
실전 6 — 팀 관리 및 비용 모니터링
팀 단위로 예산과 모델 접근을 묶어서 관리하면 가상 키보다 한 단계 더 세밀한 제어가 가능합니다. 팀 생성 후 멤버를 추가하면 멤버별로 user 또는 admin 역할을 부여할 수 있습니다.
# 팀 생성
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"}
]
}'
팀별 비용 리포트는 Python으로 자동화해서 슬랙이나 이메일로 주기적으로 보내는 게 편합니다. 아래 코드는 이번 달 팀별 지출을 예산 대비 퍼센트로 출력합니다.
import httpx
from datetime import datetime
def get_team_spend_report():
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)
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 로깅 연동
Langfuse를 연동하면 모든 LLM 요청의 프롬프트, 응답, 비용, 레이턴시가 Langfuse 대시보드에 자동으로 기록됩니다. config.yaml에 콜백 두 줄, .env에 키 두 개만 추가하면 됩니다.
# 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
클라이언트 요청에 metadata를 넣으면 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": {
"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, 에러율, 프롬프트 버전 관리, 평가 점수 추적까지 확인할 수 있습니다.
마무리
3편에서는 개인 사용을 넘어 팀 전체가 LLM을 함께 쓸 수 있는 인프라를 구축했습니다. config.yaml로 멀티 모델 라우팅을 설정하고, Docker Compose로 프로덕션 환경을 올리고, 가상 키로 팀별 모델 접근과 예산을 분리했습니다. Claude Code와 Cursor까지 Proxy에 연결하면 AI 코딩 툴 비용도 하나의 대시보드에서 통합 추적할 수 있습니다.
4편에서는 LangChain·LangGraph에서 Proxy를 통합하는 방법, 콘텐츠 필터와 PII 마스킹을 처리하는 가드레일, Prometheus + Grafana 메트릭 연동, Kubernetes/Helm 배포, Redis 기반 고가용성 설정, HTTPS와 방화벽 보안 구성을 다룹니다.
관련 글
- LiteLLM 완전 가이드 1편 — 100개+ LLM 단일 인터페이스
- LiteLLM 완전 가이드 2편 — Router, 폴백, 비용 추적, 캐싱
- OpenRouter 완전 가이드 4편 — 모니터링, 팀 운영, ZDR