목표와 현재 결과

kube-prometheus-stack에 Prometheus, Grafana, Alertmanager, kube-state-metrics, node-exporter를 구성하고 NVIDIA DCGM Exporter와 vLLM ServiceMonitor를 연결했다. 모든 monitoring Pod가 Running 상태가 되었고 Prometheus에서 dcgm-exporter, gemma4-vllm을 포함한 active target 15개가 모두 up으로 확인됐다.

동일한 chat completion 요청을 5회 실행한 결과 HTTP 200 성공률은 100%였다. 첫 요청은 9.934335초, 이후 4회는 평균 1.638398초였다. 첫 요청 이후 시간이 안정된 현상은 확인했지만, warm-up과 cache 중 어느 요소가 얼마나 기여했는지는 추가 metrics 비교가 필요하다.

실행 명령과 manifest는 내부 Runbook에 유지한다. Mount propagation 자체의 개념은 별도 기술 문서에 분리했다.

1. monitoring component 기동 상태

Prometheus stack과 DCGM Exporter가 모두 실행됐고, DCGM Exporter와 vLLM ServiceMonitor도 생성됐다. DCGM Exporter는 다른 component보다 준비에 시간이 조금 더 걸렸지만 restart 없이 1/1 Running에 도달했다.

component상태해석
Prometheus2/2 Runningmetrics 저장과 query component 정상
Grafana3/3 Runningdashboard 접속 준비 완료
Alertmanager2/2 Runningalert 처리 component 정상
node-exporter1/1 Runningnode metrics exporter 정상화
DCGM Exporter1/1 Running, restart 0GPU metrics exporter 기동 완료
dcgm-exporter ServiceMonitor생성Prometheus discovery 대상 구성
gemma4-vllm ServiceMonitor생성vLLM metrics discovery 대상 구성

기존 component의 restart는 모두 같은 과거 시점에 발생했고 현재 Pod는 Running을 유지하고 있다. 이번에 새로 기동한 node-exporter와 DCGM Exporter는 restart가 없다.

2. node-exporter mount propagation 복구

node-exporter는 image pull 이후 container runtime spec 생성 단계에서 실패했다.

host root와 DaemonSet 요구사항을 각각 확인했다.

findmnt -o TARGET,PROPAGATION /
sudo k3s kubectl -n monitoring get daemonset \
  kube-prometheus-stack-prometheus-node-exporter \
  -o jsonpath='{.spec.template.spec.containers[?(@.name=="node-exporter")].volumeMounts[?(@.name=="root")].mountPropagation}{"\n"}'
확인 항목판정
host / propagationprivate새 mount를 다른 namespace로 전달하지 않음
node-exporter 요구 modeHostToContainerhost의 새 mount 전달을 요구
실패 지점runtime spec 생성image나 application 실행 이전에 mount 조건 불일치

host 전체 설정을 변경하지 않고 Helm value에서 node-exporter의 root mount propagation을 None으로 변경했다.

sudo helm --kubeconfig /etc/rancher/k3s/k3s.yaml upgrade \
  kube-prometheus-stack \
  oci://ghcr.io/prometheus-community/charts/kube-prometheus-stack \
  --namespace monitoring \
  --version "$(sudo helm --kubeconfig /etc/rancher/k3s/k3s.yaml list \
    --namespace monitoring \
    --output json \
    | jq -r '.[] | select(.name=="kube-prometheus-stack") | .chart | sub("^kube-prometheus-stack-"; "")')" \
  --reuse-values \
  --set-string prometheus-node-exporter.hostRootFsMount.mountPropagation=None
key해석
chartkube-prometheus-stack:87.16.1기존 설치 chart version 유지
namespacemonitoring기존 monitoring namespace 유지
release statusdeployedHelm upgrade 반영 성공
revision2두 번째 release revision
overridemountPropagation=Noneprivate host root와 충돌하는 전달 요구 제거

이후 node-exporter Pod가 1/1 Running, restart 0으로 정상화됐다.

3. Prometheus active target 검증

curl -fsS http://127.0.0.1:9090/api/v1/targets \
  | jq '.data.activeTargets[] | {job: .labels.job, health: .health}'
