멀티 테넌트 LLM 서빙 실무: NVIDIA MIG·vGPU로 GPU 자원 격리하는 방법

NVIDIA MIG와 vGPU 기반 GPU 분할 기술을 활용해 단일 고스펙 GPU 자원을 여러 부서, 서비스, 모델 엔드포인트가 안전하게 나누어 쓰는 실무형 설계 방법을 정리합니다. 단순히 하나의 GPU 위에 여러 vLLM 인스턴스를 올리는 방식은 초기에는 편해 보이지만, 장문 프롬프트 유입, KV 캐시 급증, 특정 테넌트의 버스트 트래픽이 겹치면 레이턴시 튐과 OOM 전이가 발생할 수 있습니다. 이전 리포트인 LLM 서버리스 서빙 실무: KServe·Knative 기반 콜드 스타트 단축과 GPU 비용 최적화 전략이 시간차 트래픽을 줄여 유휴 GPU 비용을 회수하는 접근이었다면, 이번 글에서는 물리 GPU 내부를 MIG 인스턴스나 vGPU 단위로 나누어 멀티 테넌트 추론 인프라의 예측 가능성과 자원 격리 수준을 높이는 방법을 다룹니다.

이 글에서 바로 확인할 수 있는 내용

  • 여러 vLLM 프로세스가 같은 GPU를 공유할 때 발생하는 noisy neighbor, KV 캐시 압박, OOM 전이 위험을 파악합니다.
  • NVIDIA MIG, vGPU, time-slicing, MPS의 차이를 실무 관점에서 구분합니다.
  • A100 80GB와 H100 80GB에서 20GB급 MIG 슬라이스를 구성할 때 주의해야 할 프로파일명을 정리합니다.
  • Kubernetes Device Plugin에서 MIG 리소스가 어떻게 노출되는지 확인하고, vLLM 파드에 안전하게 바인딩하는 방법을 설명합니다.
  • 작은 LLM 여러 개를 한 장의 GPU에 배치할 때 필요한 ResourceQuota, NodeAffinity, HPA 제한값을 설계합니다.
  • MIG 프로파일 미스매치, NCCL 병렬화 실패, 특정 테넌트 트래픽 폭증, 모니터링 누락 문제를 트러블슈팅합니다.

1. 왜 LLM 멀티 테넌트 서빙에 GPU 자원 격리가 필요할까?

LLM 추론 서버를 처음 구축할 때 가장 쉽게 떠올리는 방식은 하나의 GPU 노드 안에 여러 개의 vLLM 또는 Triton 컨테이너를 띄우고, 모두 같은 cuda:0 장치를 바라보게 하는 구조입니다. 개발 환경이나 짧은 테스트에서는 이 방식도 어느 정도 동작합니다. 하지만 부서별 챗봇, 내부 검색 RAG, 코드 어시스턴트, 문서 요약 API처럼 성격이 다른 서비스가 같은 물리 GPU를 동시에 쓰는 순간부터 운영 리스크가 커집니다.

가장 흔한 문제는 noisy neighbor 현상입니다. 특정 테넌트에서 긴 컨텍스트 질의나 대량 동시 요청이 몰리면 GPU 연산 큐와 메모리 대역폭, KV 캐시 공간이 그쪽으로 치우칩니다. 그러면 같은 GPU를 공유하던 다른 서비스는 요청량이 늘지 않았는데도 p95, p99 레이턴시가 갑자기 튀는 상황을 겪게 됩니다. 사용자는 “내 서비스는 아무것도 바뀐 게 없는데 왜 느려졌는지” 이해하기 어렵고, 운영자는 원인을 찾기 위해 여러 컨테이너 로그와 GPU 메트릭을 동시에 추적해야 합니다.

두 번째 문제는 VRAM 압박입니다. vLLM은 PagedAttention과 연속 배치 처리로 KV 캐시 메모리를 효율적으로 관리하지만, 가용 VRAM 자체가 무한해지는 것은 아닙니다. 장문 대화, 높은 동시성, 큰 max-model-len, 높은 gpu-memory-utilization 값을 함께 사용하면 특정 프로세스가 메모리 한계에 먼저 도달합니다. 이때 같은 물리 GPU를 느슨하게 공유하는 구조라면 장애 범위가 다른 테넌트까지 번질 가능성이 있습니다.

