26O05c4

QK·6일 전

네. 지금까지 만든 B300 acceptance 체계를 버리는 것보다 “하드웨어/플랫폼 검증”과 “업무 모델 운영 적합성 검증”을 분리해서 한 단계 추가하는 게 가장 좋습니다.

먼저 한 가지 중요한 점은, 현재 목록 중 Qwen3.6-35B-A3B는 vLLM recipe가 있고, 35B total / 3B active MoE, 262K context로 정의되어 있습니다. GitHub gpt-oss-20b도 21B total / 3.6B active 모델이라 상대적으로 단일 B300 노드에서 운영 검증하기 쉬운 쪽입니다. OpenAI Developers 반면 GLM-5.3-Flash는 약 320B total / 18B active이며 FP8 checkpoint만 약 306 GiB이고, vLLM recipe도 advanced 모델로 분류합니다. vLLM Recipes DeepSeek-V4.1-Flash 역시 552B MoE이며 input 8B/output 16B active 구조입니다. DeepSeek 따라서 “70B가 잘 돌았으니 나머지도 괜찮다”는 방식으로 승인하면 안 됩니다.

제가 권하는 구조

기존 Stage1~6 위에 Stage7: Workload Model Qualification을 추가하는 게 깔끔합니다.

B300 Platform Acceptance
 Stage1 HW
 Stage2 Network / Storage
 Stage3a vLLM reference model
 Stage6 K8s
        │
        ▼
 ── Platform Baseline PASS ──
        │
        ▼
Stage7 Model Qualification
 │
 ├─ 7A Night Batch / Ontology
 │    ├─ GLM-5.3
 │    ├─ Qwen3.6-35B-A3B
 │    └─ gpt-oss-20b
 │
 ├─ 7B Online Discovery
 │    ├─ DeepSeek-V4.1-Flash
 │    └─ GLM-5.3-Flash
 │
 └─ 7C Document Pipeline
      ├─ PaddleOCR-VL-1.6
      ├─ docling-layout-heron
      ├─ docling-models
      └─ DocumentFigureClassifier

Stage7에서는 모델마다 Compatibility → Functional → Quality → Performance → Capacity → Soak → K8s 순으로 통과시키는 것을 권합니다.

1. 야간 Ontology 모델

여기는 TTFT를 지나치게 중요하게 볼 필요가 없습니다. 대신 한밤 동안 처리할 수 있는 총 작업량과 긴 context에서의 안정성이 중요합니다.

예를 들어 workload를 실제 ontology 작업에서 추출해:

Ontology-small
 input     4K
 output    1K
 C         4/8/16

Ontology-medium
 input     16K
 output    2K
 C         4/8

Ontology-long
 input     64K
 output    4K
 C         1/2/4

Ontology-real
 실제 ontology prompt/document distribution

으로 만드는 게 좋습니다.

여기서 주요 KPI는 jobs/hour, output tok/s, 평균 GPU utilization, HBM, KV 사용량, 오류율, timeout, 8시간 처리량입니다. TTFT/P99 ITL은 기록하되 online 서비스처럼 hard gate로 둘 필요는 없습니다.

특히 Qwen3.6-35B-A3B와 gpt-oss-20b는 작은 active parameter MoE이므로 GPU utilization과 batch/concurrency scaling을 잘 봐야 합니다. 단순 C=1 TPS만 보면 실제 야간 batch 처리 능력을 과소평가할 수 있습니다. Qwen 쪽은 공식 vLLM recipe에서 MTP speculative decoding도 지원하므로 기본 상태와 MTP ON을 별도 비교할 가치가 있습니다. GitHub

GLM-5.3은 정확한 배포 checkpoint와 엔진 지원을 먼저 고정한 뒤 같은 절차를 적용하는 편이 좋겠습니다.

2. 주간 Discovery 모델

여기는 반대로 online SLO 중심이어야 합니다.

DeepSeek-V4.1-Flash와 GLM-5.3-Flash에는 다음처럼 별도 profile을 권합니다.

Discovery-short
 input       2K
 output      256
 C           1/8/16/32/64

Discovery-medium
 input       8K
 output      512
 C           1/8/16/32

Discovery-long
 input       32K
 output      512
 C           1/4/8/16

Discovery-real
 실제 query/context 길이 분포

여기서는 우선순위가 달라집니다.

TTFT P50/P95/P99 → ITL → 성공률 → request/s → output tok/s → max sustainable concurrency 순으로 보는 것을 권합니다.

그리고 기존 Stage3a6에서 만든 saturation 방식이 아주 잘 맞습니다.

C=1
 ↓
C=8
 ↓
C=16
 ↓
C=32
 ↓
C=64
 ↓
C=128

각 단계:
 TTFT P99
 ITL
 RPS
 TPS
 error %
 HBM
 KV usage
 GPU util

어느 지점부터 TTFT가 급격하게 튀거나 오류가 발생하는지 찾아 운영 concurrency limit을 정하면 됩니다.

DeepSeek-V4.1-Flash는 특히 흥미롭습니다. 공식 설명상 552B MoE이지만 input/output active parameter가 각각 8B/16B이고, 이전 세대 대비 KV-cache 요구량도 크게 줄였다고 밝히고 있습니다. DeepSeek 따라서 이 모델에서는 Stage3a10에서 만든 KV tier 실험을 재사용해서

GPU only
   vs
local NVMe offload
   vs
remote MinIO/MemKV

를 비교할 가치가 큽니다.

다만 현재 사용하는 vLLM 0.29.0에서 이 모델의 self-hosting 경로가 실제 B300에서 지원되는지는 별도 compatibility gate를 먼저 통과시켜야 합니다. DeepSeek 공식 발표도 V4.1-Flash에 대해 open-source inference 지원 확대를 진행한다고 표현하고 있습니다. DeepSeek

