- 들어가며 — 계층을 정해 놓고 내려간다
- 1층 — 파드가 스케줄되지 않는다
- 2층 — 파드는 떴는데 GPU가 안 보인다
- 3층 — 드라이버와 툴킷 버전 불일치
- 4층 — 메모리 부족과 OOM
- 5층 — 노드에서 GPU가 사라진다
- 마치며 — 순서가 곧 실력이다
- 직접 해보기
- 시리즈
- 참고 자료
들어가며 — 계층을 정해 놓고 내려간다
GPU 장애 대응에서 가장 큰 시간 낭비는 순서 없이 여기저기 찔러 보는 것입니다. 노드에 들어가 nvidia-smi를 치고, 파드 로그를 보고, 다시 데몬셋을 재시작해 보고, 그러다 한 시간이 지납니다.
문제는 계층이 다섯 개인데 증상이 비슷하다는 데 있습니다. 파드가 안 뜨는 것은 스케줄링 문제일 수도, 자원이 없어서일 수도, 드라이버가 죽어서일 수도 있습니다. 그래서 순서를 미리 정해 놓고 위에서 아래로 내려가는 편이 항상 빠릅니다.
메트릭 이름과 설정은 2026-08-12에 공식 문서·저장소에서 확인했습니다. 버전에 따라 다를 수 있으니 사용 중인 버전에서 다시 확인하세요.
1층 — 파드가 스케줄되지 않는다
증상은 Pending입니다. 여기서 확인할 것은 두 가지뿐입니다. 노드가 자원을 광고하고 있는가, 그리고 남아 있는가입니다.
# 파드가 왜 스케줄 안 되는지 이벤트로 확인
kubectl describe pod <파드이름> | tail -20
# 노드의 총량과 할당 가능량 비교
kubectl get nodes -o custom-columns=\
'NODE:.metadata.name,CAP:.status.capacity.nvidia\.com/gpu,ALLOC:.status.allocatable.nvidia\.com/gpu'
# 아무 노드도 자원을 광고하지 않는다면 라벨부터
kubectl get nodes -L nvidia.com/gpu.count,nvidia.com/gpu.product,nvidia.com/mig.strategy
여기서 갈립니다. 자원 자체가 아예 없다면 2층으로 내려갑니다. 자원은 있는데 남은 수량이 0이면 그것은 장애가 아니라 용량 문제입니다. 그리고 총량은 8인데 할당 가능량이 7이라면 그것은 강한 단서입니다. 쿠버네티스 문서가 명시하듯 장치가 비정상으로 표시되면 kubelet은 할당 가능량만 줄이고 총량은 그대로 둡니다. 이 비대칭이 보인다면 5층으로 바로 뛰어도 됩니다.
라벨이 하나도 안 붙어 있다면 GPU Feature Discovery나 Node Feature Discovery가 돌지 않는 것이므로 오퍼레이터 쪽 문제입니다.
2층 — 파드는 떴는데 GPU가 안 보인다
컨테이너가 시작은 했는데 nvidia-smi가 실패하거나 CUDA가 장치를 못 찾는 경우입니다. 혹은 아예 컨테이너 생성 단계에서 실패합니다.
공식 트러블슈팅 문서가 지목하는 대표 오류 메시지가 있습니다. no runtime for 'nvidia' is configured입니다. 이것은 NVIDIA Container Toolkit이 컨테이너 엔진에 런타임 핸들러를 등록하지 못했다는 뜻입니다. 드라이버 문제가 아니라 툴킷 문제입니다.
# 오퍼레이터 파드 상태 한눈에
kubectl get pods -n gpu-operator
# 툴킷과 디바이스 플러그인 로그
kubectl logs -n gpu-operator nvidia-container-toolkit-daemonset-<POD-ID>
kubectl logs -n gpu-operator nvidia-device-plugin-daemonset-<POD-ID>
# validator가 어느 단계에서 막혔는지
kubectl describe pod -n gpu-operator -l app=nvidia-operator-validator
nvidia-operator-validator를 먼저 보는 습관이 중요합니다. 이 파드는 앞 단계들이 성공했는지를 순서대로 검증하므로, 어디서 막혔는지를 가장 빨리 알려 줍니다. 오퍼레이터 파드들이 Init 단계에서 멈춰 있다면 대개 드라이버 데몬셋이 아직 준비되지 않은 것이고, 다른 파드를 아무리 봐도 답이 안 나옵니다.
3층 — 드라이버와 툴킷 버전 불일치
이 계층의 증상은 미묘합니다. 어제까지 되던 것이 노드를 재부팅한 뒤 안 되거나, 새로 붙인 노드에서만 안 됩니다.
원인은 대개 셋 중 하나입니다. 커널이 업데이트되어 드라이버 모듈이 안 맞거나, 노드마다 OS 버전이 달라 오퍼레이터가 배포하는 드라이버 이미지가 맞지 않거나, 호스트에 이미 드라이버가 깔려 있는데 오퍼레이터도 드라이버를 배포하려는 상태입니다.
# 드라이버 컨테이너 로그 (커널 모듈 빌드 실패가 여기 찍힌다)
kubectl logs -n gpu-operator nvidia-driver-daemonset-<POD-ID> -c nvidia-driver-ctr
# 노드별 OS와 커널 버전 대조
kubectl get nodes -o wide
# GFD가 붙인 드라이버·런타임 버전 라벨 대조
kubectl get nodes -L nvidia.com/cuda.driver-version.full,nvidia.com/cuda.runtime-version.full
마지막 명령이 특히 유용합니다. GPU Feature Discovery가 붙이는 nvidia.com/cuda.driver-version.full과 nvidia.com/cuda.runtime-version.full 라벨을 노드끼리 비교하면 어느 노드만 다른지 한 줄로 보입니다.
호스트에 이미 드라이버가 있는 환경이라면 Helm 값에서 driver.enabled를 거짓으로 두어야 하고, 툴킷이 이미 구성되어 있다면 toolkit.enabled도 마찬가지입니다. 이 두 값을 잘못 두면 증상이 3층처럼 보이지만 원인은 설정입니다.
NVSwitch가 있는 시스템이라면 별도 조건이 하나 더 있습니다. 공식 문서는 패브릭 관리가 필요한 시스템에서 validator가 시스템이 아직 초기화되지 않았다는 메시지와 함께 실패할 수 있으며, 드라이버와 함께 fabricmanager를 설치해야 한다고 안내합니다.
4층 — 메모리 부족과 OOM
GPU 메모리 부족은 CPU 메모리 부족과 다르게 나타납니다. cgroup이 죽여 주는 것이 아니라 애플리케이션이 할당 실패로 예외를 던지거나, 조용히 성능만 무너집니다.
먼저 구분해야 할 것이 있습니다. 컨테이너의 시스템 메모리가 부족한 것인지, GPU의 프레임버퍼가 부족한 것인지입니다. 전자는 파드가 OOMKilled로 종료되고 이벤트에 남지만, 후자는 파드가 살아 있는 채로 요청만 실패합니다.
# 파드가 OOMKilled인지 확인 (시스템 메모리 쪽)
kubectl get pod <파드이름> -o jsonpath='{.status.containerStatuses[*].lastState.terminated.reason}{"\n"}'
# GPU 쪽은 메트릭으로 본다
kubectl -n gpu-operator port-forward svc/nvidia-dcgm-exporter 9400:9400
curl -s localhost:9400/metrics | grep -E 'DCGM_FI_DEV_FB_(USED|FREE)'
메트릭으로 보면 이렇습니다.
# 프레임버퍼 사용 비율이 천장인 GPU
DCGM_FI_DEV_FB_USED / (DCGM_FI_DEV_FB_USED + DCGM_FI_DEV_FB_FREE) > 0.95
# 파드별 프레임버퍼 점유 (kubernetes 매핑이 켜져 있을 때)
sum by (namespace, pod) (DCGM_FI_DEV_FB_USED)
# 추론 서버라면 선점이 답을 준다
sum by (model_name) (rate(vllm:num_preemptions_total[5m])) > 0
vLLM 같은 추론 서버는 시작할 때 GPU 메모리를 미리 잡아 두므로, 프레임버퍼 사용량이 높은 것 자체는 정상입니다. 판단 기준은 사용량이 아니라 선점 발생 여부입니다. 메모리 예약 비율은 vLLM 설정의 gpu_memory_utilization 값이 정하며, 이 값은 캐시 설정 정보 메트릭의 라벨로도 노출됩니다.
같은 카드에 여러 워크로드를 time-slicing으로 올린 상태라면 4층 문제는 필연입니다. 앞서 본 대로 복제본 사이에 메모리 격리가 없기 때문입니다.
5층 — 노드에서 GPU가 사라진다
가장 불쾌한 계층입니다. 어제 8장이던 노드가 오늘 7장입니다.
공식 트러블슈팅 문서가 이 상황을 직접 설명합니다. 디바이스 플러그인이 Xid 오류 때문에 장치를 비정상으로 표시하면 노드가 물리적으로 꽂혀 있는 것보다 적은 수의 GPU를 광고하며, 플러그인 로그에서 장치를 비정상으로 표시한다는 항목으로 확인할 수 있습니다.
# 디바이스 플러그인 로그에서 비정상 표시 확인
kubectl logs -n gpu-operator nvidia-device-plugin-daemonset-<POD-ID> | grep -i unhealthy
# 총량과 할당 가능량의 차이 재확인
kubectl describe node <노드이름> | sed -n '/Capacity/,/Allocated resources/p'
# 진단 자료 일괄 수집
curl -o must-gather.sh -L https://raw.githubusercontent.com/NVIDIA/gpu-operator/main/hack/must-gather.sh
chmod +x must-gather.sh
./must-gather.sh
메트릭 쪽 증거는 DCGM에 있습니다. 기본 CSV에 켜져 있는 DCGM_FI_DEV_XID_ERRORS는 마지막으로 마주친 Xid 오류 값을 담습니다. 다만 이것은 마지막 값이라 이력 추적에는 약합니다. 저장소 README에 따르면 exporter는 이를 감시해 Xid별로 시계열을 나누는 DCGM_EXP_XID_ERRORS_TOTAL을 제공하며, xid 라벨이 붙고 값 0은 오류 없음으로 취급되어 세지 않습니다. 이 필드는 기본 CSV에서 주석 처리되어 있으므로 쓰려면 직접 켜야 합니다.
하드웨어 열화를 보는 지표도 기본으로 켜져 있습니다. DCGM_FI_DEV_UNCORRECTABLE_REMAPPED_ROWS, DCGM_FI_DEV_CORRECTABLE_REMAPPED_ROWS, DCGM_FI_DEV_ROW_REMAP_FAILURE 세 개입니다. 특히 마지막 값이 리맵 실패를 뜻하므로, 여기에 값이 찍히면 소프트웨어로 해결할 단계가 아닙니다. 클럭 이벤트를 추적하려면 DCGM_EXP_CLOCK_EVENTS_TOTAL이 DCGM_FI_DEV_CLOCKS_EVENT_REASONS를 감시하며 clock_event 라벨로 나뉜다는 점을 기억해 두면 좋습니다.
마치며 — 순서가 곧 실력이다
다섯 계층을 한 줄씩으로 압축하면 이렇습니다. 자원이 광고되는가, 컨테이너가 장치를 보는가, 버전이 맞는가, 메모리가 충분한가, 카드가 살아 있는가. 위에서부터 확인하면 대부분의 장애가 두세 번째 명령에서 갈립니다.
한 가지 습관을 덧붙이고 싶습니다. 총량과 할당 가능량을 비교하는 것을 첫 명령으로 두는 것입니다. 이 한 줄로 5층 문제인지 아닌지가 즉시 갈리고, 그 판단만으로도 진단 경로가 절반으로 줄어듭니다.
이것으로 시리즈를 마칩니다. 오퍼레이터로 GPU를 노드에 올리고, 자원으로 광고하고, 필요하면 나눠 쓰고, 메트릭으로 관측하고, 약속을 세우고, 깨졌을 때 순서대로 내려가는 것까지가 한 바퀴였습니다. 각 단계에서 이름 하나를 추측 대신 문서에서 확인하는 습관이, 결국 새벽에 깨어 있는 시간을 줄여 줍니다.
직접 해보기
- kubectl 명령어 찾기 — 위 플레이북의 명령들을 상황별로 다시 찾아보세요.
- K8s 실습 랩 — describe와 로그 확인의 흐름을 직접 반복해 보세요.
- Kubestronaut 퀴즈 — 노드 자원과 파드 상태 진단을 문제로 점검해 보세요.
시리즈
- 이전 글: GPU 서빙 SLO와 알람 설계
- 다음 글: 이 글이 시리즈의 마지막입니다.
참고 자료
- GPU Operator Troubleshooting: https://docs.nvidia.com/datacenter/cloud-native/gpu-operator/latest/troubleshooting.html
- dcgm-exporter 기본 카운터 CSV: https://github.com/NVIDIA/dcgm-exporter/blob/main/etc/default-counters.csv
- dcgm-exporter README: https://github.com/NVIDIA/dcgm-exporter/blob/main/README.md
- Kubernetes Device Plugins: https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/device-plugins/
- gpu-operator 저장소 values.yaml: https://github.com/NVIDIA/gpu-operator/blob/main/deployments/gpu-operator/values.yaml
현재 단락 (1/58)
GPU 장애 대응에서 가장 큰 시간 낭비는 순서 없이 여기저기 찔러 보는 것입니다. 노드에 들어가 `nvidia-smi`를 치고, 파드 로그를 보고, 다시 데몬셋을 재시작해 보고,...