그래서 프로덕션 LLM 서빙에서는 “GPU를 함께 쓴다”와 “GPU를 안전하게 나누어 쓴다”를 구분해야 합니다. 단순 공유는 비용 절감처럼 보이지만 장애 격리와 성능 예측성이 약합니다. 반대로 MIG나 vGPU 기반 분할은 초기 설계가 조금 복잡하더라도 부서별 비용 배분, SLA 관리, 장애 범위 제한, 운영 책임 분리에 훨씬 유리합니다.

2. MIG, vGPU, time-slicing, MPS의 차이점

GPU 분할 기술을 설계할 때 MIG와 vGPU를 같은 의미로 섞어 쓰는 경우가 많습니다. 하지만 두 기술은 격리 방식과 적용 환경이 다릅니다. 여기에 Kubernetes time-slicing과 CUDA MPS까지 들어오면 더 헷갈리기 쉬운데요. 실무에서는 아래처럼 나누어 보면 판단이 쉬워집니다.

구분 핵심 방식 장점 주의점
MIG
(Multi-Instance GPU)
지원 GPU 내부를 독립 GPU 인스턴스로 공간 분할합니다. 각 인스턴스는 전용 SM 일부와 전용 메모리 영역을 사용합니다. LLM 추론처럼 장애 범위와 성능 예측성이 중요한 워크로드에 적합합니다. 프로파일 조합이 고정되어 있어 원하는 크기로 자유롭게 자를 수 없습니다. 대형 모델 병렬화에는 맞지 않을 수 있습니다.
NVIDIA vGPU 하이퍼바이저와 vGPU 소프트웨어를 통해 VM 단위로 GPU 자원을 제공합니다. time-sliced vGPU 또는 MIG-backed vGPU 구성이 가능합니다. VM 기반 인프라, VDI, 가상화된 AI 개발 환경과 잘 맞습니다. 라이선스, 하이퍼바이저 호환성, 스케줄링 정책 관리가 필요합니다.
Kubernetes time-slicing 하나의 GPU 또는 MIG 리소스를 여러 파드가 시간 단위로 번갈아 사용하도록 노출합니다. MIG를 지원하지 않는 GPU에서도 공유 밀도를 높일 수 있습니다. MIG 수준의 메모리 및 장애 격리를 기대하면 안 됩니다. 추론 SLA가 강한 서비스에는 신중히 적용해야 합니다.
CUDA MPS CUDA 프로세스 간 실행 효율을 높이고 일부 자원 사용 비율을 제어합니다. 구형 GPU에서 소프트웨어 기반 공유를 보완하는 수단이 될 수 있습니다. MIG와 같은 하드웨어 격리로 오해하면 안 됩니다. 운영 전 별도 장애 테스트가 필요합니다.

정리하면, Kubernetes 기반 LLM 추론 클러스터에서 “부서별로 일정한 성능과 장애 격리를 보장하고 싶다”면 MIG를 우선 검토하는 편이 안전합니다. 반대로 “GPU를 아주 많은 사용자에게 느슨하게 나누어 개발 테스트 용도로 제공하고 싶다”면 time-slicing 또는 vGPU 계열이 더 적합할 수 있습니다. 단, 이 경우에는 성능 지연과 장애 격리 한계를 문서화해 두어야 운영 분쟁을 줄일 수 있습니다.

3. MIG 프로파일을 잘못 고르면 왜 배포가 실패할까?

MIG 구성에서 가장 많이 발생하는 실수는 GPU 세대와 메모리 용량을 보지 않고 인터넷 예시의 프로파일명을 그대로 복사하는 것입니다. 예를 들어 A100 40GB에서 보이는 3g.20gb와 A100 80GB 또는 H100 80GB의 20GB급 프로파일은 같은 의미가 아닙니다. 같은 20GB라는 숫자가 보여도 compute slice 개수와 생성 가능한 인스턴스 수가 다를 수 있습니다.

특히 “80GB GPU 한 장을 20GB급 네 조각으로 나누겠다”는 요구가 있을 때는 무조건 3g.20gb를 넣으면 안 됩니다. 80GB 제품군에서는 GPU 세대별로 1g.20gb, 2g.20gb, 3g.40gb처럼 이름과 최대 생성 개수가 달라질 수 있습니다. 운영자는 먼저 장비에서 직접 nvidia-smi mig -lgip 명령으로 지원 프로파일 ID와 최대 생성 가능 수량을 확인해야 합니다.

실무에서 꼭 기억해야 할 기준

