GPU 스케줄링·학습 오케스트레이션, 무엇부터 봐야 할까?

이 영역에서 가장 먼저 물어야 할 것은 세 가지다. 우선 GPU를 물리적으로 어떻게 나눌 것인가(분할 vs 공유), 다음 대기 중인 작업을 어떤 순서로 실행할 것인가(큐·선점 정책), 마지막으로 **여러 장비에 걸친 학습을 누가 조율할 것인가(오케스트레이터 역할)**다. 이 세 결정이 시스템의 처리량, 공정성, 격리 수준을 좌우한다.

물리 GPU를 분할할 것인가, 공유할 것인가?

GPU를 분할하는 방식에 따라 프로비저닝 단위, 격리 강도, 오버헤드가 전혀 달라진다.

전용(Dedicated) 할당

한 팀이나 잡이 GPU 전체를 독점하는 방식이다. 격리는 완벽하지만, 미사용 시간이 발생한다. NVIDIA H100 기준 유휴 비용이 크기 때문에 실제로는 소수의 대규모 배치 작업이나 온프레미스 환경에서나 현실적이다. 여러 팀이 같은 클러스터를 공유해야 하면 활용률이 50% 이하로 떨어지는 경우가 많다.

MIG(Multi-Instance GPU) 분할

NVIDIA GPU(A100, H100, L40S 등)에서 지원하는 하드웨어 수준 분할이다. 예를 들어 A100을 최대 7개의 독립적인 인스턴스로 나누면, 각각이 자신의 메모리·캐시·SM(Streaming Multiprocessor) 컨텍스트를 갖는다. 이 방식은 완벽한 격리를 제공하지만 몇 가지 제약이 있다:

  • 사전 구성 필요: 부팅 시 프로파일을 고정해야 하고, 런타임에 동적 조정이 불가능하다.
  • 입도 제한: 제공하는 분할 조합이 정해져 있다(예: 1×A100 전체, 또는 2×A100-40GB+1×A100-20GB 등).
  • 메모리 상충: 전체 메모리를 나누기 때문에, 대용량 모델 학습에는 충분하지 않을 수 있다.

MIG는 추론 서빙처럼 예측 가능한 부하가 있을 때 유용하나, 학습 워크로드는 메모리·컴퓨트 요구가 들쭉날쭉하므로 맞지 않는 경우가 많다.

소프트웨어 수준 공유(Spatial Sharing)

같은 GPU에 여러 프로세스가 동시에 실행되는 방식이다. NVIDIA Teflon, OpenCL, cuStreamPrioritization, NVIDIA Grace Hopper의 멀티-인스턴스 컴퓨팅(Multi-Instance Computing) 같은 메커니즘으로 구현된다. 장점은 유연하지만, 격리 보장이 약하다:

  • L2 캐시, TLB, 메모리 버스가 경합되면 일부 작업이 다른 작업을 지연시킬 수 있다.
  • 최악의 경우 한 작업의 지연이 다른 작업의 지연을 초래하는 간섭(interference)이 발생한다.

실무 선택 기준

  • 격리 우선 + 예측 가능한 크기: MIG 고려
  • 유연성 + 활용률 중시, 격리는 soft OK: 소프트웨어 공유 + 스케줄러 우선순위 정책 조합
  • 완벽한 격리 + 비용 무관: 전용 할당 (현실적으로 드물다)

큐와 선점 정책을 어떻게 설계할 것인가?

작업이 GPU를 기다리는 상황에서 어떤 작업부터 실행할 것인가는 공정성처리량의 상충을 드러낸다.

FIFO(First-In-First-Out)

제일 간단하다. 도착 순서대로 실행한다. 구현은 쉽지만 head-of-line blocking 문제가 있다. 예를 들어 첫 작업이 GPU 메모리를 80% 점유하고 나머지가 대기하면, 20%만 쓰는 작업들이 들어올 때도 기다려야 한다.

우선순위 큐(Priority Queue)

팀, 사용자, 잡 타입별로 우선순위를 부여한다. 공정성을 제어할 수 있지만 기아(starvation) 위험이 있다. 항상 높은 우선순위 작업만 들어오면 낮은 우선순위는 영원히 실행되지 않을 수 있다.

