Skip to content

Split View: MIG와 time-slicing — GPU 한 장을 여럿이 쓰는 두 가지 방법

✨ Learn with Quiz
|

MIG와 time-slicing — GPU 한 장을 여럿이 쓰는 두 가지 방법

들어가며 — 한 장을 여럿이 쓰는 두 가지 방법

앞 글에서 GPU는 확장 리소스라 정수로만 요청되고, 하드웨어에 격리 장치가 없어 그냥 나눠 쓸 수 없다는 이야기를 했습니다. 그런데 실무에서는 나눠 써야 할 이유가 계속 생깁니다. 노트북 크기의 추론 모델 여러 개를 한 장에 올리고 싶고, 개발자 여덟 명에게 카드 한 장씩 줄 예산은 없습니다.

NVIDIA가 제공하는 답은 두 가지고, 둘은 이름만 비슷할 뿐 성질이 다릅니다. 하나는 시간을 나누고 하나는 하드웨어를 나눕니다. 고르는 기준은 성능이 아니라 격리입니다.

메트릭 이름과 설정은 2026-08-12에 공식 문서·저장소에서 확인했습니다. 버전에 따라 다를 수 있으니 사용 중인 버전에서 다시 확인하세요.

time-slicing — 시간을 나눈다

time-slicing은 디바이스 플러그인이 물리 GPU 하나를 여러 개의 복제본으로 광고하게 만드는 설정입니다. 하드웨어는 전혀 바뀌지 않습니다. 스케줄러가 보는 숫자만 늘어납니다.

공식 문서가 보여 주는 ConfigMap 구조는 이렇습니다.

apiVersion: v1
kind: ConfigMap
metadata:
  name: time-slicing-config
data:
  any: |-
    version: v1
    flags:
      migStrategy: none
    sharing:
      timeSlicing:
        renameByDefault: false
        failRequestsGreaterThanOne: false
        resources:
          - name: nvidia.com/gpu
            replicas: 4

핵심 필드는 세 개입니다. replicas는 카드 한 장을 몇 개로 광고할지 정합니다. renameByDefault는 그 복제본을 원래 이름으로 광고할지 공유 전용 이름으로 광고할지 고릅니다. 값이 참이면 광고되는 리소스가 nvidia.com/gpu.shared로 바뀝니다. 거짓이면 이름은 nvidia.com/gpu 그대로입니다. failRequestsGreaterThanOne은 한 컨테이너가 복제본을 두 개 이상 요청했을 때 실패시킬지를 정합니다.

renameByDefault가 실무에서 가장 위험한 스위치입니다. 거짓으로 두면 기존 매니페스트가 손대지 않았는데도 어느 날부터 공유 GPU를 받게 됩니다. 참으로 두면 이름이 바뀌므로 공유를 원하는 파드는 매니페스트를 고쳐야 하고, 그 대신 사고가 조용히 나지 않습니다.

적용은 ClusterPolicy를 통해 합니다.

kubectl create -n gpu-operator -f time-slicing-config.yaml

kubectl patch clusterpolicies.nvidia.com/cluster-policy \
  -n gpu-operator --type merge \
  -p '{"spec": {"devicePlugin": {"config": {"name": "time-slicing-config-all", "default": "any"}}}}'

노드마다 다른 설정을 주고 싶으면 노드에 nvidia.com/device-plugin.config 라벨을 붙여 ConfigMap의 어느 키를 쓸지 지정합니다. 복제 배수는 nvidia.com/gpu.replicas 라벨로 확인할 수 있습니다.

그리고 문서가 굵게 강조하는 한 문장이 있습니다. MIG와 달리 복제본 사이에는 메모리 격리도 장애 격리도 없다는 것입니다. 이 문장의 실무적 의미는 뒤에서 다시 다룹니다.

MIG — 하드웨어를 나눈다

MIG는 GPU를 하드웨어 수준에서 여러 인스턴스로 쪼갭니다. 인스턴스마다 메모리 슬라이스와 연산 자원이 물리적으로 분리되므로, 문서 표현대로 CUDA 애플리케이션에 분리되고 안전한 GPU 인스턴스를 제공합니다. 대신 조건이 있습니다. Ampere 이후 아키텍처 기반 GPU에서만 동작합니다. A100이 대표적인 예로 문서에 나옵니다.