MIG 프로파일 ID는 GPU 세대, 드라이버, 펌웨어, 장착 형태에 따라 달라질 수 있습니다. 따라서 블로그나 문서에 나온 숫자 ID를 그대로 운영 명령어에 넣지 말고, 반드시 해당 노드에서 조회한 프로파일 ID를 사용해야 합니다. 또한 Kubernetes에서 노출되는 리소스명도 kubectl describe node 결과에 실제로 표시된 이름을 기준으로 작성해야 합니다.

4. 하드웨어 슬라이싱 및 자원 격리 클러스터 설계 구조

단일 GPU를 MIG 모드로 전환하면 물리 GPU 내부가 여러 개의 GPU Instance와 Compute Instance로 나뉩니다. 각 인스턴스는 운영체제와 컨테이너 런타임에서 독립 CUDA 장치처럼 인식될 수 있습니다. 이 구조를 Kubernetes Device Plugin과 연결하면 파드는 전체 GPU가 아니라 nvidia.com/mig-1g.20gb, nvidia.com/mig-2g.20gb, nvidia.com/mig-3g.40gb 같은 확장 리소스를 요청하게 됩니다.

중요한 점은 이 리소스명이 문서상 예시와 항상 같다고 단정하면 안 된다는 것입니다. 실제 노드에서 어떤 이름으로 노출되는지는 MIG strategy, GPU 모델, device plugin 버전, 드라이버 조합에 따라 달라질 수 있습니다. 그래서 매니페스트를 작성하기 전에는 아래 흐름을 먼저 거쳐야 합니다.

  1. GPU 노드에서 지원 MIG 프로파일을 조회합니다.
  2. 운영 목적에 맞는 프로파일 조합을 결정합니다.
  3. MIG 인스턴스를 생성한 뒤 nvidia-smi -L로 장치 목록을 확인합니다.
  4. Kubernetes Device Plugin을 mixed 또는 운영 표준에 맞는 전략으로 배포합니다.
  5. kubectl describe node에서 실제 노출된 nvidia.com/mig-* 리소스명을 확인합니다.
  6. 그 이름을 그대로 vLLM 파드의 resources.requestsresources.limits에 넣습니다.
NVIDIA MIG 기반 멀티 테넌트 LLM 서빙 구조도: 단일 GPU를 여러 하드웨어 슬라이스로 나누고 각 vLLM 파드에 독립적으로 매핑하는 아키텍처

그림 1. GPU 슬라이싱 레이아웃: 단일 물리 GPU 내부의 연산 자원과 메모리 영역을 테넌트별 하드웨어 인스턴스로 나누어 vLLM 파드에 매핑하는 구조

5. MIG 인스턴스 분할 설정 및 vLLM 파드 연동 파이프라인

아래 예시는 80GB급 MIG 지원 GPU에서 20GB급 슬라이스를 여러 개 구성하고, Kubernetes에서 vLLM 파드가 해당 슬라이스를 요청하도록 연결하는 흐름입니다. 실제 운영에서는 H100 80GB, A100 80GB, A100 40GB, A30 등 제품별 지원 프로파일이 다르므로, 반드시 장비에서 조회한 값으로 조정해야 합니다.

단계 1: GPU 사용 프로세스 중지 및 MIG 모드 활성화

MIG 모드 변경은 GPU를 사용 중인 프로세스가 있으면 실패할 수 있습니다. Kubernetes 노드라면 먼저 해당 노드를 cordon/drain하거나, 최소한 kubelet과 GPU 사용 파드를 정리한 뒤 진행하는 것이 안전합니다.

# 1) GPU를 사용하는 파드가 새로 배치되지 않도록 노드 스케줄링 차단
kubectl cordon <gpu-node-name>

# 2) 운영 정책에 따라 GPU 사용 파드를 먼저 이동 또는 종료
# 무중단 운영이 필요하면 drain 전 PDB, replica, 트래픽 라우팅 상태를 확인합니다.
kubectl drain <gpu-node-name> --ignore-daemonsets --delete-emptydir-data

# 3) 노드에서 kubelet을 잠시 중지한 뒤 MIG 모드 활성화
sudo systemctl stop kubelet
sudo nvidia-smi -i 0 -mig 1

# 4) GPU reset이 가능한 환경이면 reset 수행
# reset이 실패하면 GPU 사용 프로세스가 남아 있거나 재부팅이 필요할 수 있습니다.
sudo nvidia-smi --gpu-reset -i 0

# 5) MIG 모드 상태 확인
nvidia-smi --query-gpu=mig.mode.current --format=csv,noheader

단계 2: 지원 프로파일 조회 및 인스턴스 생성

