본문 바로가기
기술/LLM

[Kubernetes GPU 인프라] 드라이버 설치부터 GPU Operator 자동화 및 모니터링까지

by 한결이상 2026. 9. 18.

Kubernetes GPU 인프라 A to Z: 드라이버 설치부터 GPU Operator 자동화·모니터링까지

이 글은 Kubernetes GPU 인프라 구축을 주제로 진행한 스터디 내용을 정리한 것이다. 드라이버 설치부터 NVIDIA Container Toolkit, Device Plugin 수동 배포 과정을 먼저 짚고, 이어서 왜 실전에서는 이 과정을 GPU Operator로 자동화하는지, 그리고 DCGM 기반 모니터링을 어떻게 구성하는지까지 정리했다.

들어가며 — GPU 인식은 왜 이렇게 번거로운가

CPU나 메모리는 Kubernetes가 기본으로 인식하는 리소스지만, GPU는 그렇지 않다. 호스트에 드라이버가 설치돼 있어야 하고, 컨테이너 런타임이 그 드라이버를 컨테이너 내부로 노출할 방법을 알아야 하고, Kubelet이 GPU를 스케줄링 가능한 리소스로 인식해야 한다. 이 세 계층이 각각 다른 컴포넌트(드라이버, Container Toolkit, Device Plugin)의 몫이라서, GPU 클러스터를 처음 구축할 때는 어디서 막혔는지 파악하기가 쉽지 않다.

이 글은 두 부분으로 나뉜다. 앞부분에서는 이 계층들을 하나씩 수동으로 연결해보면서 각 단계가 정확히 무슨 일을 하는지 정리하고, 뒷부분에서는 노드가 수십~수백 대로 늘어났을 때 이 수동 과정이 왜 감당하기 어려워지는지, 그리고 GPU Operator와 DCGM이 이를 어떻게 자동화·가시화하는지 정리한다.

Step 1. 노드에 GPU 드라이버가 제대로 잡혀있는지 확인하기

가장 먼저 확인할 것은 호스트 OS가 GPU를 인식하고 있는지 여부다. lspci는 PCI 버스에 연결된 장치 목록을 보여주는 명령어로, GPU 인식 여부를 확인할 때 사용한다.

lspci | grep -i nvidia

여기서 주의할 점이 있다. lscpu는 CPU 정보만 보여주는 명령어라서 GPU 인식 확인에는 쓸 수 없다. 이름이 비슷해서 혼동하기 쉬운 두 명령어다.

Ubuntu 환경이라면 ubuntu-drivers devices 명령으로 감지된 GPU와 추천 드라이버 목록을 확인할 수 있다. 여기서 한 가지 기억해둘 점은, 기본 Ubuntu 이미지에는 nvidia-smi가 포함돼 있지 않다는 것이다. 컨테이너 실행 시 GPU 드라이버 볼륨이 마운트되거나 드라이버가 설치된 이미지를 써야 인식된다.

드라이버 버전은 보통 535, 550, 560처럼 3자리 숫자로 표기된다. 그리고 커널이 업데이트될 때마다 드라이버 모듈을 다시 컴파일해줘야 하는데, 이를 자동으로 처리해주는 것이 DKMS(Dynamic Kernel Module Support)다. 커널 버전이 바뀌었는데 GPU가 갑자기 인식되지 않는 문제의 상당수는 이 재컴파일이 누락돼서 발생한다.

Step 2. NVIDIA Container Toolkit과 CDI 표준 이해하기

드라이버가 호스트에 설치돼 있어도, 컨테이너는 기본적으로 호스트의 GPU 드라이버에 접근할 방법이 없다. 이 간극을 메워주는 것이 NVIDIA Container Toolkit이다. 컨테이너 생성 시점에 호스트의 nvidia-smi 바이너리와 GPU 드라이버 라이브러리(.so 파일들)를 컨테이너 내부로 자동 바인드 마운트해준다.

Container Toolkit은 세 가지 모드로 동작한다.

  • auto: CDI 사양이 존재하면 cdi 모드로, 없으면 legacy 모드로 자동 전환한다.
  • legacy: NVML과 OCI Hook 기반으로 동작하는 기존 방식이다.
  • cdi: CDI(Container Device Interface) 표준을 따르는 방식이다.

CDI는 GPU뿐 아니라 FPGA 같은 다른 하드웨어 장치를 컨테이너에 할당하기 위한 표준 규격으로, /etc/cdi/ 또는 /var/run/cdi/ 경로의 JSON/YAML 명세 파일을 기반으로 OCI Runtime Hook(createContainer 등)을 통해 디바이스 노드와 드라이버 라이브러리를 마운트한다.

설정 파일(config.toml)을 직접 손으로 수정할 필요는 없다. 다음 명령 한 줄이면 NVIDIA 런타임이 자동으로 등록된다.

nvidia-ctk runtime --configure=docker

Docker에서 GPU를 직접 쓸 때는 --gpus all 또는 --gpus '"device=0"' 옵션으로 특정 GPU를 지정할 수 있는데, 이때도 호스트 드라이버 외에 Container Toolkit 설치가 별도로 필요하다는 점은 동일하다.