GLM-5.3-Flash도 비슷합니다. vLLM recipe에는 min_vllm_version: 0.29.0이지만 동시에 NVIDIA 쪽은 nightly/특정 이미지 사용 조건이 표시되어 있습니다. 따라서 “0.29.0 이상”만 검사하면 부족하고 실제 image/build commit까지 qualification manifest에 고정해야 합니다. GitHub

즉 이 모델들은:

model version
checkpoint revision
vLLM version
vLLM build/image digest
Transformers version
FlashInfer version
quantization
TP/EP
KV dtype
reasoning parser
tool parser

까지 하나의 승인 단위로 보는 게 좋습니다.

3. PaddleOCR / Docling은 완전히 다른 테스트가 필요합니다

이쪽은 Stage3a의 TTFT / ITL / token TPS를 그대로 적용하면 별 의미가 없습니다.

PaddleOCR-VL-1.6은 0.9B VLM이고 공식 문서상 문서 parsing, table/formula/text, scan/warp/skew/screen-photo/illumination 같은 실제 문서 열화 상황을 대상으로 합니다. PaddleOCR

Docling도 하나의 모델이 아니라 pipeline입니다. 현재 catalog를 보면 docling-layout-heron은 layout object detection, DocumentFigureClassifier-v2.5는 picture classification이고 서로 역할이 다릅니다. Docling Project

따라서 Stage7C는 페이지 단위 pipeline benchmark로 만드는 것이 좋습니다.

PDF/Image
   │
   ├─ OCR
   │    PaddleOCR-VL
   │
   ├─ Layout
   │    docling-layout-heron
   │
   ├─ Figure classification
   │    DocumentFigureClassifier
   │
   └─ structured extraction
        ↓
     downstream context

KPI도 다음처럼 바뀝니다.

Accuracy
 ├─ text accuracy
 ├─ table accuracy
 ├─ reading order
 ├─ layout detection
 ├─ figure classification
 └─ downstream extraction accuracy

Performance
 ├─ sec/page
 ├─ pages/min
 ├─ docs/hour
 ├─ GPU utilization
 ├─ peak HBM
 ├─ CPU RAM
 └─ concurrency scaling

Robustness
 ├─ normal PDF
 ├─ scanned
 ├─ rotated/skewed
 ├─ low resolution
 ├─ tables
 ├─ figures/charts
 ├─ multi-column
 └─ mixed Korean/English/numbers

특히 실제 회사 문서 100~500개 정도의 golden corpus를 만드는 게 중요합니다. 공개 benchmark 점수보다 이 데이터가 최종 gate에 훨씬 유용합니다.

Docling은 현재 layout model의 기본값이 Heron이고 batch size가 처리량과 메모리에 영향을 주는 옵션으로 제공됩니다. Docling Project 따라서 batch=1/2/4/8/16 정도로 pages/min과 VRAM을 측정하는 것이 좋습니다.

또 Heron에는 실제 업무에서 주의해서 볼 만한 사례가 있습니다. 2026년 보고된 Docling issue에는 grid-line form을 Table로 잘못 분류해 후속 table reconstruction이 깨지는 사례가 있습니다. 이는 프로젝트의 공식 성능 보증은 아니고 사용자 보고 사례이지만, 회사 양식·신청서·표 형태 form을 golden corpus에 반드시 넣어야 할 이유는 됩니다. GitHub

최종적으로는 모델마다 “Qualification Card”를 하나씩 만들면 좋습니다

예를 들면:

Model: DeepSeek-V4.1-Flash
Purpose: Discovery Online

Software
  checkpoint revision
  vLLM build
  CUDA
  driver
  quantization

Deployment
  GPU: B300 x8
  TP:
  EP:
  KV dtype:
  max-model-len:

Functional
  load             PASS
  generation       PASS
  streaming        PASS
  reasoning        PASS
  tool calling     PASS/N/A
  multimodal       PASS/N/A

Quality
  golden dataset   PASS
  regression       PASS

Performance
  TTFT P50
  TTFT P99
  ITL P99
  output TPS
  max concurrency

Capacity
  GPU-only
  local NVMe
  remote MemKV

Stability
  4h smoke
  12h soak

K8s
  startup
  readiness
  warm-up
  restart
  performance delta

Verdict
  Qualified / Conditional / Not Qualified

그리고 Llama-70B를 모든 모델의 성능 기준으로 삼지는 않는 것을 권합니다. Llama-70B는 앞으로도 B300 platform reference baseline으로 유지하고, 업무 모델은 각각 자기 baseline을 만든 뒤 버전 업그레이드 때 그 baseline과 regression 비교하는 구조가 더 정확합니다.

결국 전체 구조는:

Llama-70B
   ↓
B300 HW / vLLM / K8s 플랫폼 정상성 확인
   ↓
────────────────────────────
업무 모델별 Qualification
   ↓
Compatibility
   ↓
Functional
   ↓
Real-workload Quality
   ↓
Performance / Capacity
   ↓
Soak
   ↓
K8s
   ↓
Golden Baseline 생성
   ↓
운영
   ↓
모델/엔진 업그레이드 시 Regression Gate

가 가장 안정적입니다.

특히 지금까지 만든 스크립트 자산을 보면 Stage7을 새로 만드는 비용도 크지 않습니다. stage3a6 saturation, stage3a10 KV tier, warm-up helper, Stage6k K8s 재검증, regression gate를 대부분 재사용하고, 문서 계열만 새로운 page/document benchmark harness를 만들면 됩니다.

다음 작업으로는 Stage7 Model Qualification을 실제 v8 패키지에 추가하는 것이 좋겠습니다. stage7a_llm_qualification.sh, stage7b_online_slo.sh, stage7c_document_pipeline.py, model_qualification.yaml, stage7_summary_report.py 정도로 구성하면 지금 말씀하신 9개 모델을 같은 체계에서 관리할 수 있습니다.


완료했습니다. 기존 v8 전체를 유지하면서 Stage7 Model Qualification을 추가한 v9로 만들었습니다.

b300_validation_common_env_v9.tar.gz전체 v9 패키지