여기서 중요한 것은 숫자 ID를 고정하지 않는 것입니다. 예를 들어 어떤 장비에서는 20GB급 프로파일이 1g.20gb로 보이고, 다른 장비나 40GB 제품에서는 3g.20gb가 보일 수 있습니다. 4개 분할을 원한다면 해당 프로파일의 최대 인스턴스 수가 4 이상인지도 함께 확인해야 합니다.

# 1) 현재 GPU가 지원하는 MIG 프로파일과 프로파일 ID 확인
sudo nvidia-smi mig -lgip

# 2) 예시: 조회 결과에서 20GB급 슬라이스 4개 생성이 가능한 프로파일 ID를 선택
# 아래 PROFILE_ID에는 문서의 예시 숫자가 아니라, 현재 노드에서 조회한 ID를 넣어야 합니다.
PROFILE_ID=<조회된_프로파일_ID>

# 3) 동일 프로파일 4개 생성 예시
sudo nvidia-smi mig -i 0 -cgi ${PROFILE_ID},${PROFILE_ID},${PROFILE_ID},${PROFILE_ID} -C

# 4) 생성된 MIG 장치 확인
nvidia-smi -L

# 5) 상세 상태 확인
nvidia-smi mig -lgi
nvidia-smi mig -lci

20GB급 슬라이스 선택 기준

20GB급 MIG 슬라이스는 7B~8B급 모델의 경량 추론, 임베딩 모델, reranker, 사내 문서 요약 API처럼 모델 하나가 전체 GPU를 독점하기에는 아깝지만 지연 시간 예측성이 필요한 워크로드에 잘 맞습니다. 다만 8B 모델을 BF16으로 올릴 경우 KV 캐시 여유가 빠듯할 수 있으므로, max-model-len, 동시성, 양자화 여부, gpu-memory-utilization 값을 반드시 스테이징에서 먼저 검증해야 합니다.

단계 3: Kubernetes Device Plugin 배포 및 리소스명 확인

Kubernetes에서 MIG 장치를 파드 리소스로 쓰려면 NVIDIA Device Plugin이 MIG를 인식해야 합니다. MIG 프로파일이 섞여 있는 노드에서는 mixed 전략을 사용하면 프로파일별 리소스명이 nvidia.com/mig-* 형태로 노출됩니다. 운영 환경에서는 GPU Operator로 MIG Manager까지 함께 관리하는 방식도 많이 사용합니다.

# NVIDIA Device Plugin Helm 저장소 추가
helm repo add nvdp https://nvidia.github.io/k8s-device-plugin
helm repo update

# MIG mixed 전략으로 Device Plugin 배포
# 운영 환경에서는 사내 검증 버전을 명시적으로 pinning하는 것이 좋습니다.
helm upgrade -i nvdp nvdp/nvidia-device-plugin \
  --namespace nvidia-device-plugin \
  --create-namespace \
  --set migStrategy=mixed

# 노드에 실제 노출된 MIG 리소스 확인
kubectl describe node <gpu-node-name> | grep -A20 -E "Capacity|Allocatable|nvidia.com/mig"

예상 출력은 환경에 따라 달라지지만, 아래처럼 실제 리소스명이 보이면 그 이름을 파드 매니페스트에 그대로 사용합니다.

Capacity:
  nvidia.com/mig-1g.20gb: 4

Allocatable:
  nvidia.com/mig-1g.20gb: 4

만약 실제 출력이 nvidia.com/mig-2g.20gb 또는 nvidia.com/mig-3g.40gb라면 YAML에서도 그 이름으로 바꾸어야 합니다. 리소스명이 한 글자라도 다르면 파드는 계속 Pending 상태에 머뭅니다.

단계 4: vLLM 파드에 MIG 슬라이스 바인딩

아래 매니페스트는 하나의 vLLM 파드가 20GB급 MIG 슬라이스 하나를 요청하는 예시입니다. nvidia.com/mig-1g.20gb 부분은 반드시 본인의 클러스터에서 확인한 실제 리소스명으로 교체해야 합니다.

apiVersion: v1
kind: Namespace
metadata:
  name: llm-multi-tenant
---
apiVersion: v1
kind: Secret
metadata:
  name: hf-token
  namespace: llm-multi-tenant
type: Opaque
stringData:
  HF_TOKEN: "<huggingface_token_if_required>"
---
apiVersion: v1
kind: Pod
metadata:
  name: dept-a-vllm-pod
  namespace: llm-multi-tenant
  labels:
    app: dept-a-vllm
