LLM 라우터는 왜 로드밸런서가 아니라 캐시 배치 스케줄러일까
같은 모델 서버가 여러 대 있을 때, LLM 요청을 그냥 라운드로빈으로 나누면 왜 느려질까?
LLM 라우터는 왜 로드밸런서가 아니라 캐시 배치 스케줄러일까
- 카테고리: ai_dev
- 예상 읽기 시간: 10분
- 오늘의 질문: 같은 모델 서버가 여러 대 있을 때, LLM 요청을 그냥 라운드로빈으로 나누면 왜 느려질까?
- 핵심 출처:
- Report: GKE Inference Gateway delivers up to 92% faster AI responses - 2026-06-10, 2026-06-22 확인
- About GKE Inference Gateway powered by llm-d - 게시일 미표기, 2026-06-22 확인
- Precise Prefix Cache Aware Routing - 게시일 미표기, 2026-06-22 확인
- llm-d Router - GitHub repository, 2026-06-22 확인
- Scaling Ray Serve LLM on GKE: Performance without losing the developer experience - 2026-06-18, 2026-06-22 확인
1. 왜 지금 봐야 하나
LLM 서비스를 처음 운영할 때는 “GPU 서버를 여러 대 띄우고 로드밸런서가 골고루 나눠주면 되겠지”라고 생각하기 쉽다. 일반 웹 API라면 대체로 맞는 출발점이다. 하지만 LLM 추론은 요청 하나가 서버에 남기는 상태가 훨씬 크다. 특히 긴 시스템 프롬프트, RAG로 붙인 문서, 멀티턴 대화 기록은 모델이 한 번 읽고 끝나는 문자열이 아니라, GPU 메모리 안의 KV cache라는 계산 결과로 남는다.
그래서 같은 8대의 GPU 서버가 있어도 요청을 아무 서버에나 보내면 이미 계산해 둔 prefix를 버리고 다시 prefill을 해야 한다. 반대로 같은 prefix를 가진 요청을 그 prefix의 KV cache가 남아 있는 서버로 보내면 time to first token, 즉 첫 토큰까지 걸리는 시간이 크게 줄 수 있다.
최근 Google Cloud는 GKE Inference Gateway의 prefix-cache-aware routing을 소개하며, 기존 라운드로빈식 HTTP 로드밸런싱과 비교했을 때 공유 prefix workload에서 TTFT가 92.8% 짧아지고, inter-token latency도 62.6% 낮아졌다고 설명했다. 같은 시기 GKE 문서와 llm-d 문서는 이 흐름을 더 구조적으로 보여준다. GKE Inference Gateway는 llm-d Router를 이용해 KV cache hit, queue length, GPU/TPU utilization, LoRA adapter affinity 같은 신호로 endpoint를 고른다. llm-d의 precise prefix cache aware guide는 vLLM pod가 KV cache allocation/eviction event를 내보내고, scheduler가 block hash index를 만들어 요청 prefix가 어느 pod에 얼마나 남아 있는지 점수화한다고 설명한다.
오늘의 핵심은 이것이다.
LLM 라우팅은 “트래픽을 공평하게 나누는 일”이 아니라, 요청의 prefix와 서버의 KV cache 상태를 맞춰 prefill 재계산을 줄이는 캐시 배치 스케줄링 문제다.
2. 핵심 개념
일반적인 HTTP 로드밸런서는 CPU 사용률, 연결 수, 응답 시간, zone 같은 신호를 보고 요청을 나눈다. LLM 서버도 이런 신호가 필요하지만, 그것만으로는 부족하다. LLM 요청에는 크게 두 종류의 계산이 섞여 있기 때문이다.
첫째는 prefill이다. 모델이 입력 토큰 전체를 읽고 KV cache를 만드는 단계다. 입력이 길수록 비싸다. RAG 문서 2만 토큰, 공통 시스템 프롬프트 3천 토큰, 이전 대화 1만 토큰을 매번 새로 읽으면 첫 토큰이 늦어진다.
둘째는 decode다. 이미 만들어진 KV cache를 바탕으로 다음 토큰을 하나씩 생성하는 단계다. 여기서는 출력 길이와 동시성, batching, streaming path가 중요해진다.
Prefix caching은 prefill 비용을 줄이려는 전략이다. 여러 요청이 앞부분을 공유한다면, 그 앞부분의 KV cache를 재사용한다. 예를 들어 사내 API 문서 Q&A 봇이 항상 “다음 문서를 기준으로 답하라”는 1만 토큰 문서를 prefix로 붙이고, 마지막에 사용자 질문만 바꾼다고 하자. prefix cache가 잘 맞으면 모델은 1만 토큰 문서를 매번 다시 읽지 않고 새 질문 부분만 처리한다.
하지만 캐시는 서버별 메모리에 있다. A pod에 남아 있는 KV cache를 B pod가 자동으로 쓸 수 있는 것은 아니다. 그래서 라우터가 “지금 이 요청은 A pod로 가면 prefix cache hit가 높고, B pod로 가면 거의 miss다”를 알아야 한다. 이때 LLM 라우터는 단순 load balancer가 아니라 다음 신호를 조합하는 scheduler가 된다.
- prefix cache match: 요청 prefix가 어느 서버의 KV cache와 얼마나 겹치는가
- KV cache utilization: 서버 메모리가 이미 얼마나 차 있는가
- queue depth: 그 서버 앞에 대기 요청이 얼마나 있는가
- priority: 이 요청이 지연 민감한가, 배치 처리 가능한가
- LoRA affinity: 필요한 adapter가 이미 올라가 있는 서버인가
- model/objective rewrite: 클라이언트가 부르는 모델명과 실제 serving pool을 어떻게 매핑할 것인가
여기서 중요한 tradeoff가 생긴다. cache hit만 보고 한 서버로 몰면 queue가 길어져 tail latency가 나빠진다. load만 보고 분산하면 cache miss가 늘어 prefill을 반복한다. 좋은 라우터는 둘 중 하나를 고르는 것이 아니라, cache locality와 현재 load 사이의 점수 함수를 운영한다.
3. 최신 이슈와 연결
이번 흐름이 흥미로운 이유는 “LLM infra 최적화”가 더 이상 모델 서버 내부만의 문제가 아니기 때문이다. vLLM, SGLang, TensorRT-LLM 같은 엔진이 batching과 KV cache를 최적화해도, 앞단 라우터가 cache locality를 깨면 성능을 잃는다. 반대로 Kubernetes Gateway나 Envoy 같은 네트워크 계층이 LLM-specific signal을 이해하면, 같은 모델과 같은 하드웨어에서도 체감 지연이 달라진다.
GKE Inference Gateway 문서는 이 구조를 GKE Gateway와 llm-d Endpoint Picker로 나눈다. Gateway는 TLS, 연결 관리, request forwarding 같은 data plane을 맡고, Endpoint Picker는 ext-proc 방식으로 호출되어 요청마다 적절한 pod를 고른다. 이때 Endpoint Picker가 보는 신호는 일반 웹 서비스의 HTTP metric이 아니라 KV cache utilization, queue length, prefix cache state, LoRA adapter affinity다.
llm-d의 precise prefix cache aware routing 문서는 더 낮은 레벨을 보여준다. vLLM pod가 KV cache block allocation과 eviction event를 ZMQ로 publish한다. scheduler는 이 event를 구독해 block hash 기반 index를 만들고, incoming request를 tokenization한 뒤 같은 block hash로 매칭한다. 그 결과 “이 pod에는 요청 prefix block의 몇 %가 resident 상태인가”를 계산할 수 있다. 즉 라우팅 판단이 추측이 아니라 실제 cache event에 가까워진다.
Ray Serve LLM on GKE 글도 같은 방향을 다른 각도에서 보여준다. Ray Serve는 HAProxy integration, direct token streaming, vLLM Ray executor backend 개선을 통해 routing control plane과 token streaming data plane을 분리했다. 여기서도 핵심은 비슷하다. LLM serving에서 router가 모든 token stream을 붙잡고 있으면 event loop와 data path가 병목이 된다. routing decision은 똑똑해야 하지만, 결정 이후 token stream은 가능한 직접 흐르게 해야 한다.
정리하면 최신 LLM serving stack의 방향은 세 가지다.
- 라우터는 요청 내용과 serving state를 이해한다.
- 모델 서버는 KV cache, queue, adapter 같은 내부 상태를 외부 scheduler에 노출한다.
- routing control plane과 token streaming data plane을 분리해, 똑똑한 결정이 새로운 병목이 되지 않게 한다.
4. 개발자 관점 해석
작은 팀이 당장 GKE Inference Gateway나 llm-d를 운영하지 않더라도, 이 원리는 AI 제품 설계에 바로 영향을 준다.
첫 번째 해석은 프롬프트 구조가 인프라 성능에 영향을 준다는 점이다. prefix caching이 먹히려면 공통 prefix가 안정적이어야 한다. 매 요청마다 system prompt의 순서가 바뀌거나, timestamp·사용자별 metadata·trace id를 prefix 앞쪽에 끼워 넣으면 cache match가 깨진다. 캐시를 기대한다면 prompt를 “긴 정적 prefix + 짧은 동적 suffix”로 조립해야 한다.
두 번째 해석은 RAG가 항상 검색 최적화만의 문제가 아니라는 점이다. 같은 문서 묶음을 많은 사용자가 반복 질의하는 제품이라면, 검색 결과를 매번 prompt 중간에 다른 순서로 넣는 것보다 안정적인 문서 bundle prefix를 만들고 그 위에 질문을 붙이는 방식이 더 유리할 수 있다. 물론 freshness와 access control을 깨면 안 된다. 캐시 hit를 위해 오래된 문서를 고정하면 답변 품질이 나빠진다.
세 번째 해석은 autoscaling 지표가 달라진다는 점이다. 단순 GPU utilization만 보면 “서버가 바쁘다”는 사실은 보이지만 왜 느린지는 안 보인다. queue wait가 문제인지, prefill miss가 문제인지, decode streaming path가 문제인지, KV cache eviction이 많은지 분리해야 한다. prefix cache hit rate, TTFT, TPOT/ITL, queue depth, KV cache utilization을 함께 봐야 한다.
네 번째 해석은 multi-tenant product에서 공정성과 locality가 충돌한다는 점이다. 한 대형 고객의 긴 prefix가 특정 pod cache를 계속 점유하면 그 고객은 빨라질 수 있지만, 다른 고객은 queue에 밀리거나 cache eviction 피해를 볼 수 있다. priority와 quota, tenant isolation, adapter affinity를 같이 설계해야 한다.
5. 내 프로젝트에 적용할 체크포인트
LLM API를 직접 호스팅하거나 self-hosted inference를 검토한다면 아래를 체크해보자.
- 우리 요청은 공통 prefix가 있는가? system prompt, 정책 문서, 코드베이스 요약, API reference, few-shot 예제가 반복되는가?
- 공통 prefix가 항상 같은 byte/token 순서로 조립되는가? 요청마다 날짜, 사용자명, 로그 id가 앞부분에 섞이지 않는가?
- TTFT와 output token latency를 따로 보고 있는가? 평균 latency 하나로 prefill 병목과 decode 병목을 섞고 있지 않은가?
- 캐시 hit를 높이려다 특정 pod queue가 길어지는 상황을 감지할 수 있는가?
- RAG 문서가 자주 바뀌는가? 바뀐다면 cache reuse와 freshness 사이에 어떤 TTL 또는 invalidation 규칙이 필요한가?
- LoRA adapter를 여러 개 서빙한다면 adapter가 이미 올라간 서버로 보내는 affinity와 cold load 비용을 계측하고 있는가?
- 라우팅 계층이 token streaming data path의 병목이 되지 않는가?
반대로 managed LLM API만 쓰는 팀도 배울 점이 있다. prompt caching을 제공하는 API를 쓸 때도 prefix 안정성은 여전히 중요하다. “서버를 직접 운영하지 않는다”는 말은 “캐시 친화적인 prompt 구조를 몰라도 된다”는 뜻이 아니다. 비용과 지연을 줄이려면 프롬프트 조립 순서, 정적/동적 구간 분리, schema/tool definition의 위치를 의식해야 한다.
6. 오늘 10분 액션
오늘은 코드 없이 현재 프로젝트의 LLM 요청 하나를 열어보고 prefix를 표시해보자.
- 실제 운영 또는 개발 요청 payload 하나를 복사한다.
- 앞에서부터 “모든 사용자에게 반복되는 부분”을 형광펜 치듯 표시한다.
- 그다음 “사용자마다 바뀌지만 한 대화 안에서는 반복되는 부분”을 따로 표시한다.
- 마지막으로 “매 요청마다 바뀌는 부분”을 표시한다.
- 바뀌는 값이 prefix 앞쪽에 끼어 있다면 뒤쪽으로 옮길 수 있는지 적어본다.
예시는 이렇게 나눌 수 있다.
[정적 prefix]
- system instruction
- 출력 정책
- tool schema
- 자주 쓰는 API 문서 bundle
[대화/session prefix]
- 현재 사용자 권한 범위
- 선택된 프로젝트 요약
- 최근 대화 요약
[동적 suffix]
- 이번 사용자 질문
- 현재 timestamp
- request id
- trace/debug metadata
10분 안에 결론 하나만 내리면 된다. “우리 프롬프트는 캐시가 잘 맞는 구조인가, 매번 깨지는 구조인가?” 이 질문에 답할 수 있으면 오늘의 학습은 충분하다.
7. 더 볼 자료
- Google Cloud Blog: GKE Inference Gateway prefix caching benchmark와 사용 사례
- Google Cloud Documentation: GKE Inference Gateway와 llm-d Router 구조
- llm-d guide: precise prefix-cache-aware routing의 KV event, block hash, scorer 구조
- llm-d Router GitHub: Endpoint Picker, InferencePool, priority, flow control 개념
- Google Cloud/Anyscale Ray Serve LLM 글: routing control plane과 streaming data plane 분리
중복 회피 메모
최근 ai_dev 글은 prompt caching의 프롬프트 구조 계약, prefill/decode 지연 분리, KV cache 양자화, speculative decoding, guardrail checkpoint, MCP elicitation을 다뤘다. 이번 글은 API-level prompt caching이나 모델 서버 내부의 prefill/decode 최적화를 반복하지 않고, 여러 model server pod 앞단에서 요청을 어디로 보낼지 결정하는 prefix-cache-aware routing과 LLM 라우터의 scheduling 원리에 집중한다.
핵심 출처
로그인하면 이 글을 북마크하고, 나만 보는 한 줄 메모를 남길 수 있어요.
댓글 0
최신순 ▾혹시 이 글을 읽는 동료 개발자가 있다면, GitHub으로 로그인하고 한 줄 흔적을 남겨줘요. (스팸 방지용 로그인이에요)