
LiteLLM Proxy를 이용한 LLM Gateway 구축: 비용, 토큰 예산 관리 및 다중 Provider 동시 호출 시의 Failover
요약
LiteLLM Proxy를 활용하여 여러 LLM 제공업체를 통합 관리하는 LLM Gateway 구축 방법을 소개합니다. 비용 가시성 확보, API 키 중앙 관리, 장애 발생 시 자동 페일오버 기능을 통해 확장 가능한 AI 인프라를 구축할 수 있습니다.
핵심 포인트
- LiteLLM Proxy를 통한 OpenAI 호환 단일 인터페이스 구축
- 팀/프로젝트별 사용량 모니터링 및 비용 가시성 확보
- 특정 Provider 장애 시 다른 모델로 자동 페일오버 구현
- 다양한 LLM 제공업체 간의 요청/응답 포맷 통일
지난달 제가 대화했던 한 팀의 팀원이 그달의 LLM 청구서(bill)가 자신들도 모르는 사이에 거의 3배나 급증했다고 불평했습니다. 청구서가 나온 후에야 알게 된 것이죠. 원인은 트래픽 증가가 아니라, 한 개발자가 디버깅을 위해 동일한 서비스 내에서 모델을 Haiku에서 Opus로 변경했다가 다시 되돌리는 것을 잊어버렸기 때문입니다. 문제는 어떤 팀도 서비스 단위(per-service)의 모니터링을 하지 않고 있었다는 점입니다. 각 팀이 자신의 Provider로 API를 직접 호출하고 있었기 때문에, 누가 어떤 키를 사용하는지, 누가 얼마나 호출하는지에 대한 중앙 집중식 뷰(central view)가 없었습니다.
이는 애플리케이션과 Provider 사이에 중간 레이어(layer) 없이 여러 Provider(Bedrock, OpenAI, Anthropic API 직접 호출 등)를 동시에 사용하기 시작하며 빠르게 성장하는 팀들이 겪는 전형적인 문제입니다.
직접 통합(Direct Integration)이 확장 시 무너지는 이유
LLM을 호출하는 서비스가 1~2개일 때는 직접 호출해도 괜찮지만, 서비스가 5개 이상으로 늘어나면 다음과 같은 문제들이 반복적으로 발생합니다.
- 분산된 키(Key) — 각 서비스가 자신만의 API 키를 가지고 있으며, 어떤 것은 env var에, 어떤 것은 Secrets Manager에 저장되는 등 통일된 패턴이 없습니다.
- 팀/프로젝트 단위의 비용 가시성(Cost visibility) 부재 — 월말에 합산된 청구서만 볼 수 있을 뿐, 누가 얼마나 사용했는지는 알 수 없습니다.
- Rate limit 충돌 — 서비스 A와 B가 동일한 Provider의 동일한 키를 사용하는 경우, 피크 시간대에 A가 과도하게 호출하면 관련 없는 B까지 함께 스로틀링(throttle)을 당하게 됩니다.
- Provider 장애 시 대응 불가 — Bedrock에 스로틀링이 걸리거나 특정 리전(region)에 문제가 생겨도 다른 Provider로 자동 페일오버(fallback)되지 않으며, 수동 수정(manual fix)을 기다려야 합니다.
- 일치하지 않는 포맷 — Anthropic API에서 Bedrock으로 전환할 때, 요청(request) 및 응답(response) 형태가 다르기 때문에 코드를 수정해야 합니다.
현재 많은 팀이 사용하는 해결책은 중간에 LLM gateway를 두는 것입니다. 모든 서비스가 통과하는 단일 지점을 만들고, 게이트웨이가 대신 라우팅(routing), 예산(budget), 재시도(retry)를 관리하도록 하는 방식입니다.
LiteLLM Proxy로 무엇을 할 수 있는가
LiteLLM Proxy는 거의 모든 제공업체(OpenAI, Anthropic, Bedrock, Vertex, Azure OpenAI 등)를 OpenAI 호환 API (OpenAI-compatible API)라는 동일한 인터페이스를 통해 지원하는 오픈 소스 프록시 (open-source proxy)입니다. 클라이언트 측 애플리케이션은 백엔드에서 어떤 제공업체로 요청을 보내는지 알 필요가 없습니다. 단 하나의 엔드포인트 (endpoint)만 호출하면 프록시가 라우팅 (routing)을 직접 관리합니다.
기본 설정은 대략 다음과 같은 모습입니다 (예시이며, 실제 프로덕션 설정값이 아닙니다):
model_list:
- model_name: gpt-tier-fast
litellm_params:
...
동일한 model_name (gpt-tier-fast)이 두 개의 제공업체에 연결되어 있음에 주목하세요. 이것이 투명한 페일오버 (failover)를 가능하게 하는 지점입니다. 만약 Bedrock에서 스로틀링 (throttle)이 발생하면, 프록시는 클라이언트가 알지 못하는 사이에 자동으로 OpenAI로 라우팅합니다.
팀별 토큰 예산 관리 (Token budgeting): 전체 청구서 기준이 아닌 개별 단위
실질적인 비용 제어 (cost control)를 돕는 핵심은 가상 키 (virtual key)입니다. 각 팀이나 프로젝트는 프록시로부터 키를 요청할 수 있으며, 각각 별도의 예산 (budget)을 설정할 수 있습니다.
curl -X POST http://litellm-proxy:4000/key/generate \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-d '{
...
어떤 팀이 예산을 초과하면 키는 자동으로 차단됩니다. 월말에 재무팀이 찾아와 주의를 줄 때까지 기다릴 필요가 없습니다. 또한 팀별, 모델별 지출 현황을 확인할 수 있는 대시보드 (dashboard)가 내장되어 있습니다. 이는 "모델을 변경하고 다시 되돌리는 것을 잊어버리는" 케이스를 정확히 해결해 줍니다. 예산 경고 (budget alert)가 청구서가 폭발한 후에 나타나는 것이 아니라, 청구서가 나오기 전에 먼저 발생하기 때문입니다.
단순 재시도 (retry) 그 이상의 페일오버 (Failover) 고려 사항
단순한 (naive) 재시도 방식(에러 발생 시 단순히 다시 요청하는 방식)은 생각보다 위험합니다. 특히 에러의 원인이 속도 제한 (rate limit)인 경우, 계속해서 재시도하면 문제를 더욱 악화시킬 수 있습니다. 다음과 같은 설정을 고려해야 합니다:
- 지수 백오프 (Exponential backoff): 동일한 제공업체로 재시도할 때 적용 (2~3회 정도로 제한).
- 백업 모델/제공업체로의 폴백 (Fallback): 기존 재시도가 실패할 경우, 무제한 재시도를 하는 것이 아니라 백업 모델이나 제공업체로 전환.
- 쿨다운 기간 (Cooldown period): 특정 제공업체에 장애가 발생하면, 프록시가 일정 시간(예: 60초) 동안 해당 제공업체로의 요청을 기억하고 피한 뒤 다시 시도하도록 설정 (모든 요청마다 즉시 재시도하지 않음).
- 유스케이스 (use case)에 맞는 합리적인 타임아웃 (Timeout): 대화형 (interactive) 채팅은 짧은 타임아웃(10~15초)을 설정할 수 있지만, 배치 요약 (batch summarization)의 경우 더 길게 설정할 수 있습니다.
fallback을 설정해 두었음에도 타임아웃(timeout)을 너무 짧게(5초) 설정하여 실제 문제가 발생한 사례가 있었습니다. 그 결과, 8초 정도 기다렸다면 정상적으로 성공했을 요청이 타임아웃이 먼저 발생하여 매번 보조 provider로 fallback 되었고, 보조 provider가 더 비싸기 때문에 불필요하게 비용이 더 많이 발생하는 상황이 초래되었습니다. 여기서 얻은 교훈은 타임아웃(timeout)을 튜닝(tuning)하는 것이 fallback 로직을 설정하는 것만큼이나 중요하다는 것입니다.
RAG와 proxy — 흔히 간과하는 지점
RAG 파이프라인(pipeline)을 사용하는 경우, 임베딩 호출(embedding call)과 생성 호출(generation call)을 명확히 구분해야 합니다. 이 두 워크로드(workload)는 사용 패턴이 매우 다르기 때문입니다. 임베딩(embedding)은 새로운 문서를 인제스트(ingest)할 때 대량으로 발생하는 버스트(burst) 형태를 띠는 반면, 생성(generation)은 사용자 트래픽(user traffic)에 따라 분산됩니다. 만약 두 작업에 동일한 가상 키(virtual key)를 사용하면 예산(budget) 및 속도 제한(rate limit)이 뒤섞이게 됩니다. 따라서 워크로드 유형별로 키를 분리하여 각 유형에 적합한 속도 제한(rate limit)을 실제로 설정할 것을 권장합니다. 그렇지 않으면 제어할 수 없을 정도로 범위를 너무 넓게 잡게 됩니다.
프로덕션(production) 투입 전 체크리스트
- 애플리케이션에서 마스터 키(master key)를 직접 호출하지 말고, 팀/프로젝트별로 가상 키(virtual key)를 분리할 것
- 청구서가 폭발한 뒤에 설정하는 것이 아니라, 첫날부터 모든 키에
max_budget+budget_duration을 설정할 것 - 애플리케이션 코드에 특정 모델 이름을 하드코딩(hardcode)하는 대신, 모델 티어(model tier) (fast/cheap vs premium)를 매핑할 것
- 중요한 모델 티어(model tier)당 최소 1단계의 fallback 체인(chain)을 설정할 것
- 시스템 전체에 동일한 기본값을 사용하지 말고, 유스케이스(use case) (대화형 vs 배치)에 따라 타임아웃(timeout)을 별도로 튜닝(tune)할 것
- 임베딩 워크로드(embedding workload)와 생성 워크로드(generation workload) 간의 키를 분리할 것
- 기존의 옵저버빌리티 스택(observability stack)으로 요청(request) 수준의 로깅(logging)을 활성화할 것 (언젠가 LiteLLM에서 벗어날 경우를 대비해 proxy의 대시보드에만 의존하지 말 것)
- 설정만 믿지 말고, 스테이징(staging) 환경에서 메인 provider를 일시적으로 차단하여 실제 failover가 작동하는지 테스트할 것
사람들이 흔히 건너뛰는 부분은 마지막 항목입니다. fallback 설정을 작성해 두고 실제로 언제 트리거(trigger)되는지 테스트해 보지 않는 것입니다. 그러다 메인 provider가 실제로 다운되는 날에야 설정의 어느 부분이 잘못되었는지 알게 되는데, 이는 버그를 발견하기에 최악의 타이밍입니다.
이러한 방식의 게이트웨이(Gateway)가 모든 문제를 해결해 주는 것은 아닙니다. 팀 규모가 매우 작고 서비스가 하나이며 단일 provider만 호출한다면 오히려 과할(overkill) 수 있습니다. 하지만 1개 이상의 provider를 사용하거나 LLM을 호출하는 서비스가 3~4개 이상으로 늘어나기 시작하면, 이러한 중간 레이어(layer)를 두는 것이 디버깅(debug) 시간과 비용을 처음에 생각했던 것보다 훨씬 더 많이 절약해 줍니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기