주요 jobtarget 수health의미
dcgm-exporter1upPrometheus가 GPU exporter를 정상 scrape
gemma4-vllm1upPrometheus가 vLLM metrics를 정상 scrape
node-exporter1upnode metrics 수집 정상
kubelet3모두 upkubelet의 여러 metrics endpoint 정상
전체 active target15모두 up표시된 target에서 scrape 실패 없음

ServiceMonitor 생성뿐 아니라 실제 Prometheus target health까지 확인해 vLLM과 GPU metrics 수집 경로가 연결된 것을 검증했다.

up{job="dcgm-exporter"}

Prometheus에서 dcgm-exporter target이 up으로 조회된 화면

up{job="gemma4-vllm"}

Prometheus에서 gemma4-vllm target이 up으로 조회된 화면

4. Grafana credential 조회

sudo k3s kubectl -n monitoring get secret kube-prometheus-stack-grafana \
  -o jsonpath='{.data.admin-password}' | base64 -d

Grafana admin password 조회는 성공했다. 인증정보는 원본 실행 output이더라도 문서와 공개 가능한 기록에는 저장하지 않는다.

5. 동일 prompt 기준 요청 5회

for i in $(seq 1 5); do
  curl -sS -o /dev/null \
    -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
    }'
done
runHTTPtotal해석
12009.934335s첫 기준 요청
22001.678360s반복 요청 안정 구간 진입
32001.639604s안정 구간 유지
42001.617524s안정 구간 유지
52001.618104s안정 구간 유지
지표
성공률5/5, 100%
전체 평균3.297585s
첫 요청9.934335s
2~5회 평균1.638398s
2~5회 최소1.617524s
2~5회 최대1.678360s
첫 요청 / 반복 평균6.06배

동일 prompt에서 첫 요청 이후 응답 시간이 약 1.62~1.68초로 수렴했다. 이는 warm-up 또는 cache 영향을 시사하지만, 원인을 확정하려면 요청별 GPU utilization, KV/prefix cache, token throughput metrics를 함께 비교해야 한다.

6. Prometheus GPU와 vLLM metric 확인

6-1. GPU utilization

DCGM_FI_DEV_GPU_UTIL

Prometheus에서 조회한 RTX 4090 GPU utilization

15분 범위에서 GPU utilization이 idle 구간의 0%부터 최대 약 62%까지 변했다. 값이 시간에 따라 실제로 변한 것은 DCGM Exporter가 고정된 dummy 값이 아니라 GPU 활동을 지속적으로 수집하고 있음을 보여 준다.

6-2. GPU temperature

DCGM_FI_DEV_GPU_TEMP

Prometheus에서 조회한 RTX 4090 GPU temperature

같은 시간 범위에서 GPU temperature는 약 44~50°C로 관측됐다. utilization 변화에 따라 온도도 완만하게 변해 GPU 상태 metric 수집 경로가 정상적으로 동작하는 것을 확인했다.

metric관측 범위해석
DCGM_FI_DEV_GPU_UTIL0~62%GPU workload 변화가 시계열로 수집됨
DCGM_FI_DEV_GPU_TEMP44~50°CGPU 온도 변화가 시계열로 수집됨

6-3. vLLM 현재 상태

vllm:num_requests_running
vllm:num_requests_waiting
vllm:kv_cache_usage_perc
metric해석
vllm:num_requests_running0조회 순간 실행 중인 inference 요청 없음
vllm:num_requests_waiting0조회 순간 queue에서 대기 중인 요청 없음
vllm:kv_cache_usage_perc0조회 순간 active request가 사용하는 KV cache 없음

세 값이 모두 0인 것은 metric 수집 실패가 아니다. 이 값들은 현재 상태를 나타내는 gauge이므로 요청이 끝난 idle 시점에는 다시 0이 된다. 특히 약 1.6초에 끝나는 짧은 요청은 Prometheus scrape interval 사이에서 시작하고 종료되면 num_requests_running graph에 잡히지 않을 수도 있다. 완료된 요청의 활동량은 gauge보다 누적 counter의 증가량으로 확인하는 편이 적합하다.

increase(vllm:prompt_tokens_total[15m])