26S05d

QK·2일 전

vLLM 서빙에서 Throughput(전체 처리량)과 TTFT(첫 토큰 지연 시간)는 트레이드오프 관계를 가집니다. 대규모 배치 처리는 Throughput을 극대화하지만 긴 Prefill 연산으로 인해 기존 요청의 ITL과 신규 요청의 TTFT를 저하시킵니다.

워크로드 특성(RAG 기반 무거운 프롬프트 vs 실시간 대화형 서비스)에 맞춘 핵심 엔진 파라미터 조합과 튜닝 전략을 정리했습니다.


핵심 튜닝 플래그 상세 가이드

1. Chunked Prefill (--enable-chunked-prefill)

  • 역할: 긴 프롬프트(Prompt Prefill)를 여러 덩어리(Chunk)로 쪼개어 다른 세션의 디코딩(Decoding) 단계와 한 번의 반복(Iteration) 내에서 공동 배치(Co-batching) 처리합니다.
  • 효과:
  • 장문 프롬프트가 들어와도 기존 디코딩 요청이 멈추지 않아 ITL(Inter-Token Latency) 튐 현상 방지.
  • 긴 프롬프트 처리 중에도 다른 짧은 요청의 Prefill이 함께 끼어들 수 있어 P99 TTFT 대폭 개선.
  • Prefill과 Decode 단계가 함께 스케줄링되어 GPU 연산기(Compute Core) 활용률이 극대화되므로 Throughput 동시 상승.
  • 설정:
--enable-chunked-prefill

2. 배치 토큰 크기 (--max-num-batched-tokens)

  • 역할: 단일 포워드 패스(Iteration)에서 엔진이 한 번에 처리할 수 있는 최대 토큰 수(Prefill 토큰 + Decode 토큰)를 제한합니다.
  • 튜닝 가이드:
  • Chunked Prefill 활성화 시 이 값이 단일 Chunk의 크기를 결정합니다.
  • 기본값 (512 / 모델별 상이): TTFT와 ITL 안정성에 유리하지만 Throughput이 다소 희생됩니다.
  • Throughput 우선 (2048 ~ 8192): H100/A100 등 대규모 VRAM 및 높은 연산력을 가진 GPU에서 배치 밀도를 높여 GPU 포화를 유도할 때 권장합니다.
  • TTFT 우선 (512 ~ 1024): 프롬프트 길이가 길고 실시간 응답 체감이 중요한 챗봇/에이전트 서비스에 적합합니다.

3. 동시 처리 시퀀스 수 (--max-num-seqs)

  • 역할: 한 번에 동시 실행(Running 상태) 가능한 최대 요청 수(Concurrency)를 지정합니다.
  • 튜닝 가이드:
  • 기본값은 256입니다.
  • 동시 인입 요청이 많아 KV Cache가 부족해지면 vLLM은 요청을 큐(Waiting)에 두거나 선점(Preemption - CPU 스왑 또는 재연산)합니다.
  • 선점이 발생하면 TTFT와 ITL이 급격히 무너지므로, 최대 허용 부하 수준(예: 64, 128, 256)으로 상한을 두어 엔진 크래시 및 재연산 스왑을 방지합니다.

4. GPU 메모리 할당 비율 (--gpu-memory-utilization)

  • 역할: 전체 VRAM 중 모델 가중치 로드 및 KV Cache 할당을 위해 vLLM 프로세스가 선점할 메모리 비율을 지정합니다.
  • 튜닝 가이드:
  • 기본값: 0.90 (90%).
  • 단독 GPU 노드 환경이라면 0.92 ~ 0.95까지 상향하여 KV Cache 블록 수를 최대로 확보하는 것이 Throughput에 유리합니다.
  • 지나치게 높일 경우(> 0.96) CUDA Graph 캡처 메모리나 임시 활성화 텐서(Activation) 공간 부족으로 OOM이 발생할 수 있습니다.

