제6부 생성형 AI 플랫폼
6.8 NIM II — 배포·스케일
6.8.1 이 장에서 배우는 것 / 선수 지식
6.7에서 단일 NIM 컨테이너를 docker run으로 기동했다. 그러나 production은 컨테이너 하나가 아니다 — 트래픽에 따라 여러 복제본을 기동하고, 오토스케일하고, GPU를 배분하고, 무중단 업데이트하고, 메트릭으로 관측해야 한다. 이 장은 그 운영 — 쿠버네티스 배포(NIM Operator/Helm)·오토스케일·용량 계획·롤아웃 — 을 판다.
핵심 통찰: LLM 서빙의 스케일은 "거칠고 느리다". 복제 단위가 GPU 전체(또는 MIG 슬라이스)라 입자가 크고, 모델 로드(수십 GB 가중치·엔진)에 시작 시간이 길다. 그래서 웹 서버처럼 즉시 scale-out·scale-to-zero가 안 된다 — 용량 계획·예열·여유 복제본이 일반 마이크로서비스보다 중요하다. 이것이 LLM 운영을 특수하게 만든다.
배우는 것:
- 쿠버네티스 배포 — NIM Operator·Helm, GPU 자원 요청(
nvidia.com/gpu), readiness/liveness 프로브, GPU 노드 선택. - 오토스케일 — HPA(메트릭 기반)·KEDA(큐 기반), LLM 스케일의 거침·느림과 대응.
- GPU 배분 — NIM당 GPU 1개 vs MIG 분할(7.5)·다중 모델, GPU Operator(7.2).
- 용량 계획 — 복제본당 처리량(5.1·5.12)에서 목표 QPS의 복제본 수 산정.
- 롤아웃·무중단 업데이트 — rolling·카나리, 모델 버전 갱신.
- 관측·비용 — Prometheus·DCGM(7.3), GPU-시간·right-sizing·scale-to-zero의 함정.
선수 지식: 6.7(NIM 구조·메트릭·헬스), 5.12(인스턴스·동시성·용량 측정), 5.1(처리량/지연/goodput), 7.2~7.3(k8s GPU Operator·DCGM — 제7부에서 깊게), 7.5(MIG/MPS). 환경은 쿠버네티스 + GPU 노드. 이 장은 운영 실습 중심.
정직: NIM Operator·Helm 차트·HPA 메트릭 이름·배포 방식은 버전 의존이다 — 이 장은 쿠버네티스 GPU 운영의 원리·LLM 스케일 특성을 가르치고 구체 매니페스트는 한 형태를 예시("그 버전 문서로 확인"). k8s GPU 운영 일반은 7.2~7.3에서 더. 모든 수치는 환경 의존 개략값.
6.8.2 단일 컨테이너에서 production으로
쿠버네티스가 주는 것(7.2에서 깊게):
- 선언적 배포·복제: Deployment로 N개 복제본, 노드에 스케줄.
- 자가 치유·롤아웃: 죽은 파드 재시작, rolling update.
- GPU 스케줄링: device plugin(GPU Operator)으로 GPU를 자원으로 요청·할당.
- 오토스케일·서비스 디스커버리·로드밸런싱.
6.8.3 워크스루 (1) — 쿠버네티스 배포
NIM을 k8s에 배포한다(매니페스트는 개념, NIM Operator/Helm 사용 시 더 간단 — 버전 확인).
# nim-deployment.yaml (개념 — 실제는 NIM Operator/Helm 권장)
apiVersion: apps/v1
kind: Deployment
metadata: { name: my-llm-nim }
spec:
replicas: 2 # 복제본 수(용량 계획으로)
selector: { matchLabels: { app: my-llm-nim } }
template:
metadata: { labels: { app: my-llm-nim } }
spec:
containers:
- name: nim
image: <nim-image:tag>
resources:
limits:
nvidia.com/gpu: 1 # ★ GPU 1개 요청(device plugin, 7.2)
env:
- { name: NGC_API_KEY, valueFrom: { secretKeyRef: { name: ngc, key: key } } }
ports: [ { containerPort: 8000 } ]
readinessProbe: # ★ 준비될 때까지 트래픽 안 보냄(모델 로드 김!)
httpGet: { path: /v1/health/ready, port: 8000 }
initialDelaySeconds: 60 # 모델·엔진 로드 시간 고려(길게)
periodSeconds: 10
livenessProbe: # 죽으면 재시작
httpGet: { path: /v1/health/live, port: 8000 }
volumeMounts: [ { name: cache, mountPath: /opt/nim/.cache } ]
volumes: [ { name: cache, persistentVolumeClaim: { claimName: nim-cache } } ]
nodeSelector: { nvidia.com/gpu.present: "true" } # GPU 노드에
---
apiVersion: v1
kind: Service # 복제본 앞단 로드밸런서
metadata: { name: my-llm-nim }
spec:
selector: { app: my-llm-nim }
ports: [ { port: 80, targetPort: 8000 } ]
kubectl apply -f nim-deployment.yaml
kubectl get pods -l app=my-llm-nim # READY 될 때까지(모델 로드)
kubectl logs <pod> # 엔진 프로파일·로드 확인(6.7)
핵심 포인트:
nvidia.com/gpu: 1: GPU를 자원으로 요청 — GPU Operator의 device plugin(7.2)이 할당. GPU가 없는 노드엔 스케줄 안 됨.- readiness 프로브의
initialDelaySeconds를 길게: 모델·엔진 로드가 수십 초~분 → 그동안 트래픽을 보내면 안 됨. 짧으면 준비 전 트래픽으로 실패. - 캐시 PVC: 엔진 캐시(6.7)를 영속 볼륨에 → 재시작·재스케줄 시 빌드 회피.
- GPU 노드 선택: nodeSelector/taint로 GPU 노드에만.
6.8.4 오토스케일 — LLM은 거칠고 느리다
부하에 따라 복제본을 늘리고 줄인다. 그러나 LLM은 일반 웹 서비스와 다르다:
LLM 오토스케일의 특수성:
| 특수성 | 설명 |
|---|---|
| 입자가 거침 | 복제 단위 = GPU 전체(또는 MIG 슬라이스) → 1→2가 +100% 자원 |
| 시작이 느림 | 모델·엔진 로드 수십 초~분 → 스케일아웃이 트래픽 급증을 못 따라감 |
| 비용 큼 | GPU는 고가라 over-provision 비용 큼 |
→ "트래픽 보고 즉시 기동"이 안 됨 → 예열·여유·예측적 스케일이 중요하다.
HPA(Horizontal Pod Autoscaler). 메트릭 기반으로 복제본 조정:
# hpa.yaml — 커스텀 메트릭(예: GPU util·큐 깊이·동시 요청)으로 스케일
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata: { name: my-llm-nim }
spec:
scaleTargetRef: { apiVersion: apps/v1, kind: Deployment, name: my-llm-nim }
minReplicas: 2 # 최소(콜드 스타트 회피 위해 0 아님 권장)
maxReplicas: 10
metrics:
- type: Pods
pods:
metric: { name: nim_queue_depth } # NIM/Triton 메트릭(5.14·7.3) — 큐 깊이
target: { type: AverageValue, averageValue: "5" }
behavior: # 스케일 속도 제어(플래핑 방지)
scaleUp: { stabilizationWindowSeconds: 60 }
scaleDown: { stabilizationWindowSeconds: 300 } # 천천히 줄임(콜드 스타트 비쌈)
- 메트릭 선택: CPU·메모리가 아니라 GPU 활용률·큐 깊이·동시 요청·지연(NIM/Triton/DCGM 메트릭, 5.14·7.3). 큐 깊이·p99가 SLA에 직결.
- KEDA: 큐(요청 큐·메시지 큐) 기반 스케일에 유용 — 이벤트 기반.
- scaleDown 천천히: 줄였다 다시 기동하면 콜드 스타트 비용 → stabilization window를 길게.
- minReplicas ≥ 1(보통 ≥2): scale-to-zero는 모델 로드 지연 때문에 첫 요청이 매우 느려짐 → 콜드 스타트 트레이드오프(6.8.7).
핵심: LLM 오토스케일은 "예측·예열·여유" 가 일반 서비스보다 중요하다. 스케일아웃이 느리니 버스트에 대비한 여유 복제본, 트래픽 패턴 예측적 스케일(시간대별), 천천히 줄이기로 콜드 스타트를 피한다.
6.8.5 GPU 배분 — 1 NIM/GPU vs MIG·다중 모델
GPU를 어떻게 나눠 쓰나(7.5에서 깊게):
모델 크기·격리·테넌시로 선택한다. GPU Operator가 device plugin을 제공한다(7.2).
| 방식 | 누구에게 | 비고 |
|---|---|---|
| 1 NIM/GPU(또는 TP) | 큰 모델·고처리량 | 단순·격리, GPU 낭비 가능(작은 모델) |
| MIG 분할 | 작은 모델 다수·멀티테넌트 | 하드 격리·예측, 분할 입자 고정(7.5) |
| 다중 모델/GPU | 작은 모델 공존·활용↑ | 격리 약(MPS), 간섭 주의 |
핵심: 모델이 GPU를 다 못 채우면 MIG로 분할해 여러 NIM을 격리해 기동하면 활용·비용이 좋아진다(7.5). 큰 모델은 1 GPU(또는 다중 GPU TP). multi-LoRA(6.4·6.7)면 한 NIM이 여러 어댑터를 서빙해 GPU를 더 효율적으로.
6.8.6 워크스루 (2) — 용량 계획
목표 QPS·SLA에서 복제본 수를 산정한다(5.1·5.12의 측정 기반).
- goodput으로 계산(5.1): 단순 처리량이 아니라 SLA 만족 처리량. p99를 깨는 처리량은 못 쓴다.
- 여유(headroom): 버스트·장애(파드 종료)·스케일 지연을 위해 여유 복제본. LLM은 스케일이 느려 여유가 더 필요.
- 시퀀스 분포 반영: 긴 문맥·긴 생성이면 복제본당 처리량↓ → 더 많은 복제본(5.7 KV).
견해: measure-then-provision — 5.12·5.1처럼 복제본당 goodput을 실측하고, 목표 QPS·여유로 복제본 수를 정한다. 추측으로 과/소 프로비전하면 비용 낭비나 SLA 위반. 시간대별 트래픽이 크면 예측적 스케일(HPA + 스케줄)로 피크에 대비.
6.8.7 롤아웃·관측·비용
무중단 업데이트(rolling/카나리). 모델·NIM 버전을 바꿀 때 다운타임 없이:
| 방식 | 동작·효과 |
|---|---|
| rolling update | 새 복제본을 하나씩 기동해(readiness 통과 후) 옛것 종료 → 항상 가용 복제본 유지. maxSurge/maxUnavailable로 속도 제어. |
| 카나리 | 새 버전을 소수 복제본·소수 트래픽에 → 메트릭·품질 확인 후 확대 → 모델 회귀(6.5·7.7)·성능 저하를 조기 발견. LLM은 출력 품질 평가 병행. |
관측(5.14·7.3). Prometheus 메트릭(NIM/Triton: 처리량·큐 vs 컴퓨트 시간·p99·실패율)·DCGM(GPU util·온도·전력·메모리)·Grafana 대시보드·알림. readiness/liveness로 자가 치유.
비용(GPU-시간). GPU는 고가라 비용 통제가 핵심:
| 비용 통제 | 설명 |
|---|---|
| right-sizing | 복제본당 GPU·MIG로 모델에 맞는 자원(과대 금지) |
| scale-down(천천히)·시간대별 스케일 | 유휴 GPU↓ |
| scale-to-zero의 함정 | 모델 로드(수십초~분)로 첫 요청 매우 느림 → 트래픽 0인 시간이 길고 콜드 스타트가 허용되면 고려, 아니면 minReplicas≥1 |
| multi-LoRA(6.4) | 여러 작업을 한 GPU에 → GPU 수↓ |
| MIG | 작은 모델 격리 공존 → 활용률↑ |
견해: LLM은 콜드 스타트 비용이 크다 scale-to-zero가 일반 서비스만큼 쉽지 않다 — 모델 로드 지연을 감수할 수 있는(배치·저빈도) 경우만. 대화형은 minReplicas로 항상 예열. 비용은 right-sizing(MIG·multi-LoRA)·천천히 줄이기·예측 스케일로 통제하되, SLA(p99)를 깨면서까지 줄이지 않는다(goodput).
6.8.8 중간 점검 (체크포인트)
- [ ] NIM을 k8s에 배포할 때 GPU 자원 요청(
nvidia.com/gpu)·readiness 프로브(긴 initialDelay)·캐시 PVC·GPU 노드 선택의 필요를 안다. - [ ] LLM 오토스케일이 거칠고 느린(GPU 단위·모델 로드) 이유와, HPA 메트릭(GPU util·큐 깊이)·천천히 줄이기·여유 복제본을 안다.
- [ ] GPU 배분(1 NIM/GPU·MIG·다중 모델)을 모델 크기·격리로 선택한다(7.5).
- [ ] goodput 기반 용량 계획(복제본당 처리량 × 목표 QPS + 여유)을 한다.
- [ ] rolling/카나리 롤아웃과 관측(메트릭·DCGM)·비용(right-sizing·scale-to-zero 함정)을 안다.
증상→의심: 준비 전 트래픽으로 초기 실패 → readiness initialDelay 짧음 → 모델 로드 시간만큼 길게. 버스트에 스케일 못 따라감 → 스케일아웃 느림(모델 로드) → 여유 복제본·예측 스케일·예열. 첫 요청 매우 느림 → scale-to-zero 콜드 스타트 → minReplicas≥1. 작은 모델이 GPU 낭비 → 1 NIM/GPU 과대 → MIG·multi-LoRA. 비용 폭증 → over-provision·유휴 GPU → right-sizing·천천히 줄이기. 업데이트 시 다운타임 → rolling/카나리 미설정. 스케일 플래핑 → stabilization window 짧음.
6.8.9 트레이드오프·대안
배포·스케일 운영 선택.
| 결정 | 트레이드오프 |
|---|---|
| minReplicas≥1 vs scale-to-zero | 항상 예열(비용↑·콜드 스타트 0) vs 유휴 0(콜드 스타트 느림) |
| 여유 복제본 많음/적음 | 버스트 대응↑·비용↑ vs 비용↓·SLA 위반 위험 |
| 1 NIM/GPU vs MIG | 단순·격리·낭비 vs 활용↑·분할 입자 고정(7.5) |
| rolling vs 카나리 | 단순·빠름 vs 안전·느림(품질 확인) |
| HPA(반응) vs 예측 스케일 | 단순 vs 버스트·콜드 스타트 대응 |
견해: 대화형 production은 minReplicas로 예열 + 여유 복제본 + 천천히 줄이기가 안전하다(콜드 스타트·SLA). 배치·저빈도는 scale-to-zero·공격적 축소로 비용↓. 작은 모델·멀티테넌트는 MIG로 GPU 활용. 모델 업데이트는 카나리로 품질 회귀(6.5·7.7)를 조기 발견. 모든 결정은 goodput·비용을 측정해 균형.
비엔비디아 정직: k8s 배포·HPA·롤아웃은 쿠버네티스 표준이고 NIM은 그 위에 실행되는 컨테이너다 — KServe·Ray Serve·자체 Deployment로도 같은 원리. NIM Operator·NIM family·GPU Operator(7.2)·DCGM(7.3)의 통합이 엔비디아 스택의 편의다. LLM 스케일의 특수성(거침·느림·콜드 스타트)은 도구와 무관한 본질 — 그것을 이해하는 것이 핵심.
6.8.10 흔한 함정
- readiness 프로브 너무 짧음. 모델 로드 전에 트래픽이 와 초기 요청 실패·롤아웃 깨짐. initialDelay를 로드 시간만큼.
- scale-to-zero 무지성 적용. 콜드 스타트(모델 로드 수십초~분)로 첫 요청 매우 느림 → 대화형 부적합. minReplicas≥1.
- 스케일아웃이 버스트를 따라간다고 가정. LLM은 시작이 느려 급증을 못 따라감 → 여유 복제본·예측 스케일·예열.
- CPU/메모리로 오토스케일. LLM은 GPU 바운드라 CPU 메트릭이 부하를 반영 못 함 → GPU util·큐 깊이·동시 요청(5.14·7.3).
- 캐시 볼륨 없음. 재시작·재스케줄마다 엔진 빌드로 느림 → 캐시 PVC.
- goodput 무시한 용량 계획. 단순 처리량으로 계산하면 p99 위반 → SLA 제약 goodput으로(5.1).
- 천천히 줄이기 안 함. 트래픽 일시 감소에 축소했다 다시 콜드 스타트 → scaleDown stabilization 길게.
- GPU 낭비(작은 모델 1/GPU). MIG·multi-LoRA로 활용. 큰 모델은 1/GPU(또는 TP).
- 업데이트 품질 미검증. 모델 버전 갱신 시 회귀(6.5) 확인 없이 → 카나리·평가.
6.8.11 연습문제
연습 1 (readiness). NIM Deployment를 올렸는데 롤아웃 직후 요청이 대량 실패하다 안정됐다. readiness 관점에서 원인과 해결을 설명하라.
정답
원인: readiness 프로브의 initialDelay가 모델 로드 시간보다 짧음 — NIM은 시작 시 모델 가중치·엔진을 로드하는 데 수십 초~분이 걸리는데(6.7), 그 전에 파드가 Ready로 표시되어 Service가 트래픽을 보냄 → 아직 준비 안 된 파드가 요청을 받아 실패. 로드 완료 후 안정. 해결: ① readiness 프로브의 initialDelaySeconds를 모델 로드 시간에 맞게 충분히 길게(또는 /v1/health/ready가 실제 로드 완료를 반영하도록). ② 엔진 캐시 PVC로 로드 시간 단축. ③ rolling update의 maxUnavailable을 보수적으로(준비된 복제본 유지). 핵심: LLM은 시작이 느려 readiness가 "정말 준비됐는지"를 정확히 반영해야 트래픽이 준비 전 파드로 안 간다.
연습 2 (오토스케일 특수성). 웹 API팀이 "트래픽 2배면 HPA가 즉시 복제본 2배로 기동하니 걱정 없다"고 한다. LLM NIM에서 이 가정이 왜 위험한지 셋을 제시하라.
정답
① 시작이 느림: LLM 복제본은 모델·엔진 로드(수십초~분)로 즉시 안 뜸 → 트래픽 급증 시 새 복제본이 준비되기 전 기존 복제본이 과부하·SLA 위반. 웹 서버(초 단위 시작)와 다름. ② 입자가 거침·비쌈: 복제 단위가 GPU 전체라 1→2가 +100% 비용이 드는 자원 → 반응적 스케일이 과/소 프로비전·비용 변동 큼. ③ 콜드 스타트: scale-to-zero/축소 후 재기동이 느려 첫 요청 지연 폭증. 대응: 여유 복제본(버스트 흡수)·예측적 스케일(시간대별 미리)·예열(minReplicas)·천천히 줄이기(scaleDown window 길게). LLM 스케일은 "반응"이 아니라 "예측·여유"가 핵심.
연습 3 (용량 계획). 한 NIM 복제본이 p99 SLA 하 goodput 25 req/s다. 피크 150 req/s를 SLA 안에서 처리하려면? (a) 복제본 수, (b) 왜 여유를 더하나, (c) 긴 문맥 요청이 늘면?
정답
(a) 기본 = ceil(150/25) = 6 복제본 + 여유 → 예 8 복제본. (b) 여유 이유: ① 버스트 — 피크보다 순간 더 몰릴 수 있음, ② 장애 — 파드가 죽으면 남은 복제본이 부담(N-1 가용성), ③ 스케일 지연 — LLM 스케일아웃이 느려 급증을 즉시 못 따라가니 미리 여유. (c) 긴 문맥이 늘면: KV 캐시가 커져(5.7) 복제본당 동시 요청·goodput이 떨어짐(예 25→15 req/s) → 같은 150 req/s에 더 많은 복제본 필요(ceil(150/15)=10+여유). 시퀀스 분포가 처리량을 좌우하므로 실제 분포로 goodput을 재측정해 복제본 수를 갱신. 핵심: goodput(SLA 만족) 기반 + 여유 + 분포 반영.
연습 4 (GPU 배분). 7B 모델(GPU 메모리의 절반만 씀)을 멀티테넌트로 여러 부서에 서빙한다. 1 NIM/GPU가 GPU를 낭비한다. 어떤 배분이 나으며 트레이드오프는?
정답
7B가 GPU를 다 못 채우므로 MIG 분할(4.10·7.5)로 한 GPU를 여러 슬라이스로 나눠 부서별 NIM을 격리해 공존 → GPU 활용·비용↑, 테넌트 간 하드 격리(성능 예측·간섭 차단). k8s가 MIG 슬라이스를 자원으로 스케줄(GPU Operator, 7.2). 트레이드오프: MIG는 분할 입자가 고정(예 1g/2g/3g)이라 유연성↓, 슬라이스가 모델·부하에 맞아야. 대안: multi-LoRA NIM(6.4·6.7) — 부서별 차이가 LoRA로 표현되면 한 베이스 NIM에 여러 어댑터를 적재해 한 GPU로 여러 맞춤 모델(베이스 공유로 더 효율). 격리가 덜 필요하면 multi-LoRA, 강한 격리·다른 베이스 모델이면 MIG. 둘 다 1 NIM/GPU 낭비를 푼다.
연습 5 (콜드 스타트·비용). 비용을 줄이려 야간 트래픽 0 시간에 scale-to-zero를 켰더니 아침 첫 사용자들이 매우 느린 응답을 받는다. (a) 원인, (b) 트레이드오프, (c) 대안.
정답
(a) 콜드 스타트 — scale-to-zero로 복제본이 0이 되면, 아침 첫 요청 때 파드를 새로 기동하며 모델·엔진 로드(수십초~분) 가 일어나 그 요청들이 매우 느림(또는 타임아웃). LLM은 모델이 커서 시작이 느린 것이 본질. (b) 트레이드오프: scale-to-zero는 유휴 GPU 비용 0(야간 절약) vs 콜드 스타트 지연(첫 사용자 경험 악화). (c) 대안: ① minReplicas≥1로 항상 1개 예열(콜드 스타트 0, 야간 1 GPU 비용은 감수) — 대화형 권장. ② 예측적 스케일 — 아침 트래픽 시작 전 미리 스케일아웃(스케줄 기반). ③ scale-to-zero는 콜드 스타트가 허용되는(배치·내부·저빈도) 워크로드에만. ④ 야간엔 작은/저비용 GPU·MIG로 다운사이즈(0이 아니라 최소). 핵심: 대화형은 예열, 비용은 right-sizing·예측 스케일로 — SLA를 깨면서까지 0으로 가지 않는다.
6.8.12 요약 · 다음 장
- production NIM은 단일 컨테이너가 아니라 쿠버네티스 배포(복제·롤아웃·자가 치유)다 — GPU 자원 요청·긴 readiness·캐시 PVC·GPU 노드 선택이 기본.
- LLM 오토스케일은 거칠고 느리다(GPU 단위·모델 로드) — HPA를 GPU util·큐 깊이로 실행하되 여유 복제본·예측 스케일·예열·천천히 줄이기로 콜드 스타트·버스트에 대응. CPU 메트릭은 부적합.
- GPU 배분: 큰 모델 1 NIM/GPU(또는 TP), 작은 모델·멀티테넌트는 MIG(격리)·multi-LoRA(6.4)로 활용↑(7.5).
- 용량 계획은 goodput(SLA 만족 처리량) × 목표 QPS + 여유, 시퀀스 분포 반영. 롤아웃은 카나리로 품질 회귀 확인, 관측은 메트릭·DCGM(7.3).
- 비용은 right-sizing·천천히 줄이기·예측 스케일로 통제하되, scale-to-zero는 콜드 스타트가 허용될 때만(대화형은 예열). SLA(p99)를 깨면서 줄이지 않는다.
- 정직: NIM Operator·HPA 메트릭·매니페스트는 버전 의존, k8s GPU 운영 일반은 7.2~7.3, LLM 스케일 특수성은 도구 무관.
다음 장: 6.9 RAG I — Retriever·임베딩·벡터DB. 배포(④)를 봤으니 라이프사이클 ⑤(지식 연결)로 간다 — 6.1에서 "지식은 RAG로"라 했던 그 RAG의 검색 구성요소(임베딩·벡터DB·리트리버)를 NeMo Retriever·임베딩 NIM(6.7)과 함께 본다. 파인튜닝 없이 최신·사내 지식을 모델에 연결하는 길이다.