핵심 파일은 11_STAGE7_MODEL_QUALIFICATION_GUIDE.mdStage7 가이드, stage7_model_qualification.yaml모델 Qualification Manifest, stage7_qualification_card_template.yamlQualification Card 템플릿입니다.

구조는 다음과 같습니다.

Stage7 Model Qualification
│
├─ 7A Night Batch / Ontology
│   ├─ GLM-5.3
│   ├─ Qwen3.6-35B-A3B
│   └─ gpt-oss-20b
│
├─ 7B Online Discovery
│   ├─ DeepSeek-V4.1-Flash
│   └─ GLM-5.3-Flash
│
├─ 7C Document Pipeline
│   ├─ PaddleOCR-VL-1.6
│   ├─ docling-layout-heron
│   ├─ docling-models
│   └─ DocumentFigureClassifier
│
├─ 7D K8s Revalidation
│
└─ Qualification Summary / Card

LLM 쪽은 기존 Stage3a의 원칙을 그대로 재사용합니다.

Compatibility
    ↓
Server Ready
    ↓
Engine Warm-up
    ↓
Workload-matched Warm-up
    ↓
Concurrency Sweep
    ↓
Real-workload Quality
    ↓
4h / 8h Soak
    ↓
K8s Revalidation
    ↓
Golden Baseline

실행 파일은 stage7a_night_batch_qualification.shStage7A, stage7b_online_slo_qualification.shStage7B, stage7_llm_qualification.py공통 LLM runner, stage7c_document_pipeline.pyStage7C document harness, stage7d_k8s_model_revalidation.shStage7D K8s revalidation, stage7_summary_report.pyStage7 summary입니다.

특히 compatibility gate를 넣었습니다. 현재 프로젝트 표준 vLLM 0.29.0에서 GLM-5.3, Qwen3.6-35B-A3B, gpt-oss-20b는 Stage7 대상으로 진행할 수 있게 했습니다. Qwen3.6 recipe는 vLLM 0.17.0+를 명시하고 있고 GitHub, gpt-oss 계열은 vLLM recipe상 0.10.1+이며 현재 vLLM에서도 공식 supported model입니다. vLLM Recipes

반면 DeepSeek-V4.1-Flash는 자동 차단했습니다. 현재 공식 recipe가 vLLM 0.30.0+를 요구하기 때문에 0.29.0 환경에서 성능 테스트를 강행하면 안 됩니다. vLLM Recipes --allow-compat-override는 engineering experiment용으로만 두었고 그런 결과는 production qualification으로 인정하지 않도록 문서화했습니다.

GLM-5.3-Flash는 vLLM 0.29.0 조건은 맞지만 NVIDIA 쪽에서 nightly/specific build 조건이 있으므로 CONDITIONAL로 두었습니다. GitHub

Document 계열은 일부러 특정 Docling/PaddleOCR API를 Stage7 core에 박아 넣지 않았습니다. stage7_document_adapter_example.pyadapter skeleton과 stage7_golden_manifest_example.jsonlgolden manifest 예제를 만들었습니다. 실제 production에서 pin한 PaddleOCR/Docling 버전에 adapter만 연결하면 같은 harness로 success rate, sec/page, pages/min, text/layout/table/figure/extraction quality를 수집할 수 있습니다. PaddleOCR 공식 구조도 전체 document parsing service와 내부 VLM service를 구분하고 있으며 Blackwell 전용 배포 지침도 따로 제공합니다. PaddleOCR Docling도 Heron은 layout detection이고 picture classifier 등은 별도 pipeline stage입니다. Docling Project

예를 들어 야간 모델은 이렇게 실행합니다.

source ./validation_env.sh
./00_compatibility_check.sh

./stage7a_night_batch_qualification.sh glm_5_3
./stage7a_night_batch_qualification.sh qwen3_6_35b_a3b
./stage7a_night_batch_qualification.sh gpt_oss_20b

온라인 모델은:

./stage7b_online_slo_qualification.sh glm_5_3_flash

DeepSeek는 별도 vLLM ≥0.30 runtime/image qualification이 먼저입니다.

결과는:

python3 stage7_summary_report.py \
  --root "$STAGE7_ROOT" \
  --out "$STAGE7_ROOT/stage7_summary.md" \
  --json-out "$STAGE7_ROOT/stage7_summary.json"

으로 모읍니다. 여기서 MEASURED는 production PASS가 아니라 측정 데이터가 존재한다는 뜻으로 만들었습니다. 최종 승인은 실제 업무 golden set, performance/capacity, soak, K8s 결과까지 Qualification Card에서 확인한 뒤 내리는 구조입니다.

validation_env.sh와 00_compatibility_check.sh에도 Stage7 모델 경로, manifest, document golden corpus 등을 추가했습니다. 전체 .sh는 bash -n, 전체 .py는 py_compile, manifest YAML parsing, 빈 Stage7 결과에 대한 summary smoke test까지 실제로 다시 통과시켰습니다.

CHANGELOG_v9.mdCHANGELOG v9에도 변경사항을 정리해 두었습니다.


네. 0.29.0 → 0.30.0 업그레이드는 현재 스크립트 구조를 크게 뜯어고칠 수준은 아닙니다. 공식 0.30.0 문서와 v9의 vLLM 사용 지점을 대조해 보면, 걱정하신 두 영역 중에서는 vllm serve/vllm bench serve보다 /metrics와 모델별 추가 옵션을 더 주의해서 보는 게 맞습니다.

결론부터 보면

현재 스크립트에서 많이 쓰는 아래 옵션들은 0.30.0에서도 그대로 유지되는 쪽입니다.

vllm serve <model>
--served-model-name
--tensor-parallel-size
--gpu-memory-utilization
--max-model-len
--max-num-seqs
--enable-chunked-prefill
--no-enable-chunked-prefill
--enable-prefix-caching
--no-enable-prefix-caching
--kv-transfer-config
--host
--port