spec:
  restartPolicy: Always
  containers:
  - name: vllm-engine
    image: vllm/vllm-openai:latest
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 8000
      name: http
    env:
    - name: HF_TOKEN
      valueFrom:
        secretKeyRef:
          name: hf-token
          key: HF_TOKEN
    args:
      - --host=0.0.0.0
      - --port=8000
      - --model=meta-llama/Llama-3.1-8B-Instruct
      - --max-model-len=2048
      - --gpu-memory-utilization=0.82
    resources:
      requests:
        nvidia.com/mig-1g.20gb: "1"
        cpu: "2"
        memory: "8Gi"
      limits:
        nvidia.com/mig-1g.20gb: "1"
        cpu: "4"
        memory: "16Gi"
    volumeMounts:
    - name: dshm
      mountPath: /dev/shm
  volumes:
  - name: dshm
    emptyDir:
      medium: Memory
      sizeLimit: 4Gi

20GB급 슬라이스에서 8B 모델을 운영할 때는 컨텍스트 길이와 동시성을 과하게 잡지 않는 것이 중요합니다. 모델 가중치가 올라간 뒤 남은 VRAM이 KV 캐시로 사용되므로, --max-model-len을 크게 늘리면 처리 가능한 동시 요청 수가 급격히 줄어듭니다. 처음에는 2048 또는 4096 범위에서 검증하고, 실제 요청 길이 분포를 보고 단계적으로 조정하는 편이 안전합니다.

6. 부서별 자원 독점을 막는 ResourceQuota 설계

MIG를 구성했다고 해서 멀티 테넌트 운영이 자동으로 완성되는 것은 아닙니다. 특정 부서의 HPA가 갑자기 파드를 많이 늘리면 같은 노드의 MIG 슬라이스를 모두 선점해 다른 부서 파드가 Pending 상태에 빠질 수 있습니다. 이 문제를 막으려면 네임스페이스 단위 ResourceQuota를 적용해 최대 사용량을 제한해야 합니다.

apiVersion: v1
kind: ResourceQuota
metadata:
  name: dept-a-mig-quota
  namespace: llm-multi-tenant
spec:
  hard:
    requests.nvidia.com/mig-1g.20gb: "2"
    requests.cpu: "8"
    requests.memory: "32Gi"

위 예시는 해당 네임스페이스에서 20GB급 MIG 슬라이스를 최대 2개까지만 요청할 수 있게 제한합니다. 실제 운영에서는 부서별 서비스 중요도, 월간 예산, SLA, 야간 배치 작업 여부를 기준으로 quota를 다르게 설정하는 것이 좋습니다.

또한 부서별 고정 매핑이 필요한 경우에는 NodeAffinity와 taint/toleration을 함께 사용합니다. 예를 들어 결제 도메인, 고객지원 챗봇, 내부 개발 테스트 환경이 같은 GPU 풀을 쓰더라도 중요도가 다르다면, 실시간 서비스는 안정적인 MIG 노드에 고정하고 실험성 워크로드는 time-slicing 또는 저우선순위 노드로 분리하는 편이 운영 분쟁을 줄입니다.

7. 벤치마크 테스트 환경과 검증 항목

MIG 도입 여부를 결정할 때는 “GPU 사용률이 올라갔다”만 보면 안 됩니다. 멀티 테넌트 추론에서는 평균 처리량보다 p95/p99 레이턴시, OOM 발생 범위, 특정 파드 장애 후 복구 시간, quota 초과 시 스케줄링 동작이 더 중요합니다.

권장 검증 환경 예시: MIG 지원 80GB GPU 1장 / 20GB급 MIG 슬라이스 4개 / vLLM OpenAI-compatible server / 7B~8B급 모델 또는 양자화 모델 / 테넌트별 독립 파드 / 한 개 파드에만 장문 요청과 동시성 부하를 집중 주입

검증 항목 확인 방법 통과 기준 예시
Noisy neighbor 영향 1번 파드에만 장문 요청과 높은 동시성을 주입하고, 2~4번 파드의 p95/p99 레이턴시 변화를 비교합니다. 타 파드의 지연 증가 폭이 사전 정의한 SLA 범위 안에 있어야 합니다.
OOM 격리 의도적으로 한 파드의 컨텍스트 길이나 동시성을 높여 VRAM 한계에 도달하게 한 뒤 장애 범위를 확인합니다. 문제가 발생한 파드만 재시작되고, 다른 MIG 파드의 요청 처리는 유지되어야 합니다.
스케줄링 안정성 HPA 또는 수동 scale-out으로 quota 한계까지 파드를 늘려 Pending 원인을 확인합니다. quota 초과, 리소스 부족, 노드 affinity 불일치 원인이 이벤트에 명확히 표시되어야 합니다.
모니터링 가시성 MIG 인스턴스별 VRAM 사용량, 요청 수, 토큰 처리량, TTFT, TPOT, 재시작 횟수를 수집합니다. 테넌트별 장애 원인을 파드·MIG 인스턴스·모델 버전 단위로 역추적할 수 있어야 합니다.

