TT Lab
시작하기
배우기 러닝패스 코스

LLM 서빙

레이트리밋과 토큰 회계 게이트웨이 만들기

TT Lab 에서 이어서 보기

목표

LLM 게이트웨이를 직접 만들어 요청 단위가 아닌 토큰 단위의 제한과 회계를 구현하고, 테넌트별 예산 통제까지 완성한다.

왜 중요한가

분당 60요청 제한은 LLM 앞에서 거의 무의미합니다. 어떤 테넌트가 매 요청에 200,000토큰을 넣으면 요청 수는 제한 안이지만 비용과 GPU 점유는 다른 테넌트의 수백 배이고, 그 요청들이 KV 캐시를 독식해 다른 요청들을 선점시킵니다. 그래서 LLM 게이트웨이의 첫 번째 설계 원칙은 토큰을 세는 것입니다. 이 실습에서 특히 까다로운 부분이 4번과 8번입니다. 출력 토큰은 생성이 끝나야 알 수 있으므로, 예산 검사를 완료 후에만 하면 동시에 들어온 요청들이 모두 예산 안이라고 판단하고 통과한 뒤 함께 초과합니다. max_tokens 를 상한으로 미리 예약하고 완료 후 정산하는 방식이 이 문제를 풉니다. 실제 서비스에서 비용 사고가 나는 지점이 정확히 여기입니다.

단계

  1. /root/gw/gateway.py 를 127.0.0.1:8171 에 띄우고 POST /v1/generate 를 8170 백엔드로 전달한다. 응답이 백엔드와 동일해야 한다. 테넌트 t1 의 API 키를 /root/gw/key.txt 에, t2 의 키를 /root/gw/key2.txt 에 각각 한 줄로 저장해 둔다 — 이후 스텝의 채점이 이 두 파일을 쓴다.
  2. X-API-Key 헤더로 테넌트를 식별한다. 키-테넌트 매핑은 /opt/fixtures/llms/apikeys.json 에 있다. 키가 없거나 모르는 키면 401 이다.
  3. 테넌트별 분당 요청 제한 10 을 적용한다. 11번째 요청이 429 여야 한다. /root/gw/rpm.txt 에 allowed=10 rejected=1 을 적는다.
  4. GET /v1/usage 가 {"tenant":"...","prompt_tokens":<n>,"completion_tokens":<n>,"requests":<n>} 를 준다. 두 토큰 값이 모두 0보다 커야 한다.
  5. 분당 토큰 제한 2000 을 토큰 버킷으로 적용한다. 한도를 넘기는 요청이 429 를 받아야 한다. /root/gw/tpm.txt 에 limit=2000 consumed=<n> rejected=<n> 을 적고 rejected 는 1 이상이어야 한다.
  6. 모든 429 응답에 Retry-After 헤더가 1 이상 60 이하의 정수로 붙어야 한다. /root/gw/retry.txt 에 retry_after=<정수> 를 적는다.
  7. /opt/fixtures/llms/pricing.json 의 모델별 단가로 비용을 계산해 /root/gw/cost.csv 에 tenant,model,prompt_tokens,completion_tokens,cost_usd 헤더와 2행 이상을 적는다.
  8. 테넌트 t1 의 일간 예산을 0.01 달러로 두고 초과 시 402 를 준다. 예약은 max_tokens 기준으로 미리 잡고 완료 후 실제 사용량으로 정산한다. /root/gw/budget.txt 에 budget_usd=0.01 spent_usd=<수> blocked=true 를 적는다.

참고

게이트웨이로 백엔드 프록시하기

/root/gw/gateway.py 를 127.0.0.1:8171 에 띄우고 POST /v1/generate 를 8170 백엔드로 전달한다. 응답이 백엔드와 동일해야 한다. 테넌트 t1 의 API 키를 /root/gw/key.txt 에, t2 의 키를 /root/gw/key2.txt 에 각각 한 줄로 저장해 둔다 — 이후 스텝의 채점이 이 두 파일을 쓴다.

먼저 그대로 통과시키는 프록시를 만들고 기능을 하나씩 얹습니다.

API 키로 테넌트 식별하기

X-API-Key 헤더로 테넌트를 식별한다. 키-테넌트 매핑은 /opt/fixtures/llms/apikeys.json 에 있다. 키가 없거나 모르는 키면 401 이다.

키가 없거나 모르는 키면 401 입니다. 키를 테넌트에 매핑해 두세요.

분당 요청 제한 걸기

테넌트별 분당 요청 제한 10 을 적용한다. 11번째 요청이 429 여야 한다. /root/gw/rpm.txt 에 allowed=10 rejected=1 을 적는다.

고정 윈도우가 가장 단순합니다. 초과하면 어떤 상태 코드인지 생각해 보세요.

입력·출력 토큰 회계하기

GET /v1/usage 가 {"tenant":"...","prompt_tokens":<n>,"completion_tokens":<n>,"requests":<n>} 를 준다. 두 토큰 값이 모두 0보다 커야 한다.

둘을 따로 세야 합니다. 단가가 다르기 때문입니다.

분당 토큰 제한 걸기

분당 토큰 제한 2000 을 토큰 버킷으로 적용한다. 한도를 넘기는 요청이 429 를 받아야 한다. /root/gw/tpm.txt 에 limit=2000 consumed=<n> rejected=<n> 을 적고 rejected 는 1 이상이어야 한다.

토큰 버킷이 잘 맞습니다. 조용하던 테넌트의 버스트를 허용하면서 장기 평균을 지킵니다.

429 에 재시도 시점 담기

모든 429 응답에 Retry-After 헤더가 1 이상 60 이하의 정수로 붙어야 한다. /root/gw/retry.txt 에 retry_after=<정수> 를 적는다.

남은 시간을 계산해 정수 초로 줍니다. 이게 없으면 클라이언트들이 파도를 만듭니다.

모델별 단가로 비용 계산하기

/opt/fixtures/llms/pricing.json 의 모델별 단가로 비용을 계산해 /root/gw/cost.csv 에 tenant,model,prompt_tokens,completion_tokens,cost_usd 헤더와 2행 이상을 적는다.

출력이 입력보다 비싼 것이 보통입니다. 단가표는 픽스처에 있습니다.

예산 초과 차단과 사용량 보고하기

테넌트 t1 의 일간 예산을 0.01 달러로 두고 초과 시 402 를 준다. 예약은 max_tokens 기준으로 미리 잡고 완료 후 실제 사용량으로 정산한다. /root/gw/budget.txt 에 budget_usd=0.01 spent_usd=<수> blocked=true 를 적는다.

차단 대신 더 싼 모델로 강등하는 선택지도 있습니다. 여기서는 차단으로 구현합니다.