5. 최대 컨텍스트 길이 제약 (--max-model-len)

  • 역할: 모델이 허용하는 최대 시퀀스 길이를 제한합니다 (예: Llama 3.1의 128k 컨텍스트).
  • 튜닝 가이드:
  • vLLM은 모델의 네이티브 컨텍스트 길이(max_position_embeddings)를 기준으로 KV Cache 슬롯 공간을 계산합니다.
  • 실제 서비스 환경에서 128k를 전부 쓰지 않고 8k~16k 내외만 쓴다면 반드시 --max-model-len 8192 또는 16384로 제약해야 합니다.
  • 컨텍스트 길이를 제한하면 단일 요청이 점유할 수 있는 최대 KV Cache가 줄어들어, 더 많은 동시 요청(Concurrency)을 담을 수 있는 KV Cache 블록이 확보됩니다.

6. Prefix Caching (--enable-prefix-caching)

  • 역할: 동일하거나 중복되는 프롬프트 접두사(System Prompt, 공통 RAG 지침, Few-shot 예시 등)의 KV Cache를 버리지 않고 재사용합니다.
  • 효과:
  • 반복되는 긴 프롬프트가 들어올 때 Prefill 연산 자체를 건너뛰므로 TTFT가 수 밀리초 단위(Near-Zero)로 단축.
  • GPU 연산량 절감으로 인해 전체 Serving Throughput 급상승.

워크로드별 권장 실행 파라미터 조합

시나리오 1: 대화형 서비스 (TTFT 및 실시간성 최우선)

  • 목표: 일관된 빠른 첫 토큰 반응 속도, 짧은 대기 시간, 낮은 ITL 지터(Jitter).
python3 -m vllm.entrypoints.openai.api_server \
    --model /models/Llama-3.1-70B-Instruct \
    --tensor-parallel-size 8 \
    --gpu-memory-utilization 0.92 \
    --max-model-len 8192 \
    --enable-chunked-prefill \
    --max-num-batched-tokens 512 \
    --max-num-seqs 128 \
    --enable-prefix-caching

시나리오 2: RAG / 배치 요약 처리 (Throughput 최우선)

  • 목표: 긴 입력 프롬프트(4k~8k 토큰)가 다량 인입될 때 GPU 연산기 포화 및 초당 토큰 처리량 극대화.
python3 -m vllm.entrypoints.openai.api_server \
    --model /models/Llama-3.1-70B-Instruct \
    --tensor-parallel-size 8 \
    --gpu-memory-utilization 0.95 \
    --max-model-len 16384 \
    --enable-chunked-prefill \
    --max-num-batched-tokens 2048 \
    --max-num-seqs 256 \
    --enable-prefix-caching

튜닝 검증 및 모니터링 체크포인트

파라미터 변경 후 Phase 5 부하 테스트를 수행하며 /metrics 엔드포인트에서 아래 3가지 메트릭 추이를 확인해야 합니다.

  1. vllm:num_requests_waiting (대기 큐 지표)
  • 이 값이 지속적으로 증가한다면 서빙 한계치에 도달한 것입니다. --max-num-seqs를 늘리기보다는 클러스터 차원의 Rate Limiting 또는 노드 스케일아웃이 필요합니다.
  1. vllm:gpu_cache_usage_factor (KV Cache 포화도)
  • 부하 중 0.8~0.9 사이를 유지하는 것이 이상적입니다. 1.0에 도달하면 신규 요청이 블로킹되거나 Prefill 지연이 급증합니다.
  1. vllm:time_to_first_token_seconds (TTFT 분포)
  • --enable-chunked-prefill 적용 전후의 P95/P99 구간을 비교하여 긴 프롬프트 유입 시 TTFT 스파이크가 해소되었는지 검증합니다.

===

vLLM 서빙 엔진과 GPU 하드웨어(DCGM Exporter)의 상태를 실시간 수집하고 이상 징후를 감지하기 위한 Prometheus 설정 및 Alertmanager 경보 규칙입니다.


1. Prometheus 수집 설정 (scrape_configs)