페어 셰어(Fair Share) 또는 Dominant Resource Fairness (DRF)

시간에 따라 GPU 할당을 공정하게 돌려가는 방식이다. Apache YARN, Kubernetes에서 쓰이는 모델이다.

예를 들어 팀 A가 지금까지 GPU 시간을 100시간 썼고, 팀 B가 50시간을 썼다면, 다음 작업은 팀 B 것부터 돌려준다는 식이다. DRF는 GPU·메모리·네트워크 같은 여러 자원이 있을 때, 각 팀이 어느 한 자원만 과점유하지 않도록 조율한다.

Fairness Score = min(GPU_allocated / GPU_demand,
                      Memory_allocated / Memory_demand,
                      Network_allocated / Network_demand)
→ 가장 낮은 점수의 팀이 다음 자원을 받는다.

구현이 복잡하지만, 다중 팀 환경에서는 분쟁을 줄인다.

선점(Preemption)

실행 중인 저우선순위 작업을 멈춰서 고우선순위 작업을 먼저 돌린다. 처리량을 높이지만, 체크포인트와 복구 메커니즘이 필요하다. 없으면 작업을 중단한 모든 계산이 낭비된다.

분산 학습을 오케스트레이션하는 것은 누가 하는가?

한 번에 GPU 여러 개(또는 여러 머신)를 써서 모델을 학습하는 작업의 조율 문제다.

중앙 오케스트레이터 방식

Kubernetes Operator, Ray, Slurm 같은 도구가 작업 제출, 리소스 매칭, 배치 스케줄링을 모두 담당한다.

장점: 전체 클러스터 상태를 보고 최적 배치를 결정할 수 있다.
단점: SPOF(Single Point of Failure) 위험, 메타데이터 일관성 문제.

Kubernetes + KubeRay 조합

2024~2025년 기준, Kubernetes를 기본 인프라로 삼고 Ray를 학습 오케스트레이션 계층으로 두는 구성이 표준화되고 있다. KubeRay RayCluster CRD를 통해 Ray Head·Worker를 Kubernetes 파드로 관리하고, Ray의 Task/Actor API로 분산 학습을 제어한다.

# RayCluster 예시
apiVersion: ray.io/v1alpha1
kind: RayCluster
metadata:
  name: training-cluster
spec:
  rayVersion: "2.10"
  headGroupSpec:
    template:
      spec:
        containers:
        - name: ray-head
          image: rayproject/ray:2.10
          resources:
            limits:
              nvidia.com/gpu: "1"

이 방식은 Kubernetes의 다중 클러스터 관리와 Ray의 분산 학습 유연성을 모두 얻을 수 있다. 다만 Ray와 Kubernetes 간 상태 동기화 지연이 생길 수 있다(보통 수 초).

Ray의 내부 스케줄링

Ray Head가 Task를 Worker에 할당할 때, CPU·GPU 자원 요청(resource request)과 현재 가용 자원을 비교한다. 작업이 "gpu: 2" 자원을 요청하면, Ray는 2개 GPU를 가진 Worker를 찾아 할당한다.

@ray.remote(num_gpus=2)
def train_on_two_gpus():
    ...

이 수준에서도 선점·공정성·격리를 정책으로 설정할 수 있다. Ray 1.13+ 에서는 "placement group" 개념으로 작업 간 배치 제약을 명시할 수 있다.

멀티테넌시 격리를 어느 수준까지 강제할 것인가?

여러 팀이 같은 클러스터를 쓸 때 한 팀의 작업이 다른 팀의 작업에 끼치는 영향을 제어해야 한다.

프로세스 수준 격리 (cgroups)

Linux cgroups을 통해 프로세스별 CPU·메모리·I/O를 제한한다.

  • 구현: Kubernetes Pod resource request/limit, Docker –cpus, –memory 플래그.
  • 강도: 약함. 같은 GPU를 공유하면 메모리 버스, 캐시 충돌을 막을 수 없다.

컨테이너 격리 (runc, kata)