GPU Operator에서 MIG는 노드 라벨로 조작합니다.

# 노드가 MIG를 지원하는지 확인
kubectl get nodes -L nvidia.com/mig.capable,nvidia.com/mig.strategy

# 프로파일 적용
kubectl label nodes <노드이름> nvidia.com/mig.config=all-1g.5gb --overwrite

# 적용 상태 추적 (pending / rebooting / success / failed)
kubectl get node <노드이름> -o jsonpath='{.metadata.labels.nvidia\.com/mig\.config\.state}{"\n"}'

nvidia.com/mig.config.state가 상태 머신 역할을 합니다. 라벨을 붙인 직후 pending으로 갔다가, 필요하면 rebooting을 거쳐 success나 failed로 끝납니다. 실무에서 이 라벨을 안 보고 기다리다 시간을 버리는 경우가 많습니다.

리소스가 어떻게 광고되는지는 전략에 따라 갈립니다. single 전략은 노드의 모든 GPU에서 MIG가 켜져 있고 프로파일이 통일된 경우를 다루고, mixed 전략은 그렇지 않은 경우를 다룹니다. mixed에서는 프로파일별로 리소스가 갈라져서, 저장소 문서 기준으로 A100 40GB에서는 nvidia.com/mig-1g.5gb, nvidia.com/mig-2g.10gb, nvidia.com/mig-3g.20gb, nvidia.com/mig-7g.40gb가, A100 80GB에서는 nvidia.com/mig-1g.10gb, nvidia.com/mig-2g.20gb, nvidia.com/mig-3g.40gb, nvidia.com/mig-7g.80gb가 나타납니다. 프로파일별 개수는 nvidia.com/mig-1g.10gb.count 같은 라벨로도 노출됩니다.

Helm 차트의 기본 전략은 mig.strategy: single입니다. MIG Manager에 커스텀 설정을 주려면 ConfigMap에 config.yaml이라는 키가 반드시 있어야 한다고 차트 주석이 명시합니다.

격리 수준의 차이가 만드는 결과

두 방식의 차이를 표로 정리하면 이렇습니다.

항목time-slicingMIG
나누는 대상시간하드웨어
메모리 격리없음있음
장애 격리없음있음
지원 하드웨어사실상 제한 없음Ampere 이후
적용 방식ConfigMap과 ClusterPolicy노드 라벨과 MIG Manager
노드 재시작불필요프로파일 전환 시 필요할 수 있음
광고되는 리소스원래 이름 또는 공유 전용 이름프로파일별로 분리된 이름

가장 중요한 줄은 격리 두 줄입니다. time-slicing에서 복제본 하나가 VRAM을 다 먹으면 같은 카드의 다른 복제본이 메모리 할당에 실패합니다. 스케줄러 관점에서는 두 파드 모두 정상적으로 자원을 받았는데, 실제로는 한쪽이 다른 쪽을 굶기고 있는 상태입니다. 더 나쁜 것은 장애 격리가 없다는 쪽입니다. 한 워크로드가 GPU를 비정상 상태로 만들면 같은 카드의 모든 복제본이 함께 무너집니다.

그래서 판단 기준이 나옵니다. 복제본끼리 서로 신뢰할 수 있는가. 같은 팀의 같은 서비스를 여러 개 띄우는 것이라면 신뢰할 수 있습니다. 서로 다른 팀, 서로 다른 고객, 혹은 사용자가 코드를 올리는 노트북 환경이라면 신뢰할 수 없습니다.

무엇을 언제 고르나

정리하면 선택은 대체로 이렇게 갈립니다.

time-slicing이 맞는 경우는 개발과 실험 환경, 인터랙티브 노트북, 그리고 GPU를 아주 짧게 쓰는 배치 작업입니다. 공통점은 격리가 깨졌을 때 손해가 재시도로 끝난다는 것입니다. 신뢰 경계 안쪽이고, 사람이 보고 있고, 실패해도 다시 돌리면 됩니다.

