예전에 NVML은 무엇이고 어디에 쓰나라는 글에서 GPU 한 장의 온도와 전력, ECC 카운터를 읽는 방법을 다뤘다. 그런데 GPU 서버가 몇 대에서 몇십 대, 몇백 대로 늘어나면 고민이 달라진다. 한 장을 읽는 법보다 그 숫자들을 묶어서 운영 판단에 어떻게 쓰느냐가 더 급해진다. 이번 글은 그 이야기다.
한 줄로 줄이면, AIDC 모니터링은 "서버가 살아 있나"에서 "GPU가 건강한가, 전력이 산출물로 이어졌나, 고장 난 노드를 사람 없이 빼낼 수 있나"로 범위를 넓혀야 한다. 구체적으로는 DCGM 기반 GPU 헬스를 클러스터 단위 메트릭으로 모으고, 전력 효율을 PUE 대신 와트당 처리량으로 본다. 여기에 감지-격리-복구 자동화와 GPU 장애를 전제로 한 여유 용량까지 더해 네 가지 축으로 체계를 다시 짠다.
1. 개요
AIDC는 무엇인가
AIDC(AI Data Center)는 대규모 AI 모델의 학습과 추론을 위해 설계한 데이터센터를 가리킨다. GPU나 AI 가속기, 고속 저지연 네트워크, 고밀도 전력과 냉각 설비가 중심이다.
기존 데이터센터가 범용 컴퓨팅과 스토리지를 담는 그릇이었다면, AIDC는 AI 워크로드 하나에 맞춰 구조를 짠다는 점이 다르다.
국내에서는 2025년 6월 정부가 AI 데이터센터를 국가 SOC(사회간접자본)로 지정하고 전력, 입지, 인허가 규제 완화를 추진한다고 밝히면서 용어 자체가 널리 퍼졌다. 배경은 여기까지만 짚고 넘어간다.
기존 모니터링이 모자라는 지점
전통적인 데이터센터 모니터링은 가동률, 온습도, 전력량, PUE, 그리고 ping이나 포트 체크 같은 기본 헬스체크가 중심이다. 장애가 나면 알람이 울리고 사람이 티켓을 열어 대응한다.
GPU 클러스터에서는 이 방식이 잘 안 맞는다. 노드 64대로 돌리는 분산 학습에서 GPU 한 장의 NVLink 오류가 늘어나기 시작했다고 해보자. 그 노드는 ping도 잘 받고 kubelet도 Ready인데, 학습 작업 전체가 느려지거나 실패할 수 있다. 서버 헬스체크는 전부 초록색인데 작업은 죽어 있는 상황이다.
막상 GPU 클러스터를 맡아보면 여기서 많이 걸린다. 노드 상태가 멀쩡하니 애플리케이션 쪽을 한참 뒤지다가, 결국 nvidia-smi나 커널 로그의 XID 메시지를 보고서야 하드웨어 문제였다는 걸 안다.
무엇을 먼저 확인하나
지금 쓰는 모니터링 체계가 AIDC에 맞는지 가늠하려면 아래 질문을 던져본다.
- GPU별 ECC 오류, XID 오류, NVLink 오류 카운터를 클러스터 전체에서 한 쿼리로 볼 수 있나
- GPU가 쓴 에너지와 그 시간 동안 처리한 토큰이나 학습 스텝 수를 같은 대시보드에서 나눠볼 수 있나
- GPU 결함이 감지된 노드가 사람 개입 없이 스케줄링에서 빠지나
- 용량 계획에 GPU 장애로 빠지는 몫이 들어가 있나
하나라도 "아니오"라면 손볼 곳이 있다. 아래에서는 GPU 헬스 텔레메트리, 산출물 기준 전력 효율, 장애 대응 자동화, 장애를 전제로 한 용량 계획을 차례로 살펴본다.
2. GPU 헬스를 클러스터 단위 텔레메트리로 모은다
NVML 위에 DCGM을 얹는 이유
NVML은 GPU 한 장의 상태를 읽는 저수준 라이브러리다. 서버 한 대에서 스크립트로 읽기엔 충분하다.
하지만 수백 장을 그룹으로 묶어 헬스 정책을 걸고 작업 단위로 통계를 내려면 그 위에 관리 계층이 필요하다. 그 역할을 하는 게 NVIDIA DCGM(Data Center GPU Manager)이다. 데이터센터 GPU와 NVLink/NVSwitch 인프라를 위한 공식 관리 모니터링 도구 묶음이고, 공식 문서 기준으로 아래 기능을 제공한다.
- 헬스 모니터링과 진단: 백그라운드 헬스 체크와 Quick/Medium/Long 3단계 진단. PCIe, 메모리, 열, 전력, NVLink 등을 감시한다.
- NVLink/NVSwitch 모니터링: CRC FLIT, CRC Data, Replay, Recovery 오류 카운터와 링크 상태, 처리량.
- 작업 단위 통계: 프로세스나 작업 기준으로 에너지, 전력, 메모리, 성능을 집계한다.
- 정책과 알림: RAS 이벤트나 전력, 열 초과 시 알림을 보내거나 GPU 리셋, 페이지 은퇴 같은 조치를 건다.
- 프로파일링 메트릭: SM 활동도, 텐서 코어 활동, DRAM/PCIe/NVLink 대역폭.
노드에서 먼저 손으로 확인해본다
일단 DCGM이 깔린 노드에서 헬스 워치를 켜고 상태를 확인한다. -g는 GPU 그룹 ID다. -s 뒤에는 감시할 하위 시스템을 문자로 준다(p=PCIe, m=메모리, i=InfoROM, t=열/전력, n=NVLink, d=드라이버, x=ConnectX, a=전체).
$ dcgmi health -g 1 -s a $ dcgmi health -g 1 -c
능동 진단은 레벨을 골라 돌린다. 레벨이 올라갈수록 오래 걸리고 GPU에 부하를 준다.
# 레벨 1: 수 초, 배포 상태 점검 위주 $ dcgmi diag -r 1 # 레벨 2: 수 분, 메모리와 PCIe/NVLink까지 $ dcgmi diag -r 2 # 레벨 3: 십수 분, 스트레스 테스트 포함 $ dcgmi diag -r 3 # NVLink 오류 카운터와 링크 상태 $ dcgmi nvlink --errors -g 0 $ dcgmi nvlink --link-status
dcgmi diag -r 3은 GPU를 꽉 채워 돌리기 때문에 학습 작업이 올라가 있는 노드에서 돌리면 안 된다. 현장에서는 노드를 새로 투입할 때나 수리 후 복귀시킬 때 레벨 2~3을 돌리고, 운영 중에는 백그라운드 헬스 워치만 켜두는 식으로 나눈다.
dcgm-exporter로 Prometheus에 올린다
노드별로 dcgmi를 치는 건 확인용이고, 운영은 메트릭으로 한다. dcgm-exporter는 DCGM 텔레메트리를 Prometheus 텍스트 포맷으로 노출하는 NVIDIA 공식 오픈소스 익스포터다. 기본 포트는 9400이다.
쿠버네티스에서는 Helm 차트로 DaemonSet을 깐다. 알겠지만 GPU Operator를 이미 쓰고 있다면 dcgm-exporter가 같이 올라가 있으니 따로 설치할 필요가 없다.
$ helm repo add gpu-helm-charts https://nvidia.github.io/dcgm-exporter/helm-charts $ helm repo update $ helm install --generate-name gpu-helm-charts/dcgm-exporter
단독 서버라면 컨테이너로 띄운다. 이미지 태그는 NGC(nvcr.io)에서 드라이버와 DCGM 버전에 맞는 걸 골라야 한다. 아래 curl 출력의 온도와 전력 숫자는 메트릭 모양을 보여주려고 넣은 예시값이고, 실제로 잰 값이 아니다.
$ docker run -d --rm --gpus all --cap-add SYS_ADMIN -p 9400:9400 \
nvcr.io/nvidia/k8s/dcgm-exporter:<태그>
$ curl -s localhost:9400/metrics | grep -E 'GPU_TEMP|XID|POWER_USAGE'
DCGM_FI_DEV_GPU_TEMP{gpu="0",UUID="GPU-xxxxxxxx-...",...} 62
DCGM_FI_DEV_POWER_USAGE{gpu="0",UUID="GPU-xxxxxxxx-...",...} 387.214
DCGM_FI_DEV_XID_ERRORS{gpu="0",UUID="GPU-xxxxxxxx-...",...} 0
62는 섭씨 온도, 387.214는 와트 단위 전력이다. 실제 값은 GPU 모델과 부하, 냉각 환경에 따라 달라진다.
수집 대상은 저장소의 etc/default-counters.csv에 정의돼 있다. 이 파일을 고쳐 ConfigMap으로 넣으면 목록을 바꿀 수 있다. 수집 주기는 -c 옵션(밀리초)으로 조정하며 기본값은 30000, 즉 30초다.
AIDC에서 꼭 챙길 메트릭
기본 카운터 파일에 들어 있는 것 중 헬스 판단에 직접 쓰는 건 아래 정도다.
DCGM_FI_DEV_XID_ERRORS: 마지막으로 발생한 XID 오류 값. XID는 드라이버가 GPU 이상을 알릴 때 쓰는 오류 코드다.DCGM_FI_DEV_UNCORRECTABLE_REMAPPED_ROWS: 정정 불가능(uncorrectable) 에러 때문에 재매핑된 메모리 행 수. 재매핑은 불량 메모리 행을 예비 행으로 바꿔치는 기능이다. 정정 가능한 에러로 재매핑된 행은DCGM_FI_DEV_CORRECTABLE_REMAPPED_ROWS라는 별도 필드로 잡히니 헷갈리지 말자.DCGM_FI_DEV_ROW_REMAP_FAILURE: 행 재매핑 실패 여부.DCGM_FI_DEV_PCIE_REPLAY_COUNTER: PCIe 재전송 누적 횟수.DCGM_FI_DEV_NVLINK_BANDWIDTH_TOTAL: 전체 NVLink 처리량.DCGM_FI_PROF_PIPE_TENSOR_ACTIVE: 텐서 코어가 실제로 일한 비율.
처음 알림 규칙을 짤 때 자주 헷갈리는 게 DCGM_FI_DEV_XID_ERRORS다. 이름만 보면 누적 카운터 같지만, 도움말에 적힌 대로 "마지막 XID 값"을 담는 gauge다.
그래서 increase()를 걸면 엉뚱한 값이 나온다. 같은 XID가 반복되면 값이 안 바뀌어서 changes()로도 놓칠 수 있다. XID 알림은 0이 아닌 상태 자체를 잡고, 반복 발생 횟수는 syslog 쪽에서 따로 센다.
groups:
- name: gpu-health
rules:
- alert: GpuXidError
expr: DCGM_FI_DEV_XID_ERRORS != 0
for: 1m
labels:
severity: critical
- alert: GpuRowRemapFailure
expr: DCGM_FI_DEV_ROW_REMAP_FAILURE == 1
labels:
severity: critical
- alert: GpuUncorrectableRemapIncrease
expr: increase(DCGM_FI_DEV_UNCORRECTABLE_REMAPPED_ROWS[1h]) > 0
labels:
severity: warning
이 알림을 서버별 대시보드로 보는 데서 멈추면 아깝다. 같은 쿼리로 "오늘 XID가 난 GPU가 클러스터에 몇 장인가", "어느 랙에 몰려 있나"를 볼 수 있어야 냉각이나 전원 쪽 문제인지, 개별 GPU 문제인지 가를 수 있다. 노드 라벨에 랙과 전원 계통 정보를 붙여두면 이 집계가 한결 쉬워진다.
3. 전력 효율을 산출물 기준으로 본다
PUE가 말해주지 않는 것
PUE(Power Usage Effectiveness)는 시설 전체 전력을 IT 장비에 도달한 전력으로 나눈 값이다. 냉각과 배전 손실이 얼마나 되는지는 알려준다. 하지만 그 장비에 들어간 전력이 쓸모 있는 결과를 만들었는지는 알려주지 않는다.
GPU가 데이터 로딩을 기다리며 놀고 있어도 전력은 먹는다. PUE가 아무리 좋아도 GPU 활용률이 낮으면, 시설은 전기를 효율적으로 전달했지만 GPU는 그 전기를 헛되이 쓰고 있다.
tokens per watt라는 관점
이 지점을 짚은 사례로 Huawei가 HUAWEI CONNECT 2026에서 제시한 "tokens per watt"(와트당 토큰 산출량) 개념이 있다. 발전소와 전력망부터 랙, 컴퓨팅 클라우드, 모델 서비스까지 전 구간의 전력 소비를 실제 토큰 산출량과 직접 연결해 효율을 재자는 접근이다.
Huawei는 이를 그리드-인터랙티브 AIDC 솔루션과 함께 Watt, Heat, Bit, Construction의 "3+1" 프레임워크로 설명하고, AI 시대 데이터센터를 "토큰 공장"으로 표현했다.
Huawei 측 발표에 따르면 SenseCore가 엔드투엔드 최적화로 TPW를 80% 개선했다고 한다. 다만 벤더 발표 수치이고 측정 범위가 공개돼 있지 않으니, 지표의 방향성을 보여주는 사례 정도로 받아들이는 게 맞다.
운영 쪽에서 당장 해볼 수 있는 것
발전소까지 연결하는 건 시설 팀 몫이지만, GPU 구간은 인프라 엔지니어가 지금 바로 잴 수 있다. dcgm-exporter의 DCGM_FI_DEV_TOTAL_ENERGY_CONSUMPTION은 드라이버 로드 이후 누적 에너지(mJ) 카운터다. 여기에 rate()를 걸고 1000으로 나누면 와트가 나온다.
이 값으로 추론 서버의 토큰 카운터를 나누면 GPU 에너지 기준 효율이 나온다. 아래는 vLLM을 쓰는 경우의 예다. 토큰 메트릭 이름은 서빙 엔진마다 다르니 쓰는 엔진의 /metrics를 먼저 확인해야 한다.
# GPU 전력(W), 노드별 sum by (Hostname) (rate(DCGM_FI_DEV_TOTAL_ENERGY_CONSUMPTION[5m])) / 1000 # 생성 토큰 / GPU 에너지(J) = 줄당 토큰 수 sum(rate(vllm:generation_tokens_total[5m])) / (sum(rate(DCGM_FI_DEV_TOTAL_ENERGY_CONSUMPTION[5m])) / 1000)
이 값은 GPU 보드 전력만 분모에 넣은 것이라 CPU, 네트워크, 냉각은 빠져 있다. 그래도 같은 모델, 같은 클러스터에서 배치 크기나 양자화 설정을 바꿨을 때 전/후를 비교하는 용도로는 충분하다. 학습 클러스터라면 토큰 대신 초당 학습 스텝 수나 샘플 수를 분자로 쓴다.
제 권장은 이 지표를 GPU 활용률(DCGM_FI_DEV_GPU_UTIL) 옆에 나란히 두는 것이다. GPU_UTIL은 커널이 돌고 있던 시간 비율이라 높게 찍혀도 텐서 코어가 놀고 있을 수 있다. DCGM_FI_PROF_PIPE_TENSOR_ACTIVE와 줄당 토큰 수를 함께 봐야 "바쁜 척"과 "실제로 일한 것"이 구분된다.
4. 장애 대응을 감지-격리-복구 자동화로 옮긴다
대시보드를 사람이 보는 방식의 한계
GPU 노드 수백 대에서 XID 알림이 뜰 때마다 사람이 확인하고 cordon하고 drain하는 건 오래 못 간다. 새벽에 뜬 알림을 아침에 처리하는 사이 스케줄러는 그 노드에 새 작업을 계속 올릴 수 있다.
분산 학습이라면 그 한 노드 때문에 작업 전체가 체크포인트로 되돌아갈 수도 있다.
NVSentinel이 보여주는 방향
NVIDIA가 오픈소스로 공개한 NVSentinel은 "쿠버네티스 노드에서 GPU 결함을 감지하고 수정"하는 것이 목적인 프로젝트다. 구조를 보면 앞에서 말한 방향이 그대로 드러난다.
- 감지(Detect): DCGM 기반 GPU 헬스 모니터, syslog 헬스 모니터, 클라우드 제공자 유지보수 이벤트 모니터, NIC 헬스 모니터가 결함을 잡는다.
- 보호(Protect): Fault Quarantine이 노드를 cordon하고, Node Drainer가 워크로드를 내린다.
- 복구(Remediate): Fault Remediation이 유지보수를 트리거하고, Janitor가 노드 위에서 GPU 리셋이나 재부팅을 실제로 수행한 뒤 다시 투입한다.
요구사항은 Kubernetes 1.34 이상, Helm 3.0 이상, DCGM이 켜진 GPU Operator, cert-manager다. 이벤트 저장소로 MongoDB를 쓰기 때문에 영구 스토리지도 필요하다.
설치는 OCI 차트로 하는데, helm 명령 전에 해야 할 일이 하나 있다. nvsentinel 네임스페이스에 MongoDB 루트 비밀번호를 담은 mongodb Secret을 먼저 만들어야 한다. 이걸 빼먹으면 MongoDB와 NVSentinel 파드가 올라오지 못하고, --wait를 준 helm 명령은 타임아웃으로 끝난다. 처음 깔 때 여기서 막히기 쉽다.
$ kubectl create namespace nvsentinel
$ kubectl create secret generic mongodb -n nvsentinel \
--from-literal=mongodb-root-password="$(openssl rand -base64 32)"
$ helm upgrade --install nvsentinel oci://ghcr.io/nvidia/nvsentinel \
--version "$NVSENTINEL_VERSION" \
--namespace nvsentinel --create-namespace \
--set podMonitor.enabled=false \
--wait
재설치할 때는 조심해야 한다. 이전 설치의 MongoDB 볼륨이 남아 있다면 새 비밀번호를 만들지 말고, 기존 비밀번호를 복구해서 같은 값으로 Secret을 다시 만들어야 한다. 볼륨 안의 데이터는 예전 비밀번호로 초기화돼 있어서 새 값으로는 인증이 안 된다.
결함이 감지된 노드는 스케줄링에서 빠진다. 확인은 익숙한 명령으로 한다.
$ kubectl get nodes NAME STATUS ROLES AGE VERSION gpu-node-01 Ready <none> 30d v1.34.x gpu-node-02 Ready,SchedulingDisabled <none> 30d v1.34.x $ kubectl describe node gpu-node-02 | grep -A5 Conditions
도입할 때 먼저 막히는 곳
자동 격리를 처음 켜면 오탐이 가장 걱정된다. 일시적인 오류 하나로 노드가 줄줄이 cordon되면 오히려 용량이 모자라진다.
그래서 처음부터 복구 단계까지 전부 켜기보다, 감지와 격리만 켜고 복구는 사람이 승인하는 단계로 시작하는 걸 권한다. 몇 주 돌려서 어떤 XID가 실제로 리셋으로 해결되는지 쌓인 다음에 자동 복구를 넓힌다.
NVSentinel을 당장 못 쓰는 환경이라도 방향은 같다. DCGM 정책(dcgmi policy)으로 위반 이벤트를 받고, Alertmanager 웹훅으로 kubectl cordon을 거는 작은 컨트롤러부터 시작해도 된다. 핵심은 사람이 대시보드를 보고 판단하던 단계를 상태 기계로 바꾸는 데 있다.
# 현재 정책 확인 $ dcgmi policy -g 1 --get # 정책 위반 이벤트 대기(블로킹) $ dcgmi policy -g 1 --reg
5. GPU 장애를 전제로 용량을 잡는다
학술 클러스터의 실측 데이터
GPU가 얼마나 자주 문제를 일으키는지는 공개 데이터가 많지 않다. 참고할 만한 연구로 "Story of Two GPUs: Characterizing the Resilience of Hopper H100 and Ampere A100 GPUs"(arXiv 2503.11901)가 있다. A100과 H100 1,056장으로 구성된 Delta 시스템에서 2.5년간, 1,170만 GPU-시간 분량의 운영 데이터를 분석했다.
논문에서 운영자가 눈여겨볼 결과는 네 가지다.
- H100의 메모리 복원력이 A100보다 나빴다. 메모리 오류 기준 GPU당 MTBE(평균 오류 간격)가 A100보다 3.2배 짧았다. 즉 H100이 메모리 오류를 더 자주 겪었다.
- 반대로 주요 하드웨어 구성요소 전반의 복원력은 H100이 A100보다 유의미하게 나아졌다.
- 두 세대 모두 애플리케이션 레벨의 복구 장치가 부족해 GPU 오류가 작업 실패로 자주 이어졌다.
- 더 큰 규모로 투영했을 때 GPU 장애를 감당하려면 5% 수준의 과다 프로비저닝이 필요하다고 봤다.
이 수치는 특정 학술 클러스터 하나의 실측이지 업계 평균이 아니다. 워크로드, 냉각 방식, 펌웨어 버전에 따라 얼마든지 달라질 수 있다.
그러니 숫자 자체보다 두 가지 사실을 가져가면 된다. 신세대 GPU라고 모든 면에서 더 튼튼한 건 아니고, 장애는 일정 비율로 반드시 생긴다.
모니터링이 용량 계획까지 이어져야 하는 이유
여기서 모니터링의 범위가 하나 더 넓어진다. 격리된 노드 수, 평균 복구 시간, GPU 모델별 오류 빈도를 시계열로 쌓아두면 "우리 클러스터는 몇 %를 예비로 둬야 하나"를 자기 데이터로 답할 수 있다.
출발점은 GPU 노드 중 스케줄링 불가 상태인 노드의 비율이다. kube-state-metrics가 필요하다.
# 스케줄링 불가 상태인 GPU 노드 비율(kube-state-metrics 필요)
sum(kube_node_spec_unschedulable * on(node) group_left()
(kube_node_status_capacity{resource="nvidia_com_gpu"} > bool 0))
/
count(kube_node_status_capacity{resource="nvidia_com_gpu"} > 0)
분자에서 > bool 0의 bool을 빼면 안 된다. PromQL 비교 연산자는 bool 없이 쓰면 필터로만 동작하고, 조건을 통과한 시리즈의 원래 값을 그대로 남긴다. 그러면 곱해지는 값이 노드의 GPU 개수(예: 8)가 되고, 분자가 "cordon된 노드 수"가 아니라 "cordon된 노드들의 GPU 개수 합"으로 부풀려진다. 노드당 GPU 8장인 클러스터라면 노드 1대만 cordon돼도 8대처럼 잡힌다.
bool을 붙이면 GPU가 있는 노드마다 1이 나온다. 여기에 kube_node_spec_unschedulable(cordon이면 1, 아니면 0)을 곱해서 더하므로 분자는 cordon된 GPU 노드 수가 된다. 분모의 count()는 값이 아니라 시리즈 개수를 세기 때문에 > 0 필터만으로 GPU 노드 수가 맞게 나온다. cordon된 노드가 하나도 없을 때도 결과가 빈 값이 아니라 0으로 나와서 대시보드에 그리기 편하다.
이 비율을 몇 달 쌓아서 최댓값과 평균을 보면, 논문의 5%가 우리 환경에서 많은지 적은지 판단할 근거가 생긴다. 체크포인트 주기도 같은 데이터로 정한다. 노드 장애 간격이 짧은 클러스터라면 체크포인트를 더 자주 찍어야 재작업 비용이 줄어든다.
6. 어떤 순서로 도입하나
네 축을 한꺼번에 구축하려 하면 손이 모자란다. 제가 해보면서 무리가 없었던 순서를 정리하면 아래와 같다.
- dcgm-exporter와 XID, 행 재매핑 알림: 가장 싸고 효과가 크다. GPU Operator를 쓰면 이미 절반은 돼 있다.
- 노드 라벨에 랙, 전원 계통, GPU 모델 붙이기: 이게 있어야 개별 GPU 문제와 시설 문제를 가를 수 있다.
- 감지 후 자동 cordon: NVSentinel이든 자체 컨트롤러든 격리까지만 먼저 자동화한다.
- 에너지 대비 산출 지표: 줄당 토큰 수나 줄당 학습 스텝을 대시보드에 올리고 튜닝 전/후 비교에 쓴다.
- 격리 비율과 복구 시간 기반 용량 계획: 수개월 데이터가 쌓인 뒤 예비율을 정한다.
7. 정리
AIDC 모니터링은 가동률과 PUE를 버리자는 이야기가 아니다. 그 위에 GPU 헬스, 산출물 기준 효율, 자동 격리, 장애를 전제로 한 용량 계획을 얹자는 것이다.
NVML과 DCGM으로 읽은 숫자가 알림에서 끝나지 않고 스케줄러와 용량 계획까지 이어져야 GPU 클러스터를 제대로 운영하고 있다고 말할 수 있다. 첫걸음은 dcgm-exporter 메트릭에 XID 알림 하나 거는 것이다.