각 작업을 독립 컨테이너로 실행하여 파일시스템, PID 네임스페이스를 분리한다.

  • 구현: Kubernetes Pod, Docker.
  • 강도: 중간. GPU 자원 자체는 여전히 공유되지만, 소프트웨어 의존성 충돌은 없다.

커널 수준 격리 (VM, uVM)

작업을 경량 VM(예: Firecracker, gVisor)이나 전용 VM에서 실행한다.

  • 구현: Kubernetes + Kata runtime, AWS Lambda.
  • 강도: 높음. 커널 버그를 통한 크로스 테넌트 침입 위험이 매우 낮다.
  • 비용: 부팅 시간, 메모리 오버헤드(VM마다 커널 인스턴스 필요).

리소스 격리 + 모니터링

격리만큼 중요한 것이 이상 탐지다. 한 작업이 CPU/메모리를 비정상 수준으로 소비하면 자동 종료하거나 우선순위를 내린다. Prometheus + Alertmanager, 또는 Kubernetes metrics-server를 통해 실시간 모니터링하고, 임계값 초과 시 자동 대응할 수 있다.

활용률과 공정성 사이의 상충을 어떻게 풀 것인가?

GPU 활용률을 높이려면 항상 GPU를 쓰는 작업이 있어야 한다. 반면 공정하게 나누려면 어느 팀도 독점하지 못하게 해야 한다. 이는 근본적으로 상충한다.

활용률 우선 (bin-packing)

크기가 작은 작업들을 한 GPU에 몰아서 넣어 빈 GPU 개수를 최소화한다. 평균 활용률은 높지만, 한 팀이 오래 쓰면 다른 팀은 장시간 대기하게 된다.

공정성 우선 (round-robin, fair-share)

정해진 주기마다 돌려가며 자원을 할당한다. 활용률은 낮지만, 기다리는 시간이 예측 가능하다.

하이브리드: 동적 임계값

활용률이 일정 수준(예: 70%) 이상이면 bin-packing, 그 아래면 fair-share를 적용하는 식이다. 이론적으로는 좋지만, 작업 도착 시간이 무작위라 구현할 때 진동(oscillation) 문제가 생길 수 있다.

현실적 접근: SLA 설정

각 팀별로 **리소스 보장량(reservation)**을 정의한다. 예를 들어 팀 A는 4개 GPU를 항상 받을 권리가 있고, 나머지는 공동 풀에서 경쟁한다는 식이다. Kubernetes에서는 이를 Namespace quota + Pod priority로 구현한다.

apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-a-quota
  namespace: team-a
spec:
  hard:
    nvidia.com/gpu: "4"

체크포인트와 중단·복구 메커니즘은 필수인가?

선점 기반 스케줄링을 쓰면 실행 중인 작업을 멈춰야 할 때가 온다. 이때 지금까지의 학습 진행 상황을 저장(체크포인트)하지 않으면 모든 계산이 낭비된다.

애플리케이션 수준 체크포인트

프레임워크(PyTorch, TensorFlow)가 모델 가중치, 옵티마이저 상태, 난수 시드를 주기적으로 디스크에 저장한다. 재개 시에는 이 상태를 로드하고 학습을 계속한다.

  • 장점: 정확하고 유연함.
  • 비용: 개발 노력, 저장소 I/O, 저장 용량.

일반적으로 체크포인트는 모델 크기의 1.52배 정도 필요하다. 7B 파라미터 모델이면 대략 2856GB.

OS/런타임 수준 체크포인트

Linux CRIU(Checkpoint/Restore In Userspace)를 사용하면 애플리케이션 수정 없이 프로세스 상태를 캡처할 수 있다. Kubernetes에서 이를 활용하는 연구가 있으나, 아직 프로덕션에서 광범위하게 쓰이지는 않는다.

  • 장점: 애플리케이션 무관.
  • 단점: GPU 메모리 상태 캡처 어려움, 오버헤드 큼, 호환성 문제.

체크포인트 저장소

NFS, S3, 블록 스토리지 중 선택해야 한다.

저장소 처리량 비용 지연
NFS 낮음(네트워크 경합) 낮음 높음(ms~s)
S3 높음(대역폭 충분) 중간 높음(네트워크)
로컬 SSD 매우 높음 높음 낮음(μs)