MIG가 맞는 경우는 프로덕션 추론 서빙, 멀티테넌트, 그리고 지연 시간을 약속해야 하는 서비스입니다. 특히 SLO를 걸어야 하는 서비스라면 선택지가 사실상 하나입니다. 옆 인스턴스의 부하가 내 지연 시간에 영향을 주는 상태에서는 어떤 약속도 지킬 수 없습니다.

둘 다 아닌 경우도 흔합니다. 큰 모델을 서빙하는데 VRAM이 이미 빠듯하다면 나눌 것이 없습니다. 이때 필요한 것은 공유가 아니라 배치 크기와 KV 캐시 설정을 조정하는 일이고, 그 판단에는 관측 데이터가 필요합니다. 다음 두 글이 그 데이터를 어디서 얻는지 다룹니다.

마치며 — 공유는 자원 문제가 아니라 신뢰 문제다

GPU 공유를 자원 활용률 문제로만 보면 거의 항상 time-slicing으로 결론이 납니다. 설정이 간단하고 하드웨어를 가리지 않고 즉시 효과가 보이기 때문입니다. 그러다 어느 날 한 파드가 다른 파드를 죽이고, 원인을 찾는 데 하루가 걸립니다.

공유 방식은 활용률이 아니라 신뢰 경계로 고르는 것이 맞습니다. 같은 경계 안이면 시간을 나누고, 경계를 넘으면 하드웨어를 나눕니다. 하드웨어가 안 되면 나누지 않습니다. 이 순서를 지키면 나중에 설명하기 어려운 장애가 크게 줄어듭니다.

다음 글에서는 이렇게 구성한 GPU가 실제로 어떻게 쓰이고 있는지를 보는 방법, 즉 DCGM Exporter를 다룹니다.

직접 해보기

시리즈

참고 자료

MIG and Time-Slicing — Two Ways to Share One GPU

Introduction — Two Ways to Share One Card

The previous post argued that a GPU is an extended resource requested only in whole integers, and that with no isolation in the hardware you cannot simply share it. Yet in practice reasons to share keep appearing. You want several laptop-sized inference models on one card, and there is no budget to hand eight developers a card each.

NVIDIA offers two answers, and they share little beyond a resemblance in the pitch. One divides time; the other divides hardware. The criterion for choosing is not performance. It is isolation.

Metric names and configuration were verified against the official documentation and repositories on 2026-08-12. They can differ between releases, so check again against the version you are running.

Time-Slicing — Dividing Time

Time-slicing configures the device plugin to advertise one physical GPU as multiple replicas. The hardware does not change at all. Only the number the scheduler sees goes up.

The ConfigMap structure the official documentation shows looks like this.

apiVersion: v1
kind: ConfigMap
metadata:
  name: time-slicing-config
data:
  any: |-
    version: v1
    flags:
      migStrategy: none
    sharing:
      timeSlicing:
        renameByDefault: false
        failRequestsGreaterThanOne: false
        resources:
          - name: nvidia.com/gpu
            replicas: 4

Three fields carry the weight. replicas sets how many the single card is advertised as. renameByDefault chooses whether those replicas are advertised under the original name or a share-specific one: set it true and the advertised resource becomes nvidia.com/gpu.shared, set it false and the name stays nvidia.com/gpu. failRequestsGreaterThanOne decides whether to reject a container that asks for more than one replica.

renameByDefault is the most dangerous switch here in practice. Leave it false and existing manifests start receiving a shared GPU one day without anyone touching them. Set it true and the name changes, so pods that want sharing must be edited, but nothing breaks silently.

You apply it through the ClusterPolicy.

kubectl create -n gpu-operator -f time-slicing-config.yaml

kubectl patch clusterpolicies.nvidia.com/cluster-policy \
  -n gpu-operator --type merge \
  -p '{"spec": {"devicePlugin": {"config": {"name": "time-slicing-config-all", "default": "any"}}}}'

For per-node configuration, label the node with nvidia.com/device-plugin.config to select which key of the ConfigMap applies. The replication factor is visible as the nvidia.com/gpu.replicas label.

And the documentation carries one sentence in bold that matters most: unlike MIG, there is no memory or fault isolation between replicas. What that means operationally comes up shortly.