vLLM의 메트릭 엔드포인트(기본 포트 8000, 경로 /metrics)와 NVIDIA DCGM Exporter(기본 포트 9400)를 정기적으로 폴링하도록 설정합니다.

# prometheus.yml
scrape_configs:
  # ----------------------------------------------------
  # 1. vLLM Serving Engine Metrics
  # ----------------------------------------------------
  - job_name: 'vllm-serving'
    scrape_interval: 5s            # 큐 상태 및 실시간 캐시 변동 추적을 위해 짧은 주기 권장
    scrape_timeout: 4s
    metrics_path: /metrics
    static_configs:
      - targets: ['<GPU_NODE_IP>:8000']
        labels:
          cluster: 'gpu-platform'
          role: 'llm-inference'
          model: 'llama-3.1-70b'

  # ----------------------------------------------------
  # 2. NVIDIA DCGM Exporter (Hardware & GPU Metrics)
  # ----------------------------------------------------
  - job_name: 'dcgm-exporter'
    scrape_interval: 10s           # 하드웨어 센서/전력 모니터링 주기
    scrape_timeout: 8s
    metrics_path: /metrics
    static_configs:
      - targets: ['<GPU_NODE_IP>:9400']
        labels:
          cluster: 'gpu-platform'
          role: 'gpu-telemetry'

2. 핵심 알람 규칙 정의 (alert_rules.yml)

엔진 레벨의 성능 병목(큐잉, KV Cache 고갈, TTFT 지연)과 하드웨어 레벨의 치명적 결함(ECC 에러, 과열, XID 오류)을 분리하여 감시합니다.

groups:
  # ====================================================
  # Group 1: vLLM Inference Engine Alerts
  # ====================================================
  - name: vllm_serving_alerts
    rules:
      - alert: VLLMInstanceDown
        expr: up{job="vllm-serving"} == 0
        for: 30s
        labels:
          severity: critical
        annotations:
          summary: "vLLM serving instance is down"
          description: "Target {{ $labels.instance }} has been unreachable for more than 30 seconds."

      - alert: VLLMKVCacheSaturation
        expr: vllm:gpu_cache_usage_factor > 0.95
        for: 1m
        labels:
          severity: warning
        annotations:
          summary: "vLLM KV Cache is nearly saturated (>95%)"
          description: "Instance {{ $labels.instance }} GPU cache usage is at {{ $value | humanizePercentage }}. Risk of request eviction or queuing."

      - alert: VLLMHighRequestQueuing
        expr: vllm:num_requests_waiting > 10
        for: 1m
        labels:
          severity: warning
        annotations:
          summary: "vLLM request queue backlog detected"
          description: "Instance {{ $labels.instance }} has {{ $value }} requests queued for over 1 minute. Serving capacity is saturated."

      - alert: VLLMHighTTFTLatency
        expr: |
          histogram_quantile(0.95, sum(rate(vllm:time_to_first_token_seconds_bucket[5m])) by (le, instance, model_name)) > 2.5
        for: 3m
        labels:
          severity: warning
        annotations:
          summary: "P95 TTFT latency exceeds 2.5s"
          description: "Model {{ $labels.model_name }} on {{ $labels.instance }} P95 TTFT is {{ $value }}s for the last 5 minutes."

      - alert: VLLMRequestPreemptionDetected
        expr: rate(vllm:num_preemptions_total[2m]) > 0
        for: 30s
        labels:
          severity: critical
        annotations:
          summary: "vLLM request preemptions occurring"
          description: "Instance {{ $labels.instance }} is preempting/recomputing requests due to strict memory limits."

  # ====================================================
  # Group 2: GPU Hardware & DCGM Telemetry Alerts
  # ====================================================
  - name: gpu_hardware_alerts
    rules:
      - alert: DCGMExporterDown
        expr: up{job="dcgm-exporter"} == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "DCGM Exporter is down"
          description: "Hardware telemetry on {{ $labels.instance }} is unavailable."

      - alert: GPUCriticalTemperature
        expr: DCGM_FI_DEV_GPU_TEMP > 83
        for: 30s
        labels:
          severity: critical
        annotations:
          summary: "GPU temperature is critical (>83°C)"
          description: "GPU {{ $labels.gpu }} on {{ $labels.instance }} has reached {{ $value }}°C. Thermal throttling imminent."

      - alert: GPUClockThrottled
        expr: DCGM_FI_DEV_CLOCK_THROTTLE_REASONS > 0
        for: 1m
        labels:
          severity: warning
        annotations:
          summary: "GPU clock throttling active"
          description: "GPU {{ $labels.gpu }} on {{ $labels.instance }} is throttled (Reason bitmask: {{ $value }})."

      - alert: GPUEccDoubleBitError
        expr: increase(DCGM_FI_DEV_ECC_DBE_VOL_TOTAL[5m]) > 0
        labels:
          severity: critical
        annotations:
          summary: "Uncorrectable ECC double-bit error detected"
          description: "GPU {{ $labels.gpu }} on {{ $labels.instance }} detected double-bit ECC memory corruption. Immediate hardware check required."

      - alert: GPUXidErrorOccurred
        expr: DCGM_FI_DEV_XID_ERRORS > 0
        labels:
          severity: critical
        annotations:
          summary: "NVIDIA driver XID error detected"
          description: "GPU {{ $labels.gpu }} on {{ $labels.instance }} logged XID error code: {{ $value }}."

      - alert: GPUNVLinkErrorDetected
        expr: increase(DCGM_FI_DEV_NVLINK_CRC_FLIT_ERROR_COUNT_TOTAL[5m]) > 0
        labels:
          severity: warning
        annotations:
          summary: "NVLink CRC error count increasing"
          description: "NVLink on GPU {{ $labels.gpu }} ({{ $labels.instance }}) is reporting transmission CRC errors."

