목표와 현재 상태
동일한 single-node k3s와 RTX 4090에서 vLLM의 cache와 memory 설정을 한 번에 하나씩 변경해 안정적인 운영 기준을 찾는다.
| 구분 | 상태 | 확인 내용 |
|---|---|---|
| 최적화 engine | 완료 | vLLM과 Google Gemma 4 12B W4A16 checkpoint 선택 |
| baseline 저장 | 완료 | image, args, env, resource request·limit 기록 |
| cold cache | 완료 | 빈 PVC에서 Pod Ready까지 270초 |
| warm cache | 완료 | 같은 PVC 재사용 시 Pod Ready까지 171초 |
| GPU memory budget | 측정 진행 중 | 0.70, 0.75, 0.80, 0.85 API 5회 성공, metric 비교 대기 |
0.85 후보 | 완료 | 실제 patch 값 0.85, warm 평균 1.642079초 |
| context length | 완료 | 4096, 8192, 16384 비교 후 8192로 복구 |
| scheduler 기준선 | 완료 | max-num-seqs 1, 2, 4에서 순차 API 각 5회 성공 |
| concurrency latency | 완료 | max-num-seqs 1, 2, 4에서 concurrency 1, 2, 4, 8 모두 HTTP 200 |
| scheduler metric | 완료 | 세 후보의 지속 부하에서 running·waiting·KV cache·preemption·GPU metric 기록 |
| 최종 후보 API | 완료 | prompt 398, 1550, 3086 token에서 총 15/15 HTTP 200 |
| baseline 원복 | 완료 | context 8192, max-num-seqs 4, GPU memory utilization 0.80, 원래 PVC로 복구 |
1. 변경 전 baseline 저장
최적화 이전 설정을 YAML로 보존하고 실제 container 설정을 조회했다.
sudo k3s kubectl -n gemmaforge get deployment gemma4-12b \
-o yaml \
> ~/gemma4-12b-before-step07.yaml
sudo k3s kubectl -n gemmaforge get deployment gemma4-12b \
-o json \
| jq '.spec.template.spec.containers[]
| select(.name == "vllm")
| {image, args, env, resources}'vLLM 변경 전 container 설정
{ "image": "vllm/vllm-openai:v0.24.0", "args": [ "google/gemma-4-12B-it-qat-w4a16-ct", "--served-model-name", "gemma-4-12b-q4", "--dtype", "auto", "--max-model-len", "8192", "--max-num-seqs", "4", "--gpu-memory-utilization", "0.75", "--limit-mm-per-prompt", "{\"image\": 0, \"audio\": 0}" ], "env": [ { "name": "HF_HOME", "value": "/models/huggingface" } ], "resources": { "limits": { "cpu": "8", "memory": "24Gi", "nvidia.com/gpu": "1" }, "requests": { "cpu": "2", "memory": "8Gi" } } }
| 항목 | baseline 값 | 의미 |
|---|---|---|
| image | vllm/vllm-openai:v0.24.0 | 최적화 비교에 사용할 engine version |
| checkpoint | google/gemma-4-12B-it-qat-w4a16-ct | W4A16 QAT model artifact |
| context | 8192 | prompt와 output을 합친 최대 길이 |
| max sequences | 4 | iteration당 최대 sequence 수 |
| GPU memory utilization | 0.75 | 변경 전 실제 baseline 값 |
| Hugging Face cache | /models/huggingface | PVC mount와 일치해야 하는 cache 경로 |
| GPU limit | 1 | RTX 4090 한 장을 Pod에 할당 |
현재 설치된 vLLM이 실험 대상 인자를 지원하는지 container 내부 help로 확인했다.
sudo k3s kubectl -n gemmaforge exec \
deployment/gemma4-12b \
-- vllm serve --help=gpu-memory-utilization
sudo k3s kubectl -n gemmaforge exec \
deployment/gemma4-12b \
-- vllm serve --help=max-model-len
sudo k3s kubectl -n gemmaforge exec \
deployment/gemma4-12b \
-- vllm serve --help=max-num-seqs
sudo k3s kubectl -n gemmaforge exec \
deployment/gemma4-12b \
-- vllm serve --help=max-num-batched-tokensvLLM 최적화 인자 help 조회 결과
Arguments matching 'gpu-memory-utilization': --gpu-memory-utilization GPU_MEMORY_UTILIZATION The fraction of GPU memory to be used for the model executor, which can range from 0 to 1. For example, a value of 0.5 would imply 50% GPU memory utilization. If unspecified, will use the default value of 0.92. This is a per-instance limit, and only applies to the current vLLM instance. It does not matter if you have another vLLM instance running on the same GPU. For example, if you have two vLLM instances running on the same GPU, you can set the GPU memory utilization to 0.5 for each instance. (default: 0.92) When passing JSON CLI arguments, the following sets of arguments are equivalent: --json-arg '{"key1": "value1", "key2": {"key3": "value2"}}' --json-arg.key1 value1 --json-arg.key2.key3 value2 Additionally, list elements can be passed individually using +: --json-arg '{"key4": ["value3", "value4", "value5"]}' --json-arg.key4+ value3 --json-arg.key4+='value4,value5' Arguments matching 'max-model-len': --max-model-len MAX_MODEL_LEN Model context length (prompt and output). If unspecified, will be automatically derived from the model config. When passing via `--max-model-len`, supports k/m/g/K/M/G in human-readable format. Examples: - 1k -> 1000 - 1K -> 1024 - 25.6k -> 25,600 - -1 or 'auto' -> Automatically choose the maximum model length that fits in GPU memory. This will use the model's maximum context length if it fits, otherwise it will find the largest length that can be accommodated. Parse human-readable integers like '1k', '2M', etc. Including decimal values with decimal multipliers. Also accepts -1 or 'auto' as a special value for auto- detection. Examples: - '1k' -> 1,000 - '1K' -> 1,024 - '25.6k' -> 25,600 - '-1' or 'auto' -> -1 (special value for auto- detection) (default: None) When passing JSON CLI arguments, the following sets of arguments are equivalent: --json-arg '{"key1": "value1", "key2": {"key3": "value2"}}' --json-arg.key1 value1 --json-arg.key2.key3 value2 Additionally, list elements can be passed individually using +: --json-arg '{"key4": ["value3", "value4", "value5"]}' --json-arg.key4+ value3 --json-arg.key4+='value4,value5' Arguments matching 'max-num-seqs': --max-num-seqs MAX_NUM_SEQS Maximum number of sequences to be processed in a single iteration. The default value here is mainly for convenience when testing. In real usage, this should be set in `EngineArgs.create_engine_config`. (default: None) When passing JSON CLI arguments, the following sets of arguments are equivalent: --json-arg '{"key1": "value1", "key2": {"key3": "value2"}}' --json-arg.key1 value1 --json-arg.key2.key3 value2 Additionally, list elements can be passed individually using +: --json-arg '{"key4": ["value3", "value4", "value5"]}' --json-arg.key4+ value3 --json-arg.key4+='value4,value5' Arguments matching 'max-num-batched-tokens': --max-num-batched-tokens MAX_NUM_BATCHED_TOKENS Maximum number of tokens that can be processed in a single iteration. The default value here is mainly for convenience when testing. In real usage, this should be set in `EngineArgs.create_engine_config`. Parse human-readable integers like '1k', '2M', etc. Including decimal values with decimal multipliers. Examples: - '1k' -> 1,000 - '1K' -> 1,024 - '25.6k' -> 25,600 (default: None) When passing JSON CLI arguments, the following sets of arguments are equivalent: --json-arg '{"key1": "value1", "key2": {"key3": "value2"}}' --json-arg.key1 value1 --json-arg.key2.key3 value2 Additionally, list elements can be passed individually using +: --json-arg '{"key4": ["value3", "value4", "value5"]}' --json-arg.key4+ value3 --json-arg.key4+='value4,value5'
2. cold cache와 warm cache 비교
model을 PVC에 저장했을 때 최초 준비와 재기동 시간이 얼마나 달라지는지 비교했다. 기존 정상 PVC는 보존하고 별도의 빈 40Gi PVC를 만들었다.
# cache-cold-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: gemma4-model-cache-cold
namespace: gemmaforge
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 40Gik3s kubectl apply -f cache-cold-pvc.yaml빈 PVC를 vLLM의 model cache volume으로 연결했다.
SECONDS=0
sudo k3s kubectl -n gemmaforge patch \
deployment/gemma4-12b \
--type=strategic \
-p '{
"spec": {
"template": {
"spec": {
"volumes": [
{
"name": "model-cache",
"persistentVolumeClaim": {
"claimName": "gemma4-model-cache-cold"
}
}
]
}
}
}
}'cold cache PVC 연결 결과
deployment.apps/gemma4-12b patched
빈 PVC에서 model을 준비하고 Pod가 Ready가 될 때까지 측정했다.
sudo k3s kubectl -n gemmaforge rollout status \
deployment/gemma4-12b \
--timeout=60m
printf 'vLLM cold PVC patch 시작부터 Pod Ready까지 %s초\n' "$SECONDS"vLLM cold cache Pod Ready 시간
Waiting for deployment "gemma4-12b" rollout to finish: 0 of 1 updated replicas are available... deployment "gemma4-12b" successfully rolled out vLLM cold PVC patch 시작부터 Pod Ready까지 270초
Pod 안에서 실제 PVC mount 경로를 확인했다.
k3s kubectl -n gemmaforge get deployment gemma4-12b \
-o jsonpath='{range .spec.template.spec.containers[?(@.name=="vllm")].volumeMounts[*]}{.name}{"\t"}{.mountPath}{"\n"}{end}'vLLM volume mount 경로
model-cache /models/huggingface dshm /dev/shm
k3s kubectl -n gemmaforge exec \
deployment/gemma4-12b \
-- du -sh /models/huggingfacecold cache PVC 사용량
9.6G /models/huggingface
k3s kubectl -n gemmaforge exec \
deployment/gemma4-12b \
-- find /models/huggingface \
-maxdepth 4 \
-type dcold cache PVC의 Hugging Face model 구조
/models/huggingface /models/huggingface/xet /models/huggingface/xet/logs /models/huggingface/xet/https___cas_serv-tGqkUaZf_CBPHQ6h /models/huggingface/xet/https___cas_serv-tGqkUaZf_CBPHQ6h/staging /models/huggingface/hub /models/huggingface/hub/models--google--gemma-4-12B-it-qat-w4a16-ct /models/huggingface/hub/models--google--gemma-4-12B-it-qat-w4a16-ct/blobs /models/huggingface/hub/models--google--gemma-4-12B-it-qat-w4a16-ct/refs /models/huggingface/hub/models--google--gemma-4-12B-it-qat-w4a16-ct/snapshots /models/huggingface/hub/models--google--gemma-4-12B-it-qat-w4a16-ct/snapshots/1d2c2d7f2466070e69d6fb3fd5ce9a7d75f2f6ee /models/huggingface/hub/models--google--gemma-4-12B-it-qat-w4a16-ct/.no_exist /models/huggingface/hub/models--google--gemma-4-12B-it-qat-w4a16-ct/.no_exist/1d2c2d7f2466070e69d6fb3fd5ce9a7d75f2f6ee /models/huggingface/hub/.locks /models/huggingface/hub/.locks/models--google--gemma-4-12B-it-qat-w4a16-ct
같은 PVC를 유지한 채 rollout restart하여 저장된 model cache를 재사용했다.
SECONDS=0
sudo k3s kubectl -n gemmaforge rollout restart deployment/gemma4-12b
sudo k3s kubectl -n gemmaforge rollout status \
deployment/gemma4-12b \
--timeout=40m
printf 'vLLM warm cache Pod Ready까지 %s초\n' "$SECONDS"vLLM warm cache Pod Ready 시간
deployment.apps/gemma4-12b restarted Waiting for deployment "gemma4-12b" rollout to finish: 0 out of 1 new replicas have been updated... Waiting for deployment "gemma4-12b" rollout to finish: 0 out of 1 new replicas have been updated... Waiting for deployment "gemma4-12b" rollout to finish: 0 out of 1 new replicas have been updated... Waiting for deployment "gemma4-12b" rollout to finish: 0 of 1 updated replicas are available... deployment "gemma4-12b" successfully rolled out vLLM warm cache Pod Ready까지 171초
| 구분 | Pod Ready | 차이 | 해석 |
|---|---|---|---|
| cold cache | 270초 | 기준 | 빈 PVC에 model을 내려받고 cache를 생성하는 시간이 포함됨 |
| warm cache | 171초 | 99초 단축 | 같은 PVC의 9.6GiB cache를 재사용 |
| 개선율 | 약 36.7% | cold 대비 | PVC cache가 재기동 준비 시간을 유의미하게 줄임 |
cold PVC에는 checkpoint revision 1d2c2d7f2466070e69d6fb3fd5ce9a7d75f2f6ee의 blobs, refs, snapshots가 생성됐다. 따라서 warm 재기동의 단축은 실제 Hugging Face cache 재사용 결과로 판단한다. 기존 gemma4-model-cache PVC 복구 결과는 아직 전달되지 않았으므로 완료로 기록하지 않는다.
3. GPU memory budget
현재 Deployment 인자 위치와 baseline 값을 확인했다.
sudo k3s kubectl -n gemmaforge get deployment gemma4-12b \
-o jsonpath='{.spec.template.spec.containers[0].args}{"\n"}'vLLM baseline args
["google/gemma-4-12B-it-qat-w4a16-ct","--served-model-name","gemma-4-12b-q4","--dtype","auto","--max-model-len","8192","--max-num-seqs","4","--gpu-memory-utilization","0.75","--limit-mm-per-prompt","{\"image\": 0, \"audio\": 0}"]
이 단계는 quantization 비교가 아니라 --gpu-memory-utilization 후보 비교다. model artifact와 나머지 args를 유지하고 memory budget 값만 변경했다. vLLM에서 사용할 동일 조건의 공식 Gemma 4 12B Q8 artifact는 확인되지 않아 quantization 비교는 실행하지 않았다.
0.70
sudo k3s kubectl -n gemmaforge patch \
deployment/gemma4-12b \
--type=json \
-p='[
{
"op": "test",
"path": "/spec/template/spec/containers/0/name",
"value": "vllm"
},
{
"op": "test",
"path": "/spec/template/spec/containers/0/args/9",
"value": "--gpu-memory-utilization"
},
{
"op": "replace",
"path": "/spec/template/spec/containers/0/args/10",
"value": "0.70"
}
]'for i in $(seq 1 5); do
curl -sS \
-o "/tmp/gemmaforge-07-vllm-memory-${i}.json" \
-w "run=${i} http=%{http_code} total=%{time_total}s\n" \
http://127.0.0.1:8100/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "gemma-4-12b-q4",
"messages": [
{
"role": "user",
"content": "Kubernetes의 장점을 세 가지로 요약해줘."
}
],
"max_tokens": 128,
"temperature": 0
}'
doneGPU memory utilization 0.70 API 5회
run=1 http=200 total=2.919088s run=2 http=200 total=1.654523s run=3 http=200 total=1.643075s run=4 http=200 total=1.663435s run=5 http=200 total=1.747124s
0.75
sudo k3s kubectl -n gemmaforge patch \
deployment/gemma4-12b \
--type=json \
-p='[
{
"op": "test",
"path": "/spec/template/spec/containers/0/name",
"value": "vllm"
},
{
"op": "test",
"path": "/spec/template/spec/containers/0/args/9",
"value": "--gpu-memory-utilization"
},
{
"op": "replace",
"path": "/spec/template/spec/containers/0/args/10",
"value": "0.75"
}
]'for i in $(seq 1 5); do
curl -sS \
-o "/tmp/gemmaforge-07-vllm-memory-${i}.json" \
-w "run=${i} http=%{http_code} total=%{time_total}s\n" \
http://127.0.0.1:8100/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "gemma-4-12b-q4",
"messages": [
{
"role": "user",
"content": "Kubernetes의 장점을 세 가지로 요약해줘."
}
],
"max_tokens": 128,
"temperature": 0
}'
doneGPU memory utilization 0.75 API 5회
run=1 http=200 total=2.956636s run=2 http=200 total=1.696076s run=3 http=200 total=1.794394s run=4 http=200 total=1.661063s run=5 http=200 total=1.656697s
0.80
sudo k3s kubectl -n gemmaforge patch \
deployment/gemma4-12b \
--type=json \
-p='[
{
"op": "test",
"path": "/spec/template/spec/containers/0/name",
"value": "vllm"
},
{
"op": "test",
"path": "/spec/template/spec/containers/0/args/9",
"value": "--gpu-memory-utilization"
},
{
"op": "replace",
"path": "/spec/template/spec/containers/0/args/10",
"value": "0.80"
}
]'for i in $(seq 1 5); do
curl -sS \
-o "/tmp/gemmaforge-07-vllm-memory-${i}.json" \
-w "run=${i} http=%{http_code} total=%{time_total}s\n" \
http://127.0.0.1:8100/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "gemma-4-12b-q4",
"messages": [
{
"role": "user",
"content": "Kubernetes의 장점을 세 가지로 요약해줘."
}
],
"max_tokens": 128,
"temperature": 0
}'
doneGPU memory utilization 0.80 API 5회
run=1 http=200 total=2.768130s run=2 http=200 total=1.602542s run=3 http=200 total=1.613182s run=4 http=200 total=1.688277s run=5 http=200 total=1.709210s
0.85
sudo k3s kubectl -n gemmaforge patch \
deployment/gemma4-12b \
--type=json \
-p='[
{
"op": "test",
"path": "/spec/template/spec/containers/0/name",
"value": "vllm"
},
{
"op": "test",
"path": "/spec/template/spec/containers/0/args/9",
"value": "--gpu-memory-utilization"
},
{
"op": "replace",
"path": "/spec/template/spec/containers/0/args/10",
"value": "0.85"
}
]'for i in $(seq 1 5); do
curl -sS \
-o "/tmp/gemmaforge-07-vllm-memory-${i}.json" \
-w "run=${i} http=%{http_code} total=%{time_total}s\n" \
http://127.0.0.1:8100/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "gemma-4-12b-q4",
"messages": [
{
"role": "user",
"content": "Kubernetes의 장점을 세 가지로 요약해줘."
}
],
"max_tokens": 128,
"temperature": 0
}'
doneGPU memory utilization 0.85 API 5회
run=1 http=200 total=2.818173s run=2 http=200 total=1.630354s run=3 http=200 total=1.651002s run=4 http=200 total=1.619995s run=5 http=200 total=1.666963s
| 실제 적용값 | HTTP 성공 | run 1 | run 2~5 평균 | 판단 |
|---|---|---|---|---|
0.70 | 5/5 | 2.919088초 | 1.677039초 | 단일 요청 기능 검증 통과 |
0.75 | 5/5 | 2.956636초 | 1.702058초 | 변경 전 baseline, 기능 검증 통과 |
0.80 | 5/5 | 2.768130초 | 1.653303초 | 0.70, 0.75보다 warm 평균이 짧음 |
0.85 | 5/5 | 2.818173초 | 1.642079초 | 네 후보 중 warm 평균이 가장 짧음 |
네 번의 요청 세트는 모두 HTTP 200이었고 실제로 적용된 0.70~0.85 범위에서는 단일 요청 API가 정상 동작했다. 0.85의 warm 평균이 1.642079초로 가장 짧았지만 후보 간 차이가 작고 VRAM 최고값, KV cache 최고값, queue, restart·OOM 및 rollout 결과가 함께 기록되지 않았으므로 아직 최종 최적값으로 확정하지 않는다.
4. context length와 scheduler
같은 model과 GPU memory budget을 유지한 상태에서 max-model-len을 변경해 실제 API context 경계를 확인했다. 이후 8192로 복구하고 max-num-seqs만 변경해 순차 단일 요청 기준선을 측정했다.
공통 context payload 준비
아래 payload를 한 번 생성하고 모든 context에서 동일하게 사용했다.
for repeat in 64 256 512 768 1024; do
jq -n \
--arg content "$(for i in $(seq 1 "$repeat"); do
printf 'Kubernetes GPU serving reliability test. '
done)" \
'{
"model": "gemma-4-12b-q4",
"messages": [
{
"role": "user",
"content": $content
}
],
"max_tokens": 128,
"temperature": 0
}' > "/tmp/gemmaforge-vllm-context-${repeat}.json"
donecontext 4096
sudo k3s kubectl -n gemmaforge patch \
deployment/gemma4-12b \
--type=json \
-p='[
{
"op": "test",
"path": "/spec/template/spec/containers/0/args/5",
"value": "--max-model-len"
},
{
"op": "replace",
"path": "/spec/template/spec/containers/0/args/6",
"value": "4096"
}
]'
sudo k3s kubectl -n gemmaforge rollout status \
deployment/gemma4-12b \
--timeout=40mfor repeat in 64 256 512 768 1024; do
jq '{model, messages}' \
"/tmp/gemmaforge-vllm-context-${repeat}.json" \
| curl -fsS http://127.0.0.1:8100/tokenize \
-H 'Content-Type: application/json' \
-d @- \
| jq --arg repeat "$repeat" \
'{
repeat: $repeat,
prompt_tokens: .count,
max_model_len
}'
donecontext 4096 payload별 token 수
{ "repeat": "64", "prompt_tokens": 398, "max_model_len": 4096 } { "repeat": "256", "prompt_tokens": 1550, "max_model_len": 4096 } { "repeat": "512", "prompt_tokens": 3086, "max_model_len": 4096 } { "repeat": "768", "prompt_tokens": 4622, "max_model_len": 4096 } { "repeat": "1024", "prompt_tokens": 6158, "max_model_len": 4096 }
for repeat in 64 256 512 768 1024; do
curl -sS \
-o "/tmp/gemmaforge-vllm-context-4096-result-${repeat}.json" \
-w "context=4096 repeat=${repeat} http=%{http_code} total=%{time_total}s\n" \
http://127.0.0.1:8100/v1/chat/completions \
-H 'Content-Type: application/json' \
-d @"/tmp/gemmaforge-vllm-context-${repeat}.json"
donecontext 4096 API 요청 결과
context=4096 repeat=64 http=200 total=3.094553s context=4096 repeat=256 http=200 total=1.855284s context=4096 repeat=512 http=200 total=1.941613s context=4096 repeat=768 http=400 total=0.009488s context=4096 repeat=1024 http=400 total=0.012330s
for repeat in 64 256 512 768 1024; do
jq --arg repeat "$repeat" '{
repeat: $repeat,
error: (.error.message // null),
finish_reason: (.choices[0].finish_reason // null),
prompt_tokens: (.usage.prompt_tokens // null),
completion_tokens: (.usage.completion_tokens // null)
}' "/tmp/gemmaforge-vllm-context-4096-result-${repeat}.json"
donecontext 4096 응답과 초과 오류
{ "repeat": "64", "error": null, "finish_reason": "length", "prompt_tokens": 398, "completion_tokens": 128 } { "repeat": "256", "error": null, "finish_reason": "length", "prompt_tokens": 1550, "completion_tokens": 128 } { "repeat": "512", "error": null, "finish_reason": "length", "prompt_tokens": 3086, "completion_tokens": 128 } { "repeat": "768", "error": "This model's maximum context length is 4096 tokens. However, you requested 128 output tokens and your prompt contains at least 3969 input tokens, for a total of at least 4097 tokens. Please reduce the length of the input prompt or the number of requested output tokens. (parameter=input_tokens, value=3969)", "finish_reason": null, "prompt_tokens": null, "completion_tokens": null } { "repeat": "1024", "error": "This model's maximum context length is 4096 tokens. However, you requested 128 output tokens and your prompt contains at least 3969 input tokens, for a total of at least 4097 tokens. Please reduce the length of the input prompt or the number of requested output tokens. (parameter=input_tokens, value=3969)", "finish_reason": null, "prompt_tokens": null, "completion_tokens": null }
repeat=64·256·512는 HTTP 200으로 128 token을 생성했다. repeat=768·1024는 prompt와 output 합계가 4096을 넘어서 HTTP 400으로 거절됐다. 이는 장애가 아니라 설정한 context 경계가 정상 적용된 결과다.
context 8192
k3s kubectl -n gemmaforge patch \
deployment/gemma4-12b \
--type=json \
-p='[
{
"op": "test",
"path": "/spec/template/spec/containers/0/args/5",
"value": "--max-model-len"
},
{
"op": "replace",
"path": "/spec/template/spec/containers/0/args/6",
"value": "8192"
}
]'
sudo k3s kubectl -n gemmaforge rollout status \
deployment/gemma4-12b \
--timeout=40mcontext 8192 rollout 결과
[sudo: authenticate] Password: deployment.apps/gemma4-12b patched Waiting for deployment "gemma4-12b" rollout to finish: 0 out of 1 new replicas have been updated... Waiting for deployment "gemma4-12b" rollout to finish: 0 out of 1 new replicas have been updated... Waiting for deployment "gemma4-12b" rollout to finish: 0 out of 1 new replicas have been updated... Waiting for deployment "gemma4-12b" rollout to finish: 0 of 1 updated replicas are available... deployment "gemma4-12b" successfully rolled out
curl -fsS http://127.0.0.1:8100/v1/models \
| jq '.data[] | {id, root, max_model_len}'
for repeat in 64 256 512 768 1024; do
jq '{model, messages}' \
"/tmp/gemmaforge-vllm-context-${repeat}.json" \
| curl -fsS http://127.0.0.1:8100/tokenize \
-H 'Content-Type: application/json' \
-d @- \
| jq --arg repeat "$repeat" \
'{
repeat: $repeat,
prompt_tokens: .count,
max_model_len
}'
donecontext 8192 model과 payload별 token 수
{ "id": "gemma-4-12b-q4", "root": "google/gemma-4-12B-it-qat-w4a16-ct", "max_model_len": 8192 } { "repeat": "64", "prompt_tokens": 398, "max_model_len": 8192 } { "repeat": "256", "prompt_tokens": 1550, "max_model_len": 8192 } { "repeat": "512", "prompt_tokens": 3086, "max_model_len": 8192 } { "repeat": "768", "prompt_tokens": 4622, "max_model_len": 8192 } { "repeat": "1024", "prompt_tokens": 6158, "max_model_len": 8192 }
for repeat in 64 256 512 768 1024; do
curl -sS \
-o "/tmp/gemmaforge-vllm-context-8192-result-${repeat}.json" \
-w "context=8192 repeat=${repeat} http=%{http_code} total=%{time_total}s\n" \
http://127.0.0.1:8100/v1/chat/completions \
-H 'Content-Type: application/json' \
-d @"/tmp/gemmaforge-vllm-context-${repeat}.json"
donecontext 8192 API 요청 결과
context=8192 repeat=64 http=200 total=2.807249s context=8192 repeat=256 http=200 total=1.858033s context=8192 repeat=512 http=200 total=1.957767s context=8192 repeat=768 http=200 total=2.006220s context=8192 repeat=1024 http=200 total=2.062749s
for repeat in 64 256 512 768 1024; do
jq --arg repeat "$repeat" '{
repeat: $repeat,
error: (.error.message // null),
finish_reason: (.choices[0].finish_reason // null),
prompt_tokens: (.usage.prompt_tokens // null),
completion_tokens: (.usage.completion_tokens // null)
}' "/tmp/gemmaforge-vllm-context-8192-result-${repeat}.json"
donecontext 8192 응답 요약
{ "repeat": "64", "error": null, "finish_reason": "length", "prompt_tokens": 398, "completion_tokens": 128 } { "repeat": "256", "error": null, "finish_reason": "length", "prompt_tokens": 1550, "completion_tokens": 128 } { "repeat": "512", "error": null, "finish_reason": "length", "prompt_tokens": 3086, "completion_tokens": 128 } { "repeat": "768", "error": null, "finish_reason": "length", "prompt_tokens": 4622, "completion_tokens": 128 } { "repeat": "1024", "error": null, "finish_reason": "length", "prompt_tokens": 6158, "completion_tokens": 128 }
모든 payload가 HTTP 200으로 처리됐고 최대 6,158 prompt token과 128 completion token을 정상 생성했다.
context 16384
sudo k3s kubectl -n gemmaforge patch \
deployment/gemma4-12b \
--type=json \
-p='[
{
"op": "test",
"path": "/spec/template/spec/containers/0/args/5",
"value": "--max-model-len"
},
{
"op": "replace",
"path": "/spec/template/spec/containers/0/args/6",
"value": "16384"
}
]'
sudo k3s kubectl -n gemmaforge rollout status \
deployment/gemma4-12b \
--timeout=40mcontext 16384 rollout 결과
deployment.apps/gemma4-12b patched Waiting for deployment "gemma4-12b" rollout to finish: 0 out of 1 new replicas have been updated... Waiting for deployment "gemma4-12b" rollout to finish: 0 out of 1 new replicas have been updated... Waiting for deployment "gemma4-12b" rollout to finish: 0 out of 1 new replicas have been updated... Waiting for deployment "gemma4-12b" rollout to finish: 0 of 1 updated replicas are available... deployment "gemma4-12b" successfully rolled out
curl -fsS http://127.0.0.1:8100/v1/models \
| jq '.data[] | {id, root, max_model_len}'
for repeat in 64 256 512 768 1024; do
jq '{model, messages}' \
"/tmp/gemmaforge-vllm-context-${repeat}.json" \
| curl -fsS http://127.0.0.1:8100/tokenize \
-H 'Content-Type: application/json' \
-d @- \
| jq --arg repeat "$repeat" \
'{
repeat: $repeat,
prompt_tokens: .count,
max_model_len
}'
donecontext 16384 model과 payload별 token 수
{ "id": "gemma-4-12b-q4", "root": "google/gemma-4-12B-it-qat-w4a16-ct", "max_model_len": 16384 } { "repeat": "64", "prompt_tokens": 398, "max_model_len": 16384 } { "repeat": "256", "prompt_tokens": 1550, "max_model_len": 16384 } { "repeat": "512", "prompt_tokens": 3086, "max_model_len": 16384 } { "repeat": "768", "prompt_tokens": 4622, "max_model_len": 16384 } { "repeat": "1024", "prompt_tokens": 6158, "max_model_len": 16384 }
for repeat in 64 256 512 768 1024; do
curl -sS \
-o "/tmp/gemmaforge-vllm-context-16384-result-${repeat}.json" \
-w "context=16384 repeat=${repeat} http=%{http_code} total=%{time_total}s\n" \
http://127.0.0.1:8100/v1/chat/completions \
-H 'Content-Type: application/json' \
-d @"/tmp/gemmaforge-vllm-context-${repeat}.json"
donecontext 16384 API 요청 결과
context=16384 repeat=64 http=200 total=2.919951s context=16384 repeat=256 http=200 total=1.888584s context=16384 repeat=512 http=200 total=1.990207s context=16384 repeat=768 http=200 total=2.026655s context=16384 repeat=1024 http=200 total=2.033981s
for repeat in 64 256 512 768 1024; do
jq --arg repeat "$repeat" '{
repeat: $repeat,
error: (.error.message // null),
finish_reason: (.choices[0].finish_reason // null),
prompt_tokens: (.usage.prompt_tokens // null),
completion_tokens: (.usage.completion_tokens // null)
}' "/tmp/gemmaforge-vllm-context-16384-result-${repeat}.json"
donecontext 16384 응답 요약
{ "repeat": "64", "error": null, "finish_reason": "length", "prompt_tokens": 398, "completion_tokens": 128 } { "repeat": "256", "error": null, "finish_reason": "length", "prompt_tokens": 1550, "completion_tokens": 128 } { "repeat": "512", "error": null, "finish_reason": "length", "prompt_tokens": 3086, "completion_tokens": 128 } { "repeat": "768", "error": null, "finish_reason": "length", "prompt_tokens": 4622, "completion_tokens": 128 } { "repeat": "1024", "error": null, "finish_reason": "length", "prompt_tokens": 6158, "completion_tokens": 128 }
이번 payload의 최대 prompt는 6,158 token이므로 16384의 상한까지 검증한 것은 아니다. 다만 rollout과 모든 API 요청이 성공해 해당 설정에서 engine이 정상 기동하는 것은 확인했다.
| context | 성공 범위 | HTTP 결과 | 최대 성공 요청 | 해석 |
|---|---|---|---|---|
4096 | repeat 64·256·512 | 200 3회, 400 2회 | prompt 3,086 + output 128 | 설정한 context 경계가 정상 적용 |
8192 | repeat 64~1024 | 200 5회 | prompt 6,158 + output 128 | 현재 payload 전체 처리 |
16384 | repeat 64~1024 | 200 5회 | prompt 6,158 + output 128 | 기동 성공, 16384 상한 자체는 미검증 |
같은 긴 payload에서 8192와 16384의 응답 시간 차이는 작았다. 현재 payload가 8192 안에 모두 들어가기 때문에 더 큰 context 설정의 이점이 나타나지 않은 것으로 해석한다.
context 8192 복구
sudo k3s kubectl -n gemmaforge patch \
deployment/gemma4-12b \
--type=json \
-p='[
{
"op": "test",
"path": "/spec/template/spec/containers/0/args/5",
"value": "--max-model-len"
},
{
"op": "test",
"path": "/spec/template/spec/containers/0/args/6",
"value": "16384"
},
{
"op": "replace",
"path": "/spec/template/spec/containers/0/args/6",
"value": "8192"
}
]'context 8192 patch 복구 결과
deployment.apps/gemma4-12b patched
sudo k3s kubectl -n gemmaforge rollout status \
deployment/gemma4-12b \
--timeout=40m
sudo k3s kubectl -n gemmaforge get deployment/gemma4-12b \
-o json \
| jq -r '
.spec.template.spec.containers[]
| select(.name == "vllm")
| .args
| to_entries[]
| "\(.key)\t\(.value)"
'context 8192 복구 rollout과 args index
Waiting for deployment "gemma4-12b" rollout to finish: 0 of 1 updated replicas are available... deployment "gemma4-12b" successfully rolled out 0 google/gemma-4-12B-it-qat-w4a16-ct 1 --served-model-name 2 gemma-4-12b-q4 3 --dtype 4 auto 5 --max-model-len 6 8192 7 --max-num-seqs 8 1 9 --gpu-memory-utilization 10 0.85 11 --limit-mm-per-prompt 12 {"image": 0, "audio": 0}
args index 출력에는 값이 남지 않았지만 다음 API 결과에서 max_model_len=8192를 확인했다.
curl -fsS http://127.0.0.1:8100/health
curl -fsS http://127.0.0.1:8100/v1/models \
| jq '.data[] | {id, root, max_model_len}'context 8192 복구 API 결과
{ "id": "gemma-4-12b-q4", "root": "google/gemma-4-12B-it-qat-w4a16-ct", "max_model_len": 8192 }
scheduler max-num-seqs
context를 8192로 고정하고 max-num-seqs만 1·2·4로 변경했다. 이 요청은 순차 실행이므로 concurrency 한계가 아니라 각 설정의 단일 요청 기능 기준선만 확인한다.
max-num-seqs 1
k3s kubectl -n gemmaforge patch \
deployment/gemma4-12b \
--type=json \
-p='[
{
"op": "test",
"path": "/spec/template/spec/containers/0/args/7",
"value": "--max-num-seqs"
},
{
"op": "replace",
"path": "/spec/template/spec/containers/0/args/8",
"value": "1"
}
]'max-num-seqs 1 patch 결과
deployment.apps/gemma4-12b patched
k3s kubectl -n gemmaforge rollout status \
deployment/gemma4-12b \
--timeout=40m
sudo k3s kubectl -n gemmaforge get pods -o widemax-num-seqs 1 rollout과 Pod 상태
Waiting for deployment "gemma4-12b" rollout to finish: 0 of 1 updated replicas are available... deployment "gemma4-12b" successfully rolled out NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES gemma4-12b-858566cd49-2kjv9 1/1 Running 0 2m50s 10.42.0.211 momindesktop <none> <none>
curl -fsS http://127.0.0.1:8100/health
for i in $(seq 1 5); do
curl -sS \
-o "/tmp/gemmaforge-07-vllm-seqs-${i}.json" \
-w "run=${i} http=%{http_code} total=%{time_total}s\n" \
http://127.0.0.1:8100/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "gemma-4-12b-q4",
"messages": [
{
"role": "user",
"content": "Kubernetes의 장점을 세 가지로 요약해줘."
}
],
"max_tokens": 128,
"temperature": 0
}'
donemax-num-seqs 1 API 5회
run=1 http=200 total=2.885444s run=2 http=200 total=1.591394s run=3 http=200 total=1.592459s run=4 http=200 total=1.586838s run=5 http=200 total=1.592049s
max-num-seqs 2
k3s kubectl -n gemmaforge patch \
deployment/gemma4-12b \
--type=json \
-p='[
{
"op": "test",
"path": "/spec/template/spec/containers/0/args/7",
"value": "--max-num-seqs"
},
{
"op": "replace",
"path": "/spec/template/spec/containers/0/args/8",
"value": "2"
}
]'max-num-seqs 2 patch 결과
deployment.apps/gemma4-12b patched
k3s kubectl -n gemmaforge rollout status \
deployment/gemma4-12b \
--timeout=40m
sudo k3s kubectl -n gemmaforge get pods -o widemax-num-seqs 2 rollout과 Pod 상태
Waiting for deployment "gemma4-12b" rollout to finish: 0 of 1 updated replicas are available... deployment "gemma4-12b" successfully rolled out NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES gemma4-12b-6d6c665fb-n5jkh 1/1 Running 0 2m51s 10.42.0.212 momindesktop <none> <none>
for i in $(seq 1 5); do
curl -sS \
-o "/tmp/gemmaforge-07-vllm-seqs-${i}.json" \
-w "run=${i} http=%{http_code} total=%{time_total}s\n" \
http://127.0.0.1:8100/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "gemma-4-12b-q4",
"messages": [
{
"role": "user",
"content": "Kubernetes의 장점을 세 가지로 요약해줘."
}
],
"max_tokens": 128,
"temperature": 0
}'
donemax-num-seqs 2 API 5회
run=1 http=200 total=2.721084s run=2 http=200 total=1.596167s run=3 http=200 total=1.603960s run=4 http=200 total=1.595761s run=5 http=200 total=1.597539s
max-num-seqs 4
k3s kubectl -n gemmaforge patch \
deployment/gemma4-12b \
--type=json \
-p='[
{
"op": "test",
"path": "/spec/template/spec/containers/0/args/7",
"value": "--max-num-seqs"
},
{
"op": "replace",
"path": "/spec/template/spec/containers/0/args/8",
"value": "4"
}
]'max-num-seqs 4 patch 결과
deployment.apps/gemma4-12b patched
k3s kubectl -n gemmaforge rollout status \
deployment/gemma4-12b \
--timeout=40m
sudo k3s kubectl -n gemmaforge get pods -o widemax-num-seqs 4 rollout과 Pod 상태
Waiting for deployment "gemma4-12b" rollout to finish: 0 of 1 updated replicas are available... deployment "gemma4-12b" successfully rolled out NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES gemma4-12b-5568b65ddb-9478x 1/1 Running 0 2m41s 10.42.0.213 momindesktop <none> <none>
for i in $(seq 1 5); do
curl -sS \
-o "/tmp/gemmaforge-07-vllm-seqs-${i}.json" \
-w "run=${i} http=%{http_code} total=%{time_total}s\n" \
http://127.0.0.1:8100/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "gemma-4-12b-q4",
"messages": [
{
"role": "user",
"content": "Kubernetes의 장점을 세 가지로 요약해줘."
}
],
"max_tokens": 128,
"temperature": 0
}'
donemax-num-seqs 4 API 5회
run=1 http=200 total=2.791049s run=2 http=200 total=1.626203s run=3 http=200 total=1.629429s run=4 http=200 total=1.651312s run=5 http=200 total=1.665677s
| max-num-seqs | Pod | restart | HTTP 성공 | run 1 | run 2~5 평균 | 해석 |
|---|---|---|---|---|---|---|
1 | Running 1/1 | 0 | 5/5 | 2.885444초 | 1.590685초 | 순차 단일 요청에서 가장 짧은 warm 평균 |
2 | Running 1/1 | 0 | 5/5 | 2.721084초 | 1.598357초 | seqs 1과 거의 동일한 범위 |
4 | Running 1/1 | 0 | 5/5 | 2.791049초 | 1.643155초 | 정상 동작하지만 단일 요청 warm 평균은 약간 김 |
세 설정 모두 restart 없이 API 요청을 처리했다. 차이는 최대 약 52ms로 작고 순차 요청만 사용했으므로 max-num-seqs=1이 더 우수하다고 결론 내리지 않는다. scheduler 설정은 동시 요청 1·2·4와 queue·KV cache metric을 함께 측정해야 한다.
5. 입력 payload 확인
5절에서 생성한 /tmp/gemmaforge-vllm-context-256.json을 세 scheduler 후보가 공통으로 사용했다. context는 8192, 출력 상한은 128 token으로 고정해 max-num-seqs와 concurrency만 비교했다.
실행 전에 다음 명령으로 payload와 API 연결을 확인했다.
jq '{model, max_tokens, temperature, prompt_chars: (.messages[0].content | length)}' \
/tmp/gemmaforge-vllm-context-256.json
curl -fsS http://127.0.0.1:8100/v1/models | jq6. max-num-seqs별 동시 요청 1차 단기 측정
Areas 런북의 공통 함수와 후보별 patch 명령을 준비한 뒤, 각 후보에서 아래 입력을 실행했다.
run_vllm_load
check_vllm_idlerun_vllm_load는 concurrency 1, 2, 4, 8을 차례로 실행하고 HTTP 상태·요청 시간을 출력한다. check_vllm_idle은 모든 부하가 끝난 뒤 running·waiting과 Pod restart를 확인한다. 함수 전문과 후보별 설정 명령은 GemmaForge 최적화 런북 10절을 따른다.
아래 세 callout은 1차 단기 측정 원문이다
HTTP 상태와 요청 시간은 유효하지만 metric은 후보 판정에서 제외한다. 같은 제목의 값이 중복되거나 이전 후보 값이 섞인 이유는
[5m]범위에 rollout 전·후 Pod가 함께 포함됐고, 수 초짜리 부하 일부가 Prometheus scrape 사이에서 끝났기 때문이다. 최종 판정은 8절의 90초 지속 부하 결과를 사용한다. 원래 등호 3개로 시작하던 실행 구분선은 Dataview inline query와 충돌하므로 측정값을 유지한 채CASE ...로 표시한다.
| max-num-seqs | concurrency | HTTP 성공 | 평균 요청 시간 | 마지막 완료 | 추정 처리량 | 완료 형태 |
|---|---|---|---|---|---|---|
1 | 1 | 1/1 | 2.79초 | 2.79초 | 0.36 req/s | 1개 실행 |
1 | 2 | 2/2 | 2.82초 | 3.69초 | 0.54 req/s | 거의 직렬 |
1 | 4 | 4/4 | 4.39초 | 6.92초 | 0.58 req/s | 약 4개 wave |
1 | 8 | 8/8 | 7.74초 | 13.81초 | 0.58 req/s | 약 8개 wave |
2 | 1 | 1/1 | 2.82초 | 2.82초 | 0.35 req/s | 1개 실행 |
2 | 2 | 2/2 | 2.63초 | 2.63초 | 0.76 req/s | 2개 동시 완료 |
2 | 4 | 4/4 | 2.91초 | 3.77초 | 1.06 req/s | 약 2개 wave |
2 | 8 | 8/8 | 4.49초 | 7.05초 | 1.14 req/s | 약 4개 wave |
4 | 1 | 1/1 | 2.84초 | 2.84초 | 0.35 req/s | 1개 실행 |
4 | 2 | 2/2 | 2.66초 | 2.66초 | 0.75 req/s | 2개 동시 완료 |
4 | 4 | 4/4 | 2.00초 | 2.00초 | 2.00 req/s | 4개 동시 완료 |
4 | 8 | 8/8 | 3.06초 | 3.95초 | 2.02 req/s | 약 2개 wave |
요청 결과만 보면 max-num-seqs가 1 → 2 → 4로 증가할수록 동시에 처리되는 요청 수가 늘었다. concurrency 8의 마지막 완료 시간은 13.81초 → 7.05초 → 3.95초로 줄었고, 모든 요청은 HTTP 200이었다. 각 후보가 끝난 뒤 running과 waiting은 0, Pod restart는 0이었다.
max-num-seqs 1 동시 요청 1차 원문 — metric 판정 제외
CASE max-num-seqs=1 concurrency=1 시작 started_at=2026-07-21T17:18:44+09:00 request=1 http=200 total=2.792253s --- vLLM metric --- { "metric": "running 최고값", "value": "0" } { "metric": "running 최고값", "value": "0" } { "metric": "waiting 최고값", "value": "0" } { "metric": "waiting 최고값", "value": "0" } { "metric": "KV cache 최고값", "value": "0" } { "metric": "KV cache 최고값", "value": "0" } { "metric": "preemption 증가량", "value": "0" } { "metric": "preemption 증가량", "value": "0" } --- GPU metric --- { "metric": "GPU memory 최고값 MiB", "value": "21318" } { "metric": "GPU memory 최고값 MiB", "value": "20922" } { "metric": "GPU utilization 최고값", "value": "53" } { "metric": "GPU utilization 최고값", "value": "51" } finished_at=2026-07-21T17:18:47+09:00 CASE max-num-seqs=1 concurrency=2 시작 started_at=2026-07-21T17:19:27+09:00 request=1 http=200 total=1.943958s request=2 http=200 total=3.690435s --- vLLM metric --- { "metric": "running 최고값", "value": "0" } { "metric": "running 최고값", "value": "0" } { "metric": "waiting 최고값", "value": "0" } { "metric": "waiting 최고값", "value": "0" } { "metric": "KV cache 최고값", "value": "0" } { "metric": "KV cache 최고값", "value": "0" } { "metric": "preemption 증가량", "value": "0" } --- GPU metric --- { "metric": "GPU memory 최고값 MiB", "value": "21230" } { "metric": "GPU utilization 최고값", "value": "51" } finished_at=2026-07-21T17:19:30+09:00 CASE max-num-seqs=1 concurrency=4 시작 started_at=2026-07-21T17:19:43+09:00 request=1 http=200 total=1.880687s request=4 http=200 total=3.551251s request=3 http=200 total=5.208392s request=2 http=200 total=6.920590s --- vLLM metric --- { "metric": "running 최고값", "value": "0" } { "metric": "waiting 최고값", "value": "0" } { "metric": "KV cache 최고값", "value": "0" } { "metric": "preemption 증가량", "value": "0" } --- GPU metric --- { "metric": "GPU memory 최고값 MiB", "value": "21230" } { "metric": "GPU utilization 최고값", "value": "51" } finished_at=2026-07-21T17:19:50+09:00 CASE max-num-seqs=1 concurrency=8 시작 started_at=2026-07-21T17:20:21+09:00 request=3 http=200 total=1.877190s request=1 http=200 total=3.526747s request=2 http=200 total=5.187644s request=5 http=200 total=6.865319s request=4 http=200 total=8.504390s request=6 http=200 total=10.153074s request=8 http=200 total=11.959009s request=7 http=200 total=13.809320s --- vLLM metric --- { "metric": "running 최고값", "value": "1" } { "metric": "waiting 최고값", "value": "6" } { "metric": "KV cache 최고값", "value": "0.037851827887415035" } { "metric": "preemption 증가량", "value": "0" } --- GPU metric --- { "metric": "GPU memory 최고값 MiB", "value": "21230" } { "metric": "GPU utilization 최고값", "value": "85" } finished_at=2026-07-21T17:20:35+09:00 { "metric": "running 현재값", "value": "0" } { "metric": "waiting 현재값", "value": "0" } NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES gemma4-12b-858566cd49-zz5rj 1/1 Running 0 6m2s 10.42.0.214 momindesktop <none> <none>
max-num-seqs 2 동시 요청 1차 원문 — metric 판정 제외
현재 max-num-seqs=2 CASE max-num-seqs=2 concurrency=1 시작 started_at=2026-07-21T17:25:11+09:00 request=1 http=200 total=2.816021s --- vLLM metric --- { "metric": "running 최고값", "value": "1" } { "metric": "running 최고값", "value": "0" } { "metric": "waiting 최고값", "value": "6" } { "metric": "waiting 최고값", "value": "0" } { "metric": "KV cache 최고값", "value": "0.037851827887415035" } { "metric": "KV cache 최고값", "value": "0" } { "metric": "preemption 증가량", "value": "0" } { "metric": "preemption 증가량", "value": "0" } --- GPU metric --- { "metric": "GPU memory 최고값 MiB", "value": "21159" } { "metric": "GPU memory 최고값 MiB", "value": "21174" } { "metric": "GPU utilization 최고값", "value": "25" } { "metric": "GPU utilization 최고값", "value": "11" } finished_at=2026-07-21T17:25:14+09:00 CASE max-num-seqs=2 concurrency=2 시작 started_at=2026-07-21T17:25:38+09:00 request=1 http=200 total=2.627982s request=2 http=200 total=2.627987s --- vLLM metric --- { "metric": "running 최고값", "value": "0" } { "metric": "running 최고값", "value": "0" } { "metric": "waiting 최고값", "value": "0" } { "metric": "waiting 최고값", "value": "0" } { "metric": "KV cache 최고값", "value": "0" } { "metric": "KV cache 최고값", "value": "0" } { "metric": "preemption 증가량", "value": "0" } { "metric": "preemption 증가량", "value": "0" } --- GPU metric --- { "metric": "GPU memory 최고값 MiB", "value": "21159" } { "metric": "GPU memory 최고값 MiB", "value": "21174" } { "metric": "GPU utilization 최고값", "value": "25" } { "metric": "GPU utilization 최고값", "value": "11" } finished_at=2026-07-21T17:25:41+09:00 CASE max-num-seqs=2 concurrency=4 시작 started_at=2026-07-21T17:25:54+09:00 request=1 http=200 total=2.039433s request=4 http=200 total=2.066683s request=3 http=200 total=3.764863s request=2 http=200 total=3.763473s --- vLLM metric --- { "metric": "running 최고값", "value": "0" } { "metric": "running 최고값", "value": "0" } { "metric": "waiting 최고값", "value": "0" } { "metric": "waiting 최고값", "value": "0" } { "metric": "KV cache 최고값", "value": "0" } { "metric": "KV cache 최고값", "value": "0" } { "metric": "preemption 증가량", "value": "0" } { "metric": "preemption 증가량", "value": "0" } --- GPU metric --- { "metric": "GPU memory 최고값 MiB", "value": "21159" } { "metric": "GPU memory 최고값 MiB", "value": "21174" } { "metric": "GPU utilization 최고값", "value": "25" } { "metric": "GPU utilization 최고값", "value": "11" } finished_at=2026-07-21T17:25:58+09:00 CASE max-num-seqs=2 concurrency=8 시작 started_at=2026-07-21T17:26:12+09:00 request=1 http=200 total=1.911154s request=2 http=200 total=1.938905s request=4 http=200 total=3.624886s request=3 http=200 total=3.650917s request=6 http=200 total=5.333550s request=7 http=200 total=5.361549s request=8 http=200 total=7.050089s request=5 http=200 total=7.048837s --- vLLM metric --- { "metric": "running 최고값", "value": "0" } { "metric": "running 최고값", "value": "2" } { "metric": "waiting 최고값", "value": "0" } { "metric": "waiting 최고값", "value": "2" } { "metric": "KV cache 최고값", "value": "0" } { "metric": "KV cache 최고값", "value": "0.03900594273365743" } { "metric": "preemption 증가량", "value": "0" } --- GPU metric --- { "metric": "GPU memory 최고값 MiB", "value": "21159" } { "metric": "GPU memory 최고값 MiB", "value": "21174" } { "metric": "GPU utilization 최고값", "value": "25" } { "metric": "GPU utilization 최고값", "value": "11" } finished_at=2026-07-21T17:26:19+09:00 { "metric": "running 현재값", "value": "0" } { "metric": "waiting 현재값", "value": "0" } NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES gemma4-12b-6d6c665fb-dn82s 1/1 Running 0 5m15s 10.42.0.215 momindesktop <none> <none>
max-num-seqs 4 동시 요청 1차 원문 — metric 판정 제외
현재 max-num-seqs=4 CASE max-num-seqs=4 concurrency=1 시작 started_at=2026-07-21T17:31:27+09:00 request=1 http=200 total=2.841821s --- vLLM metric --- { "metric": "running 최고값", "value": "0" } { "metric": "running 최고값", "value": "0" } { "metric": "waiting 최고값", "value": "0" } { "metric": "waiting 최고값", "value": "0" } { "metric": "KV cache 최고값", "value": "0" } { "metric": "KV cache 최고값", "value": "0" } { "metric": "preemption 증가량", "value": "0" } { "metric": "preemption 증가량", "value": "0" } --- GPU metric --- { "metric": "GPU memory 최고값 MiB", "value": "21088" } { "metric": "GPU memory 최고값 MiB", "value": "20808" } { "metric": "GPU utilization 최고값", "value": "4" } { "metric": "GPU utilization 최고값", "value": "3" } finished_at=2026-07-21T17:31:30+09:00 CASE max-num-seqs=4 concurrency=2 시작 started_at=2026-07-21T17:31:49+09:00 request=1 http=200 total=2.655573s request=2 http=200 total=2.655332s --- vLLM metric --- { "metric": "running 최고값", "value": "0" } { "metric": "running 최고값", "value": "0" } { "metric": "waiting 최고값", "value": "0" } { "metric": "waiting 최고값", "value": "0" } { "metric": "KV cache 최고값", "value": "0" } { "metric": "KV cache 최고값", "value": "0" } { "metric": "preemption 증가량", "value": "0" } { "metric": "preemption 증가량", "value": "0" } --- GPU metric --- { "metric": "GPU memory 최고값 MiB", "value": "21088" } { "metric": "GPU memory 최고값 MiB", "value": "20808" } { "metric": "GPU utilization 최고값", "value": "4" } { "metric": "GPU utilization 최고값", "value": "3" } finished_at=2026-07-21T17:31:52+09:00 CASE max-num-seqs=4 concurrency=4 시작 started_at=2026-07-21T17:32:10+09:00 request=2 http=200 total=2.002622s request=3 http=200 total=2.002455s request=1 http=200 total=2.002685s request=4 http=200 total=2.002341s --- vLLM metric --- { "metric": "running 최고값", "value": "0" } { "metric": "running 최고값", "value": "0" } { "metric": "waiting 최고값", "value": "0" } { "metric": "waiting 최고값", "value": "0" } { "metric": "KV cache 최고값", "value": "0" } { "metric": "KV cache 최고값", "value": "0" } { "metric": "preemption 증가량", "value": "0" } { "metric": "preemption 증가량", "value": "0" } --- GPU metric --- { "metric": "GPU memory 최고값 MiB", "value": "21088" } { "metric": "GPU memory 최고값 MiB", "value": "20998" } { "metric": "GPU utilization 최고값", "value": "4" } { "metric": "GPU utilization 최고값", "value": "3" } finished_at=2026-07-21T17:32:12+09:00 CASE max-num-seqs=4 concurrency=8 시작 started_at=2026-07-21T17:32:27+09:00 request=1 http=200 total=2.153159s request=6 http=200 total=2.183842s request=4 http=200 total=2.184095s request=3 http=200 total=2.183365s request=8 http=200 total=3.952440s request=7 http=200 total=3.952997s request=5 http=200 total=3.953657s request=2 http=200 total=3.952836s --- vLLM metric --- { "metric": "running 최고값", "value": "0" } { "metric": "running 최고값", "value": "0" } { "metric": "waiting 최고값", "value": "0" } { "metric": "waiting 최고값", "value": "0" } { "metric": "KV cache 최고값", "value": "0" } { "metric": "KV cache 최고값", "value": "0" } { "metric": "preemption 증가량", "value": "0" } { "metric": "preemption 증가량", "value": "0" } --- GPU metric --- { "metric": "GPU memory 최고값 MiB", "value": "21088" } { "metric": "GPU memory 최고값 MiB", "value": "20998" } { "metric": "GPU utilization 최고값", "value": "0" } { "metric": "GPU utilization 최고값", "value": "7" } finished_at=2026-07-21T17:32:31+09:00 { "metric": "running 현재값", "value": "0" } { "metric": "waiting 현재값", "value": "0" } NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES gemma4-12b-5568b65ddb-vfn4n 1/1 Running 0 4m40s 10.42.0.216 momindesktop <none> <none>
7. 실험 구간 metric 판정
이번 실행의 latency와 HTTP 결과는 scheduler 동시성 차이를 보여주는 유효한 기록이다. 그러나 저장된 Prometheus 최고값은 후보 간 정량 비교에 바로 사용하지 않는다.
| 관찰 내용 | 원인 | 현재 판정 |
|---|---|---|
| 같은 metric 제목에 값이 2개 출력됨 | rollout 전·후 Pod 시계열이 [5m] 범위에 함께 존재하고 출력에서 pod, instance 라벨을 숨김 | 어느 값이 현재 Pod인지 구분할 수 없음 |
| 다음 후보의 첫 측정에 이전 후보의 높은 값이 남음 | 5분 범위가 직전 실험 구간과 겹침 | 후보별 최고값 비교 불가 |
max-num-seqs=4, concurrency 8에서 running·KV cache·GPU utilization이 0 | 부하가 약 4초 만에 끝나 Prometheus scrape 사이에서 실행됨 | 실제 무부하가 아니라 sampling 누락 가능성이 큼 |
| concurrency가 뒤로 갈수록 일부 시간이 짧아짐 | rollout 직후 첫 요청의 warm-up 비용과 고정된 실행 순서가 섞임 | 1회 결과만으로 미세한 latency 우열을 확정하지 않음 |
따라서 scheduler의 방향성은 확인했지만 waiting, KV cache, GPU utilization, VRAM 최고값은 재측정이 필요하다. 보완 절차는 Areas 런북의 10-10. 짧은 부하에서 누락된 metric 다시 측정에 추가했다.
max-num-seqs 1 지속 부하 최종 결과
refresh_current_vllm_pod
collect_vllm_metricsmax-num-seqs 1, concurrency 1 복구된 metric
{ "metric": "running 최고값", "series": [ { "labels": { "__name__": "vllm:num_requests_running", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.217:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-858566cd49-fdhql", "service": "gemma4-vllm" }, "max": 1 } ] } { "metric": "waiting 최고값", "series": [ { "labels": { "__name__": "vllm:num_requests_waiting", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.217:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-858566cd49-fdhql", "service": "gemma4-vllm" }, "max": 0 } ] } { "metric": "KV cache 최고값", "series": [ { "labels": { "__name__": "vllm:kv_cache_usage_perc", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.217:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-858566cd49-fdhql", "service": "gemma4-vllm" }, "max": 0.037851827887415035 } ] } { "metric": "preemption 증가량", "series": [ { "labels": { "__name__": "vllm:num_preemptions_total", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.217:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-858566cd49-fdhql", "service": "gemma4-vllm" }, "first": 0, "last": 0, "delta": 0 } ] } { "metric": "GPU memory 최고값 MiB", "series": [ { "labels": { "UUID": "GPU-3652ba18-b78d-828d-47ce-34ace2c0632d", "__name__": "DCGM_FI_DEV_FB_USED", "container": "exporter", "device": "nvidia0", "endpoint": "metrics", "exported_container": "vllm", "exported_namespace": "gemmaforge", "exported_pod": "gemma4-12b-858566cd49-fdhql", "gpu": "0", "hostname": "momindesktop", "instance": "10.42.0.186:9400", "job": "dcgm-exporter", "modelName": "NVIDIA GeForce RTX 4090", "namespace": "monitoring", "pci_bus_id": "00000000:01:00.0", "pod": "dcgm-exporter-f6wzn", "service": "dcgm-exporter" }, "max": 20531 } ] } { "metric": "GPU utilization 최고값", "series": [ { "labels": { "UUID": "GPU-3652ba18-b78d-828d-47ce-34ace2c0632d", "__name__": "DCGM_FI_DEV_GPU_UTIL", "container": "exporter", "device": "nvidia0", "endpoint": "metrics", "exported_container": "vllm", "exported_namespace": "gemmaforge", "exported_pod": "gemma4-12b-858566cd49-fdhql", "gpu": "0", "hostname": "momindesktop", "instance": "10.42.0.186:9400", "job": "dcgm-exporter", "modelName": "NVIDIA GeForce RTX 4090", "namespace": "monitoring", "pci_bus_id": "00000000:01:00.0", "pod": "dcgm-exporter-f6wzn", "service": "dcgm-exporter" }, "max": 86 } ] }
run_vllm_case 2 90max-num-seqs 1, concurrency 2 지속 부하 결과
requests=54 http_200=54 avg=2.549072s max=3.656425s metric_started_epoch=1784644875 metric_finished_epoch=1784644967 timing_file=/tmp/vllm-seqs-1-c2-timing.log { "metric": "running 최고값", "series": [ { "labels": { "__name__": "vllm:num_requests_running", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.217:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-858566cd49-fdhql", "service": "gemma4-vllm" }, "max": 1 } ] } { "metric": "waiting 최고값", "series": [ { "labels": { "__name__": "vllm:num_requests_waiting", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.217:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-858566cd49-fdhql", "service": "gemma4-vllm" }, "max": 1 } ] } { "metric": "KV cache 최고값", "series": [ { "labels": { "__name__": "vllm:kv_cache_usage_perc", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.217:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-858566cd49-fdhql", "service": "gemma4-vllm" }, "max": 0.037851827887415035 } ] } { "metric": "preemption 증가량", "series": [ { "labels": { "__name__": "vllm:num_preemptions_total", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.217:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-858566cd49-fdhql", "service": "gemma4-vllm" }, "first": 0, "last": 0, "delta": 0 } ] } { "metric": "GPU memory 최고값 MiB", "series": [ { "labels": { "UUID": "GPU-3652ba18-b78d-828d-47ce-34ace2c0632d", "__name__": "DCGM_FI_DEV_FB_USED", "container": "exporter", "device": "nvidia0", "endpoint": "metrics", "exported_container": "vllm", "exported_namespace": "gemmaforge", "exported_pod": "gemma4-12b-858566cd49-fdhql", "gpu": "0", "hostname": "momindesktop", "instance": "10.42.0.186:9400", "job": "dcgm-exporter", "modelName": "NVIDIA GeForce RTX 4090", "namespace": "monitoring", "pci_bus_id": "00000000:01:00.0", "pod": "dcgm-exporter-f6wzn", "service": "dcgm-exporter" }, "max": 20545 } ] } { "metric": "GPU utilization 최고값", "series": [ { "labels": { "UUID": "GPU-3652ba18-b78d-828d-47ce-34ace2c0632d", "__name__": "DCGM_FI_DEV_GPU_UTIL", "container": "exporter", "device": "nvidia0", "endpoint": "metrics", "exported_container": "vllm", "exported_namespace": "gemmaforge", "exported_pod": "gemma4-12b-858566cd49-fdhql", "gpu": "0", "hostname": "momindesktop", "instance": "10.42.0.186:9400", "job": "dcgm-exporter", "modelName": "NVIDIA GeForce RTX 4090", "namespace": "monitoring", "pci_bus_id": "00000000:01:00.0", "pod": "dcgm-exporter-f6wzn", "service": "dcgm-exporter" }, "max": 83 } ] } { "metric": "running 현재값", "value": "0" } { "metric": "waiting 현재값", "value": "0" } NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES gemma4-12b-858566cd49-fdhql 1/1 Running 0 16m 10.42.0.217 momindesktop <none> <none>
run_vllm_case 4 90max-num-seqs 1, concurrency 4 지속 부하 결과
requests=56 http_200=56 avg=4.269395s max=7.410069s metric_started_epoch=1784644987 metric_finished_epoch=1784645082 timing_file=/tmp/vllm-seqs-1-c4-timing.log { "metric": "running 최고값", "series": [ { "labels": { "__name__": "vllm:num_requests_running", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.217:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-858566cd49-fdhql", "service": "gemma4-vllm" }, "max": 1 } ] } { "metric": "waiting 최고값", "series": [ { "labels": { "__name__": "vllm:num_requests_waiting", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.217:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-858566cd49-fdhql", "service": "gemma4-vllm" }, "max": 3 } ] } { "metric": "KV cache 최고값", "series": [ { "labels": { "__name__": "vllm:kv_cache_usage_perc", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.217:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-858566cd49-fdhql", "service": "gemma4-vllm" }, "max": 0.037851827887415035 } ] } { "metric": "preemption 증가량", "series": [ { "labels": { "__name__": "vllm:num_preemptions_total", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.217:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-858566cd49-fdhql", "service": "gemma4-vllm" }, "first": 0, "last": 0, "delta": 0 } ] } { "metric": "GPU memory 최고값 MiB", "series": [ { "labels": { "UUID": "GPU-3652ba18-b78d-828d-47ce-34ace2c0632d", "__name__": "DCGM_FI_DEV_FB_USED", "container": "exporter", "device": "nvidia0", "endpoint": "metrics", "exported_container": "vllm", "exported_namespace": "gemmaforge", "exported_pod": "gemma4-12b-858566cd49-fdhql", "gpu": "0", "hostname": "momindesktop", "instance": "10.42.0.186:9400", "job": "dcgm-exporter", "modelName": "NVIDIA GeForce RTX 4090", "namespace": "monitoring", "pci_bus_id": "00000000:01:00.0", "pod": "dcgm-exporter-f6wzn", "service": "dcgm-exporter" }, "max": 20589 } ] } { "metric": "GPU utilization 최고값", "series": [ { "labels": { "UUID": "GPU-3652ba18-b78d-828d-47ce-34ace2c0632d", "__name__": "DCGM_FI_DEV_GPU_UTIL", "container": "exporter", "device": "nvidia0", "endpoint": "metrics", "exported_container": "vllm", "exported_namespace": "gemmaforge", "exported_pod": "gemma4-12b-858566cd49-fdhql", "gpu": "0", "hostname": "momindesktop", "instance": "10.42.0.186:9400", "job": "dcgm-exporter", "modelName": "NVIDIA GeForce RTX 4090", "namespace": "monitoring", "pci_bus_id": "00000000:01:00.0", "pod": "dcgm-exporter-f6wzn", "service": "dcgm-exporter" }, "max": 84 } ] } { "metric": "running 현재값", "value": "0" } { "metric": "waiting 현재값", "value": "0" } NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES gemma4-12b-858566cd49-fdhql 1/1 Running 0 18m 10.42.0.217 momindesktop <none> <none>
run_vllm_case 8 90max-num-seqs 1, concurrency 8 지속 부하 결과
requests=56 http_200=56 avg=7.652564s max=9.102962s metric_started_epoch=1784645103 metric_finished_epoch=1784645198 timing_file=/tmp/vllm-seqs-1-c8-timing.log { "metric": "running 최고값", "series": [ { "labels": { "__name__": "vllm:num_requests_running", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.217:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-858566cd49-fdhql", "service": "gemma4-vllm" }, "max": 1 } ] } { "metric": "waiting 최고값", "series": [ { "labels": { "__name__": "vllm:num_requests_waiting", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.217:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-858566cd49-fdhql", "service": "gemma4-vllm" }, "max": 7 } ] } { "metric": "KV cache 최고값", "series": [ { "labels": { "__name__": "vllm:kv_cache_usage_perc", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.217:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-858566cd49-fdhql", "service": "gemma4-vllm" }, "max": 0.037851827887415035 } ] } { "metric": "preemption 증가량", "series": [ { "labels": { "__name__": "vllm:num_preemptions_total", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.217:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-858566cd49-fdhql", "service": "gemma4-vllm" }, "first": 0, "last": 0, "delta": 0 } ] } { "metric": "GPU memory 최고값 MiB", "series": [ { "labels": { "UUID": "GPU-3652ba18-b78d-828d-47ce-34ace2c0632d", "__name__": "DCGM_FI_DEV_FB_USED", "container": "exporter", "device": "nvidia0", "endpoint": "metrics", "exported_container": "vllm", "exported_namespace": "gemmaforge", "exported_pod": "gemma4-12b-858566cd49-fdhql", "gpu": "0", "hostname": "momindesktop", "instance": "10.42.0.186:9400", "job": "dcgm-exporter", "modelName": "NVIDIA GeForce RTX 4090", "namespace": "monitoring", "pci_bus_id": "00000000:01:00.0", "pod": "dcgm-exporter-f6wzn", "service": "dcgm-exporter" }, "max": 20558 } ] } { "metric": "GPU utilization 최고값", "series": [ { "labels": { "UUID": "GPU-3652ba18-b78d-828d-47ce-34ace2c0632d", "__name__": "DCGM_FI_DEV_GPU_UTIL", "container": "exporter", "device": "nvidia0", "endpoint": "metrics", "exported_container": "vllm", "exported_namespace": "gemmaforge", "exported_pod": "gemma4-12b-858566cd49-fdhql", "gpu": "0", "hostname": "momindesktop", "instance": "10.42.0.186:9400", "job": "dcgm-exporter", "modelName": "NVIDIA GeForce RTX 4090", "namespace": "monitoring", "pci_bus_id": "00000000:01:00.0", "pod": "dcgm-exporter-f6wzn", "service": "dcgm-exporter" }, "max": 85 } ] } { "metric": "running 현재값", "value": "0" } { "metric": "waiting 현재값", "value": "0" } NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES gemma4-12b-858566cd49-fdhql 1/1 Running 0 20m 10.42.0.217 momindesktop <none> <none>
| concurrency | 요청 성공 | 측정 구간 | 처리량 | 평균 요청 시간 | 최대 요청 시간 | running 최고 | waiting 최고 | KV cache 최고 | preemption | GPU memory 최고 | GPU util 최고 |
|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 54/54 | 91초 | 0.593 req/s | 1.665719초 | 1.950031초 | 1 | 0 | 0.037852 | 0 | 20531 MiB | 86% |
2 | 54/54 | 92초 | 0.587 req/s | 2.549072초 | 3.656425초 | 1 | 1 | 0.037852 | 0 | 20545 MiB | 83% |
4 | 56/56 | 95초 | 0.589 req/s | 4.269395초 | 7.410069초 | 1 | 3 | 0.037852 | 0 | 20589 MiB | 84% |
8 | 56/56 | 95초 | 0.589 req/s | 7.652564초 | 9.102962초 | 1 | 7 | 0.037852 | 0 | 20558 MiB | 85% |
running 최고값이 모든 조건에서 1이고 waiting 최고값이 각각 concurrency - 1과 일치했다. max-num-seqs=1이 동시에 실행하는 sequence를 하나로 제한하고, 초과 요청을 실패시키지 않고 queue에서 순서대로 처리한 것이다.
concurrency를 1 → 8로 늘려도 처리량은 0.587~0.593 req/s로 거의 변하지 않았다. 반면 평균 요청 시간은 1.67초 → 7.65초, 최대 요청 시간은 1.95초 → 9.10초로 증가했다. 실행 slot이 하나뿐이므로 동시 요청을 늘린 효과가 처리량 증가가 아니라 queue 대기시간 증가로 나타났다.
KV cache 최고값은 모든 조건에서 약 3.79%, GPU memory는 20531~20589 MiB, GPU utilization은 83~86%로 안정적이었다. 동시에 실행되는 sequence가 항상 하나라 concurrency 증가가 KV cache나 VRAM 증가로 이어지지 않았다. preemption은 전 구간 0이어서 memory pressure로 실행 중 요청이 밀려난 흔적도 없다.
각 실행 후 running=0, waiting=0, Pod restart 0으로 복구됐다. 따라서 max-num-seqs=1은 안정적으로 동작하지만, 여러 요청이 들어오는 환경에서는 GPU 처리량을 확장하지 못하고 tail latency를 키우는 설정으로 판단한다.
max-num-seqs 2 지속 부하 최종 결과
run_vllm_case 1 90max-num-seqs 2, concurrency 1 복구된 metric
# run_vllm_case 1 90 { "metric": "running 최고값", "series": [ { "labels": { "__name__": "vllm:num_requests_running", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.218:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-6d6c665fb-wgtz8", "service": "gemma4-vllm" }, "max": 1 } ] } { "metric": "waiting 최고값", "series": [ { "labels": { "__name__": "vllm:num_requests_waiting", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.218:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-6d6c665fb-wgtz8", "service": "gemma4-vllm" }, "max": 0 } ] } { "metric": "KV cache 최고값", "series": [ { "labels": { "__name__": "vllm:kv_cache_usage_perc", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.218:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-6d6c665fb-wgtz8", "service": "gemma4-vllm" }, "max": 0.03792544570502432 } ] } { "metric": "preemption 증가량", "series": [ { "labels": { "__name__": "vllm:num_preemptions_total", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.218:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-6d6c665fb-wgtz8", "service": "gemma4-vllm" }, "first": 0, "last": 0, "delta": 0 } ] } { "metric": "GPU memory 최고값 MiB", "series": [ { "labels": { "UUID": "GPU-3652ba18-b78d-828d-47ce-34ace2c0632d", "__name__": "DCGM_FI_DEV_FB_USED", "container": "exporter", "device": "nvidia0", "endpoint": "metrics", "exported_container": "vllm", "exported_namespace": "gemmaforge", "exported_pod": "gemma4-12b-6d6c665fb-wgtz8", "gpu": "0", "hostname": "momindesktop", "instance": "10.42.0.186:9400", "job": "dcgm-exporter", "modelName": "NVIDIA GeForce RTX 4090", "namespace": "monitoring", "pci_bus_id": "00000000:01:00.0", "pod": "dcgm-exporter-f6wzn", "service": "dcgm-exporter" }, "max": 20650 } ] } { "metric": "GPU utilization 최고값", "series": [ { "labels": { "UUID": "GPU-3652ba18-b78d-828d-47ce-34ace2c0632d", "__name__": "DCGM_FI_DEV_GPU_UTIL", "container": "exporter", "device": "nvidia0", "endpoint": "metrics", "exported_container": "vllm", "exported_namespace": "gemmaforge", "exported_pod": "gemma4-12b-6d6c665fb-wgtz8", "gpu": "0", "hostname": "momindesktop", "instance": "10.42.0.186:9400", "job": "dcgm-exporter", "modelName": "NVIDIA GeForce RTX 4090", "namespace": "monitoring", "pci_bus_id": "00000000:01:00.0", "pod": "dcgm-exporter-f6wzn", "service": "dcgm-exporter" }, "max": 85 } ] } { "metric": "running 현재값", "value": "0" } { "metric": "waiting 현재값", "value": "0" } NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES gemma4-12b-6d6c665fb-wgtz8 1/1 Running 0 15m 10.42.0.218 momindesktop <none> <none>
나머지 concurrency는 수정된 run_vllm_case를 한 줄씩 실행했다.
run_vllm_case 2 90max-num-seqs 2, concurrency 2 지속 부하와 metric
# run_vllm_case 2 90 requests=104 http_200=104 avg=1.735728s max=2.119637s metric_started_epoch=1784646058 metric_finished_epoch=1784646149 timing_file=/tmp/vllm-seqs-2-c2-timing.log { "metric": "running 최고값", "series": [ { "labels": { "__name__": "vllm:num_requests_running", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.218:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-6d6c665fb-wgtz8", "service": "gemma4-vllm" }, "max": 2 } ] } { "metric": "waiting 최고값", "series": [ { "labels": { "__name__": "vllm:num_requests_waiting", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.218:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-6d6c665fb-wgtz8", "service": "gemma4-vllm" }, "max": 0 } ] } { "metric": "KV cache 최고값", "series": [ { "labels": { "__name__": "vllm:kv_cache_usage_perc", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.218:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-6d6c665fb-wgtz8", "service": "gemma4-vllm" }, "max": 0.04321988114532682 } ] } { "metric": "preemption 증가량", "series": [ { "labels": { "__name__": "vllm:num_preemptions_total", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.218:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-6d6c665fb-wgtz8", "service": "gemma4-vllm" }, "first": 0, "last": 0, "delta": 0 } ] } { "metric": "GPU memory 최고값 MiB", "series": [ { "labels": { "UUID": "GPU-3652ba18-b78d-828d-47ce-34ace2c0632d", "__name__": "DCGM_FI_DEV_FB_USED", "container": "exporter", "device": "nvidia0", "endpoint": "metrics", "exported_container": "vllm", "exported_namespace": "gemmaforge", "exported_pod": "gemma4-12b-6d6c665fb-wgtz8", "gpu": "0", "hostname": "momindesktop", "instance": "10.42.0.186:9400", "job": "dcgm-exporter", "modelName": "NVIDIA GeForce RTX 4090", "namespace": "monitoring", "pci_bus_id": "00000000:01:00.0", "pod": "dcgm-exporter-f6wzn", "service": "dcgm-exporter" }, "max": 20688 } ] } { "metric": "GPU utilization 최고값", "series": [ { "labels": { "UUID": "GPU-3652ba18-b78d-828d-47ce-34ace2c0632d", "__name__": "DCGM_FI_DEV_GPU_UTIL", "container": "exporter", "device": "nvidia0", "endpoint": "metrics", "exported_container": "vllm", "exported_namespace": "gemmaforge", "exported_pod": "gemma4-12b-6d6c665fb-wgtz8", "gpu": "0", "hostname": "momindesktop", "instance": "10.42.0.186:9400", "job": "dcgm-exporter", "modelName": "NVIDIA GeForce RTX 4090", "namespace": "monitoring", "pci_bus_id": "00000000:01:00.0", "pod": "dcgm-exporter-f6wzn", "service": "dcgm-exporter" }, "max": 86 } ] } { "metric": "running 현재값", "value": "0" } { "metric": "waiting 현재값", "value": "0" } NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES gemma4-12b-6d6c665fb-wgtz8 1/1 Running 0 15m 10.42.0.218 momindesktop <none> <none>
run_vllm_case 4 90max-num-seqs 2, concurrency 4 지속 부하와 metric
# run_vllm_case 4 90 requests=104 http_200=104 avg=2.608149s max=3.758840s metric_started_epoch=1784646220 metric_finished_epoch=1784646310 timing_file=/tmp/vllm-seqs-2-c4-timing.log { "metric": "running 최고값", "series": [ { "labels": { "__name__": "vllm:num_requests_running", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.218:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-6d6c665fb-wgtz8", "service": "gemma4-vllm" }, "max": 2 } ] } { "metric": "waiting 최고값", "series": [ { "labels": { "__name__": "vllm:num_requests_waiting", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.218:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-6d6c665fb-wgtz8", "service": "gemma4-vllm" }, "max": 2 } ] } { "metric": "KV cache 최고값", "series": [ { "labels": { "__name__": "vllm:kv_cache_usage_perc", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.218:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-6d6c665fb-wgtz8", "service": "gemma4-vllm" }, "max": 0.04321988114532682 } ] } { "metric": "preemption 증가량", "series": [ { "labels": { "__name__": "vllm:num_preemptions_total", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.218:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-6d6c665fb-wgtz8", "service": "gemma4-vllm" }, "first": 0, "last": 0, "delta": 0 } ] } { "metric": "GPU memory 최고값 MiB", "series": [ { "labels": { "UUID": "GPU-3652ba18-b78d-828d-47ce-34ace2c0632d", "__name__": "DCGM_FI_DEV_FB_USED", "container": "exporter", "device": "nvidia0", "endpoint": "metrics", "exported_container": "vllm", "exported_namespace": "gemmaforge", "exported_pod": "gemma4-12b-6d6c665fb-wgtz8", "gpu": "0", "hostname": "momindesktop", "instance": "10.42.0.186:9400", "job": "dcgm-exporter", "modelName": "NVIDIA GeForce RTX 4090", "namespace": "monitoring", "pci_bus_id": "00000000:01:00.0", "pod": "dcgm-exporter-f6wzn", "service": "dcgm-exporter" }, "max": 20681 } ] } { "metric": "GPU utilization 최고값", "series": [ { "labels": { "UUID": "GPU-3652ba18-b78d-828d-47ce-34ace2c0632d", "__name__": "DCGM_FI_DEV_GPU_UTIL", "container": "exporter", "device": "nvidia0", "endpoint": "metrics", "exported_container": "vllm", "exported_namespace": "gemmaforge", "exported_pod": "gemma4-12b-6d6c665fb-wgtz8", "gpu": "0", "hostname": "momindesktop", "instance": "10.42.0.186:9400", "job": "dcgm-exporter", "modelName": "NVIDIA GeForce RTX 4090", "namespace": "monitoring", "pci_bus_id": "00000000:01:00.0", "pod": "dcgm-exporter-f6wzn", "service": "dcgm-exporter" }, "max": 86 } ] } { "metric": "running 현재값", "value": "0" } { "metric": "waiting 현재값", "value": "0" } NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES gemma4-12b-6d6c665fb-wgtz8 1/1 Running 0 17m 10.42.0.218 momindesktop <none> <none>
run_vllm_case 8 90max-num-seqs 2, concurrency 8 지속 부하와 metric
# run_vllm_case 8 90 requests=104 http_200=104 avg=4.436486s max=7.455550s metric_started_epoch=1784646330 metric_finished_epoch=1784646422 timing_file=/tmp/vllm-seqs-2-c8-timing.log { "metric": "running 최고값", "series": [ { "labels": { "__name__": "vllm:num_requests_running", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.218:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-6d6c665fb-wgtz8", "service": "gemma4-vllm" }, "max": 2 } ] } { "metric": "waiting 최고값", "series": [ { "labels": { "__name__": "vllm:num_requests_waiting", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.218:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-6d6c665fb-wgtz8", "service": "gemma4-vllm" }, "max": 6 } ] } { "metric": "KV cache 최고값", "series": [ { "labels": { "__name__": "vllm:kv_cache_usage_perc", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.218:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-6d6c665fb-wgtz8", "service": "gemma4-vllm" }, "max": 0.04321988114532682 } ] } { "metric": "preemption 증가량", "series": [ { "labels": { "__name__": "vllm:num_preemptions_total", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.218:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-6d6c665fb-wgtz8", "service": "gemma4-vllm" }, "first": 0, "last": 0, "delta": 0 } ] } { "metric": "GPU memory 최고값 MiB", "series": [ { "labels": { "UUID": "GPU-3652ba18-b78d-828d-47ce-34ace2c0632d", "__name__": "DCGM_FI_DEV_FB_USED", "container": "exporter", "device": "nvidia0", "endpoint": "metrics", "exported_container": "vllm", "exported_namespace": "gemmaforge", "exported_pod": "gemma4-12b-6d6c665fb-wgtz8", "gpu": "0", "hostname": "momindesktop", "instance": "10.42.0.186:9400", "job": "dcgm-exporter", "modelName": "NVIDIA GeForce RTX 4090", "namespace": "monitoring", "pci_bus_id": "00000000:01:00.0", "pod": "dcgm-exporter-f6wzn", "service": "dcgm-exporter" }, "max": 20681 } ] } { "metric": "GPU utilization 최고값", "series": [ { "labels": { "UUID": "GPU-3652ba18-b78d-828d-47ce-34ace2c0632d", "__name__": "DCGM_FI_DEV_GPU_UTIL", "container": "exporter", "device": "nvidia0", "endpoint": "metrics", "exported_container": "vllm", "exported_namespace": "gemmaforge", "exported_pod": "gemma4-12b-6d6c665fb-wgtz8", "gpu": "0", "hostname": "momindesktop", "instance": "10.42.0.186:9400", "job": "dcgm-exporter", "modelName": "NVIDIA GeForce RTX 4090", "namespace": "monitoring", "pci_bus_id": "00000000:01:00.0", "pod": "dcgm-exporter-f6wzn", "service": "dcgm-exporter" }, "max": 85 } ] } { "metric": "running 현재값", "value": "0" } { "metric": "waiting 현재값", "value": "0" } NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES gemma4-12b-6d6c665fb-wgtz8 1/1 Running 0 19m 10.42.0.218 momindesktop <none> <none>
| concurrency | 요청 성공 | 측정 구간 | 처리량 | 평균 요청 시간 | 최대 요청 시간 | running 최고 | waiting 최고 | KV cache 최고 | preemption | GPU memory 최고 | GPU util 최고 |
|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 51/51 | 90초 | 0.567 req/s | 1.739537초 | 3.094595초 | 1 | 0 | 0.037925 | 0 | 20650 MiB | 85% |
2 | 104/104 | 91초 | 1.143 req/s | 1.735728초 | 2.119637초 | 2 | 0 | 0.043220 | 0 | 20688 MiB | 86% |
4 | 104/104 | 90초 | 1.156 req/s | 2.608149초 | 3.758840초 | 2 | 2 | 0.043220 | 0 | 20681 MiB | 86% |
8 | 104/104 | 92초 | 1.130 req/s | 4.436486초 | 7.455550초 | 2 | 6 | 0.043220 | 0 | 20681 MiB | 85% |
concurrency 1 → 2에서 처리량은 0.567 → 1.143 req/s로 거의 두 배가 됐다. concurrency가 2를 넘으면 running은 계속 2에 머물고 waiting이 2, 6으로 증가했다. 이때 처리량은 약 1.14 req/s에서 더 늘지 않고 평균 요청 시간만 2.61초, 4.44초로 증가했다.
KV cache 최고값은 concurrency 2부터 약 4.32%로 유지됐고 GPU memory는 20650~20688 MiB, GPU utilization은 85~86%였다. preemption과 restart가 없으므로 max-num-seqs=2는 두 요청까지 안정적으로 병렬 처리하지만 그 이상의 요청은 queue에서 기다리는 설정이다.
max-num-seqs 4 지속 부하 최종 결과
max-num-seqs=4를 적용한 같은 Pod에서 concurrency를 하나씩 올리며 실행했다.
run_vllm_case 1 90max-num-seqs 4, concurrency 1 지속 부하와 metric
# run_vllm_case 1 90 requests=54 http_200=54 avg=1.660883s max=2.819826s metric_started_epoch=1784646963 metric_finished_epoch=1784647053 timing_file=/tmp/vllm-seqs-4-c1-timing.log { "metric": "running 최고값", "series": [ { "labels": { "__name__": "vllm:num_requests_running", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.219:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-5568b65ddb-czmhh", "service": "gemma4-vllm" }, "max": 1 } ] } { "metric": "waiting 최고값", "series": [ { "labels": { "__name__": "vllm:num_requests_waiting", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.219:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-5568b65ddb-czmhh", "service": "gemma4-vllm" }, "max": 0 } ] } { "metric": "KV cache 최고값", "series": [ { "labels": { "__name__": "vllm:kv_cache_usage_perc", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.219:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-5568b65ddb-czmhh", "service": "gemma4-vllm" }, "max": 0.038091115680121246 } ] } { "metric": "preemption 증가량", "series": [ { "labels": { "__name__": "vllm:num_preemptions_total", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.219:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-5568b65ddb-czmhh", "service": "gemma4-vllm" }, "first": 0, "last": 0, "delta": 0 } ] } { "metric": "GPU memory 최고값 MiB", "series": [ { "labels": { "UUID": "GPU-3652ba18-b78d-828d-47ce-34ace2c0632d", "__name__": "DCGM_FI_DEV_FB_USED", "container": "exporter", "device": "nvidia0", "endpoint": "metrics", "exported_container": "vllm", "exported_namespace": "gemmaforge", "exported_pod": "gemma4-12b-5568b65ddb-czmhh", "gpu": "0", "hostname": "momindesktop", "instance": "10.42.0.186:9400", "job": "dcgm-exporter", "modelName": "NVIDIA GeForce RTX 4090", "namespace": "monitoring", "pci_bus_id": "00000000:01:00.0", "pod": "dcgm-exporter-f6wzn", "service": "dcgm-exporter" }, "max": 20579 } ] } { "metric": "GPU utilization 최고값", "series": [ { "labels": { "UUID": "GPU-3652ba18-b78d-828d-47ce-34ace2c0632d", "__name__": "DCGM_FI_DEV_GPU_UTIL", "container": "exporter", "device": "nvidia0", "endpoint": "metrics", "exported_container": "vllm", "exported_namespace": "gemmaforge", "exported_pod": "gemma4-12b-5568b65ddb-czmhh", "gpu": "0", "hostname": "momindesktop", "instance": "10.42.0.186:9400", "job": "dcgm-exporter", "modelName": "NVIDIA GeForce RTX 4090", "namespace": "monitoring", "pci_bus_id": "00000000:01:00.0", "pod": "dcgm-exporter-f6wzn", "service": "dcgm-exporter" }, "max": 87 } ] } { "metric": "running 현재값", "value": "0" } { "metric": "waiting 현재값", "value": "0" }
run_vllm_case 2 90max-num-seqs 4, concurrency 2 지속 부하와 metric
# run_vllm_case 2 90 requests=104 http_200=104 avg=1.740539s max=2.697617s metric_started_epoch=1784647073 metric_finished_epoch=1784647164 timing_file=/tmp/vllm-seqs-4-c2-timing.log { "metric": "running 최고값", "series": [ { "labels": { "__name__": "vllm:num_requests_running", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.219:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-5568b65ddb-czmhh", "service": "gemma4-vllm" }, "max": 2 } ] } { "metric": "waiting 최고값", "series": [ { "labels": { "__name__": "vllm:num_requests_waiting", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.219:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-5568b65ddb-czmhh", "service": "gemma4-vllm" }, "max": 0 } ] } { "metric": "KV cache 최고값", "series": [ { "labels": { "__name__": "vllm:kv_cache_usage_perc", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.219:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-5568b65ddb-czmhh", "service": "gemma4-vllm" }, "max": 0.04198679796558813 } ] } { "metric": "preemption 증가량", "series": [ { "labels": { "__name__": "vllm:num_preemptions_total", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.219:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-5568b65ddb-czmhh", "service": "gemma4-vllm" }, "first": 0, "last": 0, "delta": 0 } ] } { "metric": "GPU memory 최고값 MiB", "series": [ { "labels": { "UUID": "GPU-3652ba18-b78d-828d-47ce-34ace2c0632d", "__name__": "DCGM_FI_DEV_FB_USED", "container": "exporter", "device": "nvidia0", "endpoint": "metrics", "exported_container": "vllm", "exported_namespace": "gemmaforge", "exported_pod": "gemma4-12b-5568b65ddb-czmhh", "gpu": "0", "hostname": "momindesktop", "instance": "10.42.0.186:9400", "job": "dcgm-exporter", "modelName": "NVIDIA GeForce RTX 4090", "namespace": "monitoring", "pci_bus_id": "00000000:01:00.0", "pod": "dcgm-exporter-f6wzn", "service": "dcgm-exporter" }, "max": 20579 } ] } { "metric": "GPU utilization 최고값", "series": [ { "labels": { "UUID": "GPU-3652ba18-b78d-828d-47ce-34ace2c0632d", "__name__": "DCGM_FI_DEV_GPU_UTIL", "container": "exporter", "device": "nvidia0", "endpoint": "metrics", "exported_container": "vllm", "exported_namespace": "gemmaforge", "exported_pod": "gemma4-12b-5568b65ddb-czmhh", "gpu": "0", "hostname": "momindesktop", "instance": "10.42.0.186:9400", "job": "dcgm-exporter", "modelName": "NVIDIA GeForce RTX 4090", "namespace": "monitoring", "pci_bus_id": "00000000:01:00.0", "pod": "dcgm-exporter-f6wzn", "service": "dcgm-exporter" }, "max": 85 } ] } { "metric": "running 현재값", "value": "0" } { "metric": "waiting 현재값", "value": "0" } NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES gemma4-12b-5568b65ddb-czmhh 1/1 Running 0 11m 10.42.0.219 momindesktop <none> <none>
run_vllm_case 4 90max-num-seqs 4, concurrency 4 지속 부하와 metric
# run_vllm_case 4 90 requests=200 http_200=200 avg=1.799211s max=2.035951s metric_started_epoch=1784647184 metric_finished_epoch=1784647275 timing_file=/tmp/vllm-seqs-4-c4-timing.log { "metric": "running 최고값", "series": [ { "labels": { "__name__": "vllm:num_requests_running", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.219:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-5568b65ddb-czmhh", "service": "gemma4-vllm" }, "max": 4 } ] } { "metric": "waiting 최고값", "series": [ { "labels": { "__name__": "vllm:num_requests_waiting", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.219:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-5568b65ddb-czmhh", "service": "gemma4-vllm" }, "max": 0 } ] } { "metric": "KV cache 최고값", "series": [ { "labels": { "__name__": "vllm:kv_cache_usage_perc", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.219:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-5568b65ddb-czmhh", "service": "gemma4-vllm" }, "max": 0.04837138837788113 } ] } { "metric": "preemption 증가량", "series": [ { "labels": { "__name__": "vllm:num_preemptions_total", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.219:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-5568b65ddb-czmhh", "service": "gemma4-vllm" }, "first": 0, "last": 0, "delta": 0 } ] } { "metric": "GPU memory 최고값 MiB", "series": [ { "labels": { "UUID": "GPU-3652ba18-b78d-828d-47ce-34ace2c0632d", "__name__": "DCGM_FI_DEV_FB_USED", "container": "exporter", "device": "nvidia0", "endpoint": "metrics", "exported_container": "vllm", "exported_namespace": "gemmaforge", "exported_pod": "gemma4-12b-5568b65ddb-czmhh", "gpu": "0", "hostname": "momindesktop", "instance": "10.42.0.186:9400", "job": "dcgm-exporter", "modelName": "NVIDIA GeForce RTX 4090", "namespace": "monitoring", "pci_bus_id": "00000000:01:00.0", "pod": "dcgm-exporter-f6wzn", "service": "dcgm-exporter" }, "max": 20650 } ] } { "metric": "GPU utilization 최고값", "series": [ { "labels": { "UUID": "GPU-3652ba18-b78d-828d-47ce-34ace2c0632d", "__name__": "DCGM_FI_DEV_GPU_UTIL", "container": "exporter", "device": "nvidia0", "endpoint": "metrics", "exported_container": "vllm", "exported_namespace": "gemmaforge", "exported_pod": "gemma4-12b-5568b65ddb-czmhh", "gpu": "0", "hostname": "momindesktop", "instance": "10.42.0.186:9400", "job": "dcgm-exporter", "modelName": "NVIDIA GeForce RTX 4090", "namespace": "monitoring", "pci_bus_id": "00000000:01:00.0", "pod": "dcgm-exporter-f6wzn", "service": "dcgm-exporter" }, "max": 86 } ] } { "metric": "running 현재값", "value": "0" } { "metric": "waiting 현재값", "value": "0" } NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES gemma4-12b-5568b65ddb-czmhh 1/1 Running 0 12m 10.42.0.219 momindesktop <none> <none>
run_vllm_case 8 90max-num-seqs 4, concurrency 8 지속 부하와 metric
# run_vllm_case 8 90 requests=200 http_200=200 avg=2.714282s max=3.802103s metric_started_epoch=1784647295 metric_finished_epoch=1784647385 timing_file=/tmp/vllm-seqs-4-c8-timing.log { "metric": "running 최고값", "series": [ { "labels": { "__name__": "vllm:num_requests_running", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.219:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-5568b65ddb-czmhh", "service": "gemma4-vllm" }, "max": 4 } ] } { "metric": "waiting 최고값", "series": [ { "labels": { "__name__": "vllm:num_requests_waiting", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.219:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-5568b65ddb-czmhh", "service": "gemma4-vllm" }, "max": 4 } ] } { "metric": "KV cache 최고값", "series": [ { "labels": { "__name__": "vllm:kv_cache_usage_perc", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.219:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-5568b65ddb-czmhh", "service": "gemma4-vllm" }, "max": 0.053673844821989 } ] } { "metric": "preemption 증가량", "series": [ { "labels": { "__name__": "vllm:num_preemptions_total", "container": "vllm", "endpoint": "http", "engine": "0", "instance": "10.42.0.219:8000", "job": "gemma4-vllm", "model_name": "gemma-4-12b-q4", "namespace": "gemmaforge", "pod": "gemma4-12b-5568b65ddb-czmhh", "service": "gemma4-vllm" }, "first": 0, "last": 0, "delta": 0 } ] } { "metric": "GPU memory 최고값 MiB", "series": [ { "labels": { "UUID": "GPU-3652ba18-b78d-828d-47ce-34ace2c0632d", "__name__": "DCGM_FI_DEV_FB_USED", "container": "exporter", "device": "nvidia0", "endpoint": "metrics", "exported_container": "vllm", "exported_namespace": "gemmaforge", "exported_pod": "gemma4-12b-5568b65ddb-czmhh", "gpu": "0", "hostname": "momindesktop", "instance": "10.42.0.186:9400", "job": "dcgm-exporter", "modelName": "NVIDIA GeForce RTX 4090", "namespace": "monitoring", "pci_bus_id": "00000000:01:00.0", "pod": "dcgm-exporter-f6wzn", "service": "dcgm-exporter" }, "max": 20646 } ] } { "metric": "GPU utilization 최고값", "series": [ { "labels": { "UUID": "GPU-3652ba18-b78d-828d-47ce-34ace2c0632d", "__name__": "DCGM_FI_DEV_GPU_UTIL", "container": "exporter", "device": "nvidia0", "endpoint": "metrics", "exported_container": "vllm", "exported_namespace": "gemmaforge", "exported_pod": "gemma4-12b-5568b65ddb-czmhh", "gpu": "0", "hostname": "momindesktop", "instance": "10.42.0.186:9400", "job": "dcgm-exporter", "modelName": "NVIDIA GeForce RTX 4090", "namespace": "monitoring", "pci_bus_id": "00000000:01:00.0", "pod": "dcgm-exporter-f6wzn", "service": "dcgm-exporter" }, "max": 85 } ] } { "metric": "running 현재값", "value": "0" } { "metric": "waiting 현재값", "value": "0" } NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES gemma4-12b-5568b65ddb-czmhh 1/1 Running 0 14m 10.42.0.219 momindesktop <none> <none>
| concurrency | 요청 성공 | 측정 구간 | 처리량 | 평균 요청 시간 | 최대 요청 시간 | running 최고 | waiting 최고 | KV cache 최고 | preemption | GPU memory 최고 | GPU util 최고 |
|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 54/54 | 90초 | 0.600 req/s | 1.660883초 | 2.819826초 | 1 | 0 | 0.038091 | 0 | 20579 MiB | 87% |
2 | 104/104 | 91초 | 1.143 req/s | 1.740539초 | 2.697617초 | 2 | 0 | 0.041987 | 0 | 20579 MiB | 85% |
4 | 200/200 | 91초 | 2.198 req/s | 1.799211초 | 2.035951초 | 4 | 0 | 0.048371 | 0 | 20650 MiB | 86% |
8 | 200/200 | 90초 | 2.222 req/s | 2.714282초 | 3.802103초 | 4 | 4 | 0.053674 | 0 | 20646 MiB | 85% |
concurrency 1 → 2 → 4에서 running 최고값도 1 → 2 → 4로 증가했고, 처리량은 0.600 → 1.143 → 2.198 req/s로 확장됐다. 이 범위에서는 waiting이 모두 0이고 평균 요청 시간도 1.66~1.80초에 머물렀다.
concurrency 8에서는 running 4, waiting 4가 되어 scheduler 상한이 명확히 드러났다. 처리량은 2.222 req/s로 concurrency 4와 거의 같고 평균 요청 시간은 2.71초로 증가했다. KV cache 최고값은 5.37%, GPU memory는 최고 20650 MiB, GPU utilization은 최고 87%였으며 preemption과 restart는 없었다.
max-num-seqs 후보 비교와 선택
| max-num-seqs | 처리량 상한 | 상한까지 waiting | 초과 부하의 waiting | KV cache 최고 | GPU memory 범위 | preemption·restart | 판정 |
|---|---|---|---|---|---|---|---|
1 | 약 0.59 req/s | concurrency 1까지 0 | c2=1, c4=3, c8=7 | 0.037852 | 20531~20589 MiB | 0·0 | 단일 요청 전용에 가까워 동시 요청 처리량을 확장하지 못함 |
2 | 약 1.15 req/s | concurrency 2까지 0 | c4=2, c8=6 | 0.043220 | 20650~20688 MiB | 0·0 | 두 요청까지 안정적으로 병렬 처리 |
4 | 약 2.22 req/s | concurrency 4까지 0 | c8=4 | 0.053674 | 20579~20650 MiB | 0·0 | 네 요청까지 latency 증가 없이 처리량이 가장 크게 확장됨 |
세 후보 모두 HTTP 오류, preemption, restart 없이 안정적이었다. 처리량은 대략 max-num-seqs에 비례해 0.59 → 1.15 → 2.22 req/s로 증가했고, 각 후보에서 concurrency가 scheduler 상한을 넘을 때만 waiting이 생겼다. 이는 --max-num-seqs가 실제 동시 실행 slot 상한으로 동작했다는 직접적인 결과다.
GPU memory 차이는 전체 후보에서 약 157 MiB 이내이고 GPU utilization도 83~87% 범위라, 이번 짧은 출력 workload에서는 seqs 증가에 따른 VRAM·GPU 부담보다 처리량 이점이 훨씬 컸다. 따라서 RTX 4090 단일 GPU의 현재 workload에서는 max-num-seqs=4를 scheduler 기본값으로 선택한다. 다만 concurrency 8을 기본 운영값으로 삼는다는 뜻은 아니며, queue가 생기지 않는 운영 동시성 기준은 4다.
8. 최종 후보 입력 길이별 재측정
최종 후보 상태에서 기존 context payload의 실제 token 수를 다시 확인했다.
for repeat in 64 256 512; do
jq '{model, messages}' "/tmp/gemmaforge-vllm-context-${repeat}.json" \
| curl -fsS http://127.0.0.1:8100/tokenize \
-H 'Content-Type: application/json' \
-d @- \
| jq --arg repeat "$repeat" \
'{repeat: $repeat, count, max_model_len}'
done최종 후보 payload token 수
{ "repeat": "64", "count": 398, "max_model_len": 8192 } { "repeat": "256", "count": 1550, "max_model_len": 8192 } { "repeat": "512", "count": 3086, "max_model_len": 8192 }
각 입력 길이에서 동일 요청을 5회 실행했다.
for repeat in 64 256 512; do
for run in $(seq 1 5); do
curl -sS \
-o "/tmp/gemmaforge-vllm-final-${repeat}-${run}.json" \
-w "repeat=${repeat} run=${run} http=%{http_code} total=%{time_total}s\n" \
http://127.0.0.1:8100/v1/chat/completions \
-H 'Content-Type: application/json' \
-d @"/tmp/gemmaforge-vllm-context-${repeat}.json"
done
done최종 후보 입력 길이별 API 15회 결과
repeat=64 run=1 http=200 total=2.038277s repeat=64 run=2 http=200 total=1.671345s repeat=64 run=3 http=200 total=1.649733s repeat=64 run=4 http=200 total=1.673384s repeat=64 run=5 http=200 total=1.668339s repeat=256 run=1 http=200 total=1.765761s repeat=256 run=2 http=200 total=1.795131s repeat=256 run=3 http=200 total=1.739294s repeat=256 run=4 http=200 total=1.686424s repeat=256 run=5 http=200 total=1.693260s repeat=512 run=1 http=200 total=1.927471s repeat=512 run=2 http=200 total=1.734874s repeat=512 run=3 http=200 total=1.749710s repeat=512 run=4 http=200 total=1.786696s repeat=512 run=5 http=200 total=1.790230s
| 반복 | prompt token | 성공 | run 1 | run 2~5 평균 | 판정 |
|---|---|---|---|---|---|
64 | 398 | 5/5 | 2.038277초 | 1.665700초 | 정상 |
256 | 1550 | 5/5 | 1.765761초 | 1.728527초 | 정상 |
512 | 3086 | 5/5 | 1.927471초 | 1.765378초 | 정상 |
세 payload는 모두 context 8192 안에 있고 총 15/15 요청이 HTTP 200으로 성공했다. prompt가 398 → 3086 token으로 약 7.8배 증가했지만 warm 평균은 1.665700 → 1.765378초로 약 6.0% 증가하는 데 그쳤다. 이번 128-token 출력 workload에서는 입력 길이 증가가 latency에 미치는 영향이 비교적 작았다.
run 1이 항상 가장 느린 것은 아니므로 단일 cold 요청만으로 후보를 비교하지 않는다. 최종 상태의 기능 검증은 통과했지만, GPU memory utilization 최적값은 후보별 동일 구간 metric 비교가 없으므로 별도 확정하지 않는다.
9. baseline 원복과 확인
전체 실험 후 vLLM args와 model cache PVC를 Runbook의 baseline으로 원복했다.
sudo k3s kubectl -n gemmaforge patch \
deployment/gemma4-12b \
--type=strategic \
-p '{
"spec": {
"template": {
"spec": {
"containers": [
{
"name": "vllm",
"args": [
"google/gemma-4-12B-it-qat-w4a16-ct",
"--served-model-name",
"gemma-4-12b-q4",
"--dtype",
"auto",
"--max-model-len",
"8192",
"--max-num-seqs",
"4",
"--gpu-memory-utilization",
"0.80",
"--limit-mm-per-prompt",
"{\"image\": 0, \"audio\": 0}"
]
}
],
"volumes": [
{
"name": "model-cache",
"persistentVolumeClaim": {
"claimName": "gemma4-model-cache"
}
}
]
}
}
}
}'
sudo k3s kubectl -n gemmaforge rollout status \
deployment/gemma4-12b \
--timeout=40mvLLM baseline patch와 rollout 완료
deployment.apps/gemma4-12b patched Waiting for deployment "gemma4-12b" rollout to finish: 0 out of 1 new replicas have been updated... Waiting for deployment "gemma4-12b" rollout to finish: 0 out of 1 new replicas have been updated... Waiting for deployment "gemma4-12b" rollout to finish: 0 out of 1 new replicas have been updated... Waiting for deployment "gemma4-12b" rollout to finish: 0 of 1 updated replicas are available... deployment "gemma4-12b" successfully rolled out
원복된 API의 health와 model identity를 확인했다.
curl -fsS http://127.0.0.1:8100/health
curl -fsS http://127.0.0.1:8100/v1/models | jq원복 후 vLLM health와 model 조회
/health는 본문 없이 성공했다.curl -f가 오류 없이 다음 명령으로 진행했으므로 HTTP 성공으로 판정한다.{ "object": "list", "data": [ { "id": "gemma-4-12b-q4", "object": "model", "created": 1784648559, "owned_by": "vllm", "root": "google/gemma-4-12B-it-qat-w4a16-ct", "parent": null, "max_model_len": 8192, "permission": [ { "id": "modelperm-a736d7e732fe07db", "object": "model_permission", "created": 1784648559, "allow_create_engine": false, "allow_sampling": true, "allow_logprobs": true, "allow_search_indices": false, "allow_view": true, "allow_fine_tuning": false, "organization": "*", "group": null, "is_blocking": false } ] } ] }
| 항목 | 원복 값 | 확인 결과 |
|---|---|---|
| checkpoint | google/gemma-4-12B-it-qat-w4a16-ct | /v1/models의 root 일치 |
| served model | gemma-4-12b-q4 | /v1/models의 id 일치 |
| max model length | 8192 | /v1/models에서 확인 |
| max sequences | 4 | baseline patch 적용 후 rollout 성공 |
| GPU memory utilization | 0.80 | baseline patch 적용 후 rollout 성공 |
| model cache PVC | gemma4-model-cache | baseline patch 적용 후 rollout 성공 |
| health | HTTP 성공 | curl -fsS 오류 없음 |
원복 rollout이 성공했고 API health, model identity, context 상한까지 정상으로 확인됐다. 따라서 Step 7 실험은 원래 운영 baseline으로 돌아온 상태에서 종료했다.
현재까지의 결론
- vLLM을 Step 7 최적화 engine으로 선택했다.
- 빈 PVC에서 cold start는
270초, 같은 PVC를 재사용한 warm start는171초였다. - Hugging Face cache
9.6G와 정확한 checkpoint revision이 cold PVC에 생성됐다. - GPU memory utilization
0.70,0.75,0.80,0.85는 단일 요청 5회를 모두 처리했다. - 현재 API 시간만 보면
0.85의 warm 평균이 가장 짧지만 metric 검증 전에는 최적값으로 확정하지 않는다. - context
4096에서는 prompt4622,6158token 요청이 HTTP400으로 거부되어 설정 상한이 실제로 적용됨을 확인했다. - context
8192와16384는 준비한 최대6158token prompt와128output token 요청을 모두 처리했다. - 준비한 payload가 모두
8192안에 들어가므로 이번 결과만으로16384의 추가 효용이나 실제 상한을 판단할 수 없다. - context는
8192로 복구했고/v1/models에서max_model_len=8192를 확인했다. max-num-seqs1,2,4는 순차 단일 요청에서 모두5/5성공했고, 지속 부하 metric까지 완료했다.- 동시 요청에서도 세 후보가 모두 HTTP
200, restart0으로 끝났다. concurrency8의 마지막 완료 시간은13.81초,7.05초,3.95초로 scheduler 용량 증가가 확인됐다. - 초기 단기 측정의 Prometheus metric은 이전 Pod 시계열 혼입과 sampling 누락이 있어 후보 판정에서 제외한다.
- 보완 측정에서
max-num-seqs=1은 running 최고1, waiting 최고concurrency - 1, 처리량 약0.59 req/s로 확인됐다. - seqs 1은 restart·preemption 없이 안정적이지만 concurrency 증가를 처리량으로 전환하지 못해 대기시간과 tail latency가 증가했다.
- seqs 2는 concurrency
2까지 waiting 없이 약1.15 req/s, seqs 4는 concurrency4까지 waiting 없이 약2.22 req/s를 처리했다. - 세 후보 모두 상한을 넘는 동시 요청만 queue로 보냈고 preemption·restart는 없었다.
- VRAM 차이는 후보 전체에서 약
157 MiB이내였으므로 처리량과 queue 결과를 기준으로max-num-seqs=4, 운영 concurrency4를 scheduler 기본값으로 선택했다. - 최종 후보에서 prompt
398,1550,3086token 요청을 각 5회 실행해 총15/15HTTP200을 확인했다. - 전체 실험 후 context
8192, max-num-seqs4, GPU memory utilization0.80, 원래 model cache PVC의 baseline으로 원복했다.