추론 서비스를 운영하다 보면 GPU 청구서와 실제 사용률 사이의 간극에 놀라게 됩니다. nvidia-smi는 GPU 사용률 90%를 찍는데 정작 텐서 코어는 놀고 처리량은 기대의 절반에도 못 미치는 경우가 흔합니다. 대부분의 추론 워크로드는 배치가 작고 요청이 간헐적이라, 값비싼 가속기 한 장을 모델 하나가 통째로 점유하면서도 연산 자원 대부분을 비워 두기 때문입니다.
이 글에서는 하나의 물리 GPU를 여러 워크로드가 나눠 쓰는 세 층위를 다룹니다. 시간 분할(time-slicing), MPS(Multi-Process Service), 하드웨어 격리인 MIG(Multi-Instance GPU)입니다. 각각 격리 수준과 오버헤드가 다르며, 무엇을 쓰느냐가 활용률과 안정성을 좌우합니다. 예시는 데이터센터급 가속기와 쿠버네티스를 기준으로 하지만 개념은 단일 노드에도 적용됩니다.
왜 GPU 사용률 지표에 속으면 안 되는가
nvidia-smi의 GPU-Util은 “지난 샘플 구간 동안 하나 이상의 커널이 실행된 시간 비율”일 뿐입니다. 커널 하나가 SM(Streaming Multiprocessor)의 5%만 쓰면서 계속 돌아도 이 값은 100%로 표시됩니다. 즉 점유 시간이지 연산 밀도가 아닙니다. 실제 활용률은 DCGM의 SM 점유율과 텐서 코어 활성 비율로 봐야 합니다.
# DCGM: 진짜 활용률 지표를 본다
dcgmi dmon -e 1002,1003,1004
# 1002 SM_ACTIVE : SM 에 워프가 상주한 시간 비율
# 1003 SM_OCCUPANCY : 상주 워프 / 최대 워프 (연산 밀도)
# 1004 TENSOR_ACTIVE: 텐서 코어가 실제로 일한 시간 비율
# GPU-Util 90% 인데 TENSOR_ACTIVE 8% 라면 → 카드가 놀고 있다는 신호
추론에서 이 간극이 큰 이유는, 배치 1~4의 작은 요청은 GPU를 채울 병렬성이 부족하고 요청 사이 유휴 구간에도 가중치가 메모리를 점유해 다른 워크로드가 끼어들 수 없기 때문입니다. 연산은 남고 메모리와 스케줄 슬롯만 잠기는 낭비가 생깁니다. 공유 기법은 이 남는 연산을 여러 워크로드가 나눠 먹게 하는 것이 목표입니다.
세 가지 공유 모델: 시간 분할·MPS·MIG
공유 방식은 격리 강도 순으로 세 층위로 나뉩니다. 선택의 핵심은 “얼마나 강한 격리가 필요한가”입니다.
- 시간 분할(time-slicing): 여러 프로세스가 GPU를 번갈아 점유. 컨텍스트 스위칭으로 동작하며 메모리·연산 격리가 없어 한 프로세스의 OOM이 이웃에 번집니다. 오버서브스크립션에 가깝습니다.
- MPS: 여러 프로세스의 커널을 하나의 컨텍스트로 합쳐 동시(concurrent) 실행. 스위칭 오버헤드가 없어 처리량이 좋지만, 하드웨어 격리는 없어 한 프로세스의 오류가 전체를 무너뜨릴 수 있습니다.
- MIG: GPU를 하드웨어 수준에서 독립 인스턴스로 물리 분할. 각 인스턴스는 전용 SM·L2 슬라이스·메모리 대역폭을 가져 성능 간섭이 없습니다. 격리는 가장 강하지만 분할 단위가 고정적입니다.
판단 기준은 이렇습니다. 신뢰 경계가 같은 처리량 지향 워크로드는 MPS, 다른 테넌트가 섞이거나 지연 SLA가 엄격하면 MIG, 개발·실험용 저비용 공유는 시간 분할입니다.
MIG: 하드웨어 격리의 원리
MIG는 GPU 자원을 GPU 인스턴스(GI)와 컴퓨트 인스턴스(CI) 두 계층으로 쪼갭니다. GI는 메모리 슬라이스와 SM 그룹에 더해 전용 L2 캐시·메모리 컨트롤러 경로를 받아, 한 인스턴스의 메모리 대역폭 폭주가 옆 인스턴스 지연에 영향을 주지 않는 성능 격리(QoS)를 하드웨어로 보장합니다.
프로파일은 [SM슬라이스]g.[메모리GB]gb 형태입니다. 80GB급이라면 1g.10gb 7개, 3g.40gb 2개, 7g.80gb 1개 같은 조합이 가능합니다. 중요한 제약은 분할 지점이 물리적으로 정해져 있어 임의 비율로 자를 수 없다는 것입니다.
# MIG 활성화 (재부팅 또는 GPU 리셋 필요)
nvidia-smi -i 0 -mig 1
# 사용 가능한 프로파일 확인 (ID와 가능한 인스턴스 개수)
nvidia-smi mig -lgip
# GPU 인스턴스 생성: 1g.10gb 프로파일 4개 + 3g.40gb 1개
nvidia-smi mig -i 0 -cgi 19,19,19,19,9
# 각 GI 안에 컴퓨트 인스턴스 생성 (기본 1:1)
nvidia-smi mig -i 0 -cci
# 결과 확인 — 각 인스턴스가 고유 UUID(MIG-...)를 가진다
nvidia-smi -L
각 MIG 인스턴스는 고유 UUID로 노출되고, 프로세스는 CUDA_VISIBLE_DEVICES=MIG-...로 특정 슬라이스만 보게 됩니다. 애플리케이션 입장에서는 작은 GPU 한 장을 독점하는 것처럼 보이므로 코드 수정이 필요 없습니다.
MIG 프로파일을 워크로드에 맞추기
MIG 설계의 핵심은 프로파일 크기를 모델의 실제 요구에 맞춰 자르는 것입니다. 너무 크게 자르면 슬라이스 안에서 GPU가 놀고, 너무 작게 자르면 가중치와 KV 캐시가 들어가지 못해 OOM이 납니다. 프로파일을 정할 때는 다음을 봅니다.
- 가중치 메모리: 양자화 상태(FP16/INT8/INT4)에 따른 파라미터 용량. 슬라이스 메모리의 하한을 만듭니다.
- KV 캐시: 동시 시퀀스 수 × 컨텍스트 길이에 비례하는 활성 메모리. LLM 추론에서는 가중치보다 클 수 있습니다.
- 연산 강도: 프리필(prefill)이 많으면 SM이 많이 필요하고, 짧은 디코드 위주면 메모리 대역폭이 병목입니다.
예를 들어 7B급을 INT8로 서빙하며 컨텍스트가 짧고 요청이 잦다면 1g.10gb를 여러 개 만들어 수평 복제하는 편이 큰 슬라이스에 몰아넣는 것보다 처리량 대비 지연이 안정적입니다. 반대로 긴 컨텍스트로 KV 캐시가 큰 모델은 메모리 우선인 3g.40gb가 맞습니다. 프로파일은 연산이 아니라 메모리에서 먼저 막히는지를 기준으로 고르는 것이 실전 감각입니다.
쿠버네티스에서 MIG 노출하기
쿠버네티스에서는 NVIDIA 디바이스 플러그인이 MIG 인스턴스를 스케줄 가능한 리소스로 광고합니다. single 전략은 노드의 모든 슬라이스를 동일 프로파일로 통일해 nvidia.com/gpu로 노출하고, mixed 전략은 nvidia.com/mig-1g.10gb처럼 프로파일별 리소스명으로 노출합니다.
# 디바이스 플러그인 설정: mixed 전략으로 프로파일별 리소스 노출
apiVersion: v1
kind: ConfigMap
metadata:
name: device-plugin-config
data:
config.yaml: |
version: v1
flags:
migStrategy: "mixed" # 프로파일마다 별도 리소스명으로 노출
sharing:
timeSlicing: {} # MIG 를 쓰므로 시간 분할은 비활성
파드는 필요한 프로파일을 리소스로 요청합니다. 아래는 짧은 컨텍스트 추론 서비스가 1g.10gb 슬라이스 하나만 요청하는 예로, 물리 GPU 노드 하나가 서로 다른 서비스의 파드 여러 개를 안전하게 수용합니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: inference-small
spec:
replicas: 4
template:
spec:
containers:
- name: server
image: registry.int4.io/inference:latest
resources:
limits:
# 물리 GPU 1장이 아니라 1g.10gb 슬라이스 1개만 요청
nvidia.com/mig-1g.10gb: 1
MIG 지오메트리는 노드마다 고정이므로, 워크로드 믹스가 다양하면 노드 풀을 프로파일별로 나누는 편이 관리하기 좋습니다. 소형 추론 풀은 1g.10gb, 대형 컨텍스트 풀은 3g.40gb로 통일하는 식입니다.
MPS: 격리보다 처리량이 중요할 때
같은 신뢰 경계 안에서 여러 프로세스가 하나의 GPU를 동시에 채우고 싶다면 MPS가 답입니다. MPS 데몬은 여러 CUDA 컨텍스트를 병합해 스위칭 오버헤드 없이 커널이 겹쳐 실행되게 합니다. 배치가 작아 GPU를 다 못 채우는 추론 프로세스를 여러 개 겹치면 텐서 코어 활성 비율이 크게 오릅니다.
# MPS 데몬 시작
export CUDA_VISIBLE_DEVICES=0
export CUDA_MPS_PIPE_DIRECTORY=/tmp/nvidia-mps
export CUDA_MPS_LOG_DIRECTORY=/tmp/nvidia-mps-log
nvidia-cuda-mps-control -d
# 프로세스별 SM 사용 상한으로 한 쪽의 폭주 방지 (Volta 이후)
export CUDA_MPS_ACTIVE_THREAD_PERCENTAGE=40 # 이 프로세스는 SM 최대 40%
python serve_replica.py
MPS의 한계는 명확합니다. 메모리 격리가 없어 한 프로세스가 메모리를 다 먹으면 이웃이 OOM을 내고, 잘못된 메모리 접근으로 한 프로세스가 죽으면 같은 컨텍스트의 프로세스가 함께 죽을 수 있습니다. 따라서 MPS는 서로 신뢰하는 워크로드(같은 팀의 복제 파드) 사이에서만 쓰고, 다른 테넌트를 섞을 때는 반드시 MIG로 하드웨어 격리를 걸어야 합니다.
실전 조합은 MIG + MPS입니다. MIG로 큰 카드를 테넌트 단위로 격리하고, 각 슬라이스 안에서 그 테넌트 소유의 복제본을 MPS로 겹쳐 내부 활용률을 다시 끌어올립니다. 격리와 처리량을 동시에 얻는 패턴입니다.
동적 리파티셔닝의 제약
트래픽은 하루 안에서도 변하지만 MIG 지오메트리를 시시각각 바꾸긴 어렵습니다. 재구성은 해당 GPU의 모든 워크로드를 비워야 가능하기 때문입니다. 인스턴스를 지우려면 그 위에서 도는 프로세스가 없어야 하므로, 쿠버네티스에서는 노드를 cordon·drain한 뒤 nvidia-smi mig -dci/-dgi로 기존 인스턴스를 제거하고 새 지오메트리를 만듭니다.
이 드레인 요구 때문에 실무에서는 지오메트리를 자주 바꾸기보다 노드 풀 단위로 프로파일을 고정하고 오토스케일러로 풀 간 용량을 조절하는 편이 안전합니다. GPU 오퍼레이터를 쓰면 노드 레이블(nvidia.com/mig.config)만 바꿔도 재구성 컨트롤러가 드레인과 리파티셔닝을 순서대로 처리하므로, 손으로 nvidia-smi를 두드리는 것보다 안전합니다.
활용률을 검증하고 사이징을 되먹임하기
공유 기법을 넣었다고 활용률이 올랐다고 믿으면 안 됩니다. 반드시 지표로 확인해야 하며, 슬라이스별로 텐서 코어 활성 비율과 메모리 점유를 함께 보는 것이 핵심입니다.
# DCGM Exporter 메트릭을 슬라이스별로 집계해 사이징을 되먹임
# 목표: TENSOR_ACTIVE 는 높이고 메모리는 한계 근처까지 채운다
def diagnose(slice_metrics: dict) -> str:
tensor = slice_metrics["DCGM_FI_PROF_PIPE_TENSOR_ACTIVE"] # 0.0~1.0
mem_used = slice_metrics["DCGM_FI_DEV_FB_USED"]
mem_total = slice_metrics["DCGM_FI_DEV_FB_TOTAL"]
mem_ratio = mem_used / mem_total
if tensor < 0.20 and mem_ratio 0.92:
return "메모리 임박 → KV 캐시 축소 또는 상위 프로파일"
if tensor > 0.70 and mem_ratio < 0.60:
return "연산 병목 → 더 많은 SM(상위 프로파일) 필요"
return "균형 — 현 사이징 유지"
이 되먹임 루프가 GPU 공유의 실질입니다. “연산은 낮은데 메모리는 여유”면 슬라이스가 크다는 뜻이니 더 잘게 나누고, “메모리는 꽉 찼는데 연산은 낮다”면 프로파일을 키우기보다 KV 캐시 정책(양자화·페이지드 어텐션·최대 시퀀스 제한)을 손봐야 합니다. 지표 없이 프로파일을 정하면 MIG를 켜 놓고도 카드를 비워 두는 함정으로 되돌아갑니다.
마무리: 무엇을 언제 쓰는가
GPU 공유는 “값비싼 가속기에서 남는 연산을 되찾는” 몇 안 되는 레버입니다. 판단의 출발점은 nvidia-smi의 GPU-Util이 아니라 DCGM의 텐서 코어 활성 비율과 메모리 점유입니다. 이 둘을 보고 나면 선택은 단순해집니다.
- 시간 분할: 격리가 필요 없는 개발·실험 공유. 프로덕션 SLA에는 부적합.
- MPS: 같은 신뢰 경계의 처리량 지향 워크로드. 작은 배치를 겹쳐 활용률을 올리되 메모리·장애 격리는 없음을 감수.
- MIG: 서로 다른 테넌트, 엄격한 지연 SLA, 예측 가능한 성능이 필요할 때. 격리는 강하나 분할이 고정적.
가장 강력한 조합은 이들을 계층으로 겹치는 것입니다. MIG로 테넌트를 하드웨어 격리하고, 슬라이스 안에서 MPS로 복제본을 겹쳐 내부 활용률을 끌어올리며, DCGM 지표로 프로파일 사이징을 되먹입니다. 도구를 하나만 고집하기보다 “이 워크로드가 격리가 급한가, 처리량이 급한가”를 먼저 묻는 것이 가장 빠른 판단법입니다.