목표와 현재 결과

GCP의 NVIDIA L4 인스턴스를 단일 노드 k3s cluster로 만들고, 그 위의 Pod가 GPU를 실제로 쓸 수 있는 상태까지 연결했다.

호스트에서 nvidia-smi가 동작한다고 Pod가 GPU를 얻는 것은 아니다. driver부터 Pod의 자원 요청까지 여섯 계층이 모두 이어져야 하고, 어느 하나가 빠지면 호스트 쪽 확인은 전부 통과하는데 Pod만 GPU를 못 받는다. 이 장은 그 계층을 아래에서 위로 하나씩 연결한 기록이다.

driver 595.71.05, k3s v1.36.2+k3s1, RuntimeClass handler nvidia, Device Plugin 0.19.3까지 구성했고, node allocatable에 nvidia.com/gpu.shared: 4가 올라온 것과 컨테이너 안에서 CUDA에 접근되는 것을 확인했다.

여기서 이후 모든 장의 전제가 되는 사실이 하나 나온다. gpu.shared: 4는 GPU를 네 개로 나눈 것도, 6GB씩 격리한 것도 아니다. 스케줄 slot을 넷으로 광고할 뿐이고 메모리는 여전히 하나의 풀이다.

이 장의 증거 수준

이 단계는 환경을 세우면서 진행해 명령 원문 출력을 남기지 않은 구간이 있다. containerd 설정처럼 원문이 남은 것은 그대로 싣고, 나머지는 확인한 값만 표로 기록했다. 원문 출력이 함께 있는 기록은 03 이후 문서다.

1. 여섯 계층을 먼저 그린다

작업 순서를 정하려면 무엇이 무엇에 얹히는지부터 알아야 한다.

NVIDIA driver              호스트가 GPU를 인식하는가
→ nvidia-container-runtime  컨테이너에 GPU를 붙일 수 있는가
→ containerd runtime handler  k3s가 그 런타임을 알고 있는가
→ RuntimeClass              Pod가 그 handler를 지목할 수 있는가
→ NVIDIA Device Plugin      GPU가 스케줄 가능한 자원으로 광고되는가
→ Pod resource limit        Pod가 그 자원을 요청하는가

아래에서 위로 올라간다. 위에서부터 손대면 실패가 어느 층에서 났는지 구분되지 않는다.

2. NVIDIA driver

호스트가 GPU를 인식하는지부터 확인한다.

ubuntu-drivers devices

aplay command not found는 driver 탐색 실패가 아니다

이 명령은 오디오 유틸리티가 없으면 aplay command not found를 함께 출력한다. 하드웨어 열거 과정에서 나오는 부수적인 경고이고, NVIDIA 후보 driver를 찾지 못했다는 뜻이 아니다.

첫 단계의 오류 메시지를 실패로 읽으면 그 아래를 계속 파게 된다. 판단 근거는 이 줄이 아니라 nvidia-smi의 출력이다.

nvidia-smi
key확인값
GPUNVIDIA L4
memory23,034 MiB
driver595.71.05

nvidia-smi가 GPU와 driver를 보고하면 1층은 끝이다. 다만 이것은 호스트가 GPU를 본다는 뜻일 뿐, 컨테이너와는 아직 아무 관계가 없다.

3. k3s와 containerd runtime handler

k3s를 설치하고 노드 상태를 확인한다.

kubectl get nodes
key확인값
k3sv1.36.2+k3s1
nodeReady
구성단일 노드 control-plane

k3s는 기동 시 호스트에 nvidia-container-runtime이 있으면 containerd 설정에 handler를 자동으로 만들어 준다. 직접 작성하지 않아도 되지만 실제로 생겼는지는 확인해야 한다.

key해석
handler 이름nvidiaRuntimeClass가 지목할 이름
BinaryName/usr/bin/nvidia-container-runtime이 런타임이 컨테이너에 GPU를 붙인다
SystemdCgrouptruek3s의 cgroup 드라이버와 일치

여기서 nvidia라는 이름이 정해진다. 다음 단계의 RuntimeClass가 이 문자열을 그대로 가리켜야 하므로, 임의로 정하지 말고 이 파일에서 읽은 값을 쓴다.

4. RuntimeClass 등록

컨테이너를 어떤 런타임으로 만들지 Pod가 고를 수 있게 한다.

kubectl get runtimeclass nvidia -o yaml
key확인값
이름nvidia
handlernvidia

last-applied-configuration 경고는 정상 동작이다

kubectl apply로 등록할 때 이 경고가 나올 수 있다. 리소스가 원래 apply로 만들어지지 않아 해당 annotation이 없던 경우이며, kubectl이 annotation을 보완한 뒤 리소스는 정상 구성된다.