특히 Stage3a10의 KV offload 구조도 0.30.0 공식 문서에서 OffloadingConnector + kv_role=kv_both + TieringOffloadingSpec + secondary_tiers 형태를 그대로 사용합니다. local FS tier의 root_dir, n_read_threads, n_write_threads도 공식 예제에 그대로 있습니다. vLLM

따라서 Stage3a10을 0.30 때문에 재설계할 필요는 없습니다.


제가 수정하길 권하는 부분은 /metrics 쪽입니다

현재 v9에서 직접 /metrics를 파싱하는 핵심 스크립트는 특히:

stage3a_vllm_benchmark.sh
stage3a3_vllm_mixed_workload.sh
stage3a8_vllm_prefix_cache_ab.sh

입니다.

0.30에서도 중요한 Prometheus metric 이름 자체는 유지됩니다.

vllm:time_to_first_token_seconds
vllm:inter_token_latency_seconds

vllm:kv_cache_usage_perc

vllm:prefix_cache_queries
vllm:prefix_cache_hits

vllm:num_requests_running
vllm:num_requests_waiting

vllm:prompt_tokens_total
vllm:generation_tokens_total

TTFT와 ITL histogram도 여전히 공식 metrics입니다. vLLM

그러므로 현재 TTFT histogram parser가 바로 깨질 가능성은 낮습니다.

다만 metric parser를 정확한 metric 이름 allow-list 방식으로 바꾸는 것을 권합니다.

특히 Stage3a8에는 현재:

grep -i "cache" "${raw}"

같은 방식이 있는데, 0.30부터 KV cache 관련 metric 종류가 더 늘고 있습니다. 예를 들어 KV block residency 관련:

vllm:kv_block_lifetime_seconds
vllm:kv_block_idle_before_evict_seconds
vllm:kv_block_reuse_gap_seconds

도 지원됩니다. vLLM

그래서 앞으로는:

prefix_cache_hits
prefix_cache_queries
kv_cache_usage_perc

vs

kv_block_lifetime
kv_block_idle_before_evict
kv_block_reuse_gap

를 서로 다른 의미로 파싱하는 것이 안전합니다.


오히려 0.30에서 활용하고 싶은 기능도 있습니다

0.30에는 --kv-cache-metrics가 있습니다.

vllm serve ... \
  --kv-cache-metrics \
  --kv-cache-metrics-sample 0.01

이를 켜면 KV block의 lifetime, eviction 전 idle time, reuse gap을 관찰할 수 있습니다. vLLM

이건 Stage3a10 KV tier test에 상당히 유용합니다.

현재는:

none
local_nvme
remote_obj

에 대해 주로

TTFT
RPS
max concurrency
error rate

를 비교하고 있는데, 0.30에서는 추가로:

KV block lifetime
KV block reuse gap
KV block idle-before-eviction
KV cache usage
prefix cache hits / queries

를 기록할 수 있습니다.

그러면 우리가 전에 지적했던

local NVMe / remote MemKV secondary tier가 실제로 사용되고 있는가?

를 훨씬 잘 진단할 수 있습니다.

다만 이 metric들은 sampling 기반이고 --disable-log-stats와 같이 쓰면 안 됩니다. vLLM


0.30에서 Stage7은 오히려 좋아집니다

가장 큰 이유가 DeepSeek-V4.1-Flash입니다.

앞서 Stage7에서 이 모델을 현재 0.29.0 기준으로 BLOCKED_COMPAT 처리했는데, 0.30으로 올리면 이 compatibility blocker를 다시 평가할 수 있습니다.

따라서 manifest에서 단순히:

deepseek_v4_1_flash:
  compatibility: BLOCKED_COMPAT

를 바로 PASS로 바꾸는 게 아니라,

vLLM 0.30 설치
       ↓
model config/tokenizer offline load
       ↓
serve --help/model-specific args 확인
       ↓
1 request smoke
       ↓
streaming smoke
       ↓
8 GPU TP smoke
       ↓
Stage7B benchmark

순으로 CONDITIONAL → QUALIFIABLE로 승격시키는 것을 권합니다.


Stage7에는 0.30의 per-request metrics도 활용할 가치가 있습니다

0.30에는 --enable-per-request-metrics가 있습니다.

서버를:

vllm serve ... \
  --enable-per-request-metrics

로 실행하면 API response에 request 단위로:

time_to_first_token_ms
generation_time_ms
queue_time_ms
mean_itl_ms
tokens_per_second

를 받을 수 있습니다. vLLM

이건 특히 Stage7B online SLO에서 유용합니다.

현재 구조:

client 측 latency
+
vllm bench serve
+
Prometheus /metrics

에

per-request server timing

을 추가할 수 있기 때문입니다.

다만 공식 문서에서도 high concurrency에서는 CPU overhead가 생길 수 있다고 경고하므로, production default ON으로 하지 말고 qualification run 전용으로 켜는 게 좋습니다. vLLM


vllm bench serve 쪽은 변경이 적어 보입니다

현재 우리가 사용하는:

--backend openai-chat
--endpoint /v1/chat/completions
--host
--port
--model
--tokenizer
--dataset-name random
--random-input-len
--random-output-len
--random-range-ratio
--random-prefix-len
--num-prompts
--max-concurrency
--request-rate
--num-warmups
--save-result
--result-dir
--result-filename

계열은 0.30에서도 vllm bench serve가 계속 정식 CLI입니다. 0.30 문서에서도 request-rate, burstiness 등 serving benchmark 기능이 그대로 제공됩니다. vLLM

따라서 stage3a_warmup_lib.sh와 Stage3a 계열 benchmark를 크게 수정할 이유는 없어 보입니다.


실제로는 이렇게 업그레이드하는 것을 권합니다

바로 기존 venv를 0.30으로 덮어쓰지 않는 게 좋습니다.

현재
~/vllm-bench-env-0.29

신규
~/vllm-bench-env-0.30

두 개를 병행해서 동일 B300에서:

                 0.29       0.30
                 ────       ────