실제 성능 수치는 모델, 양자화 방식, 프롬프트 길이, batch 정책, 드라이버 버전, GPU 세대에 따라 달라집니다. 따라서 본문에 있는 수치를 그대로 성능 보장값으로 해석하기보다는, 어떤 항목을 측정해야 하는지에 집중하는 편이 안전합니다. 운영 보고서에는 최소한 p50, p95, p99 latency, TTFT(Time To First Token), TPOT(Time Per Output Token), tokens/sec, OOM 발생 시 영향 범위를 함께 남기는 것을 권장합니다.

멀티 테넌트 LLM 서빙 환경에서 MIG GPU 슬라이스별 VRAM 사용량, 토큰 처리량, 요청 지연 시간을 추적하는 모니터링 대시보드

그림 2. 가동 자산 관제: 분할된 MIG 인스턴스별 메모리 점유율, 요청량, 토큰 처리량, 장애 이벤트를 테넌트 단위로 추적하는 모니터링 화면

멀티 테넌트 가상화 인프라의 전반적인 누수 지표를 비동기 센싱하고 선제 알림을 송출하는 로깅 파이프라인의 조밀한 연동 체계는 AX 시스템 관측성 구축 방법 리포트의 지표 수집 규칙 가이드를 바인딩하여 보강하시면 중앙 관제망의 완성도를 높일 수 있습니다.

8. 운영 중 자주 발생하는 병목과 해결 방법

문제 1: 파드가 계속 Pending 상태에 머무는 경우

가장 먼저 확인할 것은 리소스명입니다. YAML에는 nvidia.com/mig-1g.20gb를 요청했는데, 실제 노드에는 nvidia.com/mig-2g.20gb만 노출되어 있으면 스케줄러는 해당 파드를 배치할 수 없습니다.

kubectl describe pod dept-a-vllm-pod -n llm-multi-tenant
kubectl describe node <gpu-node-name> | grep -A20 -E "nvidia.com/mig|Allocatable"

해결 방법은 단순합니다. 노드에 실제로 존재하는 리소스명을 확인한 뒤 파드 매니페스트의 requestslimits를 동일하게 수정합니다. ResourceQuota에도 같은 리소스명을 사용해야 합니다.

문제 2: 8B 모델이 20GB 슬라이스에서 자주 OOM이 나는 경우

20GB 슬라이스는 8B 모델을 항상 여유롭게 담는 공간이 아닙니다. BF16 모델 가중치, CUDA 그래프, KV 캐시, 활성 요청 수가 함께 VRAM을 사용하기 때문입니다. 이때는 아래 순서로 조정하는 편이 안전합니다.

  • --max-model-len을 2048 또는 4096 이하로 낮춰 실제 질의 길이와 맞춥니다.
  • --gpu-memory-utilization을 0.80~0.85 범위에서 시작하고, OOM 로그가 없을 때만 서서히 올립니다.
  • 동시성 제한을 API Gateway 또는 vLLM front proxy에서 설정합니다.
  • AWQ, GPTQ, FP8 등 검증된 경량화 모델을 별도 후보로 둡니다.
  • 실시간 서비스라면 20GB보다 큰 40GB급 프로파일을 사용하는 편이 더 안정적일 수 있습니다.

문제 3: MIG 인스턴스 여러 개를 묶어 큰 모델을 텐서 병렬로 돌리려는 경우

MIG는 작은 모델 복제본을 여러 테넌트에 독립 배치하는 데 강점이 있습니다. 반대로 하나의 큰 모델을 여러 MIG 인스턴스에 걸쳐 텐서 병렬로 나누는 구조는 기대한 만큼 단순하지 않습니다. NCCL 통신, 장치 간 경로, 프레임워크 지원 여부가 복합적으로 걸리기 때문입니다.