MIG — Dividing Hardware

MIG partitions a GPU into multiple instances at the hardware level. Each instance gets a physically separate memory slice and compute allocation, so in the wording of the documentation it provides separate and secure GPU instances for CUDA applications. There is a condition attached. It only works on GPUs based on the NVIDIA Ampere and later architectures. A100 is the example the documentation names.

Under the GPU Operator, MIG is driven by node labels.

# Check whether the node supports MIG
kubectl get nodes -L nvidia.com/mig.capable,nvidia.com/mig.strategy

# Apply a profile
kubectl label nodes <node-name> nvidia.com/mig.config=all-1g.5gb --overwrite

# Track the rollout (pending / rebooting / success / failed)
kubectl get node <node-name> -o jsonpath='{.metadata.labels.nvidia\.com/mig\.config\.state}{"\n"}'

nvidia.com/mig.config.state acts as the state machine. Right after labeling it goes to pending, passes through rebooting if needed, and lands on success or failed. Plenty of time gets wasted waiting without watching that label.

How resources are advertised depends on the strategy. The single strategy covers the case where MIG is enabled on all GPUs on a node with a uniform profile; the mixed strategy covers the case where it is not. Under mixed, resources split by profile, and per the repository documentation an A100 40GB surfaces nvidia.com/mig-1g.5gb, nvidia.com/mig-2g.10gb, nvidia.com/mig-3g.20gb, and nvidia.com/mig-7g.40gb, while an A100 80GB surfaces nvidia.com/mig-1g.10gb, nvidia.com/mig-2g.20gb, nvidia.com/mig-3g.40gb, and nvidia.com/mig-7g.80gb. Per-profile counts also appear as labels such as nvidia.com/mig-1g.10gb.count.

The chart default strategy is mig.strategy: single. To pass a custom configuration to the MIG Manager, the chart comment states the ConfigMap must have a key named config.yaml.

What the Isolation Gap Actually Produces

Tabulating the difference:

Itemtime-slicingMIG
What is dividedtimehardware
Memory isolationnoneyes
Fault isolationnoneyes
Supported hardwareeffectively unrestrictedAmpere and later
How it is appliedConfigMap and ClusterPolicynode labels and MIG Manager
Node restartnot neededcan be needed on profile change
Advertised resourceoriginal name or share-only nameseparate name per profile

The two isolation rows are the ones that matter. Under time-slicing, if one replica consumes all the VRAM, another replica on the same card fails its memory allocation. From the scheduler's point of view both pods received their resources correctly, while in reality one is starving the other. Worse is the missing fault isolation: if one workload puts the GPU into a bad state, every replica on that card goes down together.

Hence the decision criterion. Can the replicas trust each other? Several copies of one service owned by one team can. Different teams, different customers, or a notebook environment where users upload their own code cannot.

Choosing Between Them

The split usually runs like this.

Time-slicing fits development and experimentation environments, interactive notebooks, and batch jobs that touch the GPU only briefly. What those share is that a broken isolation boundary costs you a retry. You are inside a trust boundary, a human is watching, and rerunning is acceptable.

MIG fits production inference serving, multi-tenancy, and any service that has to promise latency. For anything carrying an SLO the choice is effectively made for you. No promise survives a world where the neighbor instance's load moves your latency.

Neither fits more often than people expect. If you are serving a large model and VRAM is already tight, there is nothing to divide. What you need then is not sharing but tuning batch size and KV cache configuration, and that judgment needs observability data. The next two posts cover where that data comes from.

Closing — Sharing Is a Trust Problem, Not a Utilization Problem

Treat GPU sharing purely as a utilization problem and you will land on time-slicing nearly every time. It is simple to configure, it does not care about hardware, and the effect is immediate. Then one day a pod kills another pod and finding out why takes a full day.

Choose the sharing mechanism by trust boundary, not by utilization. Inside one boundary, divide time. Across a boundary, divide hardware. If the hardware cannot, do not divide. Following that order removes a large class of incidents that are painful to explain afterwards.

The next post turns to seeing how the GPUs you configured are actually being used: the DCGM Exporter.

Try It Yourself

Series

References