빌린 것은 언제든 회수된다
한 줄 요약
팀마다 쿼터를 딱 잘라 두면 한 팀이 놀 때 그 GPU 도 놉니다. Kueue 는 ClusterQueue 들을 「cohort」 로 묶어 남는 쿼터를 빌려 쓰게 하고, 원래 주인이 돌아오면 「되찾고(reclaim)」, 같은 큐 안에서는 우선순위로 「앞질러 가게(preempt)」 합니다. 빌려 주는 쪽의 안전(빌려 간 것은 언제든 회수된다)과 빌리는 쪽의 이익(놀던 GPU 를 쓴다)을 정책 몇 줄로 맞바꾸는 장치입니다.
왜 이게 필요했나
GPU 클러스터 운영자가 매주 듣는 두 문장이 있습니다. 「우리 팀 쿼터가 꽉 찼는데 옆 팀 GPU 는 놀고 있다」 와 「우리 팀 GPU 를 빌려 간 작업 때문에 우리 작업이 못 돈다」. 둘 다 맞는 말이고, 둘을 동시에 만족시키는 것이 정책 설계의 일입니다. 쿼터를 크게 주면 첫 문장이, 딱 잘라 주면 두 번째 문장이 사라지지 않습니다.
Kueue 의 답은 「빌려 쓰되, 빌린 것은 회수 대상」 입니다. 팀 A 가 팀 B 의 놀던 쿼터를 빌려 작업을 돌리다가 팀 B 가 자기 몫을 요구하면, Kueue 는 팀 A 의 「빌린 몫으로 돌던 작업」을 내보내(evict) 다시 줄 세우고 팀 B 를 입장시킵니다. 내보낸 작업은 지워지지 않고 다시 기다립니다. 그래서 회수 대상이 되는 작업은 체크포인트를 남기며 도는 것이 전제입니다.
어떻게 동작하나
cohortName 같은 이름을 가진 ClusterQueue 들이 쿼터를 나눠 쓰는 묶음이 된다
borrowingLimit 내가 남에게서 빌릴 수 있는 최대치(없으면 무제한)
lendingLimit 내 쿼터 중 남에게 빌려 줄 수 있는 최대치(없으면 전부)
preemption.reclaimWithinCohort 남이 빌려 간 내 몫을 되찾을 때 누구를 내보낼지(Never·LowerPriority·Any)
preemption.withinClusterQueue 내 큐 안에서 우선순위가 높은 작업이 낮은 작업을 내보내는지(Never·LowerPriority·…)
WorkloadPriorityClass 큐 안의 순서와 선점에 쓰는 우선순위. 라벨 kueue.x-k8s.io/priority-class 로 붙인다
stopPolicy Hold(새 입장 중단) · HoldAndDrain(입장 중단 + 돌던 것도 내보냄)
실험 VM 에서 잰 장면입니다. 쿼터 2장씩인 team-a·team-b 를 cohort lab 으로 묶고 team-a 가 1장짜리 Job 셋을 넣자 셋 다 입장했고, team-a 의 장부에 borrowed: 1 이 찍혔습니다. 이어서 team-b 가 2장짜리 Job 을 넣자 Kueue 는 team-a 의 Workload 하나를 이렇게 내보냈습니다.
Preempted to accommodate a workload … due to reclamation within the cohort;
preemptor path: /lab/team-b; preemptee path: /lab/team-a
우선순위는 파드의 PriorityClass 와 별개인 WorkloadPriorityClass 를 씁니다. 파드 PriorityClass 는 kube-scheduler 의 선점까지 건드리지만, WorkloadPriorityClass 는 Kueue 의 입장 순서와 Kueue 선점에만 영향을 줍니다. 「큐에서 먼저 들어가되 노드에서 남의 파드를 쫓아내지는 않는다」 를 표현할 수 있는 이유입니다.
공정 분배(fair sharing)는 설정 파일의 fairSharing 을 켜면 cohort 안에서 각 큐의 「몫 대비 사용량」 이 비슷해지도록 선점 후보를 고르고, admissionFairSharing 은 과거 사용량까지 따져 입장 순서를 정합니다. 둘 다 컨트롤러 설정을 바꾸고 재시작해야 해서 이 실습에서는 읽기로만 다룹니다(미실측).
현장에서 만나는 모습
- 「빌려 쓴 작업이 쫓겨나는 것은 정상입니다.」 쫓겨난 작업 쪽에서는 사고처럼 보이지만 정책대로입니다. 그래서 Workload 의 조건과 이벤트에 「누가 누구를 왜」 가 남습니다. 이 기록을 읽을 줄 알아야 문의에 답할 수 있습니다.
- 「입장 대기 순서는 visibility API 로 봅니다.」
kubectl get --raw /apis/visibility.kueue.x-k8s.io/v1beta2/clusterqueues/<이름>/pendingworkloads가 대기열과 각자의 위치를 돌려줍니다. 「내 작업이 몇 번째인가」 에 대한 답입니다. - 「정비 전에는 Hold 를 겁니다.」 노드를 비우기 전에 큐의 stopPolicy 를 Hold 로 두면 새 작업이 들어오지 않고, 돌던 작업은 끝까지 돕니다. HoldAndDrain 은 돌던 작업까지 내보냅니다.
English corner
Borrow freely, but anything borrowed can be reclaimed.
reclaim(되찾다)과 preempt(앞질러 내보내다)는 둘 다 「쫓아낸다」 로 번역되기 쉽지만 이유가 다릅니다. 앞의 것은 주인이 돌아온 것이고, 뒤의 것은 더 급한 작업이 온 것입니다.
다음 실습에서 할 것
cohort 로 묶인 두 큐를 세우고, 빌려 쓰기 → 되찾기 → 같은 큐 안의 앞지르기를 차례로 일으킵니다. 큐에 Hold 를 걸어 새 입장을 막아 보고, visibility API 로 대기열을 읽은 뒤, 두 큐의 장부를 정리합니다.