compat check      PASS   →   ?
server start      PASS   →   ?
short c1          baseline → compare
short c64         baseline → compare
long c8           baseline → compare
prefix cache      baseline → compare
KV none           baseline → compare
KV local_nvme     baseline → compare
KV remote_obj     baseline → compare

까지만 먼저 돌리는 upgrade qualification이 좋습니다.

Stage1/2/4/5 전체를 다시 돌릴 필요는 없습니다.

우선:

00_compatibility_check
Stage3a short/long
Stage3a8 prefix
Stage3a10 KV
Stage6k K8s
Stage7 model smoke

정도면 충분합니다.

그리고 기존 regression gate가 여기서 아주 유용합니다.


v10으로 고친다면 제가 바꾸고 싶은 부분

코드 변경량은 크지 않습니다.

첫째, validation_env.sh에:

EXPECTED_VLLM_VERSION=0.30.0

을 넣고 compatibility checker가 exact version을 검사하게 합니다.

둘째, 00_compatibility_check.sh에서 현재보다 더 엄격하게:

vllm serve --help
vllm bench serve --help

필수 serve option 존재
필수 bench option 존재
/metrics endpoint
필수 metric family 존재
KV offload Python module import

를 검사합니다.

셋째, /metrics 검증을 별도:

vllm_metrics_compat.py

로 빼서:

REQUIRED
  time_to_first_token
  inter_token_latency
  kv_cache_usage
  prefix_cache_hits
  prefix_cache_queries

OPTIONAL
  kv block lifetime
  idle before eviction
  reuse gap

식으로 버전별 parser를 만들겠습니다.

넷째, Stage3a10 서버에 선택적으로:

--kv-cache-metrics
--kv-cache-metrics-sample 0.01

을 붙여 KV tier가 실제 어떤 cache behavior를 보이는지 evidence를 추가하겠습니다. 0.30 공식 문서에서 이 기능을 명시적으로 지원합니다. vLLM

다섯째, Stage7B에는 선택적으로:

--enable-per-request-metrics

를 붙여 client latency와 server-side timing을 교차 검증하겠습니다. vLLM

판단

그래서 저는 0.30.0으로 올리는 방향 자체는 꽤 괜찮다고 봅니다. 특히 DeepSeek-V4.1-Flash 때문에 Stage7까지 생각하면 0.29에 계속 머무는 것보다 얻는 게 있습니다.

다만 현재 v9를 그대로 0.30에서 돌리는 것보다는 v10에서 “0.30 migration layer”를 한 번 넣는 게 좋습니다. 기존 benchmark CLI는 거의 그대로 유지하고, compatibility checker 강화 + metrics parser 정리 + KV cache metrics + Stage7 per-request metrics를 추가하는 정도면 됩니다.

원하시면 제가 바로 v9 → v10(vLLM 0.30.0 기준)으로 실제 스크립트 전체를 수정하고, VLLM_0.29_TO_0.30_MIGRATION.md까지 포함해서 패키지로 만들어드릴 수 있습니다.


완료했습니다. v9 전체를 기반으로 vLLM 0.30.0 표준의 v10 패키지로 업데이트했습니다.

b300_validation_common_env_v10.tar.gz전체 v10 패키지 다운로드

주요 파일은 VLLM_0.29_TO_0.30_MIGRATION.md0.29 → 0.30 Migration Guide, vllm_metrics_compat.pyvLLM metrics compatibility checker, CHANGELOG_v10.mdCHANGELOG v10입니다.

핵심 변경점은 다음과 같습니다.

  • EXPECTED_VLLM_VERSION=0.30.0을 exact requirement로 변경했습니다. 다른 버전이면 preflight가 FAIL합니다.
  • 기본 venv도 ~/vllm-bench-env-0.30으로 분리했습니다.
  • 모든 shell benchmark가 PATH의 vllm 대신 ${VLLM_BIN}을 사용하도록 통일했습니다.
  • 00_compatibility_check.sh가 vllm serve와 vllm bench serve --help에서 실제 필요한 옵션 존재 여부를 검사합니다.
  • /metrics는 새 vllm_metrics_compat.py가 TTFT, ITL, KV usage, prefix-cache hits/queries, running/waiting requests family를 검사합니다. vLLM 0.30에서도 이 V1 metric family들이 사용됩니다. vLLM
  • Stage3a8의 오래된 _total prefix-cache metric 가정을 제거했습니다.
  • Stage3a10의 OffloadingConnector/TieringOffloadingSpec 구조는 유지했습니다. 이 구조와 FS secondary-tier 설정은 0.30 공식 문서와 일치합니다. vLLM
  • Stage3a10에는 선택적으로 --kv-cache-metrics --kv-cache-metrics-sample 0.01을 켤 수 있게 했습니다. canonical 성능 baseline에서는 OFF, KV 동작 진단 run에서 ON을 권장합니다. vLLM
  • Stage3a10은 각 none/local_nvme/remote_obj 종료 직전 Prometheus snapshot도 보존합니다.
  • Stage7 표준 runtime도 0.30.0으로 변경했고, DeepSeek-V4.1-Flash는 BLOCKED_COMPAT에서 CONDITIONAL로 변경했습니다. 즉 실행 가능 후보이지 자동 production-qualified라는 뜻은 아닙니다.
  • Stage7 runner는 서버 Ready 직후 /metrics schema까지 검사합니다.
  • Stage7B에는 STAGE7_ENABLE_PER_REQUEST_METRICS=1 진단 모드를 추가했습니다. 이 경우 --enable-per-request-metrics를 사용하고 per_request_metrics_probe.json을 남깁니다. 이 기능은 CPU overhead가 있을 수 있어 canonical 성능 baseline에서는 OFF가 기본입니다. vLLM

권장 migration 순서도 문서에 넣었습니다.

v9 / vLLM 0.29 baseline 보존
        ↓
별도 vLLM 0.30 venv/image 생성
        ↓
00_compatibility_check
        ↓
Llama-70B server smoke
        ↓
live /metrics compatibility
        ↓
Stage3a short / long
        ↓
Stage3a8 prefix cache
        ↓
