스케줄러를 바꾸면 무엇이 달라지나
한 줄 요약
Kueue 가 기본 스케줄러 「앞에」 입장 관문을 세운다면, KAI-Scheduler 는 스케줄러 「자체를」 바꿉니다. 파드에 schedulerName: kai-scheduler 를 적으면 그 파드는 KAI 가 배치하고, 그 순간 큐 계층·갱 스케줄링·우선순위 선점·GPU 쪼개기가 「노드를 고르는 단계」 안에서 함께 결정됩니다. NVIDIA Run:ai 의 스케줄러를 공개한 것이고, 지금은 CNCF 샌드박스 프로젝트입니다.
왜 이게 필요했나
앞 모듈들에서 두 가지 틈을 보았습니다. Kueue 는 쿼터로 입장시켰지만 노드에 자리가 없어 Pending 인 작업이 있었고, LWS 는 그룹을 함께 만들었지만 리더만 뜬 반쪽 그룹이 생겼습니다. 둘 다 「입장이나 생성」 과 「배치」 가 다른 층이라서 생기는 틈입니다.
배치 단계에서 직접 「이 파드 셋이 「모두」 들어갈 자리가 있을 때만 셋 다 놓는다」 를 결정하면 그 틈이 닫힙니다. 이것이 갱 스케줄링입니다. 같은 자리에서 「이 작업은 저 작업을 밀어낼 만큼 급한가」, 「이 카드는 반으로 나눠 줘도 되는가」 를 함께 따지는 것이 KAI 의 설계입니다. 대가도 있습니다. 클러스터에 스케줄러가 둘이 되고, KAI 가 맡은 파드는 기본 스케줄러의 플러그인 생태계를 그대로 쓰지 못합니다.
어떻게 동작하나
Queue(scheduling.run.ai/v2) 계층 큐. 자원마다 quota(보장)·limit(상한)·overQuotaWeight(남는 몫 나누기 비율), priority
PodGroup 함께 배치할 파드 묶음. pod-grouper 가 Job·JobSet·LWS 등을 보고 알아서 만든다
minMember PodGroup 이 배치되려면 함께 놓여야 하는 최소 파드 수(Job 은 주석 batch-min-member)
PriorityClass train 50 · build-preemptible 75 · build 100 · inference 125 를 깔아 준다
100 이상은 선점당하지 않는 대신 큐의 quota 안에서만 돈다
GPU 쪼개기 주석 gpu-fraction: "0.5" 또는 gpu-memory. 예약 파드가 카드 한 장을 잡고 나눠 준다
실험 VM(KAI v0.18.6, 구성요소 7개, 요청 메모리 합 약 1.8GiB)에서 잰 장면들입니다.
- 「갱」: GPU 2장짜리 파드 셋을
batch-min-member: "3"으로 넣었는데 남은 GPU 가 4장이었습니다. KAI 는 셋 다 Pending 으로 두고 PodGroup 에Resources were found for 2 pods while 3 are required for gang scheduling을 남겼습니다. 같은 Job 을 기본 스케줄러에 넣자 둘은 Running, 하나는 Pending 이었습니다. 반쪽 실행이 GPU 4장을 붙잡은 셈입니다. - 「우선순위」: train 파드 4개가 GPU 4장을 다 쓰는 중에 build 파드 하나를 넣자 36초 만에 train 하나가 밀려났습니다(
was preempted by higher priority workload). inference(125) 파드 셋을 quota 2 인 큐에 넣으면 셋째는NonPreemptibleOverQuota로 기다립니다. 선점당하지 않는 작업은 남의 몫을 빌려 쓸 수 없기 때문입니다. - 「쪼개기」:
gpu-fraction: "0.5"파드 둘이 같은 카드(NVIDIA_VISIBLE_DEVICES가 같은 UUID)를 나눠 받았고,kai-resource-reservation에 예약 파드 하나가 그 카드 한 장을 잡고 있었습니다. 메모리 격리는 기본 모드(NonMemoryEnforced)에서 강제되지 않습니다.
현장에서 만나는 모습
이 모듈의 실험에서 문서와 다르거나 문서에 없는 것을 셋 찾았습니다. 셋 다 기여 후보로 정리해 두었습니다.
- 「limit 을 비운 큐는 아무것도 못 받습니다.」 큐 문서의 표는 「limit 이 0 이거나 비어 있으면 추가 자원 없음(기본값)」 이라고 적고, 바로 아래 예시 제목은 「Unlimited Queue - default value is -1」 입니다. 실제로 gpu quota 1 에 limit 을 비운 큐에 1장짜리 파드를 넣었더니
OverLimit: … Limit is 0 GPUs로 quota 안의 1장도 받지 못했습니다. limit 은 꼭-1이나 숫자로 적으세요. - 「가짜 GPU 로 쪼개기를 시험하려면 두 값을 맞춰야 합니다.」 fake-gpu-operator 는 예약 파드를 기본값으로
runai-reservation네임스페이스에서만 찾습니다. KAI 는kai-resource-reservation에 만듭니다. 맞추지 않으면 바인더가Reached timeout while waiting for GPU reservation pod to be allocated로 계속 실패합니다. KAI 의 시험 스크립트는 이 값을 넣고 있지만 사용자 문서에는 없습니다. - 「k3s 에서는 RuntimeClass 도 막습니다.」 KAI 는 쪼갠 GPU 파드에
runtimeClassName: nvidia를 넣는데, k3s 의 RuntimeClass nvidia 는 containerd 에 그 런타임이 없으면no runtime for "nvidia" is configured로 파드를 못 띄웁니다.admission.gpuFractionRuntimeClassName=""로 끕니다.
Kueue 와 KAI 중 무엇을 고를지는 「기본 스케줄러를 그대로 둘 것인가」 로 갈립니다. 기존 워크로드와 플러그인을 건드리고 싶지 않으면 Kueue 로 입장만 다스리고, 갱·쪼개기·위상 인식 배치가 핵심이면 KAI 로 배치를 맡깁니다. Dynamo 의 멀티 노드 배포는 Grove 와 KAI 를 기본 조합으로 씁니다. 그 이야기가 다음 모듈입니다.
English corner
Gang scheduling means all or nothing at placement time, not at admission time.
gang, min member, preemptible, fraction 은 KAI 문서와 이슈에서 매일 보는 단어입니다.
다음 실습에서 할 것
큐 계층을 세우고 KAI 로 파드를 띄워 PodGroup 이 저절로 생기는 것을 봅니다. limit 을 비운 큐의 함정을 직접 밟고 고친 뒤, 갱 스케줄링으로 반쪽 실행을 막고, build 우선순위로 train 을 밀어내고, GPU 한 장을 둘이 나눠 받습니다.