What Happens When You Share GPUs by Word of Mouth
한국어 원문으로 표시합니다.
한 줄 요약
배치 스케줄러는 "누가 언제 무엇을 쓸지"를 사람의 합의가 아니라 정책으로 결정하는 시스템이다.
왜 이게 필요했나
GPU 서버가 한 대일 때는 이렇게 한다. "지금 쓰시나요?" "아뇨 쓰세요." 잘 돌아간다.
두 대가 되고 사용자가 다섯이 되면 슬랙에 예약 스레드가 생긴다. 세 대가 되고 사용자가 열이 되면 이런 일이 벌어진다.
- 누가
nvidia-smi로 확인하고 띄웠는데 그 사이 다른 사람이 먼저 띄워 OOM 으로 둘 다 죽는다 - 밤새 돌 작업이 새벽 3시에 끝났는데 아침 9시까지 GPU 가 놀았다
- 급한 실험이 있는데 앞사람 작업이 언제 끝날지 아무도 모른다
- 누군가 실수로 8장을 다 잡고 잊어버렸다
이 문제들의 공통점은 자원 할당이 상태로 관리되지 않는다는 것이다. 스케줄러는 그것을 큐와 정책으로 바꾼다.
어떻게 동작하나
Slurm 의 구성요소
| 구성요소 | 역할 | 어디서 도나 |
|---|---|---|
slurmctld |
중앙 컨트롤러. 큐 관리, 스케줄링 결정 | 관리 노드 1대(HA 시 2대) |
slurmd |
각 계산 노드의 에이전트. 작업 실행 | 모든 계산 노드 |
slurmdbd |
계정·사용량 데이터베이스(선택) | 관리 노드 |
munge |
노드 간 인증 | 모든 노드 |
munge 가 이 그림에서 가장 자주 문제를 일으킨다. 클러스터의 모든 노드가 같은 munge 키를 갖고 있어야 하고, 시계도 맞아야 한다(기본 허용 오차는 수 분). 키가 다르거나 시계가 어긋나면 Munge decode failed 가 나고 노드가 통신하지 못한다.
ls -l /etc/munge/munge.key # 0400, munge:munge 여야 한다
권한이 조금이라도 느슨하면 munged 가 아예 뜨지 않는다.
스케줄링의 기본 개념
- 노드(Node) — 계산 자원의 단위. CPU, 메모리, GRES(GPU 등)를 갖는다.
- 파티션(Partition) — 노드의 묶음이자 정책 단위. 최대 실행 시간, 우선순위, 접근 권한을 정한다. 다른 스케줄러의 "큐" 에 해당한다.
- 작업(Job) — 사용자가 제출한 자원 요청 + 실행할 것.
- 작업 스텝(Step) — 작업 안에서
srun으로 실행되는 단위.
백필(backfill)
기본 스케줄러는 우선순위 순으로 작업을 배치한다. 그런데 큰 작업이 자원을 기다리는 동안 노드가 놀 수 있다. 백필은 "앞 작업의 시작 시각을 늦추지 않는 범위에서" 뒤의 작은 작업을 먼저 채워 넣는다.
백필이 잘 동작하려면 사용자가 --time 을 정직하게 적어야 한다. 모두가 최대값을 적으면 스케줄러는 빈틈을 계산할 수 없다. 그래서 성숙한 클러스터는 "짧게 적으면 빨리 돈다" 는 인센티브를 정책으로 만든다.
자원 요청의 의미
sbatch --nodes=1 --ntasks=1 --cpus-per-task=8 --mem=64G --gres=gpu:a100:2 --time=04:00:00 train.sh
--nodes— 노드 개수--ntasks— 실행할 태스크(프로세스) 개수. MPI 랭크 수에 해당--cpus-per-task— 태스크당 CPU 코어. PyTorch DataLoader worker 수와 직결된다--mem/--mem-per-cpu— 메모리. 둘 중 하나만 쓴다--gres=gpu:<타입>:<개수>— GPU
요청하지 않은 자원은 쓸 수 없다. cgroup 으로 강제되므로, --gres=gpu:1 로 받고 코드에서 2장을 쓰려 하면 두 번째 장이 아예 보이지 않는다.
큐가 안 도는 이유를 읽는 법
배치 스케줄러를 붙이고 나면 가장 자주 듣는 말이 "내 작업이 안 돌아간다" 다. 슬럼은 그 이유를 상태 문자열로 알려 주므로, 표만 알면 대부분 스스로 해결된다.
squeue -u $USER -o "%.10i %.9P %.20j %.8T %.10M %R"
scontrol show job <작업번호> | grep -E 'JobState|Reason|NodeList'
| 사유 | 뜻 | 대개의 해결 |
|---|---|---|
Resources |
요청한 자원이 지금 비어 있지 않다 | 기다린다. 요청을 줄이면 빨라진다 |
Priority |
앞에 우선순위가 높은 작업이 있다 | 공정 공유 정책의 문제 |
QOSMaxJobsPerUserLimit |
그 QoS 의 동시 실행 상한 | 배열 작업으로 묶는다 |
AssocGrpCPUMinutesLimit |
계정의 할당량을 다 썼다 | 관리자에게 |
ReqNodeNotAvail |
그 노드가 예약·정지 중 | 노드 지정을 푼다 |
PartitionTimeLimit |
요청한 시간이 파티션 상한보다 길다 | --time 을 줄인다 |
요청을 크게 잡을수록 더 오래 기다린다. 노드 4대를 8시간 요청하면 그만큼의
빈 창이 생길 때까지 기다린다. 백필(backfill) 스케줄러는 짧고 작은 작업을
빈틈에 끼워 넣으므로, --time 을 정직하게(넉넉하지만 과하지 않게) 적으면
훨씬 빨리 시작된다. 상한을 최대로 적어 두는 습관이 스스로를 뒤로 미룬다.
노드가 drain 이면 이유가 적혀 있다.
sinfo -R # drain 사유 목록
scontrol show node <노드> | grep -E 'State|Reason'
디스크 부족, GPU 오류, 헬스체크 실패가 대부분이다. 사유를 안 지우고 노드만 되살리면 다음 검사에서 다시 빠진다.
작업이 죽었는데 이유를 모를 때는 회계 기록을 본다. 표준 출력에 아무것도
안 남고 사라지는 경우, sacct 가 종료 코드와 함께 OUT_OF_MEMORY 나
TIMEOUT 을 알려 준다.
sacct -j <작업번호> --format=JobID,State,ExitCode,MaxRSS,Elapsed,ReqMem
MaxRSS 가 ReqMem 에 붙어 있으면 메모리가 원인이다.
현장에서 만나는 모습
홈랩에서도 의미가 있다. GPU 두 장짜리 서버라도 여러 실험을 순차로 돌려야 한다면 스케줄러가 낫다. 밤새 큐에 넣어 두면 자동으로 이어서 돈다. 그리고 그 경험이 그대로 대규모 클러스터로 이어진다.
노드가 DRAIN 으로 빠지는 것. 하드웨어 오류나 GRES 불일치가 감지되면 Slurm 은 그 노드를 자동으로 뺀다. 원인을 고친 뒤 scontrol update NodeName=... State=RESUME 으로 복귀시켜야 한다. 자동으로 돌아오지 않는다 — 이것을 모르면 노드 하나가 며칠씩 놀고 있게 된다.
다음 확인에서 볼 것
이어지는 퀴즈에서는 파티션·백필·자원 요청이 각각 어떤 스케줄링 결정을 만드는지
먼저 확인한다. 그 기준을 통과한 뒤 다음 모듈부터 slurm.conf 작성, GPU GRES 정의,
sbatch 스크립트와 깨진 설정 진단을 순서대로 실습한다.