Stage3a10
 none / local_nvme / remote_obj
        ↓
0.29 baseline regression 비교
        ↓
Stage6k K8s
        ↓
Stage7 model qualification
        ↓
0.30 production baseline 승격

특히 0.29와 0.30 비교에서는 KV cache metrics와 per-request metrics를 모두 OFF로 두는 것이 중요합니다. 먼저 동일 조건으로 엔진 버전 차이만 비교한 다음, 두 번째 diagnostic run에서 관측 기능을 켜야 baseline이 오염되지 않습니다.

마지막으로 전체 .sh bash -n, 전체 .py py_compile, YAML parsing을 통과시켰고, vllm_metrics_compat.py도 합성 Prometheus 0.30 metric 데이터로 required-family PASS 동작을 확인했습니다.


가능합니다. 지금 상황에서는 “8×B300 한 노드에 모델을 몇 개 띄울 수 있나?”보다 “시간대별로 GPU ownership을 어떻게 바꿀 것인가?”로 설계하는 게 핵심입니다.

현재까지 만든 Stage1~7 결과 체계를 기준으로 보면, 저는 아래처럼 접근하겠습니다.

1. Llama-70B를 기준점으로 보면

Llama-70B는 계속 플랫폼 기준 모델(reference model) 로 남기는 게 좋습니다. Dense 70B라 TP scaling, KV cache, long-context, K8s overhead를 해석하기 쉽고, 지금까지 Stage3a/6의 baseline도 여기에 쌓여 있기 때문입니다.

업무 모델들은 성격이 꽤 다릅니다.

모델Llama-70B 대비 성격단일 8×B300 운영 관점
Llama-70BDense 70B 기준점TP8 reference
GLM-5.3743B / active 39B MoE, 1M ctx매우 큰 모델. TP8/EP 후보
Qwen3.6-35B-A3B35B / active 3B MoE, 262K매우 가벼움. 1~2 GPU 후보
gpt-oss-20b21B / active 3.6B MoE, MXFP4매우 가벼움. 1 GPU 후보
DeepSeek-V4.1-Flash552B backbone, 8~16B active, 1M ctx대형. online 핵심 서비스 후보
GLM-5.3-Flash대형 MoE, FP8 checkpoint 386GB2~4+ GPU 후보, 실측 중요
PaddleOCR-VL-1.60.9B VLMLLM과 비교 대상 아님. 소수 GPU
Docling models문서 pipeline 구성 요소GPU fraction보다는 pipeline batching

Qwen3.6-35B-A3B는 35B total/3B active이고 FP8 checkpoint 약 42GB, NVFP4 약 21GB 수준이라 B300 한 장에도 충분히 들어가는 계열입니다. vLLM Recipes gpt-oss-20b 역시 21B/3.6B active, MXFP4 약 16GB입니다. vLLM Recipes

반대로 GLM-5.3은 743B/39B active, 1M context의 대형 MoE이고, 공식 recipe도 FP8을 8 GPU에 올리는 형태를 제시합니다. Blackwell에서는 NVFP4 variant도 선택지가 됩니다. vLLM Recipes

즉 모델 크기 관점에서는 대략

                 GPU footprint

gpt-oss-20b          ■
Qwen3.6-35B          ■
PaddleOCR            ■

Llama-70B          ■■■ 정도
                  (실제로 baseline은 TP8)

GLM-5.3-Flash      ■■■■ ?
DeepSeek V4.1     ■■■■+ ?
GLM-5.3          ■■■■■■■■

                  8× B300

정도로 생각하되, ? 부분은 반드시 Stage7 결과로 결정해야 합니다.


2. 그래서 “모든 모델 상시 기동”은 권하지 않습니다

업무 특성을 보면 자연스럽게 시간대가 갈립니다.

제가 권하는 큰 구조는:

                        API / Gateway
                             │
                    ┌────────┴────────┐
                    │ Model Router    │
                    │ Admission Ctrl  │
                    └────────┬────────┘
                             │
              ┌──────────────┼──────────────┐
              │              │              │
        Discovery       Document       Ontology
         Online          Pipeline        Batch
              │              │              │
        주간 Priority     주간/상시        야간
              │              │              │
        DeepSeek /       PaddleOCR      GLM/Qwen/
        GLM Flash        + Docling      gpt-oss

그리고 GPU 배치는 시간대에 따라 변경합니다.


3. 주간은 Online Discovery에 GPU를 몰아주는 편이 좋습니다

예를 들어 첫 출발점을 이렇게 잡을 수 있습니다.

8 × B300 NODE

GPU 0 ─┐
GPU 1  │
GPU 2  ├── Discovery Primary
GPU 3 ─┘   DeepSeek-V4.1-Flash or GLM-5.3-Flash
             TP4 (초기 후보)

GPU 4 ───── Qwen3.6-35B-A3B
             TP1

GPU 5 ───── gpt-oss-20b
             TP1

GPU 6 ─┐
GPU 7 ─┴── Document Pipeline
           PaddleOCR / Docling

단, 이것은 초기 qualification topology이지 최종값은 아닙니다.

DeepSeek-V4.1-Flash는 특히 주의가 필요합니다. 현재 recipe는 552B backbone, prompt 8B active / output 16B active, 1M context이며 B300도 대상 하드웨어로 표시되어 있습니다. vLLM Recipes

그래서 이 모델은 TP2/TP4/TP8을 Stage7에서 직접 비교해야 합니다.

DeepSeek V4.1

TP2
 ├─ TTFT
 ├─ ITL
 ├─ RPS
 ├─ HBM
 └─ P99

TP4
 ├─ ...
 └─ ...

TP8
 ├─ ...
 └─ ...

TP8이 가장 빠르다고 해서 TP8을 선택하면 안 됩니다.

TP4가 예를 들어 TP8 성능의 85~90%를 내면서 online SLO를 만족한다면 나머지 4 GPU를 다른 서비스에 쓰는 편이 노드 전체 효율은 훨씬 높습니다.


