KAI-Scheduler 로 갱·우선순위·GPU 쪼개기를 다룬다
이 실습은 진짜 쿠버네티스에서 돕니다
VM 안에 k3s 한 대가 떠 있고, 가짜 GPU 4장과 KAI-Scheduler v0.18.6 가 올라 있습니다. 쪼개기를 위해 fake-gpu-operator 의 예약 네임스페이스를 KAI 와 맞추고, KAI 가 RuntimeClass 를 넣지 않게 설정해 두었습니다(왜 그런지는 읽기 참고). 처음 뜨는 데 4~5분 걸립니다. 세션이 끝나면 VM 과 파일은 사라집니다.
목표
KAI 의 큐 계층·PodGroup·갱 스케줄링·우선순위 선점·GPU 쪼개기를 차례로 직접 일으키고, 그 결정이 PodGroup 과 이벤트에 어떤 문장으로 남는지 읽습니다.
왜 중요한가
GPU 클러스터에서 가장 비싼 낭비는 「일부만 떠서 기다리는 작업」 과 「한 장을 다 못 쓰는 작은 작업」 입니다. 갱 스케줄링은 앞의 것을, 쪼개기는 뒤의 것을 줄입니다. 그리고 우선순위가 선점과 쿼터를 어떻게 바꾸는지 알아야 「왜 내 학습이 밀려났나」 와 「왜 내 추론이 quota 를 못 넘나」 에 답할 수 있습니다. 실습 3단계의 함정은 실제 문서와 다른 동작이라 꼭 밟아 보세요.
단계
- 큐
lab·research·ops를/root/gsb-kai/queues.yaml로 만듭니다. - KAI 로 띄운 Pod
p1을/root/gsb-kai/p1.yaml로 만들고, 생긴 PodGroup 이름을/root/gsb-kai/podgroup.txt에 적습니다. - limit 을 비운 큐
nolimit에 Podnl-1을 넣어 Pending 사유를/root/gsb-kai/nolimit.txt에 담습니다. nolimit의 gpu limit 을 -1 로 바꿔nl-1을 Running 으로 만듭니다.- 갱 Job
gang(1장 × 3, min-member 3)을 넣어 남은 2장에도 셋 다 Pending 인 사유를/root/gsb-kai/gang.txt에 담습니다. gang을 지우고 train 파드tr-1·tr-2로 GPU 를 채운 뒤 build 파드bd-1로 하나를 밀어냅니다.- 남은 train 파드를 지우고
gpu-fraction: "0.5"파드fr-1·fr-2를/root/gsb-kai/fraction.yaml로 만들어 같은 GPU 를 나눠 받게 합니다. /root/gsb-kai/report.json에gpu_allocated,reservation_pods,shared_gpu를 적습니다.
참고
kubectl get queues.scheduling.run.ai,kubectl -n kai-lab get podgroups,kubectl -n kai-lab get events가 기본 화면입니다.- 작업은
kai-scheduler네임스페이스가 아니라kai-lab에 넣습니다. - 흔한 실수 1: schedulerName 을 빼는 것. 그러면 기본 스케줄러가 배치하고 KAI 의 큐·갱·쪼개기가 하나도 적용되지 않습니다.
- 흔한 실수 2: 7단계에서 gpu-fraction 과 nvidia.com/gpu limits 를 함께 적는 것. KAI 입장 웹훅이 거절합니다.
큐 계층을 세운다
「/root/gsb-kai/queues.yaml」 에 KAI 큐 셋을 적어 적용하세요. 부모 lab(gpu quota 4, limit -1), 자식 research(부모 lab, gpu quota 2, limit 4), 자식 ops(부모 lab, gpu quota 2, limit -1). cpu·memory 는 셋 다 quota -1, limit -1 입니다.
apiVersion 은 scheduling.run.ai/v2, 종류는 Queue 입니다. 작업은 자식(잎) 큐에만 넣을 수 있습니다.
KAI 로 파드 하나를 띄우면 PodGroup 이 생긴다
kai-lab 네임스페이스에 Pod p1(GPU 1장, schedulerName: kai-scheduler, 라벨 kai.scheduler/queue: research)을 「/root/gsb-kai/p1.yaml」 로 만들어 Running 으로 두세요. KAI 가 만든 PodGroup 의 이름을 「/root/gsb-kai/podgroup.txt」 에 한 줄로 적으세요.
pod-grouper 가 파드마다(또는 상위 워크로드마다) PodGroup 을 만들고 파드 주석 pod-group-name 에 그 이름을 적습니다.
limit 을 비운 큐의 함정
부모 lab 아래에 gpu quota 1 이고 「limit 을 적지 않은」 큐 nolimit 을 만들고, 그 큐에 GPU 1장짜리 Pod nl-1 을 넣으세요. quota 가 1인데도 nl-1 은 Pending 입니다. PodGroup 이 남긴 사유를 「/root/gsb-kai/nolimit.txt」 에 담으세요.
PodGroup 의 status.schedulingConditions 에 사유가 있습니다. limit 을 비우면 0 으로 읽힙니다.
limit 을 -1 로 적어 고친다
큐 nolimit 의 gpu limit 을 -1(상한 없음)로 바꾸세요. 기다리던 nl-1 이 Running 이 되어야 합니다.
kubectl patch 로 spec.resources.gpu.limit 만 바꿉니다. 다음 스케줄링 주기에 바로 반영됩니다.
모두 아니면 아무것도
GPU 1장짜리 파드 셋을 함께 놓아야 하는 Job gang(parallelism 3, 주석 kai.scheduler/batch-min-member: "3", 큐 research, KAI 스케줄러)을 넣으세요. 남은 GPU 가 2장이라 둘은 놓을 수 있지만 셋 다 Pending 이어야 합니다. PodGroup 의 사유를 「/root/gsb-kai/gang.txt」 에 담으세요.
큐 라벨은 Job 과 파드 템플릿 모두에, 주석은 Job 에 답니다. 기본 스케줄러라면 두 파드는 떴을 것입니다. 큐 research 의 limit(4)도 넘지 않게 장수를 맞췄습니다.
build 가 train 을 밀어낸다
Job gang 을 지우고, 우선순위 train 인 1장짜리 Pod tr-1, tr-2 를 큐 research 에 넣어 GPU 4장을 다 채우세요. 그다음 우선순위 build 인 1장짜리 Pod bd-1 을 같은 큐에 넣으세요. bd-1 이 Running 이 되고 train 파드 하나가 밀려나야 합니다.
우선순위는 파드의 priorityClassName 입니다. build(100)는 선점당하지 않는 대신 큐의 quota 안에서만 돌고, train(50)은 선점당할 수 있습니다.
GPU 한 장을 둘이 나눠 받는다
먼저 남은 train 파드를 지워 GPU 한 장을 비우세요. 그다음 주석 gpu-fraction: "0.5" 를 단 Pod fr-1, fr-2(큐 research, KAI 스케줄러, nvidia.com/gpu limits 없음)를 「/root/gsb-kai/fraction.yaml」 로 만들어 적용하세요. 둘 다 Running 이고 같은 GPU 를 받아야 합니다.
쪼갠 요청은 limits 가 아니라 주석으로 적습니다. 둘을 함께 쓰면 안 됩니다. KAI 는 kai-resource-reservation 에 예약 파드를 만들어 카드 한 장을 잡고, 두 파드에 같은 장치를 넘깁니다.
누가 GPU 를 쥐고 있나
「/root/gsb-kai/report.json」 에 gpu_allocated(노드에 배치된 Pending·Running 파드가 limits 로 요청한 nvidia.com/gpu 의 합, 모든 네임스페이스), reservation_pods(kai-resource-reservation 의 파드 수), shared_gpu(fr-1 이 받은 NVIDIA_VISIBLE_DEVICES 값) 세 키를 적으세요.
쪼갠 파드는 nvidia.com/gpu limits 가 없습니다. 그 몫은 예약 파드가 한 장으로 쥐고 있습니다. 그래서 노드 장부에는 예약 파드가 보입니다.