실무에서는 로컬 SSD에 쓴 후 비동기로 원격 저장소에 복제하는 구성이 많다. 이렇게 하면 체크포인트 지연을 줄이면서도 영속성을 확보한다.

어떤 용도와 규모에 어떤 설계가 맞는가?

소규모 팀(팀 수: 23, GPU: 416개)

  • 할당 방식: 전용 또는 soft 공유 + 스크립트 기반 큐(예: 간단한 Python 스크립트).
  • 오케스트레이션: 중앙 스케줄러 불필요. 각 팀이 직접 GPU를 예약.
  • 격리: cgroups 수준 충분. 완벽한 격리 필요 없음.
  • 체크포인트: 필수. 충돌 가능성이 높음.

중규모 조직(팀 수: 520, GPU: 50500개)

  • 할당 방식: MIG 또는 소프트웨어 공유 + 우선순위 큐.
  • 오케스트레이션: Kubernetes 기반, 필요하면 Ray.
  • 격리: 컨테이너 수준(Kubernetes Pod).
  • 공정성: Fair-share 또는 quota 기반.

대규모 클라우드/AI 연구소(팀 수: 50+, GPU: 1000+)

  • 할당 방식: 혼합. 일부는 MIG(추론), 일부는 공유(학습).
  • 오케스트레이션: Kubernetes + KubeRay 또는 Slurm.
  • 격리: 컨테이너 + 모니터링·자동 대응.
  • 공정성: DRF 또는 예약 기반 SLA.
  • 체크포인트: 선점 지원 필수. 분산 스토리지(S3/HDFS) 연동.

흔한 실수: "스케줄러만 좋으면 된다"는 착각

시스템 설계자들이 빠지기 쉬운 함정은 스케줄러 알고리즘 최적화에만 집중하는 것이다. 결과적으로 격리 부족, 모니터링 부재, 체크포인트 메커니즘 미흡 같은 기초가 무너진다.

실제로 중요한 것은:

  1. 격리 수준 명확히: 완벽한 격리가 필요한가, 아니면 soft OK인가를 먼저 결정하고 그에 맞는 방식(MIG, cgroups, VM)을 선택한다.

  2. 모니터링·알림: 작업이 GPU 메모리를 비정상 소비하거나 CPU를 도핑하면 즉시 감지하고 대응한다. 이것이 없으면 한 팀의 버그가 전체 시스템을 마비시킨다.

  3. 체크포인트 인프라: 선점 정책을 쓰려면 체크포인트가 반드시 동반되어야 한다. 없으면 처리량이 오히려 떨어진다.

  4. SLA 문서화: "GPU 대기 시간이 보통 X시간", "Y% 활용률" 같은 목표를 명시하고, 그에 맞는 설정을 검증한다. 막연한 "최적화"는 한계가 있다.

핵심 정리

  • GPU 분할 방식(전용/MIG/공유)과 격리 정책(cgroups/컨테이너/VM)이 모든 설계의 출발점이다. 완벽한 격리와 높은 활용률은 양립하기 어렵다.

  • 큐와 선점 정책은 공정성과 처리량의 상충을 드러낸다. FIFO는 간단하지만 head-of-line blocking이 있고, DRF는 공정하지만 복잡하다.

  • Kubernetes 기반 인프라에 KubeRay를 얹으면 다중 팀 환경에서 분산 학습 스케줄링을 통일할 수 있다. 다만 메타데이터 동기화 지연(수 초)을 감안해야 한다.

  • 멀티테넌시 격리는 리소스 제한(cgroups)과 모니터링·자동 대응이 함께 작동할 때 의미 있다. 격리 수준을 정하고 나머지 인프라를 그에 맞춰 구성한다.

  • 체크포인트는 선점 기반 스케줄링의 필수 조건이다. 애플리케이션 수준에서 구현하고, 로컬 SSD + 비동기 원격 복제 구성으로 지연을 최소화한다.

자주 묻는 질문

GPU를 처음부터 분할해야 할까, 아니면 먼저 전용으로 시작했다가 필요해지면 공유로 바꿀까?

