GPU 서버 관리
ML/DL 워크로드를 위한 GPU 인프라 구축과 운영. 클라우드 GPU 선택, 드라이버 설정, 모니터링, 비용 최적화를 다룬다.
1. GPU 기초 지식
1.1 GPU vs CPU
| 항목 |
CPU |
GPU |
| 코어 수 |
8-64개 (강력함) |
수천 개 (단순함) |
| 메모리 |
시스템 RAM (TB 가능) |
VRAM (최대 80GB) |
| 적합한 작업 |
순차 처리, 복잡한 로직 |
병렬 처리, 행렬 연산 |
| ML 용도 |
전처리, 추론 (일부) |
학습, 대규모 추론 |
1.2 NVIDIA GPU 라인업 (2024)
| GPU |
VRAM |
용도 |
가격대 |
| RTX 4090 |
24GB |
개인/소규모 학습 |
~$1,600 |
| A10G |
24GB |
추론 최적화 |
클라우드 |
| L4 |
24GB |
추론 (Ada) |
클라우드 |
| A100 |
40/80GB |
학습/추론 (범용) |
~$15,000 |
| H100 |
80GB |
대규모 학습 (최신) |
~$30,000 |
| H200 |
141GB |
초대규모 LLM |
~$40,000 |
1.3 GPU 메모리 (VRAM) 요구사항
모델 크기와 VRAM 관계 (Float16 기준):
- 파라미터 수 x 2 bytes = 모델 크기
- 학습 시: 모델 + Optimizer + Gradients + Activations
-> 대략 모델 크기의 4-6배 필요
예시:
- LLaMA 7B: 14GB 모델 -> 학습 시 ~56GB+ 필요
- LLaMA 13B: 26GB 모델 -> 학습 시 ~100GB+ 필요
- LLaMA 70B: 140GB 모델 -> 멀티 GPU 필수
추론 시 (Float16):
- 7B 모델: ~14GB
- 13B 모델: ~26GB
- 70B 모델: ~140GB (8x A100 또는 2x H100)
양자화 (INT8/INT4):
- 7B 모델 INT8: ~7GB
- 7B 모델 INT4: ~4GB
2. 클라우드 GPU 인스턴스
2.1 AWS GPU 인스턴스
| 인스턴스 |
GPU |
VRAM |
vCPU |
메모리 |
비용/시간 (온디맨드) |
| g4dn.xlarge |
T4 x1 |
16GB |
4 |
16GB |
~$0.526 |
| g4dn.12xlarge |
T4 x4 |
64GB |
48 |
192GB |
~$3.912 |
| g5.xlarge |
A10G x1 |
24GB |
4 |
16GB |
~$1.006 |
| g5.48xlarge |
A10G x8 |
192GB |
192 |
768GB |
~$16.288 |
| p3.2xlarge |
V100 x1 |
16GB |
8 |
61GB |
~$3.06 |
| p3.8xlarge |
V100 x4 |
64GB |
32 |
244GB |
~$12.24 |
| p4d.24xlarge |
A100 x8 |
320GB |
96 |
1152GB |
~$32.77 |
| p5.48xlarge |
H100 x8 |
640GB |
192 |
2TB |
~$98.32 |
2.2 GCP GPU 인스턴스
| 인스턴스 |
GPU |
VRAM |
비용/시간 (온디맨드) |
| n1-standard-4 + T4 |
T4 x1 |
16GB |
~$0.35 + $0.35 |
| n1-standard-8 + V100 |
V100 x1 |
16GB |
~$0.38 + $2.48 |
| a2-highgpu-1g |
A100 x1 |
40GB |
~$3.67 |
| a2-ultragpu-8g |
A100 x8 |
640GB |
~$29.39 |
| a3-highgpu-8g |
H100 x8 |
640GB |
~$77.00 |
2.3 용도별 추천
| 용도 |
AWS |
GCP |
특징 |
| 개발/실험 |
g4dn.xlarge |
T4 |
저렴, 소규모 모델 |
| 추론 서빙 |
g5.xlarge |
L4 |
추론 최적화, 비용 효율 |
| 소규모 학습 |
g5.2xlarge |
A100 x1 |
중간 모델 학습 |
| 대규모 학습 |
p4d.24xlarge |
A100 x8 |
분산 학습 |
| LLM 학습 |
p5.48xlarge |
H100 x8 |
최고 성능, 최고 비용 |
3. GPU 환경 설정
3.1 NVIDIA 드라이버 설치 (Ubuntu)
# 1. 기존 드라이버 제거
sudo apt-get purge nvidia*
sudo apt-get autoremove
# 2. 드라이버 설치 (권장)
sudo apt-get update
sudo apt-get install -y nvidia-driver-535
# 또는 runfile로 설치
wget https://us.download.nvidia.com/tesla/535.154.05/NVIDIA-Linux-x86_64-535.154.05.run
sudo sh NVIDIA-Linux-x86_64-535.154.05.run
# 3. 재부팅 후 확인
sudo reboot
nvidia-smi
# CUDA 12.1 설치
wget https://developer.download.nvidia.com/compute/cuda/12.1.0/local_installers/cuda_12.1.0_530.30.02_linux.run
sudo sh cuda_12.1.0_530.30.02_linux.run
# 환경 변수 설정
echo 'export PATH=/usr/local/cuda-12.1/bin:$PATH' >> ~/.bashrc
echo 'export LD_LIBRARY_PATH=/usr/local/cuda-12.1/lib64:$LD_LIBRARY_PATH' >> ~/.bashrc
source ~/.bashrc
# 확인
nvcc --version
3.3 cuDNN 설치
# NVIDIA 개발자 등록 후 다운로드
# https://developer.nvidia.com/cudnn
tar -xvf cudnn-linux-x86_64-8.9.7.29_cuda12-archive.tar.xz
sudo cp cudnn-*-archive/include/cudnn*.h /usr/local/cuda/include
sudo cp -P cudnn-*-archive/lib/libcudnn* /usr/local/cuda/lib64
sudo chmod a+r /usr/local/cuda/include/cudnn*.h /usr/local/cuda/lib64/libcudnn*
3.4 PyTorch with CUDA
# CUDA 12.1용 PyTorch
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
# 확인
python -c "import torch; print(torch.cuda.is_available()); print(torch.cuda.get_device_name(0))"
3.5 Docker with GPU
# NVIDIA Container Toolkit 설치
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/libnvidia-container/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit
sudo systemctl restart docker
# GPU 컨테이너 실행
docker run --gpus all nvidia/cuda:12.1.0-base-ubuntu22.04 nvidia-smi
# 특정 GPU만 사용
docker run --gpus '"device=0,1"' ...
4. GPU 모니터링
4.1 nvidia-smi
# 기본 정보
nvidia-smi
# 지속적 모니터링 (1초 간격)
nvidia-smi -l 1
# 특정 정보만 (스크립팅용)
nvidia-smi --query-gpu=timestamp,name,pci.bus_id,driver_version,temperature.gpu,utilization.gpu,utilization.memory,memory.total,memory.free,memory.used --format=csv
# 프로세스별 GPU 사용량
nvidia-smi pmon -s um -d 1
# dmon (상세 메트릭)
nvidia-smi dmon -s pucvmet
4.2 nvidia-smi 출력 해석
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 535.154.05 Driver Version: 535.154.05 CUDA Version: 12.2 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
|===============================+======================+======================|
| 0 NVIDIA A100-SXM4... On | 00000000:00:04.0 Off | 0 |
| N/A 32C P0 52W / 400W| 1234MiB / 40960MiB | 0% Default |
+-------------------------------+----------------------+----------------------+
해석:
- Persistence-M: On = 드라이버 상주 (권장)
- Temp: GPU 온도 (80C 이상 주의)
- Perf: 성능 상태 (P0=최대, P8=절전)
- Pwr:Usage/Cap: 현재/최대 전력
- Memory-Usage: VRAM 사용량
- GPU-Util: GPU 연산 사용률 (학습 중 90%+ 권장)
4.3 DCGM (Data Center GPU Manager)
# 설치
sudo apt-get install datacenter-gpu-manager
# 서비스 시작
sudo systemctl enable nvidia-dcgm
sudo systemctl start nvidia-dcgm
# 메트릭 조회
dcgmi discovery -l
dcgmi dmon -e 100,101,140,150,155,156
# 100: GPU 사용률
# 101: 메모리 사용률
# 140: 전력 사용량
# 150: 온도
# 155: SM Clock
# 156: Memory Clock
4.4 Prometheus + Grafana
# DCGM Exporter (Kubernetes)
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: dcgm-exporter
namespace: gpu-monitoring
spec:
selector:
matchLabels:
app: dcgm-exporter
template:
metadata:
labels:
app: dcgm-exporter
spec:
containers:
- name: dcgm-exporter
image: nvcr.io/nvidia/k8s/dcgm-exporter:3.2.5-3.1.8-ubuntu22.04
ports:
- containerPort: 9400
name: metrics
securityContext:
runAsNonRoot: false
runAsUser: 0
volumeMounts:
- name: pod-gpu-resources
readOnly: true
mountPath: /var/lib/kubelet/pod-resources
volumes:
- name: pod-gpu-resources
hostPath:
path: /var/lib/kubelet/pod-resources
---
apiVersion: v1
kind: Service
metadata:
name: dcgm-exporter
namespace: gpu-monitoring
spec:
selector:
app: dcgm-exporter
ports:
- port: 9400
name: metrics
5. 멀티 GPU 학습
5.1 GPU 선택
import torch
# 사용 가능한 GPU 확인
print(f"GPU 수: {torch.cuda.device_count()}")
for i in range(torch.cuda.device_count()):
print(f"GPU {i}: {torch.cuda.get_device_name(i)}")
# 특정 GPU 사용
device = torch.device("cuda:0") # 첫 번째 GPU
device = torch.device("cuda:1") # 두 번째 GPU
# 환경 변수로 제한
import os
os.environ["CUDA_VISIBLE_DEVICES"] = "0,1" # GPU 0, 1만 사용
5.2 DataParallel (단순, 비권장)
import torch.nn as nn
model = MyModel()
if torch.cuda.device_count() > 1:
model = nn.DataParallel(model)
model = model.to("cuda")
# 단점:
# - GIL 병목
# - GPU 0에 메모리 집중
# - 통신 오버헤드
5.3 DistributedDataParallel (권장)
import torch
import torch.distributed as dist
import torch.multiprocessing as mp
from torch.nn.parallel import DistributedDataParallel as DDP
def setup(rank, world_size):
os.environ['MASTER_ADDR'] = 'localhost'
os.environ['MASTER_PORT'] = '12355'
dist.init_process_group("nccl", rank=rank, world_size=world_size)
def cleanup():
dist.destroy_process_group()
def train(rank, world_size):
setup(rank, world_size)
# 모델을 해당 GPU로 이동
model = MyModel().to(rank)
model = DDP(model, device_ids=[rank])
# 데이터 샘플러 설정
sampler = torch.utils.data.distributed.DistributedSampler(
dataset, num_replicas=world_size, rank=rank
)
dataloader = DataLoader(dataset, sampler=sampler, batch_size=32)
for epoch in range(epochs):
sampler.set_epoch(epoch) # 셔플을 위해 필요
for batch in dataloader:
# 학습 로직
pass
cleanup()
if __name__ == "__main__":
world_size = torch.cuda.device_count()
mp.spawn(train, args=(world_size,), nprocs=world_size)
5.4 torchrun 사용 (권장)
# 단일 노드, 4 GPU
torchrun --nproc_per_node=4 train.py
# 멀티 노드 (2노드, 각 4 GPU)
# Node 0:
torchrun --nnodes=2 --nproc_per_node=4 --node_rank=0 \
--master_addr=10.0.0.1 --master_port=29500 train.py
# Node 1:
torchrun --nnodes=2 --nproc_per_node=4 --node_rank=1 \
--master_addr=10.0.0.1 --master_port=29500 train.py
# train.py (torchrun용)
import torch.distributed as dist
from torch.nn.parallel import DistributedDataParallel as DDP
def main():
dist.init_process_group(backend="nccl")
local_rank = int(os.environ["LOCAL_RANK"])
torch.cuda.set_device(local_rank)
model = MyModel().to(local_rank)
model = DDP(model, device_ids=[local_rank])
# 학습 로직
dist.destroy_process_group()
if __name__ == "__main__":
main()
6. 비용 최적화
6.1 Spot/Preemptible 인스턴스
| 클라우드 |
이름 |
절감율 |
주의사항 |
| AWS |
Spot Instance |
60-90% |
2분 전 경고 후 중단 |
| GCP |
Preemptible/Spot VM |
60-91% |
24시간 후 반드시 중단 |
| Azure |
Spot VM |
최대 90% |
언제든 중단 가능 |
# 체크포인트 필수
def save_checkpoint(model, optimizer, epoch, path):
torch.save({
'epoch': epoch,
'model_state_dict': model.state_dict(),
'optimizer_state_dict': optimizer.state_dict(),
}, path)
# 주기적 저장 (매 에폭 또는 N 스텝)
if step % 1000 == 0:
save_checkpoint(model, optimizer, epoch, f"checkpoint_{step}.pt")
6.2 비용 비교 (월 기준, 24/7 기준)
AWS p4d.24xlarge (A100 x8):
- 온디맨드: $32.77 x 720h = $23,594/월
- Spot: ~$10,000/월 (가용시)
- Reserved 1년: ~$15,000/월
GCP a2-ultragpu-8g (A100 x8):
- 온디맨드: $29.39 x 720h = $21,161/월
- Spot: ~$6,000-8,000/월
- CUD 1년: ~$15,000/월
6.3 비용 절감 전략
1. 학습/추론 분리
- 학습: Spot/Preemptible 사용
- 추론: 작은 인스턴스 (g5, T4)
2. 인스턴스 타입 최적화
- 모델 크기에 맞는 VRAM 선택
- 추론 시 양자화로 작은 GPU 사용 가능
3. 오토스케일링
- 트래픽 기반 스케일링
- 야간/주말 축소
4. Reserved/Committed Use
- 지속적 워크로드는 예약 인스턴스
- 1년 약정 시 30-60% 절감
5. 혼합 전략
- 기본 용량: 예약 인스턴스
- 피크 트래픽: Spot/온디맨드
7. 트러블슈팅
7.1 일반적인 문제
| 증상 |
원인 |
해결 |
| CUDA out of memory |
VRAM 부족 |
batch size 줄임, gradient accumulation |
| GPU 사용률 낮음 |
데이터 로딩 병목 |
num_workers 증가, 데이터 전처리 |
| 학습 속도 느림 |
통신 오버헤드 |
NCCL 설정, 네트워크 확인 |
| nvidia-smi 안됨 |
드라이버 문제 |
드라이버 재설치 |
| CUDA version mismatch |
버전 불일치 |
PyTorch/CUDA 버전 맞춤 |
7.2 CUDA OOM 해결
# 1. Batch size 줄이기
batch_size = 16 # 원래 32
# 2. Gradient Accumulation
accumulation_steps = 4
for i, batch in enumerate(dataloader):
loss = model(batch) / accumulation_steps
loss.backward()
if (i + 1) % accumulation_steps == 0:
optimizer.step()
optimizer.zero_grad()
# 3. Mixed Precision Training
from torch.cuda.amp import autocast, GradScaler
scaler = GradScaler()
for batch in dataloader:
with autocast(): # FP16으로 forward
loss = model(batch)
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
optimizer.zero_grad()
# 4. Gradient Checkpointing
from torch.utils.checkpoint import checkpoint
class MyModel(nn.Module):
def forward(self, x):
x = checkpoint(self.block1, x) # 메모리 절약
x = checkpoint(self.block2, x)
return x
# 5. 명시적 메모리 정리
torch.cuda.empty_cache()
7.3 디버깅 명령어
# GPU 프로세스 확인
fuser -v /dev/nvidia*
# GPU 메모리 상세
nvidia-smi --query-compute-apps=pid,used_memory --format=csv
# CUDA 에러 디버깅
CUDA_LAUNCH_BLOCKING=1 python train.py
# NCCL 디버깅
NCCL_DEBUG=INFO python train.py
# PyTorch 메모리 디버깅
python -c "import torch; print(torch.cuda.memory_summary())"
참고 자료