같은 경고가 05의 rollout undo에서도 나오는데 그쪽은 성격이 다르다. 거기서는 선언형 관리와 명령형 되돌리기가 어긋나는 문제라 실제로 손을 봐야 한다.

RuntimeClass는 어떤 런타임으로 컨테이너를 만들 것인가만 정한다. GPU가 몇 장 보일지, 몇 장을 배정받을지는 아직 정해지지 않았다. 이 구분이 뒤에서 계속 문제가 된다.

5. Device Plugin과 time-slicing

여기까지는 컨테이너가 GPU를 쓸 수 있는 상태일 뿐, Kubernetes는 GPU를 스케줄 가능한 자원으로 알지 못한다. Device Plugin이 그 광고를 맡는다.

Helm release와 ConfigMap으로 배포하고 time-slicing을 설정했다.

key
releasenvdp
namespacenvidia-device-plugin
chart / app version0.19.3 / 0.19.3
상태deployed
time-slicing 분할4

5-1. 노출된 자원 확인

kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.allocatable.nvidia\.com/gpu\.shared}{"\n"}{end}'
key확인값해석
allocatablenvidia.com/gpu.shared: 4스케줄 가능한 slot 4개
자원 이름gpu.sharednvidia.com/gpu가 아니다

자원 이름이 nvidia.com/gpu가 아니라 nvidia.com/gpu.shared인 것에 주의한다. time-slicing 설정에서 이름을 바꾸도록 해 두었기 때문이며, Pod manifest가 nvidia.com/gpu를 요청하면 이 노드에서는 스케줄되지 않는다.

5-2. slot은 메모리가 아니다

이 프로젝트 전체에서 가장 자주 오해를 부르는 지점이라 여기서 못박아 둔다.

오해실제
GPU가 네 장이 됐다물리 GPU는 한 장 그대로다
6GB씩 네 구획으로 나뉘었다메모리는 나뉘지 않는다. 하나의 풀이다
slot을 받으면 그만큼 메모리를 확보한다slot과 메모리 배정은 무관하다

4가 뜻하는 것은 **“이 노드에 GPU를 요구하는 Pod를 넷까지 배치해도 된다”**는 스케줄러용 회계 숫자다. 넷이 실제로 메모리에 들어가는지는 스케줄러가 알지 못한다.

L4는 MIG를 지원하지 않으므로 하드웨어 수준의 물리 분할 자체가 불가능하다. 메모리 분배를 강제하려면 각 워크로드가 스스로 상한을 지켜야 하고, vLLM에서는 --gpu-memory-utilization이 그 역할을 한다. 02에서 이 값을 실측한다.

이 성질 때문에 05에서 예상과 다른 실패가 나온다. GPU 메모리가 모자라도 Pod는 Pending이 되지 않는다. slot이 남아 있으면 스케줄러는 배치를 성공시키고, 부족은 컨테이너가 뜬 뒤에야 드러난다.

6. Pod에서 GPU 접근 검증

계층을 다 이었으니 실제로 컨테이너 안에서 GPU가 보이는지 확인한다. 호스트 nvidia-smi는 이 질문에 답하지 못한다.

CUDA 이미지로 smoke test Pod를 띄우고 완료 상태를 확인했다.

확인 항목결과
Pod 완료정상 종료
컨테이너 내부 GPU 접근확인

이 단계가 통과해야 앞의 다섯 층이 실제로 이어진 것이다. 앞 단계는 전부 “설정이 존재한다”는 확인이고, 여기만 “동작한다”는 확인이다.

7. 확인된 범위

계층결과
NVIDIA driver확인, 595.71.05, L4 23,034 MiB
containerd runtime handler확인, handler nvidia 자동 생성
k3s확인, v1.36.2+k3s1, node Ready
RuntimeClass확인, handler nvidia
Device Plugin확인, 0.19.3 deployed
자원 광고확인, nvidia.com/gpu.shared: 4
컨테이너 GPU 접근확인, CUDA smoke test 완료

아래는 이 단계에서 확인하지 않았다.

- driver 설치 과정의 명령 원문 출력
- Helm chart values와 time-slicing ConfigMap 원문
- CUDA smoke test Pod의 manifest와 출력 원문
- 여러 Pod가 slot을 동시에 점유할 때의 동작

마지막 항목이 다음 문서로 이어진다. slot이 넷이라는 것과 네 워크로드가 실제로 올라간다는 것은 다른 문제이고, 그 차이는 메모리에서 갈린다.