실무에서는 MIG 인스턴스 하나에 모델 복제본 하나를 올리는 TP1 구성을 기본값으로 두는 편이 안전합니다. 14B, 32B, 70B급 모델처럼 병렬화가 필요한 경우에는 MIG로 잘게 나누기보다, 분할하지 않은 전체 GPU 또는 여러 물리 GPU를 사용하는 별도 노드풀로 분리하는 것을 권장합니다.

문제 4: 특정 부서 트래픽이 갑자기 증가해 다른 서비스 배치가 막히는 경우

이 문제는 MIG 자체의 문제가 아니라 클러스터 자원 정책의 문제입니다. 해결하려면 HPA 상한, ResourceQuota, 우선순위 클래스, PodDisruptionBudget, 트래픽 게이트웨이의 동시성 제한을 함께 설계해야 합니다.

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: dept-a-vllm-hpa
  namespace: llm-multi-tenant
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: dept-a-vllm
  minReplicas: 1
  maxReplicas: 2
  metrics:
  - type: Pods
    pods:
      metric:
        name: requests_per_second
      target:
        type: AverageValue
        averageValue: "20"

HPA의 maxReplicas는 단순히 트래픽만 보고 정하면 안 됩니다. 해당 네임스페이스가 사용할 수 있는 MIG 슬라이스 수, 모델 로딩 시간, 장애 시 재시작 여유 슬롯까지 고려해야 합니다.

9. 프로덕션 배포 전 체크리스트

  • ✅ 현재 GPU 모델이 MIG를 지원하며, 원하는 프로파일 조합이 실제로 생성 가능한지 nvidia-smi mig -lgip로 확인했나요?
  • ✅ 80GB GPU에서 20GB급 슬라이스 4개를 만들 때 프로파일명을 무조건 3g.20gb로 쓰지 않고, 장비별 실제 프로파일 ID를 확인했나요?
  • ✅ Kubernetes 노드의 CapacityAllocatable에 표시된 nvidia.com/mig-* 리소스명을 파드 YAML과 ResourceQuota에 동일하게 반영했나요?
  • ✅ 20GB급 슬라이스에서 7B~8B 모델을 사용할 경우, max-model-len, 동시성, KV 캐시 사용량, 양자화 여부를 스테이징에서 먼저 검증했나요?
  • ✅ 한 테넌트 파드의 OOM 또는 재시작이 다른 테넌트의 p95/p99 레이턴시에 어떤 영향을 주는지 부하 테스트로 확인했나요?
  • ✅ 부서별 ResourceQuota, HPA maxReplicas, NodeAffinity, 우선순위 정책을 함께 설계했나요?
  • ✅ TTFT, TPOT, tokens/sec, GPU 메모리 사용량, 파드 재시작 횟수, Pending 이벤트를 테넌트별로 모니터링하나요?

10. 참고한 기술 자료


📊 멀티 테넌트 LLM 서빙 실무 Q&A

Q. 20GB급 MIG 슬라이스 하나에 8B 모델을 올려도 괜찮을까요?

A. 가능은 하지만 여유가 크다고 보면 안 됩니다. BF16 기준 8B 모델은 가중치만으로도 상당한 VRAM을 사용하고, 실제 서비스에서는 KV 캐시와 동시 요청 메모리가 추가됩니다. 그래서 20GB급 슬라이스에서는 max-model-len을 낮게 시작하고, 동시성 제한을 함께 두는 것이 안전합니다. 장문 질의가 많거나 안정적인 p99 latency가 중요하다면 40GB급 프로파일 또는 양자화 모델을 검토하는 편이 좋습니다.

Q. A100 80GB에서 20GB 슬라이스 4개를 만들 때 3g.20gb를 쓰면 되나요?

A. 그대로 쓰면 안 됩니다. 3g.20gb는 A100 40GB 계열 문서나 예시에서 자주 보이는 이름이고, 80GB 제품에서는 메모리 비율에 따라 프로파일명이 달라질 수 있습니다. 80GB GPU에서 20GB급 슬라이스를 구성하려면 먼저 nvidia-smi mig -lgip로 현재 장비가 제공하는 프로파일명을 확인해야 합니다. Kubernetes에서도 kubectl describe node에 표시된 리소스명을 기준으로 매니페스트를 작성해야 합니다.

Q. MIG와 time-slicing을 같이 쓰면 더 많은 파드를 올릴 수 있나요?

