[vLLM] RunPod L40S에서 콜드 스타트 최적화해보기
RunPod L40S에서 Qwen3-8B의 vLLM 캐시 최적화를 적용해보고 새 컨테이너 기동 시간을 166초에서 120초로 줄인 결과를 정리했습니다.
vLLM으로 모델을 서빙해보면 서버가 준비되기까지 생각보다 오래 걸립니다. 온프레미스 기반으로 플랫폼 과정을 확인하면서 L40S 환경으로 몇 가지 vLLM 설정을 적용해봤습니다.
Qwen3-8B를 배포한 뒤 같은 Pod에서 vLLM을 다시 실행하자 기동 시간이 크게 줄었습니다. 캐시를 명시적으로 저장하면 새 컨테이너에서도 효과가 있는지 이어서 살펴봤습니다.
테스트 환경
- GPU: NVIDIA L40S 48GB × 1
- 모델:
Qwen/Qwen3-8B - vLLM:
0.27.1 - Torch:
2.13.0+cu129 - dtype: BF16
- 최대 컨텍스트: 8,192 토큰
- 최대 동시 시퀀스: 16
- 영구 볼륨: 100GB,
/workspace
기동 시간은 vllm serve 실행부터 /health 요청이 성공할 때까지 측정했습니다.
모델은 미리 /workspace에 받아둔 상태로 비교했습니다.
적용한 설정
RunPod의 vLLM 콜드 스타트 최적화 글을 참고해 네 가지 설정을 골라 적용했습니다.
- vLLM 컴파일 캐시:
/workspace에 저장 - Safetensors 가중치: prefetch 방식으로 로드
- CUDA 그래프 캡처 크기: 동시 요청 수 16 이하
- Hugging Face: 이미 저장된 모델만 쓰도록 오프라인 모드 적용
export PATH=/workspace/.venvs/vllm/bin:$PATH
export HF_HOME=/workspace/.cache/huggingface
export HF_HUB_OFFLINE=1
export VLLM_CACHE_ROOT=/workspace/vllm-opt/vllm-cache
vllm serve 'Qwen/Qwen3-8B' \
--dtype bfloat16 \
--max-model-len 8192 \
--max-num-seqs 16 \
--safetensors-load-strategy prefetch \
--compilation-config '{"cudagraph_capture_sizes":[1,2,4,8,16]}' \
--port 8000
측정 결과
| 조건 | 기동 시간 |
|---|---|
| 새 컨테이너, 기존 설정 | 166초 |
| 실행 중인 Pod에서 vLLM 프로세스만 재기동 | 32초 |
| Pod 중단 후 새 컨테이너에서 최적화 기동 | 120초 |
새 컨테이너 기준으로 166초에서 120초로 약 28% 줄었습니다. 실행 중인 Pod에서 vLLM 프로세스만 다시 띄웠을 때는 모델과 로컬 캐시를 모두 재사용해 32초가 걸렸습니다.
32초는 Pod를 중단했다가 다시 시작한 결과가 아닙니다. Pod를 중단하면 /workspace의 모델과 vLLM 컴파일 캐시는 유지되지만, 컨테이너 로컬의 GPU 커널 준비 상태는 사라졌습니다.
컴파일 캐시 효과
적용한 설정 가운데 vLLM 컴파일 캐시의 효과가 가장 컸습니다.
torch.compile: 34.77초 → 0.46초- 모델 적재: 6.41초 → 6.17초
- CUDA 그래프 캡처: 두 경우 모두 약 1초
- Chat Completions API: HTTP 200
Safetensors prefetch와 CUDA 그래프 범위 축소는 이번 환경에서 큰 차이를 만들지 못했습니다. Hugging Face 오프라인 모드도 정상 적용됐지만 절감 시간을 따로 구분하기는 어려웠습니다.
Pod를 다시 시작했을 때 남은 시간의 대부분은 GPU 커널 JIT 준비에 사용됐습니다. vLLM 컴파일 캐시는 0.46초 만에 불러왔지만, 이후 커널 준비에 약 79초가 걸렸습니다.
다음 실험
다음에는 새 컨테이너마다 다시 발생하는 GPU 커널 JIT 준비 시간을 줄일 수 있는지 측정해보고자 합니다.
동시에 왜 최적화되는지 원리 자체를 정리하여 추가 포스팅 예정입니다.