32GB VRAM을 가진 RTX 5090 한 장에서
Qwen3.8-27B를 서빙하려면 일반 가중치 대신 5090용 NVFP4 체크포인트를 쓰는 편이 현실적입니다. 여기서는 Runpod Secure Cloud에 Pod를 만들고, vLLM으로 gittensor-model-hub/Qwen3.8-27B-NVFP4-RTX5090을 띄운 다음 OpenAI 호환 API로 호출하는 과정을 정리합니다.모델은 미리
hf download로 받지 않습니다. vllm serve에 Hugging Face 저장소 ID를 넘기면 첫 실행 때 자동으로 다운로드합니다.공유 템플릿 배포
Runpod 계정이 없다면 여기에서 가입하면 됩니다. 이미 계정이 있다면 Qwen3.8 RTX 5090 배포 템플릿을 열어 바로 시작할 수 있습니다.
템플릿에는 이미지, 80GB Volume Disk, API와 SSH에 필요한 포트를 넣어 두었습니다. 배포 전에 Container Disk를 기본 20GB에서 50GB로 늘립니다. 그다음
Secure Cloud, RTX 5090 × 1, CUDA 13.0을 지원하는 호스트가 선택됐는지 확인하고 Pod를 시작하세요. GPU 재고와 시간당 비용은 배포 시점의 화면에서 확인하면 됩니다.이 테스트에서 사용한 구성은 다음과 같습니다.
템플릿 이미지는
HF_HOME과 UV_CACHE_DIR을 /workspace/.cache 아래로 지정합니다. 모델과 다운로드 캐시는 Volume Disk에 남고, /opt/vllm-env의 Python 환경은 Container Disk에 설치됩니다.8000/http는 vLLM API를 Runpod HTTP Proxy로 열기 위한 포트입니다. 서버도 반드시 0.0.0.0:8000에서 대기해야 외부에서 들어올 수 있습니다.SSH 접속과 GPU 확인
Pod의
Connect 패널에서 SSH 명령을 복사해 접속합니다. 환경에 따라 Direct TCP 주소나 ssh.runpod.io를 쓰는 SSH Proxy 명령이 나올 수 있으므로, 직접 주소를 조합하지 말고 패널의 명령을 그대로 쓰세요. 외부 포트는 Pod를 재시작할 때 바뀌 수 있습니다.ssh root@<SSH_HOST> -p <SSH_PORT> -i ~/.ssh/id_ed25519접속했으면 GPU와 PyTorch가 정상적으로 연결됐는지, 두 디스크에 여유가 있는지를 먼저 봅니다.
nvidia-smi
python3 - <<'PY'
import torch
print("PyTorch:", torch.__version__)
print("CUDA build:", torch.version.cuda)
print("CUDA available:", torch.cuda.is_available())
print("GPU:", torch.cuda.get_device_name(0))
print("Compute capability:", torch.cuda.get_device_capability(0))
PY
df -h / /workspace이 Pod에서는 RTX 5090의 compute capability가
12.0, 전체 VRAM이 32,607 MiB로 확인됐습니다.vLLM 설치
글과 함께 두는
bootstrap.sh는 /opt/vllm-env에 독립 venv를 만들고, 이 테스트에서 사용한 vLLM과 Blackwell NVFP4 의존성을 설치합니다.Pod에 스크립트 세 개를 올린 뒤 다음처럼 실행합니다.
cd /workspace/qwen38-vllm
chmod +x bootstrap.sh serve.sh benchmark.sh
./bootstrap.sh저장소로 옮기기 전에는 아래 명령만 복사해 실행해도 됩니다.
python3 -m venv /opt/vllm-env
UV_CACHE_DIR=/workspace/.cache/uv uv pip install \
--python /opt/vllm-env/bin/python \
'vllm==0.27.1' \
'flashinfer-python>=0.6.13' \
'nvidia-cutlass-dsl>=4.5.2'uv 다운로드 캐시도 Volume Disk에 두어 Pod를 다시 시작했을 때 재사용합니다. 검증 시
/opt/vllm-env는 약 7.6GB를 사용했습니다.Qwen3.8 27B 서버 실행
별도의 모델 다운로드 명령은 필요 없습니다. 아래와 같이 저장소 ID를 주면 vLLM이
HF_HOME 캐시에서 모델을 찾고, 없으면 Hugging Face에서 받습니다.export PATH=/opt/vllm-env/bin:$PATH
export HF_HOME=/workspace/.cache/huggingface
export MAX_JOBS=2
vllm serve gittensor-model-hub/Qwen3.8-27B-NVFP4-RTX5090 \
--quantization modelopt \
--kv-cache-dtype fp8 \
--trust-remote-code \
--max-model-len 262144 \
--max-num-seqs 16 \
--gpu-memory-utilization 0.97 \
--reasoning-parser qwen3 \
--enable-auto-tool-choice \
--tool-call-parser qwen3_xml \
--host 0.0.0.0 \
--port 8000같은 명령은
serve.sh에 들어 있습니다. MAX_JOBS=2는 RTX 5090용 FlashInfer 커널을 컴파일할 때 CPU 메모리 사용량이 한꺼번에 커지는 것을 피하려고 둔 값입니다.첫 기동에서는 모델 다운로드 뒤에도 SM120용 FlashInfer JIT, 프로파일링, CUDA graph 생성이 이어집니다. 검증 Pod는
serve.sh 실행 후 약 24분에 API가 준비됐습니다. 시간을 정해 놓고 기다리지 말고, 다른 SSH 터미널에서 readiness를 확인하세요.until curl -fsS http://127.0.0.1:8000/v1/models >/dev/null; do
echo 'vLLM is still starting...'
sleep 10
done
echo 'vLLM is ready'서버 로그에서 확인한 주요 값은 다음과 같습니다.
--max-model-len 262144는 서버에 설정한 최대 길이입니다. 재현 검증에서 로그로 확인한 GPU KV cache는 271,342 tokens, 262,144-token 요청 기준 예상 동시성은 1.04x였습니다.로컬 API 확인
다른 SSH 터미널을 하나 열고 모델 목록을 조회합니다.
curl -sS http://127.0.0.1:8000/v1/models | jq그다음 실제 채팅 요청을 보냅니다.
curl -sS http://127.0.0.1:8000/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "gittensor-model-hub/Qwen3.8-27B-NVFP4-RTX5090",
"messages": [
{"role": "user", "content": "17 + 25는 몇이야? 숫자로만 답해줘."}
],
"temperature": 0,
"max_tokens": 16,
"chat_template_kwargs": {"enable_thinking": false}
}' | jq실제 응답에서는 다음 내용을 확인했습니다.
{
"model": "gittensor-model-hub/Qwen3.8-27B-NVFP4-RTX5090",
"choices": [
{
"message": {
"role": "assistant",
"content": "42"
},
"finish_reason": "stop"
}
]
}Qwen3.8은 기본으로 thinking을 사용합니다. 짧은 답을 확인하는 이 요청에서 thinking을 끄지 않으면 reasoning이 16토큰을 모두 쓰고
content: null로 끝납니다. enable_thinking: false를 넣은 요청은 독립 재현 테스트에서도 content: "42"를 반환했습니다.Runpod HTTP Proxy 외부 호출
로컬 PC에서 Pod ID만 바꿔 같은 API를 호출할 수 있습니다.
export POD_ID='<YOUR_POD_ID>'
export BASE_URL="https://${POD_ID}-8000.proxy.runpod.net"
curl -sS "$BASE_URL/v1/models" | jq
curl -sS "$BASE_URL/v1/chat/completions" \
-H 'Content-Type: application/json' \
-d '{
"model": "gittensor-model-hub/Qwen3.8-27B-NVFP4-RTX5090",
"messages": [
{"role": "user", "content": "17 + 25는 몇이야? 숫자로만 답해줘."}
],
"temperature": 0,
"max_tokens": 16,
"chat_template_kwargs": {"enable_thinking": false}
}' | jq -r '.choices[0].message.content'외부 호출에서도 모델 목록과 chat completion을 확인했습니다. 인증 없이
8000/http를 열면 엔드포인트도 공개됩니다. 잠깐 쓰고 버리는 테스트가 아니라면 vLLM 앞에 인증 리버스 프록시를 두는 편이 좋습니다. Runpod HTTP Proxy는 최대 연결 시간이 100초이므로 긴 응답은 스트리밍으로 받거나 TCP 접속을 검토하세요.vLLM 벤치마크
vLLM에는 실행 중인 OpenAI 호환 서버를 측정하는
vllm bench serve가 들어 있습니다. benchmark.sh를 실행하면 일반 요청, 동시성 4, 62K 긴 입력을 순서대로 측정하고 결과 원본을 /workspace/qwen38-vllm/results에 남깁니다../benchmark.sh아래는 같은 Pod와 서버 설정에서 직접 측정한 값입니다.
동시성을 1에서 4로 늘리면 총 출력 처리량은
71.86 tok/s에서 238.67 tok/s로 늘었고, 한 요청이 첫 토큰을 받는 평균 시간은 161.06ms에서 302.04ms로 늘었습니다. 여러 요청을 한꺼번에 처리하면 전체 처리량은 좋아지지만 개별 요청의 대기 시간은 늘어납니다.62K 입력은 첫 토큰까지
8.69초가 걸렸습니다. 262K context를 설정했더라도 입력이 길어질수록 prefill 시간과 메모리 부담이 커집니다. 실제 서비스에서 사용할 입력 길이로 다시 측정해야 합니다.모델 캐시 재사용
공유 템플릿은 이미 다음 경로를 사용합니다. 같은 Pod를 Stop했다가 다시 시작하면
/workspace의 모델 캐시를 재사용합니다.export HF_HOME=/workspace/.cache/huggingface
export UV_CACHE_DIR=/workspace/.cache/uv모델은 약 20GB이지만 uv 캐시와 임시 파일도 함께 쌓입니다. 템플릿의 80GB Volume Disk면 이 모델 하나를 다운로드하고 벤치마크 결과를 남기기에 여유가 있습니다.
Pod 종료
서버 터미널에서
Ctrl+C로 vLLM을 종료한 뒤 Runpod 콘솔에서 Pod를 Stop합니다. 상태가 EXITED로 바뀐 것까지 확인하세요. GPU 사용료는 멈추지만 남겨 둔 Volume Disk에는 스토리지 비용이 계속 붙을 수 있습니다.본문의 Runpod 링크 일부에는 추천인 코드가 포함돼 있습니다.