Step 3. K8s(K3s)에 NVIDIA 런타임 연동하기

Docker 레벨에서 GPU를 쓸 수 있게 됐다고 해서 K8s가 자동으로 GPU를 인식하는 것은 아니다. K8s(특히 K3s처럼 내장 containerd를 쓰는 배포판)에서는 containerd의 config.toml에 NVIDIA 런타임이 등록됐는지를 별도로 확인해야 한다.

grep nvidia /var/lib/rancher/k3s/agent/etc/containerd/config.toml

K3s는 자체 containerd를 내장하고 있기 때문에, /etc/rancher/k3s/config.toml.tmpl 설정 수정이나 NVIDIA 런타임 패치 작업이 별도로 필요한 경우가 많다. 이 지점이 일반 K8s와 K3s 환경 간에 GPU 연동 절차가 가장 크게 갈리는 부분이다.

Step 4. Device Plugin 배포로 GPU를 K8s 리소스로 등록하기

런타임까지 연동됐다면, 이제 K8s API 서버가 "이 노드에 GPU가 몇 개 있다"는 사실을 알게 해줘야 한다. 이 역할을 하는 것이 NVIDIA Device Plugin이다.

Device Plugin은 DaemonSet으로 배포돼야 한다. 노드별로 GPU 접근이 필요하기 때문에 Deployment가 아니라 모든 노드에 하나씩 떠 있어야 하는 구조다. Helm으로 설치할 수 있다.

helm repo add nvdp https://nvidia.github.io/k8s-device-plugin
helm install nvdp nvdp/k8s-device-plugin

Device Plugin은 각 노드의 GPU를 감지해서 nvidia.com/gpu라는 확장 리소스(Extended Resource)로 K8s API 서버에 등록한다. 여기서 알아둘 만한 특성이 하나 있는데, nvidia.com/gpu 같은 확장 리소스는 limits만 지정하면 requests가 자동으로 동일한 값으로 설정된다는 점이다. CPU/메모리처럼 requests와 limits를 따로 신경 쓸 필요가 없다.

내부적으로는 /var/lib/kubelet/device-plugins/ 경로의 gRPC 소켓 파일을 통해 Kubelet과 통신하며 GPU 리소스를 할당하고 상태를 알린다.

Step 5. 최소 권한으로 GPU 파드 배포하기

Device Plugin까지 배포되면 파드 스펙에 GPU 요청을 명시할 수 있다.

resources:
  limits:
    nvidia.com/gpu: 1

여기에 앞서 설정한 runtimeClassName을 함께 지정해야 GPU가 실제로 할당된다. runtimeClassName 지정 방식의 장점은 보안 측면에서 드러난다. 과거에는 GPU 파드에 Privileged 권한을 주거나 호스트 경로를 통째로 hostPath로 마운트하는 방식이 흔했지만, runtimeClassName을 지정하는 방식으로 전환하면 Privileged 권한과 hostPath 없이도 최소 권한으로 GPU를 사용할 수 있다. 컨테이너 런타임이 필요한 드라이버 바이너리와 라이브러리만 선별적으로 마운트해주기 때문이다.

Step 6. 여기까지 다 수동으로 하면 무슨 일이 생기나

Step 1부터 5까지의 과정—드라이버 설치, Container Toolkit 설정, K8s 런타임 연동, Device Plugin 배포, 보안 설정—을 노드 한두 대에서 한다면 크게 부담스럽지 않다. 문제는 노드가 수십, 수백 대로 늘어날 때다.

노드마다 드라이버 버전과 CUDA 버전이 조금씩 어긋나기 시작하고, 새 노드가 추가될 때마다 같은 설정을 반복해야 하며, 커널 업데이트 시점마다 DKMS 재컴파일이 제대로 됐는지 일일이 확인하기 어려워진다. 이 지점이 바로 GPU Operator로 전환을 고려하게 되는 시점이다.

Step 7. GPU Operator로 드라이버·툴킷·익스포터 일괄 자동화하기

NVIDIA GPU Operator는 드라이버, Container Toolkit, Device Plugin, 모니터링 익스포터까지 필요한 컴포넌트를 일괄로 설치하고 관리해주는 오퍼레이터다. 앞서 수동으로 진행했던 Step 1~4 과정을 선언적으로 관리할 수 있게 해준다.

여기서 중요한 두 구성요소가 있다.

NFD(Node Feature Discovery)는 노드의 GPU 모델, 드라이버 버전 같은 하드웨어 특성을 감지해서 K8s 노드 라벨로 자동 등록한다. 이를 통해 특정 GPU가 있는 노드로 파드를 스케줄링하는 것이 가능해진다.

GFD(GPU Feature Discovery)는 NFD의 GPU 특화 버전으로, CPU·GPU 속성을 동적으로 라벨링하는 역할을 한다.

