Kueue 로 GPU 쿼터를 세우고 줄을 세운다
이 실습은 진짜 쿠버네티스에서 돕니다
VM 안에 k3s 한 대가 떠 있고, fake-gpu-operator 가 가짜 GPU 4장을, Kueue v0.20.3 가 큐를 맡습니다. 처음 뜨는 데 3~4분 걸립니다. 세션이 끝나면 VM 과 파일은 사라집니다.
목표
큐·쿼터·입장의 흐름을 직접 세워, 「입장은 Kueue 가, 배치는 스케줄러가」 를 눈으로 확인합니다.
왜 중요한가
GPU 를 여러 팀이 나눠 쓰기 시작하면 「누가 먼저, 얼마나」 가 곧바로 분쟁이 됩니다. ResourceQuota 는 거절만 할 수 있어 배치 작업에 맞지 않고, Kueue 는 그 자리에 줄을 세웁니다. 큐의 장부(ClusterQueue status)를 읽을 줄 알면 「내 학습이 왜 안 도는가」 에 한 줄로 답할 수 있습니다. 그리고 라벨 하나 빠뜨리면 그 장부를 통째로 지나친다는 함정도 같이 봅니다.
단계
- ResourceFlavor
fake-gpu(nodeLabelsnvidia.com/gpu.present: "true")를/root/gsb-kueue/flavor.yaml로 만듭니다. - ClusterQueue
team-a(cpu 2·memory 2Gi·nvidia.com/gpu 2)를/root/gsb-kueue/cq.yaml로 만들어 Active 로 둡니다. team-a네임스페이스에 LocalQueuelq를/root/gsb-kueue/lq.yaml로 만듭니다.- GPU 1장짜리 Job
train-1을/root/gsb-kueue/job-1.yaml로 넣어 입장·Running 을 봅니다. train-2,train-3을 넣고, 기다리는train-3의 사유를/root/gsb-kueue/waiting.txt에 담습니다.- 라벨 없는 Job
sneaky를 넣어 Kueue 를 지나치는 것을 보고/root/gsb-kueue/bypass.txt에suspended=,workload=를 적습니다. - ClusterQueue 장부를
/root/gsb-kueue/usage.json에admitted,pending,gpu_reserved로 적습니다. train-1을 지워train-3이 스스로 입장해 Running 이 되게 합니다.
참고
kubectl get clusterqueue,localqueue -A와kubectl -n team-a get workloads가 장부의 첫 화면입니다.- Job 을 다시 만들려면 먼저 지우세요. Job 의 템플릿은 만든 뒤에 바뀌지 않습니다.
- 흔한 실수 1: 예제를 그대로 붙여 apiVersion 이 v1beta1 인 것. 이 판은 v1beta2 만 받습니다.
- 흔한 실수 2: 4단계에서 라벨 이름을 queue 나 queue-name 으로만 적는 것. 키는
kueue.x-k8s.io/queue-name입니다.
ResourceFlavor: 어떤 노드의 자원인가
ResourceFlavor fake-gpu 를 「/root/gsb-kueue/flavor.yaml」 로 만들어 적용하세요. spec.nodeLabels 에 nvidia.com/gpu.present: "true" 가 있어야 합니다(fake-gpu-operator 가 GPU 노드에 붙인 라벨입니다).
이 판의 Kueue 는 apiVersion kueue.x-k8s.io/v1beta2 만 받습니다. nodeLabels 는 입장할 때 파드의 nodeSelector 로 들어갑니다.
ClusterQueue: 쿼터의 주인
ClusterQueue team-a 를 「/root/gsb-kueue/cq.yaml」 로 만들어 적용하세요. 플레이버 fake-gpu 에서 cpu 2, memory 2Gi, nvidia.com/gpu 2 를 명목 쿼터로 두고, namespaceSelector: {{}} 로 모든 네임스페이스를 받습니다. 상태가 Active 여야 합니다.
coveredResources 에 적은 자원은 모든 플레이버가 빠짐없이 적어야 합니다. 플레이버가 아직 없으면 ClusterQueue 는 Active 가 되지 않습니다.
LocalQueue: 네임스페이스의 창구
team-a 네임스페이스에 LocalQueue lq 를 「/root/gsb-kueue/lq.yaml」 로 만들어 ClusterQueue team-a 에 잇고 적용하세요.
LocalQueue 는 쿼터를 갖지 않습니다. 어느 ClusterQueue 로 갈지만 적습니다.
첫 Job 이 입장한다
GPU 1장짜리 Job train-1 을 「/root/gsb-kueue/job-1.yaml」 로 만들어 team-a 에 넣으세요. 라벨 kueue.x-k8s.io/queue-name: lq 가 있어야 하고, 입장(admitted)해서 파드가 Running 이어야 합니다.
입장하면 Kueue 가 플레이버의 노드 라벨을 파드 템플릿의 nodeSelector 에 넣어 줍니다. 파드 스펙을 열어 확인해 보세요.
쿼터가 차면 줄을 선다
같은 꼴로 train-2, train-3 을 넣으세요. 쿼터가 2장이라 train-3 은 입장하지 못하고 멈춘(suspend) 채 기다립니다. 그 Workload 가 남긴 사유를 「/root/gsb-kueue/waiting.txt」 에 담으세요.
Kueue 가 Job 마다 만든 Workload 의 조건(QuotaReserved 등)에 사유가 있습니다. Job 의 spec.suspend 도 함께 보세요.
라벨 없는 Job 은 Kueue 를 지나친다
큐 라벨이 「없는」 GPU 1장짜리 Job sneaky 를 team-a 에 넣으세요. 쿼터가 찼는데도 바로 Running 이 됩니다. 확인한 것을 「/root/gsb-kueue/bypass.txt」 에 suspended=(sneaky 의 spec.suspend), workload=(Kueue 가 만든 Workload 이름, 없으면 none) 두 줄로 적으세요.
기본 설정에서 Kueue 는 queue-name 라벨이 있는 Job 만 관리합니다. 라벨이 없으면 Workload 도 만들지 않습니다.
ClusterQueue 의 장부를 읽는다
ClusterQueue team-a 의 status 에서 입장한 Workload 수, 기다리는 Workload 수, 예약된 GPU 수를 읽어 「/root/gsb-kueue/usage.json」 에 admitted, pending, gpu_reserved 세 키로 적으세요.
status.admittedWorkloads, status.pendingWorkloads, 그리고 status.flavorsReservation 안의 nvidia.com/gpu total 입니다. sneaky 는 이 장부에 없습니다.
앞 작업이 끝나면 줄이 저절로 줄어든다
train-1 을 지우세요. 그러면 기다리던 train-3 이 스스로 입장해 Running 이 됩니다. 아무것도 다시 제출하지 않습니다.
Job 을 지우면 그 Workload 가 사라지면서 쿼터가 풀리고, Kueue 가 줄의 다음 Workload 를 입장시킵니다. 파드가 SIGTERM 에 바로 끝나야 GPU 도 바로 풀립니다.