Split View: vLLM 내부 구조 (1) — 요청 하나가 토큰이 되어 나오기까지
vLLM 내부 구조 (1) — 요청 하나가 토큰이 되어 나오기까지
- 이 시리즈가 다루는 것
- 요청 하나의 전체 경로
- API 서버: 요청을 엔진의 언어로 바꾸는 곳
- 엔진 코어: 스케줄러와 KV 캐시 매니저
- 워커와 샘플러: 실제로 GPU를 만지는 층
- 설정이 한 객체로 모이는 이유
- 직접 해보기
- 참고 자료
이 시리즈가 다루는 것
LLM을 직접 서빙해 본 사람은 대개 비슷한 순서로 벽을 만납니다. 처음에는 그냥 돌아가기만 해도 다행이고, 그다음에는 왜 이렇게 느린지가 궁금해지고, 결국에는 왜 갑자기 메모리가 터지는지를 알아야만 합니다. 이 세 질문의 답은 전부 vLLM 안쪽에 있습니다.
이 시리즈는 vLLM을 "빠른 서버"라는 결과가 아니라 구조로 봅니다. 모두 7편이고 1편인 이 글은 지도에 해당합니다. 요청 하나가 들어와 토큰이 되어 나가는 경로를 처음부터 끝까지 한 번 훑고, 나머지 편에서 각 구간을 확대합니다.
시작 전에 버전 이야기를 먼저 해야 합니다. vLLM은 변화가 빠르고 내부를 크게 다시 쓴 적이 있습니다. 공식 V1 가이드는 스케줄러, KV 캐시 매니저, 워커, 샘플러, API 서버가 다시 설계되었다고 밝히고 있습니다. 그래서 이 시리즈의 설명은 V1 이후 구조를 기준으로 합니다. 오래된 글과 설명이 어긋난다면 대개 그 글이 V0 시절 이야기입니다.
내용은 2026-08-12에 공식 문서·소스에서 확인했습니다. vLLM은 변화가 빠르니 설정값과 동작은 사용 중인 버전의 문서로 다시 확인하세요.
요청 하나의 전체 경로
말로 풀기 전에 그림으로 먼저 봅니다.
클라이언트 HTTP 요청
│
▼
[API 서버 프로세스] HTTP 수신 · 입력 처리 · 토큰화
│
▼
[엔진 코어 프로세스] 스케줄러 + KV 캐시 매니저
│ "이번 스텝에 누구를 몇 토큰씩 넣을까"
▼
[GPU 워커 프로세스] ModelRunner → 모델 forward → 로짓
│
▼
샘플러 로짓에서 다음 토큰 하나를 고름
│
├───────────▶ 스트리밍으로 클라이언트에 한 조각 전달
│
└───────────▶ 아직 안 끝난 요청은 다시 스케줄러로
이 그림에서 가장 중요한 것은 마지막 화살표입니다. 토큰 하나가 나온 뒤에도 요청은 끝나지 않고 다시 스케줄러로 돌아갑니다. 요청 하나를 끝까지 처리하고 다음 요청을 보는 구조가 아니라, 살아 있는 모든 요청이 매 스텝마다 조금씩 전진합니다. vLLM의 성능 이야기는 결국 이 루프를 어떻게 빈틈없이 채우느냐의 문제입니다.
API 서버: 요청을 엔진의 언어로 바꾸는 곳
vLLM에는 진입점이 두 개 있습니다. 공식 아키텍처 문서에 따르면 오프라인 추론은 LLM 클래스가 담당하고, 온라인 서빙은 vllm serve 명령이나 OpenAI 호환 API 서버가 담당합니다. 그리고 이 OpenAI 호환 서버는 AsyncLLMEngine을 사용합니다. 비동기 래퍼가 백그라운드 루프를 돌리기 때문에, HTTP 요청이 여러 개 동시에 들어와도 각각이 서로를 기다리지 않습니다.
여기서 하는 일 자체는 화려하지 않습니다. 채팅 템플릿을 적용하고, 문자열을 토큰 열로 바꾸고, 샘플링 설정을 정리해서 엔진이 이해하는 요청 객체로 만듭니다. 다만 이 단계에서 이미 성능이 갈립니다. 프롬프트를 어떻게 조립했느냐가 5편에서 다룰 접두사 캐시 적중 여부를 결정하고, 입력 길이가 6편에서 다룰 길이 한도에 그대로 걸립니다.
엔진 코어: 스케줄러와 KV 캐시 매니저
V1의 프로세스 구성은 공식 문서에 이렇게 정리되어 있습니다. HTTP를 받는 API 서버 프로세스가 있고, 스케줄러를 돌리며 KV 캐시를 관리하는 엔진 코어 프로세스가 데이터 병렬 랭크마다 하나 있고, GPU 하나당 워커 프로세스가 하나 있습니다. 문서가 든 예시로 GPU 4장짜리 배포는 API 서버 1 + 엔진 코어 1 + GPU 워커 4, 합쳐서 6개 프로세스가 됩니다.
프로세스를 나눈 이유는 단순합니다. HTTP 파싱과 토큰화는 파이썬이 CPU를 쓰는 일이고, 모델 실행은 GPU를 쓰는 일입니다. 둘이 같은 프로세스에 있으면 CPU 작업이 GPU 루프를 막습니다. 실제로 처리량이 안 나오는 배포를 들여다보면 GPU 사용률은 낮은데 CPU 한 코어가 100퍼센트인 경우가 흔합니다.
엔진 코어 안에서 스케줄러와 KV 캐시 매니저는 한 몸처럼 움직입니다. 스케줄러가 "이 요청을 이번 스텝에 넣겠다"고 정하려면 그만큼의 KV 캐시 블록을 실제로 확보할 수 있어야 하기 때문입니다. 확보하지 못하면 스케줄러는 이미 돌고 있던 요청을 뒤로 물리기까지 합니다. 이 선점 동작이 4편의 주제입니다.
워커와 샘플러: 실제로 GPU를 만지는 층
아키텍처 문서의 정의는 간결합니다. Worker는 모델 추론을 돌리는 프로세스이고, ModelRunner는 모델을 적재하고 실행하는 역할이며, 그 안의 Model이 실제 torch 모듈 인스턴스입니다.
워커가 forward를 한 번 돌리면 배치 안의 각 요청에 대해 다음 토큰의 로짓이 나옵니다. 샘플러는 여기에 temperature, top_p, top_k 같은 설정을 적용해 토큰 하나를 고릅니다. 요청마다 샘플링 설정이 다를 수 있는데도 같은 배치에 섞일 수 있다는 점이 중요합니다. 배치는 모델 계산을 공유할 뿐이고, 무엇을 뽑을지는 요청별로 따로 결정됩니다.
설정이 한 객체로 모이는 이유
문서가 강조하는 설계 하나가 VllmConfig입니다. 필요한 정보를 전부 담은 설정 객체를 만들어 두고 이것을 넘긴다는 발상입니다. 덕분에 모델을 다 올린 뒤에 가중치를 고치는 대신, 초기화 시점에 샤딩과 양자화를 함께 적용할 수 있습니다. 거대한 모델에서는 이 차이가 결정적입니다. 한 장에 안 들어가는 모델을 일단 올린 다음 쪼갤 수는 없기 때문입니다.
이 구조는 운영에도 영향을 줍니다. --max-model-len이나 --gpu-memory-utilization 같은 값이 기동 시점에 굳어져 이후 전부에 영향을 주는 이유가 여기 있습니다. 그래서 vLLM 튜닝은 런타임 조절이 아니라 대부분 기동 인자 설계입니다.
이제 지도는 그려졌습니다. 다음 편부터는 이 경로에서 가장 많은 사람이 걸려 넘어지는 구간을 하나씩 확대합니다.
직접 해보기
- LLM GPU 메모리(VRAM) 계산기 — 지금 올리려는 모델의 가중치와 KV 캐시가 GPU에 들어가는지 먼저 계산해 보세요. 이 시리즈에서 계속 나오는 "KV 캐시가 실질 한계를 정한다"는 말이 숫자로 보입니다.
- LLM API 비용 계산기 — 직접 서빙과 API 호출 중 어느 쪽이 유리한지 워크로드를 넣어 비교해 보세요.
- 다음 편: vLLM 내부 구조 (2) — PagedAttention은 무엇을 쪼갰는가
참고 자료
- vLLM Architecture Overview (docs.vllm.ai) — 진입점,
LLM,LLMEngine,AsyncLLMEngine,Worker,ModelRunner,VllmConfig, V1 프로세스 구성의 1차 출처입니다. - vLLM V1 Guide (docs.vllm.ai) — 스케줄러·KV 캐시 매니저·워커·샘플러·API 서버가 재설계되었다는 서술의 출처입니다.
- Efficient Memory Management for Large Language Model Serving with PagedAttention, arXiv:2309.06180 — vLLM과 PagedAttention을 처음 제시한 논문입니다. 2편에서 자세히 다룹니다.
Inside vLLM (1) — The Full Path From One Request to One Token
- What This Series Covers
- The Full Path of One Request
- The API Server: Where Requests Become the Engine's Language
- Engine Core: Scheduler and KV Cache Manager
- Worker and Sampler: The Layer That Actually Touches the GPU
- Why Configuration Collapses Into One Object
- Try It Yourself
- References
What This Series Covers
Anyone who has served an LLM in production tends to hit the same walls in the same order. First you are just glad it runs at all. Then you start wondering why it is so slow. Eventually you need to know why memory suddenly blows up. The answers to all three questions live inside vLLM.
This series treats vLLM as a structure, not as the end result of a fast server. There are seven parts total, and this first one is the map. It walks the full path a single request takes from arrival to token output, start to finish, and the remaining parts zoom into each segment.
Before starting, a note on versions is necessary. vLLM moves fast and has gone through a major internal rewrite. The official V1 guide states that the scheduler, KV cache manager, worker, sampler, and API server were all redesigned. So this series describes the structure as it stands after V1. If an older post disagrees with something here, it is usually describing the V0 era.
Everything here was verified against the official documentation and source on 2026-08-12. vLLM moves fast, so re-check settings and behavior against the documentation for the version you are running.
The Full Path of One Request
Before walking through it in words, here is the picture.
Client HTTP request
│
▼
[API server process] Receive HTTP, process input, tokenize
│
▼
[Engine core process] Scheduler + KV cache manager
│ "who gets how many tokens this step"
▼
[GPU worker process] ModelRunner → model forward → logits
│
▼
Sampler Picks one next token from the logits
│
├───────────▶ Stream one piece back to the client
│
└───────────▶ Unfinished requests go back to the scheduler
The most important part of this picture is the last arrow. After a single token comes out, the request is not done. It goes back to the scheduler. This is not a structure that finishes one request before looking at the next. Every live request advances a little on every step. The story of vLLM performance ultimately comes down to how tightly this loop gets packed.
The API Server: Where Requests Become the Engine's Language
vLLM has two entry points. According to the official architecture documentation, offline inference is handled by the LLM class, and online serving is handled by the vllm serve command or the OpenAI-compatible API server. That OpenAI-compatible server uses AsyncLLMEngine. Because the async wrapper runs a background loop, multiple concurrent HTTP requests do not have to wait on each other.
What happens here is not glamorous. It applies the chat template, turns strings into token sequences, and packages the sampling settings into a request object the engine understands. But performance already starts to diverge at this stage. How the prompt is assembled determines whether it hits the prefix cache discussed in part 5, and the input length runs straight into the length limits covered in part 6.
Engine Core: Scheduler and KV Cache Manager
The official documentation lays out the V1 process layout like this: an API server process that receives HTTP, one engine core process per data-parallel rank that runs the scheduler and manages the KV cache, and one worker process per GPU. In the example the documentation gives, a deployment with 4 GPUs comes out to 1 API server + 1 engine core + 4 GPU workers, 6 processes total.
The reason for splitting processes is simple. HTTP parsing and tokenization are Python work that uses the CPU, and model execution uses the GPU. If both live in the same process, CPU work blocks the GPU loop. Look inside a deployment that is not hitting expected throughput and it is common to find low GPU utilization alongside one CPU core pegged at 100 percent.
Inside the engine core, the scheduler and the KV cache manager move as one unit. Before the scheduler can decide that a request goes into this step, it has to actually be able to secure that many KV cache blocks. When it cannot, the scheduler will even push back a request that is already running. That preemption behavior is the topic of part 4.
Worker and Sampler: The Layer That Actually Touches the GPU
The architecture documentation's definitions are terse. Worker is the process that runs model inference, ModelRunner is responsible for loading and running the model, and the Model inside it is the actual torch module instance.
One forward pass from the worker produces next-token logits for every request in the batch. The sampler then applies settings like temperature, top_p, and top_k to pick a single token. What matters is that requests with different sampling settings can still sit in the same batch. The batch only shares the model computation; what gets picked out is decided separately per request.
Why Configuration Collapses Into One Object
One design the documentation emphasizes is VllmConfig. The idea is to build a single config object that holds everything needed and pass that around. This makes it possible to apply sharding and quantization together at initialization time, instead of loading the model and then patching its weights afterward. For huge models this difference is decisive, because you cannot load a model that does not fit on one GPU and then split it up after the fact.
This structure also affects operations. It is why values like --max-model-len and --gpu-memory-utilization freeze in place at startup and then affect everything downstream. That is why tuning vLLM is, for the most part, a matter of designing the startup arguments rather than adjusting things at runtime.
The map is drawn now. Starting with the next part, we zoom into the segments of this path where the most people trip.
Try It Yourself
- LLM GPU Memory (VRAM) Calculator — Check whether the weights and KV cache of the model you are about to deploy actually fit on the GPU. This turns the "KV cache sets the real limit" idea that keeps coming up in this series into an actual number.
- LLM API Cost Calculator — Plug in your workload to compare whether self-hosting or calling an API comes out ahead.
- Next: Inside vLLM (2) — Why PagedAttention Splits the KV Cache Into Pages
References
- vLLM Architecture Overview (docs.vllm.ai) — The primary source for entry points,
LLM,LLMEngine,AsyncLLMEngine,Worker,ModelRunner,VllmConfig, and the V1 process layout. - vLLM V1 Guide (docs.vllm.ai) — The source for the statement that the scheduler, KV cache manager, worker, sampler, and API server were redesigned.
- Efficient Memory Management for Large Language Model Serving with PagedAttention, arXiv:2309.06180 — The paper that first introduced vLLM and PagedAttention. Covered in detail in part 2.