4. GLM-5.3-Flash도 TP4부터 검증할 가치가 있습니다

공식 recipe에도 FP8 + TP4 예제가 있고, 심지어 8 GPU 노드를

GPU 0-3 : Prefill TP4
GPU 4-7 : Decode TP4

로 나누는 Prefill/Decode disaggregation 구성이 제시됩니다. vLLM Recipes

다만 저는 처음부터 P/D 분리를 권하지 않습니다.

먼저:

Phase 1
GLM-5.3-Flash TP4

Phase 2
GLM-5.3-Flash TP8

Phase 3
필요한 경우
Prefill TP4 + Decode TP4

순서가 좋습니다.

P/D disaggregation은 Stage7B 결과에서

prefill-heavy workload
+
높은 concurrency
+
TTFT SLO 문제

가 실제로 나타났을 때 도입하면 됩니다.


5. Qwen과 gpt-oss는 “남는 GPU 활용”에 아주 좋습니다

이 두 모델은 대형 모델과 운영 전략이 다릅니다.

Qwen3.6-35B-A3B는 FP8 42GB이고 공식 recipe도 TP1을 기본 예제로 사용합니다. vLLM Recipes

따라서 B300에서는 우선:

Qwen
TP1
gpu-memory-utilization ≈ 0.8~0.9

부터 테스트하면 됩니다.

TP2/TP4를 처음부터 줄 이유는 별로 없습니다.

Stage7A에서

TP1 C1/4/8/16
TP2 C1/4/8/16

만 비교해도 판단할 수 있습니다.

TP1에서 야간 처리량 목표를 만족하면 GPU 한 장만 할당하면 됩니다.

gpt-oss-20b도 마찬가지입니다.

그래서 야간 ontology job이 여러 종류라면:

GPU0  Qwen instance #1
GPU1  Qwen instance #2

GPU2  gpt-oss #1
GPU3  gpt-oss #2

GPU4-7 GLM-5.3 또는 다른 large ontology model

같은 scale-out 형태도 고려할 수 있습니다.

작은 모델은 TP4 하나보다 TP1 replica 4개가 batch throughput에 유리한 경우가 많습니다. 이것은 Stage7A의 jobs/hour 결과로 결정하면 됩니다.


6. 야간에는 topology를 완전히 바꿔도 됩니다

여기서 Kubernetes가 큰 장점이 됩니다.

주간:

GPU0-3  Discovery
GPU4    Qwen
GPU5    gpt-oss
GPU6-7  Document

였다면 야간에는:

GPU0 ───────────────┐
GPU1                │
GPU2                │
GPU3                ├── GLM-5.3 TP8
GPU4                │
GPU5                │
GPU6                │
GPU7 ───────────────┘

로 바꿔도 됩니다.

GLM-5.3은 FP8 기준으로도 대형 모델이고 공식 recipe 역시 단일 8-GPU 노드 구성을 제시합니다. vLLM Recipes

그래서 GLM-5.3 때문에 GPU 8장을 24시간 점유할 이유는 없습니다.

Ontology 작업이 야간이라면:

22:00
 online capacity 축소
        ↓
 small model drain
        ↓
 GLM pod scale 0→1
        ↓
 model warm-up
        ↓
 Performance Ready
        ↓
 ontology queue 처리
        ↓
 job 완료
        ↓
 GLM pod 종료
        ↓
 daytime topology 복원

구조가 경제적입니다.


7. Document workload에는 GPU 2장을 먼저 주지 않아도 됩니다

PaddleOCR-VL-1.6은 0.9B 모델입니다. PaddleOCR

그리고 Docling은 하나의 거대한 VLM이 아니라:

PDF
 ↓
OCR
 ↓
Layout
 ↓
Table
 ↓
Figure classifier
 ↓
Structured document

pipeline입니다.

Heron도 layout object detection stage이고 CUDA를 지원합니다. Docling Project

따라서 처음에는:

GPU6
 ├─ PaddleOCR
 ├─ Docling layout
 └─ classifier

처럼 GPU 1장으로 시작하는 것을 권합니다.

Stage7C 결과가

target = 300 pages/min

GPU1
  420 pages/min

이면 끝입니다.

GPU를 더 줄 이유가 없습니다.

반대로

GPU1
  170 pages/min

GPU2
  325 pages/min

이라면 2 GPU 또는 replica 2개로 확장하면 됩니다.

Docling 자체에도 OCR batching 등 throughput과 메모리를 교환하는 batch 옵션이 있으므로 GPU 개수보다 먼저 batch size를 조정할 가치가 있습니다. Docling Project


8. Stage7 결과에서 최종 configuration을 뽑는 방법

여기가 제일 중요합니다.

각 모델에 대해 단순히 최고 TPS를 찾지 말고 Feasible Configuration Set을 만드세요.

예를 들어 DeepSeek 결과가:

ConfigP99 TTFTITLRPSHBM/GPU
TP23.2s29ms11245GB
TP41.7s18ms23171GB
TP81.2s15ms29112GB

라고 가정하겠습니다.

서비스 SLO가

P99 TTFT < 2 sec
ITL < 25 ms

라면 TP2는 탈락합니다.

TP4와 TP8은 둘 다 통과합니다.

그러면 TP4를 기본 후보로 두는 겁니다.

왜냐하면 TP8의 추가 성능보다 GPU 4장을 다른 서비스에 사용할 수 있는 가치가 클 수 있기 때문입니다.

즉 selection algorithm은:

1. Quality PASS

2. Stability PASS

3. SLO PASS

4. 그중 최소 GPU 개수 선택

5. 그 구성에서
   max-num-seqs
   max-num-batched-tokens
   max-model-len
   gpu-memory-utilization
   KV dtype
   TP / EP
   speculative decoding
   조정

6. 다시 soak

7. K8s Stage7D

8. production config 승격

입니다.

“TPS가 가장 높은 config”가 아니라 “SLO를 만족하는 최소 GPU config”를 찾는 방식입니다.


9. vLLM parameter도 이런 순서로 결정하면 됩니다