일반적으로 전용 할당으로 시작한다. 이유는 간단하다: 각 팀의 워크로드 특성(메모리 요구, 배치 크기, 지속 시간)을 알기 전에 분할 방식을 고정하기 어렵기 때문이다. 3개월 정도 운영하며 활용률 데이터를 모은 후, GPU가 실제로 유휴 상태로 오래 남아 있다면 그때 공유 정책(우선순위, 큐)을 도입한다. MIG는 추론 워크로드가 증가한 후에 고려하는 것이 현실적이다.

선점(Preemption)을 쓰면 처리량이 정말 늘어나나?

경우에 따라 다르다. 체크포인트 오버헤드가 중요한데, 조사 사례를 보면:

  • 최악: 체크포인트 비용(저장·복원) > 선점으로 얻는 대기 시간 단축 → 처리량 감소.
  • 최선: 체크포인트 비용이 5% 미만이고, 대기 중인 긴급 작업이 많으면 → 처리량 10~20% 증가 관찰.

따라서 반드시 체크포인트 지연 측정(대기 중 작업 유형 분석 포함)을 먼저 해야 한다.

Ray와 Kubernetes를 함께 쓸 때 누가 스케줄링을 결정하는가?

두 계층이 협력한다:

  1. Kubernetes는 RayCluster 파드를 어느 노드에 배치할지 결정.
  2. Ray Head는 Task를 Ray Worker 간에 분배.

충돌이 생길 수 있다. 예: Kubernetes가 Worker 파드를 노드 A에 배치했는데, Ray Head가 그 Worker의 GPU가 없다고 판단(메타데이터 미동기)하면 Task가 대기한다. 이를 피하려면 Kubernetes의 GPU device plugin과 Ray의 resource tracking을 정기적으로 재동기화해야 한다. KubeRay 커뮤니티는 이 문제를 잘 알고 있으며, 최신 버전(2.10+)에서 개선 사항을 추가 중이다.

4개 GPU를 4개 팀에 1개씩 주는 것 vs 2개 팀에 2개씩 주는 것, 어느 것이 더 공정한가?

"공정함"의 정의에 따라 다르다:

  • 균등 분배: 모든 팀이 같은 수를 받는 관점 → 1개씩이 공정.
  • 수요 기반: 큰 모델을 학습하는 팀이 2개 필요하면 → 2개씩.
  • 누적 사용 시간: 지난달에 팀 A가 이미 100시간 썼으면 → 이번달에 팀 B 우선.

실무에서는 reservation(예약) + burst 모델을 쓴다. 각 팀은 보장 GPU를 받고(예: 2개), 초과 필요 시 대기 중인 여유 자원에서 가져간다. Kubernetes ResourceQuota가 이를 구현할 수 있다.

체크포인트를 자동으로 주기적으로 저장하면 되지 않나?

이상적이지만 트레이드오프가 있다:

  • 저장 빈도가 높으면 → I/O 오버헤드로 학습 속도 저하(보통 5~15% 느려짐).
  • 저장 빈도가 낮으면 → 선점 시 손실되는 계산이 많아짐.

실제로는 학습 곡선과 체크포인트 비용을 측정해서 최적 간격(예: 에포크마다, 손실 값이 새로운 최저가 될 때마다)을 정한다. 또한 체크포인트를 메인 학습과 비동기로 저장(다른 스레드/프로세스)하면 학습 정지 시간을 줄일 수 있다.

MIG로 GPU를 나누면 원래대로 돌릴 수 없나?

MIG는 부팅 시에만 활성화되고, 런타임에 변경할 수 없다. GPU를 부팅하지 않고 재설정하는 것도 불가능하다(공식 NVIDIA 문서 기준). 즉, MIG 설정을 바꾸려면:

  1. MIG 모드 비활성화 (모든 작업 중단 필요).
  2. 새로운 프로파일로 재설정.
  3. 부팅.

따라서 미리 예상 가능한 워크로드 분포(예: 70% 추론, 30% 학습)를 기반으로 MIG 프로파일을 정해야 한다. 오류 수정이나 예상 밖의 요청 증가는 결국 전용 GPU를 추가하는 선택지로 귀결되는 경우가 많다.