A. 일부 환경에서는 MIG 인스턴스 위에 time-slicing을 결합해 더 많은 파드 접근권을 노출할 수 있습니다. 하지만 이 경우 한 MIG 슬라이스 내부에서도 다시 시간 공유가 일어나므로 지연 시간 예측성이 낮아질 수 있습니다. 실시간 대화형 LLM 서비스라면 MIG 인스턴스 하나에 파드 하나를 기본값으로 두고, 개발 테스트나 비실시간 작업에만 time-slicing을 제한적으로 적용하는 편이 안전합니다.

Q. MIG를 지원하지 않는 V100, T4 같은 GPU에서는 어떻게 나누어 써야 하나요?

A. MIG를 지원하지 않는 GPU에서는 Kubernetes time-slicing이나 MPS 같은 소프트웨어 기반 공유 방식을 검토할 수 있습니다. 다만 이 방식은 MIG와 같은 하드웨어 수준 메모리 및 장애 격리를 제공하지 않습니다. 따라서 개발 환경, 배치성 작업, 낮은 우선순위 추론에는 사용할 수 있지만, 장애 전이가 치명적인 프로덕션 실시간 서비스에는 별도 노드 분리나 전용 GPU 할당을 우선 고려하는 것이 좋습니다.

Q. 부서별 고정 할당과 공용 풀 방식 중 어느 쪽이 비용 관리에 유리한가요?

A. 장기 운영에서는 부서별 최소 보장량을 정해 두고, 남는 슬롯만 공용 풀로 운영하는 혼합 방식이 가장 현실적입니다. 모든 자원을 공용 풀로 열어두면 특정 부서의 테스트 트래픽이 중요한 서비스의 슬롯을 밀어낼 수 있습니다. 반대로 완전히 고정 할당만 하면 야간이나 비업무 시간의 유휴 자원이 늘어납니다. 따라서 핵심 서비스는 고정 MIG 슬라이스를 보장하고, 실험성 워크로드는 낮은 우선순위 공용 풀로 보내는 구조가 비용과 안정성의 균형을 맞추기 좋습니다.

결론: 멀티 테넌트 LLM 서빙의 핵심은 GPU를 많이 쓰는 것이 아니라 안전하게 나누는 것입니다

엔터프라이즈 LLM 인프라에서 비용 최적화는 단순히 GPU 사용률을 100%에 가깝게 끌어올리는 문제가 아닙니다. 실제 운영에서는 특정 부서의 장문 요청이 다른 부서의 응답 시간을 흔들지 않아야 하고, 한 파드의 OOM이 전체 추론망 장애로 번지지 않아야 하며, 누가 어떤 GPU 자원을 얼마나 사용했는지 설명할 수 있어야 합니다.

NVIDIA MIG는 이런 요구에 맞춰 단일 고성능 GPU를 여러 독립 인스턴스로 나누는 강력한 선택지입니다. 다만 MIG를 제대로 쓰려면 GPU 세대별 프로파일 차이, Kubernetes Device Plugin의 리소스 노출 방식, vLLM의 KV 캐시 메모리 특성, ResourceQuota와 HPA 제한값까지 함께 설계해야 합니다. 특히 20GB급 슬라이스에 7B~8B 모델을 올리는 구성은 비용 효율이 좋지만, 컨텍스트 길이와 동시성 조절 없이는 안정적인 운영이 어렵습니다.

따라서 권장하는 출발점은 명확합니다. 먼저 장비에서 지원하는 MIG 프로파일을 직접 조회하고, 작은 모델 복제본을 단일 MIG 인스턴스에 하나씩 배치하며, 테넌트별 quota와 모니터링 지표를 함께 적용하십시오. 이후 실제 요청 길이와 p95/p99 레이턴시 데이터를 보며 20GB, 40GB, 전체 GPU 노드풀을 분리해 나가면, 고가의 GPU 자산을 낭비하지 않으면서도 부서 간 간섭이 적은 LLM 서빙 환경을 만들 수 있습니다.

작성자 메모: 이 리포트는 2026년 6월 기준 공개된 NVIDIA MIG, Kubernetes Device Plugin, GPU Operator, vLLM 배포 문서를 바탕으로 정리했습니다. 실제 MIG 프로파일명, 프로파일 ID, Kubernetes 리소스명은 GPU 모델, 드라이버, device plugin 버전, 운영체제, GPU Operator 사용 여부에 따라 달라질 수 있습니다. 상용 배포 전에는 반드시 스테이징 노드에서 nvidia-smi mig -lgip, nvidia-smi -L, kubectl describe node 출력값을 기준으로 매니페스트를 검증하시기 바랍니다.

디지털 아키텍트 (Digital Architect)

댓글