Stage7 결과에서 바로 Kubernetes 설정으로 가지 말고 모델별로 production profile을 하나 만드세요.

예:

model: deepseek-v4.1-flash

resources:
  gpu: 4

vllm:
  tensor_parallel_size: 4
  gpu_memory_utilization: 0.90
  kv_cache_dtype: fp8

  max_model_len: 131072
  max_num_seqs: 32
  max_num_batched_tokens: 8192

features:
  prefix_cache: true
  speculative_decoding: true

slo:
  p99_ttft_ms: 2000
  p99_itl_ms: 25
  max_error_rate: 0.01

qualification:
  stage7: PASS
  soak_hours: 8
  k8s_revalidation: PASS

여기서 특히 max_model_len은 모델이 지원하는 최대 context로 설정할 필요가 없습니다.

DeepSeek가 1M을 지원한다고 해서:

--max-model-len 1048576

을 production default로 쓰는 것은 별개의 문제입니다.

실제 Discovery query의 P99 context가 50K라면:

64K
96K
128K

정도로 제한하는 게 더 나을 수 있습니다.

남는 KV capacity를 concurrency에 사용할 수 있기 때문입니다.


10. 그래서 실제 트래픽 분포가 Stage7에서 가장 중요합니다

Stage7에서 현재 synthetic workload만 돌리고 끝내지 말고 production candidate마다:

Input tokens
P50
P90
P95
P99
MAX

Output tokens
P50
P90
P95
P99

Concurrent requests
P50
P95
P99

Requests/sec
P50
P95
peak

를 넣으세요.

그리고 benchmark를 그 분포로 재현합니다.

예를 들어 Discovery가 실제로:

input
P50   3K
P95  12K
P99  28K

output
P50  300
P95  900

peak concurrency 22

라면,

32K input / C32

가 production qualification의 핵심 test가 됩니다.


11. KV tier도 모든 모델에 켜지 않는 게 좋습니다

Stage3a10에서 만든

none
local_nvme
remote_obj

결과를 모델별로 적용합니다.

판정 방식은 간단합니다.

GPU-only가:

SLO PASS
+
required concurrency PASS
+
required context PASS

면 offload 하지 않는 게 우선입니다.

그런데 GPU-only:

C16 PASS
C32 OOM

이고 local NVMe:

C32 PASS
TTFT +8%

라면 local NVMe가 의미가 있습니다.

remote MemKV도 마찬가지입니다.

즉:

GPU KV
   │
   ├─ capacity 충분 → 그대로 사용
   │
   └─ 부족
       ↓
    Local NVMe
       │
       ├─ SLO PASS → 선택 가능
       │
       └─ 부족
           ↓
       Remote MemKV

순으로 보는 게 좋습니다.

remote tier는 성능 향상 기술이라기보다 capacity/재사용/운영 유연성 기술로 보는 편이 맞습니다.


12. 최종적으로 제가 생각하는 1-node 운영 모습

초기 production candidate는 이런 형태가 가장 현실적입니다.

                 ┌──────────────────┐
                 │ API Gateway      │
                 │ Auth / RateLimit │
                 └────────┬─────────┘
                          │
                 ┌────────▼────────┐
                 │ Model Router    │
                 └───┬────┬────┬──┘
                     │    │    │
          ┌──────────┘    │    └───────────┐
          ▼               ▼                ▼

      Discovery        Small LLM        Document
       Online            Pool           Pipeline

  DeepSeek V4.1      Qwen TP1          PaddleOCR
        or           gpt-oss TP1          +
  GLM-5.3-Flash                       Docling

     GPU 0-3          GPU 4-5          GPU 6
                                           
                         GPU 7
                  spare / burst / replica

그리고 야간에는:

                    Night Scheduler
                           │
                           ▼
             Discovery replicas scale down
                           │
                           ▼
                   GPU resources free
                           │
                           ▼
               GLM-5.3 TP8 start
                           │
                     warm-up gate
                           │
                           ▼
                  Ontology Queue
                           │
                           ▼
                       complete
                           │
                           ▼
                     GLM scale 0
                           │
                           ▼
                daytime topology restore

를 첫 번째 운영안으로 검증하겠습니다.

다만 DeepSeek V4.1-Flash TP4가 반드시 맞다는 의미는 아닙니다. 공식 recipe에는 B300이 지원 대상이고 TP 크기 및 scheduler knobs도 workload-dependent라고 명시되어 있습니다. vLLM Recipes Stage7B의 TP2/4/8 결과가 그 결정을 내려야 합니다.


운영에서 중요한 한 가지 원칙

이렇게 구성하면 vllm serve 명령을 Deployment YAML에 직접 하드코딩하지 않는 편이 좋습니다.

각 모델에:

qualification result
       ↓
production-profile.yaml
       ↓
Helm values
       ↓
Deployment

라는 흐름을 만드는 게 좋습니다.

즉 Stage7 결과가 production configuration의 source of truth가 되게 합니다.

예를 들어:

Stage7 result
 TP4
 C32
 max-model-len 128K
 max-num-seqs 32
 batched-tokens 8192
 FP8 KV
 GPU KV
 8h soak PASS
        ↓
model-profile/deepseek-v4.1-flash.yaml
        ↓
Helm
        ↓
K8s Deployment
        ↓
Stage7D
        ↓
Production

가 됩니다.

이렇게 해두면 나중에 vLLM 0.30 → 0.31, checkpoint 변경, FP8→NVFP4 변경이 생겨도 Stage7 재실행 → profile 갱신 → regression gate → 배포라는 동일한 절차를 사용할 수 있습니다.

현재 만든 v10에서 다음 단계로 가장 가치가 큰 것은 이 부분입니다. Stage7 결과 JSON을 읽어서 model-production-profile.yaml과 K8s Helm values를 자동 생성하는 단계를 붙이면 지금까지 만든 acceptance/qualification 체계가 실제 운영 배포 설정까지 연결됩니다.

profile
engineer

0개의 댓글