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
확인값
GPU
NVIDIA L4
memory
23,034 MiB
driver
595.71.05
nvidia-smi가 GPU와 driver를 보고하면 1층은 끝이다. 다만 이것은 호스트가 GPU를 본다는 뜻일 뿐, 컨테이너와는 아직 아무 관계가 없다.
3. k3s와 containerd runtime handler
k3s를 설치하고 노드 상태를 확인한다.
kubectl get nodes
key
확인값
k3s
v1.36.2+k3s1
node
Ready
구성
단일 노드 control-plane
k3s는 기동 시 호스트에 nvidia-container-runtime이 있으면 containerd 설정에 handler를 자동으로 만들어 준다. 직접 작성하지 않아도 되지만 실제로 생겼는지는 확인해야 한다.
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.allocatable.nvidia\.com/gpu\.shared}{"\n"}{end}'
key
확인값
해석
allocatable
nvidia.com/gpu.shared: 4
스케줄 가능한 slot 4개
자원 이름
gpu.shared
nvidia.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.3deployed
자원 광고
확인, nvidia.com/gpu.shared: 4
컨테이너 GPU 접근
확인, CUDA smoke test 완료
아래는 이 단계에서 확인하지 않았다.
- driver 설치 과정의 명령 원문 출력- Helm chart values와 time-slicing ConfigMap 원문- CUDA smoke test Pod의 manifest와 출력 원문- 여러 Pod가 slot을 동시에 점유할 때의 동작
마지막 항목이 다음 문서로 이어진다. slot이 넷이라는 것과 네 워크로드가 실제로 올라간다는 것은 다른 문제이고, 그 차이는 메모리에서 갈린다.