3. 주요 메트릭 및 대시보드 쿼리 참조표

모니터링 영역PromQL 표현식이상 기준 / 해석
토큰 생성 처리량sum(rate(vllm:request_generation_tokens_total[1m])) by (instance)초당 생성 토큰 수(TPS) 측정
KV Cache 여유량(1 - vllm:gpu_cache_usage_factor) * 10010% 미만으로 떨어질 경우 대기열 적체 임박
실행 중인 동시 요청vllm:num_requests_running현재 GPU에서 동시 Decoding 중인 시퀀스 수
대기 큐 요청 수vllm:num_requests_waiting지속적으로 0보다 크면 서빙 노드 증설 필요
GPU 전력 사용량DCGM_FI_DEV_POWER_USAGE스펙상 정격 TDP 대비 피크 도달 여부 점검
GPU SM 연산 점유율DCGM_FI_DEV_GPU_UTILPrefill 구간에서 100% 도달, Decode 구간에서는 통상 HBM 대역폭(DCGM_FI_DEV_MEM_COPY_UTIL)과 함께 확인

===

K8s 조인 전 GPU 노드 단독 환경에서는 RHEL 10.2의 Podman과 systemd(또는 Podman Pod)를 활용해 Prometheus, Grafana, DCGM Exporter를 간략하게 묶어 올리는 구성이 가장 깔끔합니다.

호스트 네트워크 모드(--net=host)를 사용하면 복잡한 포트 포워딩이나 브리지 인터페이스 설정 없이, 호스트에 떠 있는 vLLM(:8000)과 DCGM Exporter(:9400)의 메트릭을 즉시 스크랩하고 Grafana(:3000) 웹 UI로 모니터링할 수 있습니다.


Step 1. 사전 준비 (설정 파일 및 디렉터리 생성)

GPU 노드의 호스트 디렉터리에 Prometheus 설정과 Grafana 데이터 경로를 생성합니다.

# 1. 설정 및 데이터 디렉터리 생성
mkdir -p /opt/monitoring/{prometheus,grafana_data}
chmod 777 /opt/monitoring/grafana_data  # Grafana 컨테이너 UID(472) 쓰기 권한

