하나가 죽으면 모두 다시
한 줄 요약
분산 학습 하나는 파드 여러 개가 「한 몸」처럼 움직여야 하는 일입니다. 서로의 주소를 알아야 하고, 하나가 죽으면 모두 다시 시작해야 하고, 누가 끝나면 전체가 끝난 것인지 정해야 합니다. Job 은 이 셋을 모릅니다. JobSet 은 역할이 다른 Job 여러 개를 하나의 객체로 묶고, 그 셋을 선언으로 적게 해 줍니다.
왜 이게 필요했나
PyTorch 분산 학습을 Job 하나로 띄우려 해 보면 바로 막힙니다. 랭크 0 이 마스터 주소를 열고 나머지가 거기 붙어야 하는데, Job 의 파드 이름은 무작위 접미사가 붙어 미리 알 수 없습니다. 인덱스 Job(completionMode: Indexed)이 이름 문제를 풀어 주었지만, 「매개 서버 둘과 워커 여덟」 처럼 역할이 다른 파드 묶음은 여전히 Job 여러 개로 나눠 띄우고 손으로 엮어야 했습니다. 워커 하나가 GPU 오류로 죽으면 나머지 일곱은 멈춘 랭크를 기다리며 GPU 를 붙잡고 있습니다. 누군가 전체를 내렸다 다시 올려야 합니다.
Kubeflow 의 PyTorchJob·MPIJob 이 각자 이 문제를 풀어 왔고, 프레임워크마다 비슷한 코드가 반복됐습니다. JobSet 은 그 공통 부분을 쿠버네티스 SIG 의 API 하나로 뽑아낸 것입니다. Kubeflow Trainer v2 의 TrainJob 이 내부에서 JobSet 을 만들어 쓰는 이유가 그것입니다.
어떻게 동작하나
replicatedJobs 역할마다 Job 템플릿과 그 Job 을 몇 벌 만들지(replicas)
network 기본으로 JobSet 이름의 헤드리스 서비스를 만들고 파드에 hostname·subdomain 을 준다
→ <jobset>-<역할>-<job번호>-<파드번호>.<jobset> 으로 서로를 찾는다
failurePolicy maxRestarts 만큼, Job 하나가 실패하면 JobSet 전체를 다시 만든다(restart-attempt 라벨이 오른다)
successPolicy operator All|Any 와 targetReplicatedJobs: 누가 끝나면 전체가 끝난 것인지
startupPolicy InOrder 면 앞 역할이 Ready 가 된 뒤에 다음 역할을 띄운다
실험 VM(JobSet v0.13.0, 컨트롤러 이미지 36MB, 준비 17초)에서 확인한 것입니다.
- 워커 파드의
hostname -f가train-leader-0-0.train.team-a.svc.cluster.local이었고, 워커는 그 이름으로 리더를 찾았습니다. - 워커 하나를 실패시키자
restarts=1이 되고 조건에applying RestartJobSet failure policy action (first failed job: train-worker-0)이 남았습니다. 리더까지 다시 만들어집니다. - Kueue 의 큐 라벨을 붙이면 JobSet 전체가 「Workload 하나」(역할마다 podSet 하나)가 됩니다. 쿼터를 넘는 JobSet 은 한 파드도 만들어지지 않고
… previously considered podsets requests (1) + current podset request (4) > maximum capacity (4)같은 사유로 통째로 기다렸습니다. 일부만 떠서 GPU 를 붙잡는 일이 없습니다.
현장에서 만나는 모습
- 「설치 순서가 결과를 바꿉니다.」 Kueue 를 먼저 깔고 JobSet 을 나중에 깔면 Kueue 로그에
No matching API in the server for job framework, deferring setting up controller가 찍히고, 실측으로 약 1분 뒤에야 JobSet 통합이 켜졌습니다. 그 사이에 제출한 JobSet 은 멈춘 채 아무 일도 일어나지 않습니다. 이 실습의 VM 은 JobSet 을 먼저 올렸습니다. - 「PID 1 의 sleep 은 신호를 무시합니다.」 다시 만들 때 옛 파드가 30초씩 Terminating 에 머물러 재시작이 느려 보입니다. 컨테이너 명령을
trap으로 감싸면 바로 끝납니다. 실제 학습 코드에서는 SIGTERM 을 받아 체크포인트를 쓰고 끝내는 것이 같은 이야기입니다. - 「배치를 묶는 것과 자리를 묶는 것은 다릅니다.」 JobSet 은 파드를 함께 만들 뿐, 함께 「배치」되는 것까지 보장하지는 않습니다. 노드 자리가 모자라면 일부만 뜰 수 있습니다. 그 보장은 Kueue 의 입장(쿼터)이나 갱 스케줄러(KAI·Volcano)가 맡습니다. 다음 모듈들이 그 이야기입니다.
English corner
A JobSet restarts as a whole; that is the point.
replicated job, failure policy, success policy 는 JobSet 문서의 목차 그대로입니다.
다음 실습에서 할 것
리더 하나와 워커 둘로 된 JobSet 을 띄워 서로의 이름을 확인하고, 워커를 일부러 실패시켜 전체 재시작을 보고, 리더만 끝나면 끝나는 평가 JobSet 을 만듭니다. 그다음 Kueue 큐에 넣어 한 덩어리로 입장하는 것과, 쿼터를 넘으면 한 파드도 뜨지 않는 것을 봅니다.