다만 운영 규모가 커질 때 고려해야 할 지점이 하나 있다. etcd는 기본적으로 2GB(최대 8GB) 용량 제한이 있는데, 노드 수백 대 규모에서 라벨 업데이트가 잦아지면 etcd 데이터베이스 팽창과 쓰기 레이턴시 증가로 이어질 수 있다. 대규모 클러스터에서 GPU Operator를 운영할 때는 라벨링 주기나 옵션을 조정해서 이 부하를 관리할 필요가 있다.

Step 8. DCGM Exporter + Grafana로 GPU 모니터링 대시보드 구축하기

GPU Operator로 클러스터가 자동화됐다면, 다음은 가시성 확보다. 일반적인 Prometheus 스택만 설치해서는 GPU 메트릭이 수집되지 않는다. GPU 사용량, 온도, 메모리 등의 지표를 노출하려면 DCGM Exporter(dcgm-exporter)를 별도로 배포해야 한다.

DCGM Exporter를 배포하면 Prometheus가 GPU 메트릭을 수집할 수 있게 되고, 이를 Grafana 대시보드로 시각화할 수 있다. 커뮤니티에서 널리 쓰이는 대시보드(ID 23382)는 VRAM 사용량, PCIe 에러, NVLink 대역폭 패널을 기본으로 제공한다.

장애 알림 설정도 이 위에서 구성할 수 있다. DCGM_FI_DEV_XID_ERRORS 메트릭을 기반으로 XID 에러를 감지하는 알림 규칙을 만들 수 있는데, 특히 31, 43, 79, 92번처럼 치명적인 것으로 분류되는 XID 코드를 크리티컬 알림으로 설정해두는 것이 권장된다.

Step 9. "왜 도구마다 GPU 사용률이 다르게 나올까" — 모니터링 지표 제대로 읽기

GPU 모니터링을 실제로 운영하다 보면 흔히 마주치는 혼란이 있다. 같은 시점, 같은 GPU를 보고 있는데도 nvidia-smi나 nvtop이 보여주는 사용률과 DCGM 또는 하드웨어 프로파일링 카운터 기반 도구가 보여주는 사용률이 다르게 나오는 경우다.

이 차이는 측정 방식 자체가 다르기 때문에 생긴다. nvidia-smi나 nvtop 계열 도구는 GPU 커널이 실행 중이기만 하면 그 시간을 100%로 집계하는, 시간 기준(time-based) 방식이다. 반면 DCGM의 SM Activity나 하드웨어 성능 카운터를 직접 수집하는 프로파일링 도구는 실제로 SM 연산 파이프라인이 가동된 비율을 측정하기 때문에, 커널이 "실행 중"이라고 해서 반드시 연산 유닛을 꽉 채워 쓰고 있는 것은 아니라는 점까지 반영한다. 그 결과 nvidia-smi 기준으로는 100%인데 DCGM이나 프로파일링 도구 기준으로는 50~75% 수준으로 낮게 나오는 상황이 흔히 발생한다.

이 차이를 모르면 "사용률이 100%인데 왜 처리량이 기대만큼 안 나오지?"라는 질문에 답하기 어렵다. 실제로는 GPU가 바쁘게 "실행 중" 상태이긴 하지만, 연산 유닛을 완전히 채워 쓰지 못하고 있다는 신호일 수 있다는 것을 알아야 다음 진단(배치 크기 조정, 커널 최적화 등)으로 넘어갈 수 있다. 모니터링 대시보드에 여러 도구의 지표를 함께 올려두고 있다면, 이 지표들이 같은 것을 재고 있지 않다는 전제를 깔고 해석하는 것이 안전하다.

마무리 — 체크리스트로 정리하기

수동 설치 경로와 자동화 경로를 하나의 흐름으로 정리하면 다음과 같다.

수동으로 GPU를 K8s에 연결하는 경로

  1. lspci로 GPU 인식 확인, 드라이버 설치 여부 점검
  2. NVIDIA Container Toolkit 설치 및 모드(auto/legacy/cdi) 확인
  3. containerd config.toml에 NVIDIA 런타임 등록 확인 (K3s는 별도 경로 주의)
  4. Device Plugin을 DaemonSet으로 배포해 nvidia.com/gpu 리소스 등록
  5. runtimeClassName 지정으로 최소 권한 파드 배포

클러스터 규모가 커졌을 때의 경로

  1. GPU Operator로 드라이버·툴킷·Device Plugin 일괄 자동화 (NFD/GFD 라벨링, 대규모 클러스터의 etcd 부하 고려)
  2. DCGM Exporter + Grafana로 모니터링 대시보드 구축, XID 에러 알림 설정
  3. 도구별 사용률 측정 방식 차이를 이해하고 지표를 해석

두 경로를 모두 짚어본 이유는, 자동화 도구를 쓰더라도 그 밑에서 무슨 일이 일어나는지 알고 있어야 문제가 생겼을 때 어느 계층을 봐야 하는지 판단할 수 있기 때문이다. GPU Operator가 실패했을 때 결국 확인하게 되는 것도 드라이버 로그, Container Toolkit 설정, Device Plugin 파드 상태 같은 수동 경로의 각 지점들이다.