# 2. Prometheus 수집 설정 파일 작성 (/opt/monitoring/prometheus/prometheus.yml)
cat <<'EOF' > /opt/monitoring/prometheus/prometheus.yml
global:
  scrape_interval: 5s
  evaluation_interval: 5s

scrape_configs:
  # 1. GPU 하드웨어 메트릭 (DCGM Exporter)
  - job_name: 'dcgm'
    static_configs:
      - targets: ['127.0.0.1:9400']

  # 2. vLLM 추론 엔진 서빙 메트릭
  - job_name: 'vllm'
    metrics_path: /metrics
    static_configs:
      - targets: ['127.0.0.1:8000']
EOF

Step 2. Podman 컨테이너 3종 기동

호스트의 GPU와 네트워크를 직접 공유하도록 실행합니다.

1) NVIDIA DCGM Exporter 실행 (GPU 메트릭 노출: 9400 포트)

RHEL 10의 CDI(Container Device Interface)를 통해 GPU 장치 접근 권한을 넘겨줍니다.

podman run -d --name dcgm-exporter \
    --restart unless-stopped \
    --device nvidia.com/gpu=all \
    --net=host \
    nexus.internal:8082/nvidia/k8s-device-plugin/dcgm-exporter:latest

동작 확인:

curl -s http://127.0.0.1:9400/metrics | grep DCGM_FI_DEV_GPU_TEMP

2) Prometheus 실행 (시계열 데이터 수집: 9090 포트)

podman run -d --name prometheus \
    --restart unless-stopped \
    --net=host \
    -v /opt/monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro,Z \
    nexus.internal:8082/prom/prometheus:latest \
    --config.file=/etc/prometheus/prometheus.yml \
    --storage.tsdb.retention.time=7d

동작 확인:

curl -s http://127.0.0.1:9090/-/ready
# "Prometheus is Ready." 출력 확인

3) Grafana 실행 (시각화 대시보드: 3000 포트)

podman run -d --name grafana \
    --restart unless-stopped \
    --net=host \
    -v /opt/monitoring/grafana_data:/var/lib/grafana:Z \
    nexus.internal:8082/grafana/grafana:latest

Step 3. Grafana 대시보드 연동

  1. 웹 브라우저 접속
  • URL: http://<GPU_NODE_IP>:3000 (외부망 ConnectX-6 IP)
  • 초기 계정/비밀번호: admin / admin (첫 로그인 시 변경)
  1. Prometheus 데이터 소스 등록
  • Connections \rightarrow Data Sources \rightarrow Add data source \rightarrow Prometheus
  • Server URL: [http://127.0.0.1:9090](http://127.0.0.1:9090) (호스트 네트워크 모드이므로 로컬호스트 지정)
  • Save & test 클릭 후 정상 연결 확인.
  1. 오프라인 대시보드 등록 (에어갭 환경)
  • 인터넷 연결이 없으므로 대시보드 번호(ID) 입력 방식 대신, JSON 파일을 Import합니다.
  • GPU 하드웨어 대시보드: NVIDIA 공식 DCGM Exporter 대시보드 JSON (외부망에서 사전 다운로드한 NVIDIA Grafana Dashboard JSON)을 Dashboards \rightarrow New \rightarrow Import로 업로드.
  • vLLM 대시보드: vLLM GitHub 저장소(examples/vllm_prometheus_grafana/)에 포함된 기본 대시보드 JSON을 Import하여 TTFT, TPS, KV Cache 추이를 시각화.

Step 4. 테스트 종료 후 일괄 정리 (One-liner)

성능 테스트가 끝나고 추후 정식 K8s Worker 노드로 조인할 때는 아래 명령으로 깔끔하게 제거할 수 있습니다.

# 모니터링 컨테이너 일괄 중지 및 삭제
podman rm -f dcgm-exporter prometheus grafana

# (선택) 임시 수집 데이터 정리
rm -rf /opt/monitoring
profile
engineer

0개의 댓글