가짜 GPU 로 장치 플러그인의 광고와 할당을 본다
이 실습은 진짜 쿠버네티스에서 돕니다
VM 안에 k3s 한 대가 떠 있고, fake-gpu-operator 가 그 노드에 가짜 GPU 2장을 광고합니다. KWOK 컨트롤러도 미리 올려 두었습니다. 처음 뜨는 데 3분쯤 걸립니다. 세션이 끝나면 VM 과 파일은 사라집니다.
목표
장치 플러그인이 노드에 무엇을 적고 컨테이너에 무엇을 넘기는지 직접 보고, 확장 자원의 규칙과 GPU 장부 계산을 손에 익힙니다.
왜 중요한가
GPU 파드가 Pending 일 때 가장 먼저 볼 곳은 「노드가 몇 장을 광고하는가」 와 「이미 몇 장이 배치됐는가」 입니다. 스케줄러는 그 두 숫자의 차만 봅니다. 이 실습에서 그 장부를 손으로 계산해 보면, 나중에 큐·쿼터·갱 스케줄링이 끼어들어도 맨 아래에서 무엇이 결정되는지 잃지 않습니다. 그리고 가짜 GPU 가 진짜와 어디서 갈라지는지(컨테이너 안)를 함께 확인합니다.
단계
- 실제 노드가 광고하는 GPU 를
/root/gsb-fake/advertised.txt에node=,capacity=,allocatable=,product=네 줄로 적습니다. fake-lab네임스페이스에 GPU 1장짜리 Podgpu-1을/root/gsb-fake/gpu-1.yaml로 만들어 Running 으로 둡니다.gpu-1안의 환경 변수와nvidia-smi출력을/root/gsb-fake/inside.txt에 담습니다.- requests 1·limits 2 인 파드와 limits 0.5 인 파드를 만들어 보고 거절 메시지를
/root/gsb-fake/rules.txt에 모읍니다. gpu-2,gpu-3을 만들어gpu-3이 Pending 이 되게 하고 사유를/root/gsb-fake/pending.txt에 담습니다.- 풀 크기를 4장으로 바꾼 값을
/root/gsb-fake/values.yaml에 적어 helm upgrade 하고, 노드가 실제로 4장을 광고해gpu-3이 Running 이 되게 합니다. - KWOK 노드
gpu-a를/root/gsb-fake/kwok-node.yaml로 만들고 GPU 1장짜리 Podfar-1을 그 위에 둡니다. - GPU 장부를
/root/gsb-fake/report.json에nodes,allocatable_total,requested,free로 적습니다.
참고
- 실제 노드만 고르려면
kubectl get node -l '!type'처럼 KWOK 노드에 붙인 라벨을 빼고 고릅니다. - 컨테이너 명령은 SIGTERM 에 바로 끝나게 쓰면 파드를 지울 때 30초를 기다리지 않습니다. 예시의
trap줄이 그 역할입니다. - 흔한 실수 1: 6단계에서 helm upgrade 만 하고 기다리는 것. 노드별 토폴로지 ConfigMap 이 그대로라 광고 수가 바뀌지 않습니다.
- 흔한 실수 2: 7단계에서 톨러레이션만 주고 노드 선택을 빼는 것. 파드가 실제 노드로 가 버립니다.
노드가 광고하는 GPU 를 읽는다
실제 노드(KWOK 가 아닌 노드)가 광고하는 GPU 를 「/root/gsb-fake/advertised.txt」 에 node=, capacity=, allocatable=, product= 네 줄로 적으세요. product 는 노드 라벨 nvidia.com/gpu.product 의 값입니다.
GPU 수는 노드 오브젝트의 status.capacity 와 status.allocatable 에, 모델 이름은 라벨에 있습니다. 확장 자원 이름에 점이 있어서 jsonpath 에서는 점 앞에 역슬래시가 필요합니다.
파드 하나에 GPU 한 장을 준다
fake-lab 네임스페이스에 GPU 1장을 limits 로 요청하는 Pod gpu-1 을 「/root/gsb-fake/gpu-1.yaml」 로 만들어 적용하세요. 이미지는 registry.k8s.io/e2e-test-images/busybox:1.36.1-1 이고, Running 이어야 합니다.
확장 자원은 limits 에만 적어도 됩니다. requests 는 자동으로 같은 값이 됩니다. 컨테이너가 바로 끝나면 Running 을 볼 수 없으니 오래 자는 명령을 주세요.
컨테이너는 무엇을 받았나
gpu-1 컨테이너 안의 환경 변수 전체와 nvidia-smi 출력을 「/root/gsb-fake/inside.txt」 한 파일에 담으세요. MOCK_NVIDIA_VISIBLE_DEVICES= 줄이 있어야 합니다.
kubectl exec 로 컨테이너 안에서 env 와 nvidia-smi 를 차례로 실행합니다. nvidia-smi 는 장치 플러그인이 Allocate 응답으로 넣어 준 가짜입니다.
확장 자원의 두 규칙을 일부러 어긴다
두 파드를 만들어 보고 API 서버가 낸 거절 메시지를 「/root/gsb-fake/rules.txt」 에 모으세요. (가) requests 1·limits 2 로 요청하는 파드 (나) limits 를 0.5 로 요청하는 파드. 둘 다 거절되어야 하고 fake-lab 에 남으면 안 됩니다.
CPU 는 0.5 도, requests 와 limits 를 다르게 주는 것도 됩니다. 확장 자원은 둘 다 안 됩니다. 거절 메시지는 표준 오류로 나오므로 2>&1 로 함께 담습니다.
노드를 꽉 채워 Pending 을 만든다
GPU 1장씩 요청하는 Pod gpu-2, gpu-3 을 fake-lab 에 차례로 만드세요. 노드가 2장뿐이라 gpu-3 은 Pending 이 됩니다. 스케줄러가 남긴 사유를 「/root/gsb-fake/pending.txt」 에 담으세요.
사유는 파드의 PodScheduled 조건 메시지나 이벤트에 있습니다. 노드가 하나뿐이라 문장이 짧습니다.
광고 수를 늘려 기다리던 파드를 풀어 준다
fake-gpu-operator 의 풀 크기를 4장으로 바꾸세요. 값은 「/root/gsb-fake/values.yaml」 에 적고(runtimeClass.enabled: false 포함) 그 파일로 helm upgrade 합니다. 노드가 실제로 4장을 광고하고 gpu-3 이 Running 이 되어야 합니다. 차트는 oci://ghcr.io/run-ai/fake-gpu-operator/fake-gpu-operator 판 0.2.1, 릴리스 이름 gpu-operator, 네임스페이스 gpu-operator 입니다.
helm upgrade 만으로는 이미 등록된 노드가 바뀌지 않습니다. 노드마다 만들어 둔 토폴로지 ConfigMap 을 status-updater 가 다시 계산하지 않기 때문입니다. 그 ConfigMap 을 지운 뒤 status-updater 를, 그다음 device-plugin 을 다시 띄워 보세요. 실측에서 노드의 광고 수는 device-plugin 을 다시 띄운 뒤에야 바뀌었습니다.
kubelet 없는 노드에도 GPU 를 붙인다
KWOK 노드 gpu-a 를 「/root/gsb-fake/kwok-node.yaml」 로 만들어 적용하세요. 풀 라벨 run.ai/simulated-gpu-node-pool: default 와 KWOK 테인트가 있어야 합니다. 그다음 GPU 1장을 요청하는 Pod far-1 을 fake-lab 에 만들어 gpu-a 위에 Running 으로 두세요.
KWOK 컨트롤러는 kwok.x-k8s.io/node: fake 주석이 붙은 노드만 맡습니다. 파드가 그 노드로 가려면 테인트를 견디는 톨러레이션과, 실제 노드가 아니라 그 노드를 고르게 하는 노드 선택이 필요합니다.
GPU 장부를 직접 계산한다
지금 클러스터의 GPU 장부를 「/root/gsb-fake/report.json」 에 적으세요. nodes(GPU 를 광고하는 노드 이름 → 장수), allocatable_total(그 합), requested(노드에 배치된 Pending·Running 파드가 limits 로 요청한 합, 모든 네임스페이스), free(앞의 둘의 차) 네 키입니다.
노드 쪽은 status.allocatable, 파드 쪽은 spec.nodeName 이 있는 파드의 컨테이너 limits 를 더합니다. 아직 노드를 못 받은 파드는 장부에 들어가지 않습니다.